Приведение атрибута ActiveModel при возврате

В моем проекте Rails 3.1.1 у меня есть ActiveModel, которая взаимодействует с API (вырвано из книги Пола Дикса, сокращено для удобства чтения):

class Job
  include ActiveModel::Validations
  include ActiveModel::Serializers::JSON

  ATTRIBUTES = [ :id,
                 :title,
                 :description,
                 :company_id ]

  attr_accessor *ATTRIBUTES

  validates_presence_of     :title, :description
  validates_numericality_of :company_id, :id

  def initialize(attributes = {})
    self.attributes = attributes
  end

  def attributes
    ATTRIBUTES.inject(
      ActiveSupport::HashWithIndifferentAccess.new
      ) do |result, key|

      result[key] = read_attribute_for_validation(key)
      result
      end
  end

  def attributes=(attrs)
     attrs.each_pair {|k, v| send("#{k}=", v)}
  end

  def read_attribute_for_validation(key)
    send(key)
  end

  # More method definitions...
end

Я создаю экземпляр @job в своем контроллере, действие new (company_id — это ключ сегмента в маршруте: /companies/:company_id/jobs/new) следующим образом:

@job = Job.new(company_id: params[:company_id])

Затем, используя CanCan, я проверяю разрешения пользователя на создание для создания задания. По сути, CanCan проверяет, соответствует ли атрибут company_id текущего_пользователя атрибуту company_id задания. Эта проверка завершается неудачей, поскольку @job.company_id возвращается как строка.

Конечно, я могу использовать params[:company_id].to_i при создании экземпляра объекта, но это похоже на обходной путь, который мне придется повторить позже.

Вопрос: есть ли способ сделать мою работу ActiveModel более "распознающей типы" и заставить ее возвращать int для вызова @job.company_id?

Я погуглил, проверил activemodel исходный код, но, похоже, не нашел ответа. Любая помощь приветствуется.

Обновление Я больше думал о чем-то вроде блока schema для ActiveModel, такого же, как в ActiveResource.


person Simon Bagreev    schedule 27.10.2011    source источник


Ответы (3)


attr_accessor *ATTRIBUTES

создайте такой метод:

def company_id
  @company_id
end

Вы можете просто переопределить это с помощью

def company_id
  @company_id.to_i
end
person moritz    schedule 27.10.2011
comment
Я думал об этом, но это означало бы, что мне придется делать то же самое для всех атрибутов int в модели. Это определенно более элегантно, чем использование .to_i в контроллере. Если я не получу лучшего ответа, ваш будет принят. Спасибо за уделенное время. - person Simon Bagreev; 27.10.2011

Отвечаю на свой вопрос....

В ответе mosch было предложено переопределить геттер для company_id в моей ActiveModel. Однако мне пришлось бы повторить это для всех атрибутов _id в модели. Поэтому я решил преобразовать все атрибуты _id в целые числа при инициализации объекта. Следующее:

def attributes=(attrs)
    attrs.each_pair do |k, v|
      if "#{k}".include? "_id"
        send("#{k}=", v.to_i)
      else
        send("#{k}=", v)
      end
    end
end
person Simon Bagreev    schedule 28.10.2011

Я предполагаю, что ваша компания has_many => :jobs? Если да, то можно попробовать

def new
  @company = Company.find(params[:company_id])
  @job = @company.jobs.new
end
person Michael De Silva    schedule 27.10.2011
comment
И Company, и Job являются ActiveModels, поэтому традиционные has_many и belongs_to здесь не работают... - person Simon Bagreev; 27.10.2011