У меня есть модель, которая использует сериализованный столбец:
class Form < ActiveRecord::Base
serialize :options, Hash
end
Есть ли способ сделать эту сериализацию использованием JSON вместо YAML?
У меня есть модель, которая использует сериализованный столбец:
class Form < ActiveRecord::Base
serialize :options, Hash
end
Есть ли способ сделать эту сериализацию использованием JSON вместо YAML?
В Rails 3.1 вы можете просто
class Form < ActiveRecord::Base
serialize :column, JSON
end
Надеюсь, что поможет
В Rails 3.1 вы можете использовать пользовательские кодеры с serialize
.
class ColorCoder
# Called to deserialize data to ruby object.
def load(data)
end
# Called to convert from ruby object to serialized data.
def dump(obj)
end
end
class Fruits < ActiveRecord::Base
serialize :color, ColorCoder.new
end
Надеюсь, что это поможет.
Литература:
Определение serialize
:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb#L556
Кодер YAML по умолчанию, который поставляется с рельсами: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/coders/yaml_column.rb
И здесь происходит вызов load
:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/read.rb#L132
Обновление
См. нижеприведенный ответ с высоким рейтингом для более подходящего ответа Rails >= 3.1. Это отличный ответ для Rails < 3.1.
Возможно, это то, что вы ищете.
Form.find(:first).to_json
Обновление
1) Установите "json" gem:
gem install json
2) Создайте класс JsonWrapper
# lib/json_wrapper.rb
require 'json'
class JsonWrapper
def initialize(attribute)
@attribute = attribute.to_s
end
def before_save(record)
record.send("#{@attribute}=", JsonWrapper.encrypt(record.send("#{@attribute}")))
end
def after_save(record)
record.send("#{@attribute}=", JsonWrapper.decrypt(record.send("#{@attribute}")))
end
def self.encrypt(value)
value.to_json
end
def self.decrypt(value)
JSON.parse(value) rescue value
end
end
3) Добавить обратные вызовы модели:
#app/models/user.rb
class User < ActiveRecord::Base
before_save JsonWrapper.new( :name )
after_save JsonWrapper.new( :name )
def after_find
self.name = JsonWrapper.decrypt self.name
end
end
4) Проверьте это!
User.create :name => {"a"=>"b", "c"=>["d", "e"]}
Это не совсем СУХОЙ, но я сделал все возможное. Если кто-то может исправить after_find
в User
модели, это будет здорово.
Мои требования не нуждались в повторном использовании кода на этом этапе, поэтому мой дистиллированный код является вариантом ответа выше:
require "json/ext"
before_save :json_serialize
after_save :json_deserialize
def json_serialize
self.options = self.options.to_json
end
def json_deserialize
self.options = JSON.parse(options)
end
def after_find
json_deserialize
end
Приветствия, довольно легко в конце!
serialize :attr, JSON
с помощью метода composed_of
работает следующим образом:
composed_of :auth,
:class_name => 'ActiveSupport::JSON',
:mapping => %w(url to_json),
:constructor => Proc.new { |url| ActiveSupport::JSON.decode(url) }
где url - атрибут, который будет сериализован с использованием json и auth - новый метод, доступный на вашей модели, который сохраняет его значение в json-формате атрибуту url. (еще не полностью протестирован, но, похоже, работает)
Я написал свой собственный кодер YAML, который принимает значение по умолчанию. Вот класс:
class JSONColumn
def initialize(default={})
@default = default
end
# this might be the database default and we should plan for empty strings or nils
def load(s)
s.present? ? JSON.load(s) : @default.clone
end
# this should only be nil or an object that serializes to JSON (like a hash or array)
def dump(o)
JSON.dump(o || @default)
end
end
Так как load
и dump
- это методы экземпляра, то для определения модели требуется, чтобы экземпляр передавался как второй аргумент в serialize
. Вот пример:
class Person < ActiveRecord::Base
validate :name, :pets, :presence => true
serialize :pets, JSONColumn.new([])
end
Я попытался создать новый экземпляр, загрузить экземпляр и сбросить экземпляр в IRB, и все это, казалось, работало правильно. Я написал сообщение в блоге об этом тоже.
Более простым решением является использование composed_of
, как описано в этом сообщении в блоге Михаила Рыкова. Мне нравится это решение, потому что оно требует меньшего количества обратных вызовов.
Вот его суть:
composed_of :settings, :class_name => 'Settings', :mapping => %w(settings to_json),
:constructor => Settings.method(:from_json),
:converter => Settings.method(:from_json)
after_validation do |u|
u.settings = u.settings if u.settings.dirty? # Force to serialize
end
Алеран, вы использовали этот метод с Rails 3? У меня есть одна и та же проблема, и я шел к сериализации, когда я столкнулся с этим сообщением Майклом Рыковым, но комментировать его блог невозможно, или, по крайней мере, на этом посту. Насколько я понимаю, он говорит, что вам не нужно определять класс настроек, однако, когда я пытаюсь это сделать, он продолжает говорить мне, что параметр Setting не определен. Поэтому мне просто интересно, использовали ли вы его и что еще нужно было описать? Спасибо.