Виртуальные атрибуты ActiveRecord в качестве атрибутов записи

У меня проблема с to_json, не отображающим мои виртуальные атрибуты

class Location < ActiveRecord::Base
    belongs_to :event
    before_create :generate_oid
    validates_associated :event

    attr_accessor :event_oid

    def event_oid
      @event_oid = event.oid
    end
end

event_oid не является частью массива, возвращаемого:

Location.first.attributes

Это особенно проблема для меня при использовании to_json, который автоматически сериализует атрибуты записи для jason. to_json опускает мой виртуальный атрибут.

Как вы можете сделать виртуальный атрибут обработанным как фактический атрибут экземпляра?

Изменить:

to_json - это всего лишь пример метода, где мой виртуальный атрибут, обработанный как фактический атрибут, будет приятным.

Ответ 1

Вы хотите изменить хэш атрибутов. Здесь содержится немного дополнительного кода, чтобы гарантировать, что нужные вам атрибуты готовы к использованию с to_json или другим методом, который зависит от атрибутов загрузки объектов.

class Location < ActiveRecord::Base
    belongs_to :event
    before_create :generate_oid
    validates_associated :event

    after_save :event_oid

    attr_accessor :event_oid

    def event_oid
      @event_oid = @attributes["event_oid"] = event.oid if event.nil?
    end       

    def after_initialize
      event_oid
    end


end

to_json и множество других методов, которые генерируют списки вещей на основе атрибутов объектов. Что заполняется при инициализации объекта таблицами и именами базы данных, к сожалению, переменные экземпляра не обновляют этот хеш.

P.S. это не очень DRY, если у вас есть несколько атрибутов, которые вы хотите использовать таким образом. Вы можете использовать массив символов, детерминированных имен методов и блок class_eval для одновременного применения этого процесса к нескольким символам.

Внимание

Мы возимся с внутренностями рельсов. Там не сказано, как это может привести к провалу других вещей. Я не тестировал больше, чем save и to_json, оба из которых работают, когда хэш атрибутов содержит ключи, которые также не являются именами столбцов. Поэтому используйте его на свой страх и риск.

Ответ 2

Как насчет to_json(:methods => [:event_oid]), это работает?

Ответ 3

Просто выполните #to_json самостоятельно:

class Location < ActiveRecord::Base
  def to_json(options={})
    options[:methods] ||= []
    options[:methods] << :event_oid
    super(options)
  end
end

Ответ 4

Я попробовал François Beausoleil ответить и получил его для работы после изменения to_json до as_json

def as_json(options={})
  options[:methods] ||= []
  options[:methods] << :class_to_s
  super(options)
end

Ответ 6

Старый вопрос, но то, что задал ОП, теперь возможно и просто, если вы знаете, как это сделать. Вот полный работающий пример, но все, что вам нужно, находится в классе Location. Оператор attribute расширяет атрибуты модели, а after_initialize заботится о присвоении значения.

require 'active_record'  

ActiveRecord::Base.establish_connection(  
  :adapter=> "sqlite3",  
  :database=> ":memory:"  
)  

ActiveRecord::Schema.define do
  create_table :events do |table|
    table.column :name, :string
  end
  create_table :locations do |table|
    table.column :name, :string
    table.column :event_id, :integer
  end
end

class Event < ActiveRecord::Base
  has_one :location
end

class Location < ActiveRecord::Base
  belongs_to :event
  attribute :event_name, :string

  after_initialize do
    self.event_name = event.name
  end
end

Event.create(name: 'Event1')
Location.create(name: 'Location1', event_id: 1)
p Model.attribute_names
p Event.first
p Event.first.location

#["id", "name", "event_id", "event_name"]
#<Event id: 1, name: "Event1">
#<Location id: 1, name: "Location1", event_id: 1, event_name: "Event1">