I met very strange problem. I have Timetable model and try to write my custom validation. So, now i’m just trying to add test error for field to make sure that everything is ok. But it doesn’t work. So, i try to update object of Timetable model but when i don’t use my test custom validation everything works perfect. Otherwise i get such error:
NoMethodError in Timetables#update
undefined method `map' for nil:NilClass
32:
33: <div class="controls">
34: <%= f.select( :curriculum_id,
35: options_for_select( @subjects_with_curriculums,
36: @tt.curriculum_id ),
37: { :include_blank => true }) %>
38: </div>
Here is my model:
# == Schema Information
#
# Table name: timetables
#
# id :integer not null, primary key
# curriculum_id :integer
# school_class_id :integer
# tt_day_of_week :string(255)
# tt_number_of_lesson :integer
# tt_room :string(255)
# tt_type :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class Timetable < ActiveRecord::Base
belongs_to :curriculum
belongs_to :school_class
has_many :lessons
validates :school_class_id, :presence => { :message => "should exist" }
validates :tt_day_of_week,
:presence => true,
:inclusion => { :in => %w(Mon Tue Wed Thu Fri) }
validates :tt_number_of_lesson,
:presence => true,
:inclusion => {
:in => 1..9,
:message => "should have 1..9 symbols"
}
validates :tt_room,
:length => {
:maximum => 3,
:message => "should have 3 symbols"
},
:allow_blank => true
validates :tt_type,
:inclusion => { :in => ["Primary lesson", "Extra"] },
:allow_blank => true
validate :test
def test
errors.add(:tt_number_of_lesson, "test")
end
end
My controller:
# encoding: UTF-8
class TimetablesController < ApplicationController
...
def edit
@types_of_lesson = collect_types_of_lesson
@tt = Timetable.find( params[:id] )
@subjects_with_curriculums = collect_subjects_with_curriculums( @tt.school_class )
end
def update
@tt = Timetable.find( params[:id] )
if @tt.update_attributes( params[:timetable] )
flash[:success] = "Расписание успешно обновлено!"
redirect_to timetables_path
else
flash.now[:error] = @tt.errors.full_messages.to_sentence :last_word_connector => ", ",
:two_words_connector => ", "
render 'edit'
end
end
private
# Collecting subjects names for school class and curriculum_id for each subject.
def collect_subjects_with_curriculums( school_class )
subjects = school_class.curriculums.collect do |c|
[ c.qualification.subject.subject_name, c.id ]
end
end
def timetable_for_class_with_existance_data( school_class )
return [] if Timetable.all.empty?
Timetable.select do |t|
( t.school_class.class_code == school_class.class_code ) and
not ( t.tt_room.blank? ) and not ( t.tt_type.blank? ) and
not ( t.curriculum_id.nil? )
end.to_a
end
# Return for school class it's timetable.
def timetable_for_class( school_class )
Timetable.select{|t| t.school_class.class_code == school_class.class_code }.to_a
end
def subjects_of_class( school_class )
subjects = school_class.curriculums.collect do |c|
c.qualification.subject.subject_name
end
end
# Return sorted by number of lesson tometable for one day.
def sorted_timetable_for_day( timetable, day )
timetable.select{ |t| t.tt_day_of_week == day }
.sort_by{ |e| e[:tt_number_of_lesson] }
end
# Return russian name for type of lesson.
def collect_types_of_lesson
[ ["Обязательно занятие", "Primary lesson"], ["Электив", "Extra"] ]
end
# Check if timetable already has been created for school class.
def timetable_exists?( school_class )
not timetable_for_class( school_class ).empty?
end
end
My view
<%= form_for @tt, :html => {:class => "form-horizontal"} do |f| %>
<%= field_set_tag do %>
<%= f.hidden_field :tt_number_of_lesson %>
<%= f.hidden_field :tt_day_of_week %>
<%= f.hidden_field :school_class_id %>
<div class="control-group">
<%= f.label :tt_day_of_week, "Day of the week", :class => "control-label" %>
<div class="controls">
<%= content_tag( :span, translate_day_of_week( @tt.tt_day_of_week ),
:class =>"input-xlarge uneditable-input span2" ) %>
</div>
</div>
<div class="control-group">
<%= f.label :tt_number_of_lesson, "Number of lesson", :class => "control-label" %>
<div class="controls">
<%= content_tag( :span, @tt.tt_number_of_lesson,
:class =>"input-xlarge uneditable-input span1" ) %>
</div>
</div>
<hr/>
<div class="control-group">
<%= f.label :curriculum_id, "Type of subject", :class => "control-label" %>
<div class="controls">
<%= f.select( :curriculum_id,
options_for_select( @subjects_with_curriculums,
@tt.curriculum_id ),
{ :include_blank => true }) %>
</div>
</div>
<div class="control-group">
<%= f.label :tt_room, "Code of the room", :class => "control-label" %>
<div class="controls">
<%= f.text_field :tt_room, :class => "span2", :maxlength => 3 %>
</div>
</div>
<div class="control-group">
<%= f.label :tt_type, "Type of the lesson", :class => "control-label" %>
<div class="controls">
<%= f.select( :tt_type,
options_for_select( @types_of_lesson,
@tt.tt_type ),
{ :include_blank => true }) %>
</div>
</div>
<%= f.submit "Update", :class => "btn btn-large btn-warning" %>
<% end %>
<% end %>
When i remove:
<div class="control-group">
<%= f.label :curriculum_id, "Type of subject", :class => "control-label" %>
<div class="controls">
<%= f.select( :curriculum_id,
options_for_select( @subjects_with_curriculums,
@tt.curriculum_id ),
{ :include_blank => true }) %>
</div>
</div>
<div class="control-group">
<%= f.label :tt_type, "Type of the lesson", :class => "control-label" %>
<div class="controls">
<%= f.select( :tt_type,
options_for_select( @types_of_lesson,
@tt.tt_type ),
{ :include_blank => true }) %>
</div>
</div>
I can view my test error. I can’t figure what is going on.
You say this in your
updatecontroller:That simply tells Rails to render the edit template, it doesn’t run any of the code associated with the
editcontroller, it simply rendersedit.html.erbin the current context.From the Layouts and Rendering in Rails Guide:
Pay particular attention to the last sentence:
You’re setting
@subjects_with_curriculumsin theeditcontroller but using theeditview from theupdatecontroller. The result is that@subjects_with_curriculumsisnilwhenedit.html.erbtries to use it whenupdatetries to render the edit view.You’ll need to set
@subjects_with_curriculumsin yourupdatecontroller, you might need@types_of_lessonas well.