active_model_serializers 0.8.3 → 0.10.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +19 -20
  4. data/CHANGELOG.md +8 -67
  5. data/CONTRIBUTING.md +31 -0
  6. data/Gemfile +14 -1
  7. data/{MIT-LICENSE.txt → LICENSE.txt} +3 -2
  8. data/README.md +166 -495
  9. data/Rakefile +6 -12
  10. data/active_model_serializers.gemspec +21 -19
  11. data/lib/action_controller/serialization.rb +28 -27
  12. data/lib/active_model/serializer/adapter/fragment_cache.rb +78 -0
  13. data/lib/active_model/serializer/adapter/json/fragment_cache.rb +15 -0
  14. data/lib/active_model/serializer/adapter/json.rb +52 -0
  15. data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +22 -0
  16. data/lib/active_model/serializer/adapter/json_api.rb +152 -0
  17. data/lib/active_model/serializer/adapter/null.rb +11 -0
  18. data/lib/active_model/serializer/adapter.rb +87 -0
  19. data/lib/active_model/serializer/array_serializer.rb +32 -0
  20. data/lib/active_model/serializer/configuration.rb +13 -0
  21. data/lib/active_model/serializer/fieldset.rb +40 -0
  22. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  23. data/lib/active_model/serializer.rb +179 -436
  24. data/lib/active_model_serializers.rb +9 -86
  25. data/lib/generators/serializer/USAGE +0 -3
  26. data/lib/generators/serializer/serializer_generator.rb +1 -6
  27. data/lib/generators/serializer/templates/serializer.rb +2 -13
  28. data/test/action_controller/adapter_selector_test.rb +51 -0
  29. data/test/action_controller/explicit_serializer_test.rb +110 -0
  30. data/test/action_controller/json_api_linked_test.rb +173 -0
  31. data/test/{serialization_scope_name_test.rb → action_controller/serialization_scope_name_test.rb} +7 -11
  32. data/test/action_controller/serialization_test.rb +365 -0
  33. data/test/adapter/fragment_cache_test.rb +27 -0
  34. data/test/adapter/json/belongs_to_test.rb +41 -0
  35. data/test/adapter/json/collection_test.rb +59 -0
  36. data/test/adapter/json/has_many_test.rb +36 -0
  37. data/test/adapter/json_api/belongs_to_test.rb +147 -0
  38. data/test/adapter/json_api/collection_test.rb +89 -0
  39. data/test/adapter/json_api/has_many_embed_ids_test.rb +45 -0
  40. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  41. data/test/adapter/json_api/has_many_test.rb +106 -0
  42. data/test/adapter/json_api/has_one_test.rb +59 -0
  43. data/test/adapter/json_api/linked_test.rb +257 -0
  44. data/test/adapter/json_test.rb +34 -0
  45. data/test/adapter/null_test.rb +25 -0
  46. data/test/adapter_test.rb +43 -0
  47. data/test/array_serializer_test.rb +21 -67
  48. data/test/fixtures/poro.rb +206 -0
  49. data/test/serializers/adapter_for_test.rb +50 -0
  50. data/test/serializers/associations_test.rb +106 -0
  51. data/test/serializers/attribute_test.rb +23 -0
  52. data/test/serializers/attributes_test.rb +28 -0
  53. data/test/serializers/cache_test.rb +128 -0
  54. data/test/serializers/configuration_test.rb +15 -0
  55. data/test/serializers/fieldset_test.rb +26 -0
  56. data/test/{generators_test.rb → serializers/generators_test.rb} +1 -27
  57. data/test/serializers/meta_test.rb +78 -0
  58. data/test/serializers/options_test.rb +21 -0
  59. data/test/serializers/serializer_for_test.rb +56 -0
  60. data/test/serializers/urls_test.rb +26 -0
  61. data/test/test_helper.rb +22 -13
  62. metadata +101 -42
  63. data/DESIGN.textile +0 -586
  64. data/Gemfile.edge +0 -9
  65. data/bench/perf.rb +0 -43
  66. data/cruft.md +0 -19
  67. data/lib/active_model/array_serializer.rb +0 -104
  68. data/lib/active_model/serializer/associations.rb +0 -233
  69. data/lib/active_record/serializer_override.rb +0 -16
  70. data/lib/generators/resource_override.rb +0 -13
  71. data/test/association_test.rb +0 -592
  72. data/test/caching_test.rb +0 -96
  73. data/test/no_serialization_scope_test.rb +0 -34
  74. data/test/serialization_test.rb +0 -392
  75. data/test/serializer_support_test.rb +0 -51
  76. data/test/serializer_test.rb +0 -1465
  77. data/test/test_fakes.rb +0 -217
