ActiveRecord сериализуется с помощью JSON вместо YAML



У меня есть модель, которая использует последовательный столбец:



class Form < ActiveRecord::Base
serialize :options, Hash
end


есть ли способ сделать эту сериализацию использовать JSON вместо YAML?

653   8  

8 ответов:

в 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

обновление

см. ответ mid с высоким рейтингом ниже для гораздо более подходящих рельсов >= 3.1 ответ. Это отличный ответ для Rails

наверное, это то, что вы ищете.

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"]}

PS:

это не совсем сухой, но я сделал все возможное. Если кто-то может исправить 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  

Ура, довольно легко, в конце концов!

я написал свой собственный кодер 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, и все это, казалось, работало правильно. Я написал блоге об этом тоже.

The 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. (еще не полностью протестирован, но, похоже, работает)

более простое решение-использовать 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

Алеран, вы использовали этот метод с рельсами 3? У меня несколько такая же проблема, и я направлялся к сериализации, когда я столкнулся с этим сообщением Михаила Рыкова, но комментировать его блог невозможно, или, по крайней мере, на этом посту. Насколько я понимаю, он говорит, что вам не нужно определять класс настроек, однако когда я пытаюсь это сделать, он продолжает говорить мне, что настройка не определена. Поэтому мне просто интересно, использовали ли вы его и что еще нужно было описать? Спасибо.

Comments

    Ничего не найдено.