Skip to content
This repository has been archived by the owner on Jul 24, 2018. It is now read-only.

Commit

Permalink
More transparency. Ensure scopes are applied even in overridden methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
dim committed Apr 20, 2011
1 parent 5cdfb5f commit 55b96ee
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .gitignore
@@ -1 +1 @@
pkg/
*.gem
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
0.3.0
0.8.0
52 changes: 33 additions & 19 deletions lib/serialization_scopes.rb
Expand Up @@ -2,42 +2,56 @@ module SerializationScopes
extend ActiveSupport::Concern

included do
class_inheritable_reader :serialization_scopes
write_inheritable_attribute :serialization_scopes, {}
class_attribute :serialization_scopes, :instance_writer => false
alias_method_chain :to_xml, :scopes
alias_method_chain :as_json, :scopes
end

module ClassMethods

def serialization_scope(name, options = {})
self.serialization_scopes ||= {}
serialization_scopes[name.to_sym] = options
end

def scoped_serialization_options(options = {})
options ||= {}
name = (options || {})[:scope]
scopes = name.present? && serialization_scopes[name.to_sym] ? serialization_scopes[name.to_sym] : serialization_scopes[:default]
options = options.dup
scopes.each do |key, scope_options|
custom_options = options[key]
options[key] = if key == :except
custom_options ? (Array.wrap(custom_options) + Array.wrap(scope_options)).uniq : Array.wrap(scope_options)
elsif [:only, :methods, :include].include?(key)
custom_options ? Array.wrap(custom_options) & Array.wrap(scope_options) : Array.wrap(scope_options)
else
custom_options ? custom_options : scope_options
end
options = options.try(:clone) || {}
name = options[:scope].try(:to_sym)
scopes = name.present? && serialization_scopes.key?(name) ? serialization_scopes[name] : serialization_scopes[:default]

scopes.each do |key, defaults|
options[key] = options[key] ? Resolver.scope(key, defaults, options[key]) : defaults
end if scopes

options
end

end

def to_xml(options = {})
super self.class.scoped_serialization_options(options)
module Resolver

def self.scope(key, defaults, settings)
defaults = Array.wrap(defaults)
settings = Array.wrap(settings)

case key
when :except
(settings + defaults).uniq
when :only, :methods, :include
settings & defaults
else
settings
end
end

end

def to_xml_with_scopes(options = {})
to_xml_without_scopes self.class.scoped_serialization_options(options)
end

def as_json(options = {})
super self.class.scoped_serialization_options(options)
def as_json_with_scopes(options = {})
as_json_without_scopes self.class.scoped_serialization_options(options)
end

end
Expand Down
38 changes: 25 additions & 13 deletions spec/serialization_scopes_spec.rb
Expand Up @@ -38,17 +38,23 @@ def another
end

class AnotherModel < ActiveRecord::Base
serialization_scope :default, :only => :another
serialization_scope :default, :only => :name
after_initialize :set_defaults

def self.columns
@columns ||= [
ActiveRecord::ConnectionAdapters::Column.new('another', nil, 'string')
ActiveRecord::ConnectionAdapters::Column.new('id', nil, 'integer'),
ActiveRecord::ConnectionAdapters::Column.new('name', nil, 'string')
]
end

# Method override
def to_json(options={})
super(options)
end

def set_defaults
self.another = 'val'
self.name = 'val'
end
end

Expand All @@ -73,8 +79,12 @@ class SomeResource < ActiveResource::Base
)
end

def serialize(object, options = {})
ActiveSupport::JSON.decode(object.to_json(options))
end

def as_hash(options = {})
ActiveSupport::JSON.decode(SomeModel.new.to_json(options))
serialize(SomeModel.new, options)
end

it 'should constraint to_json' do
Expand Down Expand Up @@ -118,26 +128,28 @@ def options_for(custom_options)
end

it 'should use default serialization scope when serialized as part of another object' do
i = SomeModel.new
ActiveSupport::JSON.decode([i].to_json).should == [{ "name" => "Any", "currency" => "USD", "id" => 1 }]
ActiveSupport::JSON.decode({:k => i}.to_json).should == { 'k' => { "name" => "Any", "currency" => "USD", "id" => 1 } }
serialize(:k => SomeModel.new).should == { 'k' => { "name" => "Any", "currency" => "USD", "id" => 1 } }
end

it 'should not fail when passed nil options' do
options_for(nil).should == { :only => [:id, :name], :methods => [:currency] }
options_for(nil).should == { :only => [:id, :name], :methods => :currency }
end

it 'should keep scope option' do
options_for(:scope => :nested)[:scope].should == :nested
end

it 'should pass the scope to the nested object so that they can use own settings' do
as_hash(:scope => :nested)['another'].should == { 'another' => 'val' }
as_hash(:scope => :nested)['another'].should == { 'name' => 'val' }
end

it 'should be enabled on ActiveResource models' do
json = SomeResource.new(:id => 1, :name => 'a name', :secret => 'some secret').to_json
ActiveSupport::JSON.decode(json).should == { 'some_resource' => { 'id' => 1, 'name' => 'a name' } }
res = SomeResource.new(:id => 1, :name => 'a name', :secret => 'some secret')
serialize(res).should == { 'some_resource' => { 'id' => 1, 'name' => 'a name' } }
end

it 'should allow custom serialization methods' do
serialize(AnotherModel.new).should == {'name' => 'val'}
end

it 'should not tamper options' do
Expand All @@ -147,9 +159,9 @@ def options_for(custom_options)
end

it 'should not tamper nested options' do
original = { :only => [:secret] }
original = { :only => :secret }
SomeModel.new.to_json(original)
original.should == { :only => [:secret] }
original.should == { :only => :secret }
end

end

0 comments on commit 55b96ee

Please sign in to comment.