data/Rakefile CHANGED
@@ -1,18 +1,12 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
3
- require "rake/testtask"
4
2
 
5
- desc 'Run tests'
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << 'lib'
8
- t.libs << 'test'
9
- t.pattern = 'test/**/*_test.rb'
10
- t.verbose = true
11
- end
3
+ require 'rake/testtask'
12
4
 
13
- desc 'Benchmark'
14
- task :bench do
15
- load 'bench/perf.rb'
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.ruby_opts = ['-r./test/test_helper.rb']
9
+ t.verbose = true
16
10
  end
17
11
 
18
12
  task :default => :test
@@ -1,24 +1,26 @@
1
- # -*- encoding: utf-8 -*-
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_model/serializer/version'
2
5
 
3
- $:.unshift File.expand_path("../lib", __FILE__)
4
- require "active_model/serializers/version"
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "active_model_serializers"
8
+ spec.version = ActiveModel::Serializer::VERSION
9
+ spec.authors = ["Steve Klabnik"]
10
+ spec.email = ["steve@steveklabnik.com"]
11
+ spec.summary = %q{Conventions-based JSON generation for Rails.}
12
+ spec.description = %q{ActiveModel::Serializers allows you to generate your JSON in an object-oriented and convention-driven manner.}
13
+ spec.homepage = "https://github.com/rails-api/active_model_serializers"
14
+ spec.license = "MIT"
5
15
 
6
- Gem::Specification.new do |gem|
7
- gem.authors = ["José Valim", "Yehuda Katz"]
8
- gem.email = ["jose.valim@gmail.com", "wycats@gmail.com"]
9
- gem.description = %q{Making it easy to serialize models for client-side use}
10
- gem.summary = %q{Bringing consistency and object orientation to model serialization. Works great for client-side MVC frameworks!}
11
- gem.homepage = "https://github.com/rails-api/active_model_serializers"
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
12
20
 
13
- gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
- gem.files = `git ls-files`.split("\n")
15
- gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
- gem.name = "active_model_serializers"
17
- gem.require_paths = ["lib"]
18
- gem.version = ActiveModel::Serializer::VERSION
21
+ spec.add_dependency "activemodel", ">= 4.0"
19
22
 
20
- gem.add_dependency 'activemodel', '>= 3.0'
21
- gem.add_development_dependency "rails", ">= 3.0"
22
- gem.add_development_dependency "pry"
23
- gem.add_development_dependency "minitest"
23
+ spec.add_development_dependency "rails", ">= 4.0"
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake"
24
26
  end
@@ -1,32 +1,13 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
1
3
  module ActionController
2
- # Action Controller Serialization
3
- #
4
- # Overrides render :json to check if the given object implements +active_model_serializer+
5
- # as a method. If so, use the returned serializer instead of calling +to_json+ on the object.
6
- #
7
- # This module also provides a serialization_scope method that allows you to configure the
8
- # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+
9
- # to the current user:
10
- #
11
- # class ApplicationController < ActionController::Base
12
- # serialization_scope :current_user
13
- # end
14
- #
15
- # If you need more complex scope rules, you can simply override the serialization_scope:
16
- #
17
- # class ApplicationController < ActionController::Base
18
- # private
19
- #
20
- # def serialization_scope
21
- # current_user
22
- # end
23
- # end
24
- #
25
4
  module Serialization
26
5
  extend ActiveSupport::Concern
27
6
 
28
7
  include ActionController::Renderers
29
8
 
9
+ ADAPTER_OPTION_KEYS = [:include, :fields, :root, :adapter]
10
+
30
11
  included do
31
12
  class_attribute :_serialization_scope
