Ответ 1
используйте встроенный метод serialize
:
class Whatever < ActiveRecord::Base
serialize :params, HashWithIndifferentAccess
end
см. ActiveRecord:: Основные документы по сериализации для получения дополнительной информации.
Я ранее имел:
serialize :params, JSON
Но это вернет JSON и преобразует символы хеш-ключа в строки. Я хочу ссылаться на хеш с помощью символов, как это наиболее часто встречается при работе с хэшами. Я кормлю его символами, Rails возвращает строки. Чтобы этого избежать, я создал свой собственный приемник/сеттер. Установщик достаточно прост (JSON-кодирование), получателем является:
def params
read_attribute(:params) || JSON.parse(read_attribute(:params).to_json).with_indifferent_access
end
Я не мог ссылаться на params
напрямую, потому что это вызовет цикл, поэтому я использую read_attribute
, и теперь мои хеш-ключи могут ссылаться на символы или строки. Однако это не обновляет хэш:
model.params.merge!(test: 'test')
puts model.params # => returns default params without merge
Что заставляет меня думать, что хэш ссылается на копию.
Мой вопрос двоякий. Могу ли я продлить активную запись JSON-сериализации, чтобы возвращать безразличный хэш доступа (или не преобразовывать символы в строки), и все еще иметь работу hash, как указано выше, слиянием? Если нет, что я могу сделать, чтобы улучшить мой геттер, чтобы model.params.merge!
работал?
Я надеялся на что-то похожее (что работает):
def params_merge!(hash)
write_attribute(:params, read_attribute(:params).merge(hash))
end
# usage: model.params_merge!(test: 'test')
Еще лучше, просто запустите Rails, чтобы вернуть хэш с равнодушным доступом или не преобразовать мои символы в строки! Цените любую помощь.
используйте встроенный метод serialize
:
class Whatever < ActiveRecord::Base
serialize :params, HashWithIndifferentAccess
end
см. ActiveRecord:: Основные документы по сериализации для получения дополнительной информации.
Проводка комментария как ответа, по запросу @fguillen... Предостережение: Я обычно не рубист... поэтому это может быть не идиоматично или эффективно. Функционально, он получил меня, что я хотел. Кажется, работает в Rails 3.2 и 4.0...
В application_helper.rb
:
module ApplicationHelper
class JSONWithIndifferentAccess
def self.load(str)
obj = HashWithIndifferentAccess.new(JSON.load(str))
#...or simply: obj = JSON.load(str, nil, symbolize_names:true)
obj.freeze #i also want it set all or nothing, not piecemeal; ymmv
obj
end
def self.dump(obj)
JSON.dump(obj)
end
end
end
В моей модели у меня есть поле под названием rule_spec
, сериализованное в поле text
:
serialize :rule_spec, ApplicationHelper::JSONWithIndifferentAccess
В конце концов, я понял, что мне нужны символы, а не безразличный доступ, но, настраивая метод загрузки, вы можете получить либо поведение.
Использование HashWithIndifferentAccess
велико, но оно по-прежнему действует как Hash, и оно может только сериализоваться как YAML в базе данных.
Мои предпочтения, используя Postgres 9.3 и выше, - использовать тип столбца json в Postgres. Это означает, что когда таблица будет прочитана, ActiveRecord получит Hash непосредственно из Postgres.
create_table "gadgets" do |t|
t.json "info"
end
ActiveRecord serialize
требует, чтобы вы предоставили ему один класс, который отвечает за чтение/запись данных и сериализацию/десериализацию.
Таким образом, вы можете создать объект, который выполняет задание, наследуя от HashWithIndifferentAccess
или моих предпочтений, Hashie::Mash
. Затем вы реализуете сериализацию как методы класса dump
и load
.
class HashieMashStoredAsJson < Hashie::Mash
def self.dump(obj)
ActiveSupport::JSON.encode(obj.to_h)
end
def self.load(raw_hash)
new(raw_hash || {})
end
end
В вашей модели вы можете указать этот класс для сериализации.
class Gadget < ActiveRecord::Base
serialize :info, HashieMashStoredAsJson
# This allows the field to be set as a Hash or anything compatible with it.
def info=(new_value)
self[:info] = HashieMashStoredAsJson.new new_value
end
end
Если вы не используете тип столбца json в Postgres, реализация немного меняется
Полный код и документация здесь: с использованием типа столбца JSON и используя тип столбца строки.
В итоге я использовал вариант решения bimsapi, который позволяет отображать всю структуру JSON в равнодушные хэши (т.е. массивы хэшей массивов и т.д.).
module JsonHelper
class JsonWithIndifferentAccess
def self.load(str)
self.indifferent_access JSON.load(str)
end
def self.dump(obj)
JSON.dump(obj)
end
private
def self.indifferent_access(obj)
if obj.is_a? Array
obj.map!{|o| self.indifferent_access(o)}
elsif obj.is_a? Hash
obj.with_indifferent_access
else
obj
end
end
end
end