mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Added ActiveRecord::Base#to_json/from_json (currently does not support :include like to_xml) [DHH]. Added ActiveRecord::Base#from_xml [DHH]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7519 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
dc399b96c8
commit
e86d1cd621
9 changed files with 160 additions and 53 deletions
|
@ -1,5 +1,12 @@
|
|||
*SVN*
|
||||
|
||||
* Added ActiveRecord::Base#to_json/from_json (currently does not support :include like to_xml) [DHH]
|
||||
|
||||
* Added ActiveRecord::Base#from_xml [DHH]. Example:
|
||||
|
||||
xml = "<person><name>David</name></person>"
|
||||
Person.new.from_xml(xml).name # => "David"
|
||||
|
||||
* Define dynamic finders as real methods after first usage. [bscofield]
|
||||
|
||||
* Deprecation: remove deprecated threaded_connections methods. Use allow_concurrency instead. [Jeremy Kemper]
|
||||
|
|
|
@ -49,7 +49,7 @@ require 'active_record/locking/pessimistic'
|
|||
require 'active_record/migration'
|
||||
require 'active_record/schema'
|
||||
require 'active_record/calculations'
|
||||
require 'active_record/xml_serialization'
|
||||
require 'active_record/serialization'
|
||||
require 'active_record/attribute_methods'
|
||||
|
||||
ActiveRecord::Base.class_eval do
|
||||
|
@ -65,7 +65,7 @@ ActiveRecord::Base.class_eval do
|
|||
include ActiveRecord::Transactions
|
||||
include ActiveRecord::Reflection
|
||||
include ActiveRecord::Calculations
|
||||
include ActiveRecord::XmlSerialization
|
||||
include ActiveRecord::Serialization
|
||||
include ActiveRecord::AttributeMethods
|
||||
end
|
||||
|
||||
|
|
|
@ -158,6 +158,7 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def method_missing(method, *args, &block)
|
||||
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
||||
|
@ -218,4 +219,4 @@ module ActiveRecord
|
|||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
59
activerecord/lib/active_record/serialization.rb
Normal file
59
activerecord/lib/active_record/serialization.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
module ActiveRecord #:nodoc:
|
||||
module Serialization
|
||||
class Serializer #:nodoc:
|
||||
attr_reader :options
|
||||
|
||||
def initialize(record, options = {})
|
||||
@record, @options = record, options.dup
|
||||
end
|
||||
|
||||
# To replicate the behavior in ActiveRecord#attributes,
|
||||
# :except takes precedence over :only. If :only is not set
|
||||
# for a N level model but is set for the N+1 level models,
|
||||
# then because :except is set to a default value, the second
|
||||
# level model can have both :except and :only set. So if
|
||||
# :only is set, always delete :except.
|
||||
def serializable_attribute_names
|
||||
attribute_names = @record.attribute_names
|
||||
|
||||
if options[:only]
|
||||
options.delete(:except)
|
||||
attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
|
||||
else
|
||||
options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
|
||||
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
|
||||
end
|
||||
|
||||
attribute_names
|
||||
end
|
||||
|
||||
def serializable_method_names
|
||||
Array(options[:methods]).inject([]) do |method_attributes, name|
|
||||
method_attributes << :name if @record.respond_to?(name.to_s)
|
||||
method_attributes
|
||||
end
|
||||
end
|
||||
|
||||
def serializable_names
|
||||
serializable_attribute_names + serializable_method_names
|
||||
end
|
||||
|
||||
def serializable_record
|
||||
returning(serializable_record = {}) do
|
||||
serializable_names.each { |name| serializable_record[name] = @record.send(name) }
|
||||
end
|
||||
end
|
||||
|
||||
def serialize
|
||||
# overwrite to implement
|
||||
end
|
||||
|
||||
def to_s(&block)
|
||||
serialize(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'active_record/serializers/xml_serializer'
|
||||
require 'active_record/serializers/json_serializer'
|
|
@ -0,0 +1,18 @@
|
|||
module ActiveRecord #:nodoc:
|
||||
module Serialization
|
||||
def to_json(options = {}, &block)
|
||||
JsonSerializer.new(self, options).to_s
|
||||
end
|
||||
|
||||
def from_json(json)
|
||||
self.attributes = ActiveSupport::JSON.decode(json)
|
||||
self
|
||||
end
|
||||
|
||||
class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
|
||||
def serialize
|
||||
serializable_record.to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
module ActiveRecord #:nodoc:
|
||||
module XmlSerialization
|
||||
module Serialization
|
||||
# Builds an XML document to represent the model. Some configuration is
|
||||
# availble through +options+, however more complicated cases should use
|
||||
# override ActiveRecord's to_xml.
|
||||
|
@ -124,15 +124,14 @@ module ActiveRecord #:nodoc:
|
|||
serializer = XmlSerializer.new(self, options)
|
||||
block_given? ? serializer.to_s(&block) : serializer.to_s
|
||||
end
|
||||
|
||||
def from_xml(xml)
|
||||
self.attributes = Hash.from_xml(xml).values.first
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class XmlSerializer #:nodoc:
|
||||
attr_reader :options
|
||||
|
||||
def initialize(record, options = {})
|
||||
@record, @options = record, options.dup
|
||||
end
|
||||
|
||||
class XmlSerializer < ActiveRecord::Serialization::Serializer #:nodoc:
|
||||
def builder
|
||||
@builder ||= begin
|
||||
options[:indent] ||= 2
|
||||
|
@ -164,17 +163,7 @@ module ActiveRecord #:nodoc:
|
|||
# level model can have both :except and :only set. So if
|
||||
# :only is set, always delete :except.
|
||||
def serializable_attributes
|
||||
attribute_names = @record.attribute_names
|
||||
|
||||
if options[:only]
|
||||
options.delete(:except)
|
||||
attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
|
||||
else
|
||||
options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
|
||||
attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
|
||||
end
|
||||
|
||||
attribute_names.collect { |name| Attribute.new(name, @record) }
|
||||
serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
|
||||
end
|
||||
|
||||
def serializable_method_attributes
|
||||
|
@ -265,8 +254,6 @@ module ActiveRecord #:nodoc:
|
|||
yield builder if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :to_s, :serialize
|
||||
|
||||
class Attribute #:nodoc:
|
||||
attr_reader :name, :value, :type
|
16
activerecord/test/fixtures/contact.rb
vendored
Normal file
16
activerecord/test/fixtures/contact.rb
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
class Contact < ActiveRecord::Base
|
||||
# mock out self.columns so no pesky db is needed for these tests
|
||||
def self.column(name, sql_type = nil, options = {})
|
||||
@columns ||= []
|
||||
@columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, options[:default], sql_type.to_s, options[:null])
|
||||
end
|
||||
|
||||
column :name, :string
|
||||
column :age, :integer
|
||||
column :avatar, :binary
|
||||
column :created_at, :datetime
|
||||
column :awesome, :boolean
|
||||
column :preferences, :string
|
||||
|
||||
serialize :preferences
|
||||
end
|
47
activerecord/test/serialization_test.rb
Normal file
47
activerecord/test/serialization_test.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/contact'
|
||||
|
||||
class SerializationTest < Test::Unit::TestCase
|
||||
FORMATS = [ :xml, :json ]
|
||||
|
||||
def setup
|
||||
@contact_attributes = {
|
||||
:name => 'aaron stack',
|
||||
:age => 25,
|
||||
:avatar => 'binarydata',
|
||||
:created_at => Time.utc(2006, 8, 1),
|
||||
:awesome => false,
|
||||
:preferences => { :gem => 'ruby' }
|
||||
}
|
||||
|
||||
@contact = Contact.new(@contact_attributes)
|
||||
end
|
||||
|
||||
def test_serialize_should_be_reversible
|
||||
for format in FORMATS
|
||||
@serialized = Contact.new.send("to_#{format}")
|
||||
contact = Contact.new.send("from_#{format}", @serialized)
|
||||
|
||||
assert_equal @contact_attributes.keys.collect(&:to_s).sort, contact.attributes.keys.collect(&:to_s).sort, "For #{format}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_serialize_should_allow_attribute_only_filtering
|
||||
for format in FORMATS
|
||||
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
|
||||
contact = Contact.new.send("from_#{format}", @serialized)
|
||||
assert_equal @contact_attributes[:name], contact.name, "For #{format}"
|
||||
assert_nil contact.avatar, "For #{format}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_serialize_should_allow_attribute_except_filtering
|
||||
for format in FORMATS
|
||||
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
|
||||
contact = Contact.new.send("from_#{format}", @serialized)
|
||||
assert_nil contact.name, "For #{format}"
|
||||
assert_nil contact.age, "For #{format}"
|
||||
assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +1,10 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/contact'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/author'
|
||||
require 'fixtures/tagging'
|
||||
require 'fixtures/comment'
|
||||
|
||||
class Contact < ActiveRecord::Base
|
||||
# mock out self.columns so no pesky db is needed for these tests
|
||||
def self.columns() @columns ||= []; end
|
||||
def self.column(name, sql_type = nil, default = nil, null = true)
|
||||
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
|
||||
end
|
||||
|
||||
column :name, :string
|
||||
column :age, :integer
|
||||
column :avatar, :binary
|
||||
column :created_at, :datetime
|
||||
column :awesome, :boolean
|
||||
column :preferences, :string
|
||||
|
||||
serialize :preferences
|
||||
end
|
||||
|
||||
class XmlSerializationTest < Test::Unit::TestCase
|
||||
def test_should_serialize_default_root
|
||||
@xml = Contact.new.to_xml
|
||||
|
@ -47,18 +31,6 @@ class XmlSerializationTest < Test::Unit::TestCase
|
|||
assert_match %r{<created_at}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_attribute_filtering
|
||||
@xml = Contact.new.to_xml :only => [:age, :name]
|
||||
assert_match %r{<name}, @xml
|
||||
assert_match %r{<age}, @xml
|
||||
assert_no_match %r{<created-at}, @xml
|
||||
|
||||
@xml = Contact.new.to_xml :except => [:age, :name]
|
||||
assert_no_match %r{<name}, @xml
|
||||
assert_no_match %r{<age}, @xml
|
||||
assert_match %r{<created-at}, @xml
|
||||
end
|
||||
|
||||
def test_should_include_yielded_additions
|
||||
@xml = Contact.new.to_xml do |xml|
|
||||
xml.creator "David"
|
||||
|
|
Loading…
Reference in a new issue