diff --git a/app/models/recurring_todo.rb b/app/models/recurring_todo.rb index 1a87c305..25eb512a 100644 --- a/app/models/recurring_todo.rb +++ b/app/models/recurring_todo.rb @@ -29,86 +29,31 @@ class RecurringTodo < ActiveRecord::Base validates_length_of :description, :maximum => 100 validates_length_of :notes, :maximum => 60000, :allow_nil => true - validate :period_specific_validations - validate :starts_and_ends_on_validations - validate :set_recurrence_on_validations + validate :period_validation + validate :pattern_specific_validations - def period_specific_validations - if %W[daily weekly monthly yearly].include?(recurring_period) - self.send("validate_#{recurring_period}") + def pattern_specific_validations + if pattern + pattern.validate else - errors.add(:recurring_period, "is an unknown recurrence pattern: '#{recurring_period}'") + errors[:recurring_todo] << "Invalid recurrence period '#{recurring_period}'" end end - def validate_daily - if (!only_work_days) && daily_every_x_days.blank? - errors[:base] << "Every other nth day may not be empty for recurrence setting" + def pattern + if valid_period? + @pattern = eval("RecurringTodos::#{recurring_period.capitalize}RepeatPattern.new(user)") + @pattern.build_from_recurring_todo(self) end + @pattern end - def validate_weekly - if weekly_every_x_week.blank? - errors[:base] << "Every other nth week may not be empty for recurrence setting" - end - something_set = false - %w{sunday monday tuesday wednesday thursday friday saturday}.each { |day| something_set ||= self.send("on_#{day}") } - errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set + def valid_period? + %W[daily weekly monthly yearly].include?(recurring_period) end - def validate_monthly - case recurrence_selector - when 0 # 'monthly_every_x_day' - errors[:base] << "The day of the month may not be empty for recurrence setting" if monthly_every_x_day.blank? - errors[:base] << "Every other nth month may not be empty for recurrence setting" if monthly_every_x_month.blank? - when 1 # 'monthly_every_xth_day' - errors[:base] <<"Every other nth month may not be empty for recurrence setting" if monthly_every_x_month2.blank? - errors[:base] <<"The nth day of the month may not be empty for recurrence setting" if monthly_every_xth_day.blank? - errors[:base] <<"The day of the month may not be empty for recurrence setting" if monthly_day_of_week.blank? - else - raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'" - end - end - - def validate_yearly - case recurrence_selector - when 0 # 'yearly_every_x_day' - errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year.blank? - errors[:base] << "The day of the month may not be empty for recurrence setting" if yearly_every_x_day.blank? - when 1 # 'yearly_every_xth_day' - errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year2.blank? - errors[:base] << "The nth day of the month may not be empty for recurrence setting" if yearly_every_xth_day.blank? - errors[:base] << "The day of the week may not be empty for recurrence setting" if yearly_day_of_week.blank? - else - raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'" - end - end - - def starts_and_ends_on_validations - errors[:base] << "The start date needs to be filled in" if start_from.blank? - case ends_on - when 'ends_on_number_of_times' - errors[:base] << "The number of recurrences needs to be filled in for 'Ends on'" if number_of_occurences.blank? - when "ends_on_end_date" - errors[:base] << "The end date needs to be filled in for 'Ends on'" if end_date.blank? - else - errors[:base] << "The end of the recurrence is not selected" unless ends_on == "no_end_date" - end - end - - def set_recurrence_on_validations - # show always or x days before due date. x not null - case target - when 'show_from_date' - # no validations - when 'due_date' - errors[:base] << "Please select when to show the action" if show_always.nil? - unless show_always - errors[:base] << "Please fill in the number of days to show the todo before the due date" if show_from_delta.blank? - end - else - raise Exception.new, "unexpected value of recurrence target selector '#{target}'" - end + def period_validation + errors.add(:recurring_period, "is an unknown recurrence pattern: '#{recurring_period}'") unless valid_period? end # the following recurrence patterns can be stored: @@ -627,7 +572,7 @@ class RecurringTodo < ActiveRecord::Base def remove_from_project! self.project = nil self.save - end + end def clear_todos_association unless todos.nil? diff --git a/app/models/recurring_todos/abstract_repeat_pattern.rb b/app/models/recurring_todos/abstract_repeat_pattern.rb index 028d9ebf..5c7ac546 100644 --- a/app/models/recurring_todos/abstract_repeat_pattern.rb +++ b/app/models/recurring_todos/abstract_repeat_pattern.rb @@ -8,6 +8,30 @@ module RecurringTodos @user = user end + def start_from + get :start_from + end + + def end_date + get :end_date + end + + def ends_on + get :ends_on + end + + def target + get :target + end + + def show_always + get :show_always + end + + def show_from_delta + get :show_from_delta + end + def build_recurring_todo(attributes) @recurring_todo = @user.recurring_todos.build(attributes) end @@ -16,7 +40,53 @@ module RecurringTodos recurring_todo.assign_attributes(attributes) recurring_todo end - + + def build_from_recurring_todo(recurring_todo) + @recurring_todo = recurring_todo + @attributes = recurring_todo.attributes + end + + def validate + starts_and_ends_on_validations + set_recurrence_on_validations + end + + def starts_and_ends_on_validations + errors[:base] << "The start date needs to be filled in" if start_from.blank? + case ends_on + when 'ends_on_number_of_times' + errors[:base] << "The number of recurrences needs to be filled in for 'Ends on'" if number_of_occurences.blank? + when "ends_on_end_date" + errors[:base] << "The end date needs to be filled in for 'Ends on'" if end_date.blank? + else + errors[:base] << "The end of the recurrence is not selected" unless ends_on == "no_end_date" + end + end + + def set_recurrence_on_validations + # show always or x days before due date. x not null + case target + when 'show_from_date' + # no validations + when 'due_date' + errors[:base] << "Please select when to show the action" if show_always.nil? + unless show_always + errors[:base] << "Please fill in the number of days to show the todo before the due date" if show_from_delta.blank? + end + else + raise Exception.new, "unexpected value of recurrence target selector '#{target}'" + end + end + + def errors + @recurring_todo.errors + end + + def get attribute + # handle attribute as symbol and as string + @attributes[attribute] || @attributes[attribute.to_s] + end + end end \ No newline at end of file diff --git a/app/models/recurring_todos/daily_repeat_pattern.rb b/app/models/recurring_todos/daily_repeat_pattern.rb index 6b742018..87f14350 100644 --- a/app/models/recurring_todos/daily_repeat_pattern.rb +++ b/app/models/recurring_todos/daily_repeat_pattern.rb @@ -7,9 +7,19 @@ module RecurringTodos end def every_x_days - @recurring_todo.every_other1 + get :every_other1 end + def only_work_days? + get :only_work_days + end + + def validate + super + errors[:base] << "Every other nth day may not be empty for this daily recurrence setting" if (!only_work_days?) && every_x_days.blank? + end + + end end \ No newline at end of file diff --git a/app/models/recurring_todos/monthly_repeat_pattern.rb b/app/models/recurring_todos/monthly_repeat_pattern.rb index 57c0027c..703849a8 100644 --- a/app/models/recurring_todos/monthly_repeat_pattern.rb +++ b/app/models/recurring_todos/monthly_repeat_pattern.rb @@ -6,12 +6,49 @@ module RecurringTodos super user end + def recurrence_selector + get :recurrence_selector + end + def every_x_day? - @recurring_todo.recurrence_selector == 0 + get(:recurrence_selector) == 0 end def every_xth_day? - @recurring_todo.recurrence_selector == 1 + get(:recurrence_selector) == 1 + end + + def every_x_month + # in case monthly pattern is every day x, return every_other2 otherwise + # return a default value + get(:recurrence_selector) == 0 ? get(:every_other2) : 1 + end + + def every_x_month2 + # in case monthly pattern is every xth day, return every_other2 otherwise + # return a default value + get(:recurrence_selector) == 1 ? get(:every_other2) : 1 + end + + def every_xth_day(default=nil) + get(:every_other3) || default + end + + def day_of_week + get :every_count + end + + def validate + super + case recurrence_selector + when 0 # 'monthly_every_x_day' + errors[:base] << "Every other nth month may not be empty for recurrence setting" if every_x_month.blank? + when 1 # 'every_xth_day' + errors[:base] <<"Every other nth month may not be empty for recurrence setting" if every_x_month2.blank? + errors[:base] <<"The day of the month may not be empty for recurrence setting" if day_of_week.blank? + else + raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'" + end end end diff --git a/app/models/recurring_todos/weekly_repeat_pattern.rb b/app/models/recurring_todos/weekly_repeat_pattern.rb index 484e01e3..dbe5e65d 100644 --- a/app/models/recurring_todos/weekly_repeat_pattern.rb +++ b/app/models/recurring_todos/weekly_repeat_pattern.rb @@ -6,6 +6,27 @@ module RecurringTodos super user end + def every_x_week + get :every_other1 + end + + { monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }.each do |day, number| + define_method("on_#{day}") do + on_xday number + end + end + + def on_xday(n) + get(:every_day) && get(:every_day)[n, 1] != ' ' + end + + def validate + super + errors[:base] << "Every other nth week may not be empty for weekly recurrence setting" if every_x_week.blank? + something_set = %w{sunday monday tuesday wednesday thursday friday saturday}.inject(false) { |set, day| set || self.send("on_#{day}") } + errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set + end + end end \ No newline at end of file diff --git a/app/models/recurring_todos/yearly_repeat_pattern.rb b/app/models/recurring_todos/yearly_repeat_pattern.rb index a9688019..27a34ce0 100644 --- a/app/models/recurring_todos/yearly_repeat_pattern.rb +++ b/app/models/recurring_todos/yearly_repeat_pattern.rb @@ -6,6 +6,47 @@ module RecurringTodos super user end + def recurrence_selector + get :recurrence_selector + end + + def month_of_year + get :every_other2 + end + + def every_x_day + get :every_other1 + end + + def every_xth_day + get :every_other3 + end + + def day_of_week + get :every_count + end + + def month_of_year2 + # if recurrence pattern is every xth day in a month, return month otherwise + # return a default value + get(:recurrence_selector) == 1 ? get(:every_other2) : Time.zone.now.month + end + + def validate + super + case recurrence_selector + when 0 # 'yearly_every_x_day' + errors[:base] << "The month of the year may not be empty for recurrence setting" if month_of_year.blank? + errors[:base] << "The day of the month may not be empty for recurrence setting" if every_x_day.blank? + when 1 # 'yearly_every_xth_day' + errors[:base] << "The month of the year may not be empty for recurrence setting" if month_of_year2.blank? + errors[:base] << "The nth day of the month may not be empty for recurrence setting" if every_xth_day.blank? + errors[:base] << "The day of the week may not be empty for recurrence setting" if day_of_week.blank? + else + raise "unexpected value of recurrence selector '#{recurrence_selector}'" + end + end + end end \ No newline at end of file diff --git a/test/models/recurring_todo_test.rb b/test/models/recurring_todo_test.rb index a1bd2d1e..18ee005d 100644 --- a/test/models/recurring_todo_test.rb +++ b/test/models/recurring_todo_test.rb @@ -277,7 +277,7 @@ class RecurringTodoTest < ActiveSupport::TestCase def test_toggle_completion assert @yearly.active? - assert @yearly.toggle_completion! + assert @yearly.toggle_completion!, "toggle of completion should succeed" assert @yearly.completed? # entering completed state should set completed_at @@ -329,7 +329,7 @@ class RecurringTodoTest < ActiveSupport::TestCase assert_raise(Exception){ @every_month.valid? } @yearly.recurrence_selector = 99 - assert_raise(Exception){ @yearly.valid? } + assert_raise(RuntimeError){ @yearly.valid? } end def test_every_n_the_day_must_be_filled