views:

29

answers:

2

I have a Service model with title:string description:string date:datetime. I would like to implement a system to create multiple services based on date patterns, e.g. when the user create a new service for Oct 20 he can choose to "repeat" it once a month for 5 months. The final result should be that 6 services are created, due to Oct 20, Nov 20, etc. The way I implemented this is working, but really ugly. I have something like this in my view:

<input type="radio" name="each" value="none" checked="true"/>No<br/>
<input type="radio" name="each" value="week"/>Each week for <input type="text" name="weeks_number" /> weeks.<br/>
<input type="radio" name="each" value="month"/>Each month for <input type="text" name="months_number" /> months.<br/>

In my controller, I calculate the dates the various services will take place, than create N Service objects (where N is the number specified in params[:weeks_number] or params[:months_number]) and save each of them.

This doesn't sound good, does it? First of all, I was thinking about moving all the logic for creating "multiple" services into the model. The next big thing is: how to clean up the view and the controller? I'd like my controller to be as simple as

@service = Service.new(params[:service])

In order to do this I'll have to change something in the model (this should be called virtual attributes) in order to let the controller believe that something like

@service.repeat_method = "month"
@service.repeat = 6

actually makes sense and will cause the creation of 6 different rows in my db, when calling @service.save.

Here is where I'm stuck and in need of help. Does my reasoning make any sense? And how to implement this?

Thank you.

A: 

I wouldn't have the repeat_method and repeat attributes on a Service instance doing the magic you describe in the question. I'd have a create_repeats method something like this:

# Create repeats of this Service
# how_many:: the number of repeats to create
# repeat_method:: whether to repeat 'weekly' or 'monthly'
def create_repeats(how_many, repeat_frequency)
  # Set the first thing to be cloned as this instance
  rep = self

  # Create how_many copies increasing the date each time
  how_many.times do
    rep = rep.clone
    if repeat_frequency == 'weekly'
      rep.date += 1.week
    elsif repeat_frequency == 'monthly'
      rep.date += 1.month
    else
      raise "Unrecognized repeat frequency."
    end
    rep.save!
  end
end

Then your controller code will be something like the following:

@service = Service.new(params[:service])
if @service.save
  if !params[:weeks_number].blank?
    repeats = params[:weeks_number].to_i
    repeat_frequency = 'weekly'
  elsif !params[:months_number].blank?
    repeats = params[:months_number].to_i
    repeat_frequency = 'monthly'
  end
  @service.create_repeats(repeats, repeat_frequency)
end

I know this doesn't defer all the logic to the model but some parameters really need specific logic to determine how they are translated from the view to the model layer.

You may also want to check that params[:weeks_number] or params[:months_number] are a valid number and that the user has not specified both.

Shadwell
A: 

I followed Shadwell's suggestion. While not moving all to the model, I ended up with reasonably clean views and controller:

View:

<%= radio_button_tag :repeat_type, :none %> No<br/>
<%= radio_button_tag :repeat_type, :week %> Every week for <%= text_field_tag :repeat_weeks %> weeks.<br/>
<%= radio_button_tag :repeat_type, :fixed_month %> Every month for <%= text_field_tag :repeat_fixed_months %> months. (Eg. Oct 2, Nov 2, etc.)<br/>
<%= radio_button_tag :repeat_type, :fluid_month %> Each month for <%= text_field_tag :repeat_fluid_months %> months. (Eg. last Sunday of March, last sunday of April, etc.)

Controller:

def create
  @service = Service.new(params[:service])
  if @service.save
    frequency = params[:repeat_type]
    how_many = params[("repeat" + frequency + "s").to_sym]
    @service.create_repeats(how_many, frequency)
    redirect_to @service, :notice => "Servizio creato."
  else
    render :action => :new
  end
end

Model:

def create_repeats(how_many, frequency)
  replica = self
  how_many.times do
    replica = replica.clone
    case frequency
      when "week" then
        replica.date += 1.week
      when "fixed_month" then
        replica.date += 1.month
      when "fluid_month" then
        replica.date = replica.date.next_similar_date
      else
        return
    end
    replica.save!
  end
end
Alberto