mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-22 23:24:07 +01:00
Start major refactoring of recurring_todos. Started with creating new recurring todos.
All current and new tests pass
This commit is contained in:
parent
8e13059df1
commit
78c07d52b7
26 changed files with 1218 additions and 32 deletions
|
|
@ -107,7 +107,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
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 '#{recurrence_target}'"
|
||||
raise Exception.new, "unexpected value of recurrence target selector '#{target}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
module RecurringTodos
|
||||
|
||||
class AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
@user = user
|
||||
@attributes = attributes
|
||||
@filterred_attributes = filter_attributes(attributes)
|
||||
@saved = false
|
||||
end
|
||||
|
||||
def filter_attributes(attributes)
|
||||
raise Exception.new, "filter_attributes should be overridden"
|
||||
end
|
||||
|
||||
def filter_generic_attributes(attributes)
|
||||
attributes['tag_list'] =
|
||||
{
|
||||
recurring_period: attributes["recurring_period"],
|
||||
description: attributes['description'],
|
||||
notes: attributes['notes'],
|
||||
tag_list: tag_list_or_empty_string(attributes),
|
||||
start_from: attributes['start_from'],
|
||||
end_date: attributes['end_date'],
|
||||
ends_on: attributes['ends_on'],
|
||||
show_always: attributes['show_always'],
|
||||
target: attributes['target'],
|
||||
project: attributes[:project],
|
||||
context: attributes[:context],
|
||||
target: attributes['recurring_target'],
|
||||
show_from_delta: attributes['recurring_show_days_before'],
|
||||
show_always: attributes['recurring_show_always']
|
||||
}
|
||||
end
|
||||
|
||||
# build does not add tags. For tags, the recurring todos needs to be saved
|
||||
def build
|
||||
@recurring_todo = @pattern.build_recurring_todo
|
||||
|
||||
@recurring_todo.context = @filterred_attributes[:context]
|
||||
@recurring_todo.project = @filterred_attributes[:project]
|
||||
end
|
||||
|
||||
def save
|
||||
build
|
||||
@saved = @recurring_todo.save
|
||||
@recurring_todo.tag_with(@filterred_attributes[:tag_list]) if @saved && @filterred_attributes[:tag_list].present?
|
||||
return @saved
|
||||
end
|
||||
|
||||
def saved_recurring_todo
|
||||
if !@saved
|
||||
raise Exception.new, @recurring_todo.valid? ? "Recurring todo was not saved yet" : "Recurring todos was not saved because of validation errors"
|
||||
end
|
||||
|
||||
@recurring_todo
|
||||
end
|
||||
|
||||
def attributes
|
||||
@pattern.attributes
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_list_or_empty_string(attributes)
|
||||
# avoid nil
|
||||
attributes['tag_list'].blank? ? "" : attributes['tag_list'].strip
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
42
app/models/recurring_todos/abstract_repeat_pattern.rb
Normal file
42
app/models/recurring_todos/abstract_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
module RecurringTodos
|
||||
|
||||
class AbstractRepeatPattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
@attributes = attributes
|
||||
@user = user
|
||||
end
|
||||
|
||||
def build_recurring_todo
|
||||
@recurring_todo = @user.recurring_todos.build(mapped_attributes)
|
||||
end
|
||||
|
||||
def mapped_attributes
|
||||
@attributes
|
||||
end
|
||||
|
||||
def attributes
|
||||
mapped_attributes
|
||||
end
|
||||
|
||||
def map(mapping, key, source_key)
|
||||
mapping[key] = mapping[source_key]
|
||||
mapping.except(source_key)
|
||||
end
|
||||
|
||||
def get_selector(key)
|
||||
raise Exception.new, "recurrence selector pattern (#{key}) not given" unless @attributes.key?(key)
|
||||
raise Exception.new, "unknown recurrence selector pattern: '#{@attributes[key]}'" unless valid_selector?(@attributes[key])
|
||||
|
||||
selector = @attributes[key]
|
||||
@attributes = @attributes.except(key)
|
||||
return selector
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
raise Exception.new, "valid_selector? should be overridden in subclass of AbstractRepeatPattern"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
19
app/models/recurring_todos/daily_recurring_todos_builder.rb
Normal file
19
app/models/recurring_todos/daily_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
module RecurringTodos
|
||||
|
||||
class DailyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
attr_reader :recurring_todo, :pattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes)
|
||||
@pattern = DailyRepeatPattern.new(user, @filterred_attributes)
|
||||
end
|
||||
|
||||
def filter_attributes(attributes)
|
||||
@filterred_attributes = filter_generic_attributes(attributes)
|
||||
%w{daily_selector daily_every_x_days}.each{|key| @filterred_attributes[key] = attributes[key] if attributes.key?(key)}
|
||||
@filterred_attributes
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
42
app/models/recurring_todos/daily_repeat_pattern.rb
Normal file
42
app/models/recurring_todos/daily_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
module RecurringTodos
|
||||
|
||||
class DailyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
super user, attributes
|
||||
@selector = get_selector('daily_selector')
|
||||
end
|
||||
|
||||
def mapped_attributes
|
||||
mapping = @attributes
|
||||
|
||||
mapping[:only_work_days] = only_work_days?(@selector)
|
||||
|
||||
mapping[:every_other1] = mapping['daily_every_x_days']
|
||||
mapping = mapping.except('daily_every_x_days')
|
||||
|
||||
mapping
|
||||
end
|
||||
|
||||
def every_x_days
|
||||
@recurring_todo.every_other1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def only_work_days?(daily_selector)
|
||||
case daily_selector
|
||||
when 'daily_every_x_day'
|
||||
return false
|
||||
when 'daily_every_work_day'
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{daily_every_x_day daily_every_work_day}.include?(selector)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
module RecurringTodos
|
||||
|
||||
class MonthlyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes)
|
||||
@pattern = MonthlyRepeatPattern.new(user, @filterred_attributes)
|
||||
end
|
||||
|
||||
def filter_attributes(attributes)
|
||||
@filterred_attributes = filter_generic_attributes(attributes)
|
||||
|
||||
%w{
|
||||
monthly_selector monthly_every_x_day monthly_every_x_month
|
||||
monthly_every_x_month2 monthly_every_xth_day monthly_day_of_week
|
||||
}.each do |key|
|
||||
@filterred_attributes[key] = attributes[key] if attributes.key?(key)
|
||||
end
|
||||
|
||||
@filterred_attributes
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
49
app/models/recurring_todos/monthly_repeat_pattern.rb
Normal file
49
app/models/recurring_todos/monthly_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
module RecurringTodos
|
||||
|
||||
class MonthlyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
super user, attributes
|
||||
@selector = get_selector('monthly_selector')
|
||||
end
|
||||
|
||||
def mapped_attributes
|
||||
mapping = @attributes
|
||||
|
||||
mapping = map(mapping, :every_other1, 'monthly_every_x_day')
|
||||
mapping = map(mapping, :every_other3, 'monthly_every_xth_day')
|
||||
mapping = map(mapping, :every_count, 'monthly_day_of_week')
|
||||
|
||||
mapping[:every_other2] = mapping[get_every_other2]
|
||||
mapping = mapping.except('monthly_every_x_month').except('monthly_every_x_month2')
|
||||
|
||||
mapping[:recurrence_selector] = get_recurrence_selector
|
||||
|
||||
mapping
|
||||
end
|
||||
|
||||
def every_x_day?
|
||||
@recurring_todo.recurrence_selector == 0
|
||||
end
|
||||
|
||||
def every_xth_day?
|
||||
@recurring_todo.recurrence_selector == 1
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_recurrence_selector
|
||||
@selector=='monthly_every_x_day' ? 0 : 1
|
||||
end
|
||||
|
||||
def get_every_other2
|
||||
get_recurrence_selector == 0 ? 'monthly_every_x_month' : 'monthly_every_x_month2'
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{monthly_every_x_day monthly_every_xth_day}.include?(selector)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
104
app/models/recurring_todos/recurring_todos_builder.rb
Normal file
104
app/models/recurring_todos/recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
module RecurringTodos
|
||||
|
||||
class RecurringTodosBuilder
|
||||
|
||||
attr_reader :builder, :project, :context, :tag_list, :user
|
||||
|
||||
def initialize (user, attributes)
|
||||
@user = user
|
||||
@attributes = attributes
|
||||
|
||||
parse_dates
|
||||
parse_project
|
||||
parse_context
|
||||
|
||||
@builder = create_builder(attributes['recurring_period'])
|
||||
end
|
||||
|
||||
def create_builder(selector)
|
||||
if %w{daily weekly monthly yearly}.include?(selector)
|
||||
return eval("RecurringTodos::#{selector.capitalize}RecurringTodosBuilder.new(@user, @attributes)")
|
||||
else
|
||||
raise Exception.new("Unknown recurrence selector (#{selector})")
|
||||
end
|
||||
end
|
||||
|
||||
def build
|
||||
@builder.build
|
||||
end
|
||||
|
||||
def save
|
||||
@project.save if @new_project_created
|
||||
@context.save if @new_context_created
|
||||
|
||||
return @builder.save
|
||||
end
|
||||
|
||||
def saved_recurring_todo
|
||||
@builder.saved_recurring_todo
|
||||
end
|
||||
|
||||
def recurring_todo
|
||||
@builder.recurring_todo
|
||||
end
|
||||
|
||||
def attributes
|
||||
@builder.attributes
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_dates
|
||||
%w{end_date start_from}.each {|date| @attributes[date] = @user.prefs.parse_date(@attributes[date])}
|
||||
end
|
||||
|
||||
def parse_project
|
||||
if project_specified_by_name?
|
||||
@project = @user.projects.where(:name => project_name).first
|
||||
unless @project
|
||||
@project = @user.projects.build(:name => project_name)
|
||||
@new_project_created = true
|
||||
end
|
||||
else
|
||||
@project = @attributes['project_id'].present? ? @user.projects.find(@attributes['project_id']) : nil
|
||||
end
|
||||
@attributes[:project] = @project
|
||||
end
|
||||
|
||||
def parse_context
|
||||
if context_specified_by_name?
|
||||
@context = @user.contexts.where(:name => context_name).first
|
||||
unless @context
|
||||
@context = @user.contexts.build(:name => context_name)
|
||||
@new_context_created = true
|
||||
end
|
||||
else
|
||||
@context = @attributes['context_id'].present? ? @user.contexts.find(@attributes['context_id']) : nil
|
||||
end
|
||||
@attributes[:context] = @context
|
||||
end
|
||||
|
||||
def project_specified_by_name?
|
||||
return false if @attributes['project_id'].present?
|
||||
return false if project_name.blank?
|
||||
return false if project_name == 'None'
|
||||
true
|
||||
end
|
||||
|
||||
def context_specified_by_name?
|
||||
return false if @attributes['context_id'].present?
|
||||
return false if context_name.blank?
|
||||
true
|
||||
end
|
||||
|
||||
def project_name
|
||||
@attributes['project_name'].strip unless @attributes['project_name'].nil?
|
||||
end
|
||||
|
||||
def context_name
|
||||
@attributes['context_name'].strip unless @attributes['context_name'].nil?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
22
app/models/recurring_todos/weekly_recurring_todos_builder.rb
Normal file
22
app/models/recurring_todos/weekly_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
module RecurringTodos
|
||||
|
||||
class WeeklyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes)
|
||||
@pattern = WeeklyRepeatPattern.new(user, @filterred_attributes)
|
||||
end
|
||||
|
||||
def filter_attributes(attributes)
|
||||
@filterred_attributes = filter_generic_attributes(attributes)
|
||||
|
||||
weekly_attributes = %w{weekly_selector weekly_every_x_week}
|
||||
%w{monday tuesday wednesday thursday friday saturday sunday}.each{|day| weekly_attributes << "weekly_return_#{day}"}
|
||||
weekly_attributes.each{|key| @filterred_attributes[key] = attributes[key] if attributes.key?(key)}
|
||||
|
||||
@filterred_attributes
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
29
app/models/recurring_todos/weekly_repeat_pattern.rb
Normal file
29
app/models/recurring_todos/weekly_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
module RecurringTodos
|
||||
|
||||
class WeeklyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
super user, attributes
|
||||
end
|
||||
|
||||
def mapped_attributes
|
||||
mapping = @attributes
|
||||
|
||||
mapping = map(mapping, :every_other1, 'weekly_every_x_week')
|
||||
|
||||
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }.each{|day, index| mapping = map_day(mapping, :every_day, "weekly_return_#{day}", index)}
|
||||
|
||||
mapping
|
||||
end
|
||||
|
||||
def map_day(mapping, key, source_key, index)
|
||||
mapping[key] ||= ' ' # avoid nil
|
||||
mapping[source_key] ||= ' ' # avoid nil
|
||||
|
||||
mapping[key] = mapping[key][0, index] + mapping[source_key] + mapping[key][index+1, mapping[key].length]
|
||||
mapping
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
24
app/models/recurring_todos/yearly_recurring_todos_builder.rb
Normal file
24
app/models/recurring_todos/yearly_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
module RecurringTodos
|
||||
|
||||
class YearlyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes)
|
||||
@pattern = YearlyRepeatPattern.new(user, @filterred_attributes)
|
||||
end
|
||||
|
||||
def filter_attributes(attributes)
|
||||
@filterred_attributes = filter_generic_attributes(attributes)
|
||||
|
||||
%w{ yearly_selector yearly_month_of_year yearly_month_of_year2
|
||||
yearly_every_x_day yearly_every_xth_day yearly_day_of_week
|
||||
}.each do |key|
|
||||
@filterred_attributes[key] = attributes[key] if attributes.key?(key)
|
||||
end
|
||||
|
||||
@filterred_attributes
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
46
app/models/recurring_todos/yearly_repeat_pattern.rb
Normal file
46
app/models/recurring_todos/yearly_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
module RecurringTodos
|
||||
|
||||
class YearlyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
super user, attributes
|
||||
@selector = get_selector('yearly_selector')
|
||||
end
|
||||
|
||||
def mapped_attributes
|
||||
mapping = @attributes
|
||||
|
||||
mapping[:recurrence_selector] = get_recurrence_selector
|
||||
|
||||
mapping[:every_other2] = mapping[get_every_other2]
|
||||
mapping = mapping.except('yearly_month_of_year').except('yearly_month_of_year2')
|
||||
|
||||
mapping = map(mapping, :every_other1, 'yearly_every_x_day')
|
||||
mapping = map(mapping, :every_other3, 'yearly_every_xth_day')
|
||||
mapping = map(mapping, :every_count, 'yearly_day_of_week')
|
||||
|
||||
mapping
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_recurrence_selector
|
||||
@selector=='yearly_every_x_day' ? 0 : 1
|
||||
end
|
||||
|
||||
def get_every_other2
|
||||
case get_recurrence_selector
|
||||
when 0
|
||||
'yearly_month_of_year'
|
||||
when 1
|
||||
'yearly_month_of_year2'
|
||||
end
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{yearly_every_x_day yearly_every_xth_day}.include?(selector)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue