How to handle date fields in a non-model Rails for

2019-07-16 12:18发布

问题:

I am creating a non-model object that will be used with a Rails form builder by using ActiveModel. This is a Rails 3 project. Here's an example of what I have so far:

class SalesReport
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :promotion_code, :start_date, :end_date

  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end

  def persisted?
    false
  end
end

I happen to be using HAML and simple_form, but that's not important. Ultimately, I'm just using standard Rails date select fields:

= simple_form_for [:admin, @report], as: :report, url: admin_reports_path do |f|
  = f.input :promotion_code, label: 'Promo Code'
  = f.input :start_date, as: :date
  = f.input :end_date, as: :date
  = f.button :submit

Rails splits up the date fields into individual fields, so when the form is submitted, there are actually 3 date fields that are submitted:

{
  "report" => {
    "start_date(1i)" => "2014",
    "start_date(2i)" => "4",
    "start_date(3i)" => "1"
  }
}

In my SalesReport object, I'm assigning the params to my attr methods, but I'm getting an error that I don't have a start_date(1i)= method, which I obviously haven't defined. Ultimately, I'd like to end up with a Date object that I can use instead of 3 separate fields.

How should I handle these date fields in my non-model object?

回答1:

In your initialization you could manually assign the values from attributes to the class methods and down below override you start_date and end_date setter methods.

class SalesReport
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :promotion_code, :start_date, :end_date

  def initialize(attributes = {})
    @promotion_code = attributes['promotion_code']
    year = attributes['start_date(1i)']
    month = attributes['start_date(2i)']
    day = attributes['start_date(3i)']
    self.start_date = [year, month, day]
  end

  def start_date=(value)
    if value.is_a?(Array)
      @start_date = Date.new(value[0].to_i, value[1].to_i, value[2].to_i)
    else
      @start_date = value
    end
  end

  def persisted?
    false
  end
end   

This should allow you to give the setter a Date instance or an Array with the separate date elements and the setter will assign the correct date to @start_date. Just do the same for @end_date.

Hope this can help you.