J'ai un modèle qui utilise une colonne sérialisée:
class Form < ActiveRecord::Base
serialize :options, Hash
end
Existe-t-il un moyen de faire en sorte que cette sérialisation utilise JSON au lieu de YAML?
Dans Rails 3.1, vous pouvez simplement
class Form < ActiveRecord::Base
serialize :column, JSON
end
J'espère que cela pourra aider
Dans Rails 3.1, vous pouvez utiliser des codeurs personnalisés avec 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
J'espère que cela t'aides.
Références:
Définition de serialize
: https://github.com/Rails/rails/blob/master/activerecord/lib/active_record/base.rb#L556
Le codeur YAML par défaut fourni avec Rails: https://github.com/Rails/rails/blob/master/activerecord/lib/active_record/coders/yaml_column.rb
Et c’est là que l’appel à la load
se produit: https://github.com/Rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/read.rb#L132 =
Mettre à jour
Voir la réponse mieux notée ci-dessous pour une réponse Rails> = 3.1 bien plus appropriée. C'est une excellente réponse pour Rails <3.1.
C'est probablement ce que vous recherchez.
Form.find(:first).to_json
Mettre à jour
1) Installez 'json' gem :
gem install json
2) Créer une classe 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) Ajouter des rappels de modèle:
#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) Testez-le!
User.create :name => {"a"=>"b", "c"=>["d", "e"]}
Ce n'est pas tout à fait sec, mais j'ai fait de mon mieux. Si quelqu'un peut corriger after_find
dans le modèle User
, ce sera génial.
Mes exigences n'ayant pas nécessité beaucoup de réutilisation de code à ce stade, mon code distillé est une variante de la réponse ci-dessus:
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
Salut, assez facile à la fin!
La méthode serialize :attr, JSON
using composed_of
fonctionne comme ceci:
composed_of :auth,
:class_name => 'ActiveSupport::JSON',
:mapping => %w(url to_json),
:constructor => Proc.new { |url| ActiveSupport::JSON.decode(url) }
où url est l'attribut à sérialiser à l'aide de json et auth est la nouvelle méthode disponible sur votre modèle qui enregistre sa valeur au format json dans l'attribut url. (pas encore complètement testé mais semble fonctionner)
J'ai écrit mon propre codeur YAML, qui prend un défaut. Voici la classe:
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
Étant donné que load
et dump
sont des méthodes d'instance, il est nécessaire de transmettre une instance en tant que second argument à serialize
dans la définition du modèle. En voici un exemple:
class Person < ActiveRecord::Base
validate :name, :pets, :presence => true
serialize :pets, JSONColumn.new([])
end
J'ai essayé de créer une nouvelle instance, de charger une instance et de transférer une instance dans IRB, et tout semblait bien fonctionner. J'ai aussi écrit un blog post à ce sujet.
Une solution plus simple consiste à utiliser composed_of
comme décrit dans cet article de blog de Michael Rykov . J'aime cette solution car elle nécessite moins de rappels.
En voici l'essentiel:
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
Aleran, avez-vous utilisé cette méthode avec Rails 3? J'ai un peu le même problème et je me dirigeais vers une publication en série lorsque j'ai lu ce billet de Michael Rykov, mais il est impossible de commenter sur son blog, ou du moins sur ce billet. Si je comprends bien, il dit qu'il n'est pas nécessaire de définir la classe Settings. Toutefois, lorsque j'essaie, il ne cesse de me dire que Setting n'est pas défini. Je me demandais donc simplement si vous l'aviez utilisé et quoi de plus aurait dû être décrit? Merci.