32
13
  self._serialization_scope = :current_user
@@ -37,15 +18,35 @@ module ActionController
37
18
  respond_to?(_serialization_scope, true)
38
19
  end
39
20
 
40
- def default_serializer_options
21
+ def get_serializer(resource)
22
+ @_serializer ||= @_serializer_opts.delete(:serializer)
23
+ @_serializer ||= ActiveModel::Serializer.serializer_for(resource)
24
+
25
+ if @_serializer_opts.key?(:each_serializer)
26
+ @_serializer_opts[:serializer] = @_serializer_opts.delete(:each_serializer)
27
+ end
28
+
29
+ @_serializer
30
+ end
31
+
32
+ def use_adapter?
33
+ !(@_adapter_opts.key?(:adapter) && !@_adapter_opts[:adapter])
41
34
  end
42
35
 
43
36
  [:_render_option_json, :_render_with_renderer_json].each do |renderer_method|
44
37
  define_method renderer_method do |resource, options|
45
- json = ActiveModel::Serializer.build_json(self, resource, options)
38
+ @_adapter_opts, @_serializer_opts =
39
+ options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] }
40
+
41
+ if use_adapter? && (serializer = get_serializer(resource))
42
+
43
+ @_serializer_opts[:scope] ||= serialization_scope
44
+ @_serializer_opts[:scope_name] = _serialization_scope
46
45
 
47
- if json
48
- super(json, options)
46
+ # omg hax
47
+ object = serializer.new(resource, @_serializer_opts)
48
+ adapter = ActiveModel::Serializer::Adapter.create(object, @_adapter_opts)
49
+ super(adapter, options)
49
50
  else
50
51
  super(resource, options)
51
52
  end
