Подтвердить что ты не робот

Серийный цикл ActiveRecord с использованием JSON вместо YAML

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

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

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

4b9b3361

Ответ 1

В Rails 3.1 вы можете просто

class Form < ActiveRecord::Base
  serialize :column, JSON
end

Надеюсь, что поможет

Ответ 2

В 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

Ответ 3

Обновление

См. нижеприведенный ответ с высоким рейтингом для более подходящего ответа 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"]}

PS:

Это не совсем СУХОЙ, но я сделал все возможное. Если кто-то может исправить after_find в User модели, это будет здорово.

Ответ 4

Мои требования не нуждались в повторном использовании кода на этом этапе, поэтому мой дистиллированный код является вариантом ответа выше:

  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  

Приветствия, довольно легко в конце!

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

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