@@ -0,0 +1,78 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ class FragmentCache
5
+
6
+ attr_reader :serializer
7
+
8
+ def initialize(adapter, serializer, options, root)
9
+ @root = root
10
+ @options = options
11
+ @adapter = adapter
12
+ @serializer = serializer
13
+ end
14
+
15
+ def fetch
16
+ klass = serializer.class
17
+ # It will split the serializer into two, one that will be cached and other wont
18
+ serializers = fragment_serializer(serializer.object.class.name, klass)
19
+
20
+ # Instanciate both serializers
21
+ cached_serializer = serializers[:cached].constantize.new(serializer.object)
22
+ non_cached_serializer = serializers[:non_cached].constantize.new(serializer.object)
23
+
24
+ cached_adapter = @adapter.class.new(cached_serializer, @options)
25
+ non_cached_adapter = @adapter.class.new(non_cached_serializer, @options)
26
+
27
+ # Get serializable hash from both
28
+ cached_hash = cached_adapter.serializable_hash
29
+ non_cached_hash = non_cached_adapter.serializable_hash
30
+
31
+ # Merge both results
32
+ @adapter.fragment_cache(cached_hash, non_cached_hash)
33
+ end
34
+
35
+ private
36
+
37
+ def cached_attributes(klass, serializers)
38
+ cached_attributes = (klass._cache_only) ? klass._cache_only : serializer.attributes.keys.delete_if {|attr| klass._cache_except.include?(attr) }
39
+ non_cached_attributes = serializer.attributes.keys.delete_if {|attr| cached_attributes.include?(attr) }
40
+
41
+ cached_attributes.each do |attribute|
42
+ options = serializer.class._attributes_keys[attribute]
43
+ options ||= {}
44
+ # Add cached attributes to cached Serializer
45
+ serializers[:cached].constantize.attribute(attribute, options)
46
+ end
47
+
48
+ non_cached_attributes.each do |attribute|
49
+ options = serializer.class._attributes_keys[attribute]
50
+ options ||= {}
51
+ # Add non-cached attributes to non-cached Serializer
52
+ serializers[:non_cached].constantize.attribute(attribute, options)
53
+ end
54
+ end
55
+
56
+ def fragment_serializer(name, klass)
57
+ cached = "#{name.capitalize}CachedSerializer"
58
+ non_cached = "#{name.capitalize}NonCachedSerializer"
59
+
60
+ Object.const_set cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(cached)
61
+ Object.const_set non_cached, Class.new(ActiveModel::Serializer) unless Object.const_defined?(non_cached)
62
+
63
+ klass._cache_options ||= {}
64
+ klass._cache_options[:key] = klass._cache_key if klass._cache_key
65
+
66
+ cached.constantize.cache(klass._cache_options)
67
+
68
+ cached.constantize.fragmented(serializer)
69
+ non_cached.constantize.fragmented(serializer)
70
+
71
+ serializers = {cached: cached, non_cached: non_cached}
72
+ cached_attributes(klass, serializers)
73
+ serializers
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ class Json < Adapter
5
+ class FragmentCache
6
+
7
+ def fragment_cache(cached_hash, non_cached_hash)
8
+ non_cached_hash.merge cached_hash
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ require 'active_model/serializer/adapter/json/fragment_cache'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class Adapter
6
+ class Json < Adapter
7
+ def serializable_hash(options = {})
8
+ if serializer.respond_to?(:each)
9
+ @result = serializer.map{|s| self.class.new(s).serializable_hash }
10
+ else
11
+ @hash = {}
12
+
13
+ @core = cache_check(serializer) do
14
+ serializer.attributes(options)
15
+ end
16
+
17
+ serializer.each_association do |name, association, opts|
18
+ if association.respond_to?(:each)
19
+ array_serializer = association
20
+ @hash[name] = array_serializer.map do |item|
21
+ cache_check(item) do
22
+ item.attributes(opts)
23
+ end
24
+ end
25
+ else
26
+ if association
27
+ @hash[name] = cache_check(association) do
28
+ association.attributes(options)
29
+ end
30
+ elsif opts[:virtual_value]
31
+ @hash[name] = opts[:virtual_value]
32
+ else
33
+ @hash[name] = nil
34
+ end
35
+ end
36
+ end
37
+ @result = @core.merge @hash
38
+ end
39
+
40
+ if root = options.fetch(:root, serializer.json_key)
41
+ @result = { root => @result }
42
+ end
43
+ @result
44
+ end
45
+ end
46
+
47
+ def fragment_cache(cached_hash, non_cached_hash)
48
+ Json::FragmentCache.new().fragment_cache(cached_hash, non_cached_hash)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ class JsonApi < Adapter
5
+ class FragmentCache
6
+
7
+ def fragment_cache(root, cached_hash, non_cached_hash)
8
+ hash = {}
9
+ core_cached = cached_hash.first
10
+ core_non_cached = non_cached_hash.first
11
+ no_root_cache = cached_hash.delete_if {|key, value| key == core_cached[0] }
12
+ no_root_non_cache = non_cached_hash.delete_if {|key, value| key == core_non_cached[0] }
13
+ cached_resource = (core_cached[1]) ? core_cached[1].merge(core_non_cached[1]) : core_non_cached[1]
14
+ hash = (root) ? { root => cached_resource } : cached_resource
15
+ hash.merge no_root_non_cache.merge no_root_cache
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,152 @@
1
+ require 'active_model/serializer/adapter/json_api/fragment_cache'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class Adapter
6
+ class JsonApi < Adapter
7
+ def initialize(serializer, options = {})
8
+ super
9
+ serializer.root = true
10
+ @hash = { data: [] }
11
+
12
+ if fields = options.delete(:fields)
13
+ @fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
14
+ else
15
+ @fieldset = options[:fieldset]
16
+ end
17
+ end
18
+
19
+ def serializable_hash(options = {})
20
+ if serializer.respond_to?(:each)
21
+ serializer.each do |s|
22
+ result = self.class.new(s, @options.merge(fieldset: @fieldset)).serializable_hash
23
+ @hash[:data] << result[:data]
24
+
25
+ if result[:included]
26
+ @hash[:included] ||= []
27
+ @hash[:included] |= result[:included]
28
+ end
29
+ end
30
+ else
31
+ @hash[:data] = attributes_for_serializer(serializer, @options)
32
+ add_resource_links(@hash[:data], serializer)
33
+ end
34
+ @hash
35
+ end
36
+
37
+ def fragment_cache(cached_hash, non_cached_hash)
38
+ root = false if @options.include?(:include)
39
+ JsonApi::FragmentCache.new().fragment_cache(root, cached_hash, non_cached_hash)
40
+ end
41
+
42
+ private
43
+
44
+ def add_links(resource, name, serializers)
45
+ resource[:links] ||= {}
46
+ resource[:links][name] ||= { linkage: [] }
47
+ resource[:links][name][:linkage] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
48
+ end
49
+
50
+ def add_link(resource, name, serializer, val=nil)
51
+ resource[:links] ||= {}
52
+ resource[:links][name] = { linkage: nil }
53
+
54
+ if serializer && serializer.object
55
+ resource[:links][name][:linkage] = { type: serializer.type, id: serializer.id.to_s }
56
+ end
57
+ end
58
+
59
+ def add_included(resource_name, serializers, parent = nil)
60
+ unless serializers.respond_to?(:each)
61
+ return unless serializers.object
62
+ serializers = Array(serializers)
63
+ end
64
+ resource_path = [parent, resource_name].compact.join('.')
65
+ if include_assoc?(resource_path)
66
+ @hash[:included] ||= []
67
+
68
+ serializers.each do |serializer|
69
+ attrs = attributes_for_serializer(serializer, @options)
70
+
71
+ add_resource_links(attrs, serializer, add_included: false)
72
+
73
+ @hash[:included].push(attrs) unless @hash[:included].include?(attrs)
74
+ end
75
+ end
76
+
77
+ serializers.each do |serializer|
78
+ serializer.each_association do |name, association, opts|
79
+ add_included(name, association, resource_path) if association
80
+ end if include_nested_assoc? resource_path
81
+ end
82
+ end
83
+
84
+ def attributes_for_serializer(serializer, options)
85
+ if serializer.respond_to?(:each)
86
+ result = []
87
+ serializer.each do |object|
88
+ options[:fields] = @fieldset && @fieldset.fields_for(serializer)
89
+ result << cache_check(object) do
90
+ options[:required_fields] = [:id, :type]
91
+ attributes = object.attributes(options)
92
+ attributes[:id] = attributes[:id].to_s
93
+ result << attributes
94
+ end
95
+ end
96
+ else
97
+ options[:fields] = @fieldset && @fieldset.fields_for(serializer)
98
+ options[:required_fields] = [:id, :type]
99
+ result = cache_check(serializer) do
100
+ result = serializer.attributes(options)
101
+ result[:id] = result[:id].to_s
102
+ result
103
+ end
104
+ end
105
+ result
106
+ end
107
+
108
+ def include_assoc?(assoc)
109
+ return false unless @options[:include]
110
+ check_assoc("#{assoc}$")
111
+ end
112
+
113
+ def include_nested_assoc?(assoc)
114
+ return false unless @options[:include]
115
+ check_assoc("#{assoc}.")
116
+ end
117
+
118
+ def check_assoc(assoc)
119
+ include_opt = @options[:include]
120
+ include_opt = include_opt.split(',') if include_opt.is_a?(String)
121
+ include_opt.any? do |s|
122
+ s.match(/^#{assoc.gsub('.', '\.')}/)
123
+ end
124
+ end
125
+
126
+ def add_resource_links(attrs, serializer, options = {})
127
+ options[:add_included] = options.fetch(:add_included, true)
128
+
129
+ serializer.each_association do |name, association, opts|
130
+ attrs[:links] ||= {}
131
+
132
+ if association.respond_to?(:each)
133
+ add_links(attrs, name, association)
134
+ else
135
+ if opts[:virtual_value]
136
+ add_link(attrs, name, nil, opts[:virtual_value])
137
+ else
138
+ add_link(attrs, name, association)
139
+ end
140
+ end
141
+
142
+ if options[:add_included]
143
+ Array(association).each do |association|
144
+ add_included(name, association)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ class Null < Adapter
5
+ def serializable_hash(options = {})
6
+ {}
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,87 @@
1
+ require 'active_model/serializer/adapter/fragment_cache'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ class Adapter
6
+ extend ActiveSupport::Autoload
7
+ autoload :Json
8
+ autoload :Null
9
+ autoload :JsonApi
10
+
11
+ attr_reader :serializer
12
+
13
+ def initialize(serializer, options = {})
14
+ @serializer = serializer
15
+ @options = options
16
+ end
17
+
18
+ def serializable_hash(options = {})
19
+ raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
20
+ end
21
+
22
+ def as_json(options = {})
23
+ hash = serializable_hash(options)
24
+ include_meta(hash)
25
+ end
26
+
27
+ def self.create(resource, options = {})
28
+ override = options.delete(:adapter)
29
+ klass = override ? adapter_class(override) : ActiveModel::Serializer.adapter
30
+ klass.new(resource, options)
31
+ end
32
+
33
+ def self.adapter_class(adapter)
34
+ "ActiveModel::Serializer::Adapter::#{adapter.to_s.classify}".safe_constantize
35
+ end
36
+
37
+ def fragment_cache(*args)
38
+ raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
39
+ end
40
+
41
+ private
42
+
43
+ def cache_check(serializer)
44
+ @cached_serializer = serializer
45
+ @klass = @cached_serializer.class
46
+ if is_cached?
47
+ @klass._cache.fetch(cache_key, @klass._cache_options) do
48
+ yield
49
+ end
50
+ elsif is_fragment_cached?
51
+ FragmentCache.new(self, @cached_serializer, @options, @root).fetch
52
+ else
53
+ yield
54
+ end
55
+ end
56
+
57
+ def is_cached?
58
+ @klass._cache && !@klass._cache_only && !@klass._cache_except
59
+ end
60
+
61
+ def is_fragment_cached?
62
+ @klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
63
+ end
64
+
65
+ def cache_key
66
+ (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key
67
+ end
68
+
69
+ def meta
70
+ serializer.meta if serializer.respond_to?(:meta)
71
+ end
72
+
73
+ def meta_key
74
+ serializer.meta_key || "meta"
75
+ end
76
+
77
+ def root
78
+ serializer.json_key
79
+ end
80
+
81
+ def include_meta(json)
82
+ json[meta_key] = meta if meta && root
83
+ json
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,32 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class ArraySerializer
4
+ include Enumerable
5
+ delegate :each, to: :@objects
6
+
7
+ attr_reader :meta, :meta_key
8
+
9
+ def initialize(objects, options = {})
10
+ options.merge!(root: nil)
11
+
12
+ @objects = objects.map do |object|
13
+ serializer_class = options.fetch(
14
+ :serializer,
15
+ ActiveModel::Serializer.serializer_for(object)
16
+ )
17
+ serializer_class.new(object, options)
18
+ end
19
+ @meta = options[:meta]
20
+ @meta_key = options[:meta_key]
21
+ end
22
+
23
+ def json_key
24
+ @objects.first.json_key if @objects.first
25
+ end
26
+
27
+ def root=(root)
28
+ @objects.first.root = root if @objects.first
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ module Configuration
4
+ include ActiveSupport::Configurable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do |base|
8
+ base.config.array_serializer = ActiveModel::Serializer::ArraySerializer
9
+ base.config.adapter = :json
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Fieldset
4
+
5
+ def initialize(fields, root = nil)
6
+ @root = root
7
+ @raw_fields = fields
8
+ end
9
+
10
+ def fields
11
+ @fields ||= parsed_fields
12
+ end
13
+
14
+ def fields_for(serializer)
15
+ key = serializer.json_key || serializer.class.root_name
16
+ fields[key.to_sym]
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :raw_fields, :root
22
+
23
+ def parsed_fields
24
+ if raw_fields.is_a?(Hash)
25
+ raw_fields.inject({}) { |h,(k,v)| h[k.to_sym] = v.map(&:to_sym); h}
26
+ elsif raw_fields.is_a?(Array)
27
+ if root.nil?
28
+ raise ArgumentError, 'The root argument must be specified if the fileds argument is an array.'
29
+ end
30
+ hash = {}
31
+ hash[root.to_sym] = raw_fields.map(&:to_sym)
32
+ hash
33
+ else
34
+ {}
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,5 @@
1
1
  module ActiveModel
2
2
  class Serializer
3
- VERSION = "0.8.3"
3
+ VERSION = "0.10.0.rc1"
4
4
  end
5
5
  end