active_model_serializers 0.8.3 → 0.10.0.rc2

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +18 -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 +169 -495
  9. data/Rakefile +6 -12
  10. data/active_model_serializers.gemspec +21 -19
  11. data/lib/action_controller/serialization.rb +36 -27
  12. data/lib/active_model/serializer/adapter/flatten_json.rb +12 -0
  13. data/lib/active_model/serializer/adapter/fragment_cache.rb +78 -0
  14. data/lib/active_model/serializer/adapter/json/fragment_cache.rb +15 -0
  15. data/lib/active_model/serializer/adapter/json.rb +50 -0
  16. data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +23 -0
  17. data/lib/active_model/serializer/adapter/json_api.rb +156 -0
  18. data/lib/active_model/serializer/adapter/null.rb +11 -0
  19. data/lib/active_model/serializer/adapter.rb +96 -0
  20. data/lib/active_model/serializer/array_serializer.rb +35 -0
  21. data/lib/active_model/serializer/configuration.rb +13 -0
  22. data/lib/active_model/serializer/fieldset.rb +40 -0
  23. data/lib/active_model/serializer/railtie.rb +8 -0
  24. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  25. data/lib/active_model/serializer.rb +177 -440
  26. data/lib/active_model_serializers.rb +10 -86
  27. data/lib/generators/serializer/USAGE +0 -3
  28. data/lib/generators/serializer/resource_override.rb +12 -0
  29. data/lib/generators/serializer/serializer_generator.rb +1 -6
  30. data/lib/generators/serializer/templates/serializer.rb +2 -13
  31. data/test/action_controller/adapter_selector_test.rb +53 -0
  32. data/test/action_controller/explicit_serializer_test.rb +134 -0
  33. data/test/action_controller/json_api_linked_test.rb +179 -0
  34. data/test/action_controller/rescue_from_test.rb +32 -0
  35. data/test/{serialization_scope_name_test.rb → action_controller/serialization_scope_name_test.rb} +7 -11
  36. data/test/action_controller/serialization_test.rb +383 -0
  37. data/test/adapter/fragment_cache_test.rb +27 -0
  38. data/test/adapter/json/belongs_to_test.rb +48 -0
  39. data/test/adapter/json/collection_test.rb +73 -0
  40. data/test/adapter/json/has_many_test.rb +36 -0
  41. data/test/adapter/json_api/belongs_to_test.rb +157 -0
  42. data/test/adapter/json_api/collection_test.rb +96 -0
  43. data/test/adapter/json_api/has_many_embed_ids_test.rb +45 -0
  44. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  45. data/test/adapter/json_api/has_many_test.rb +110 -0
  46. data/test/adapter/json_api/has_one_test.rb +61 -0
  47. data/test/adapter/json_api/linked_test.rb +283 -0
  48. data/test/adapter/json_test.rb +34 -0
  49. data/test/adapter/null_test.rb +25 -0
  50. data/test/adapter_test.rb +43 -0
  51. data/test/array_serializer_test.rb +31 -63
  52. data/test/fixtures/poro.rb +230 -0
  53. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  54. data/test/{generators_test.rb → generators/serializer_generator_test.rb} +2 -36
  55. data/test/serializers/adapter_for_test.rb +50 -0
  56. data/test/serializers/associations_test.rb +127 -0
  57. data/test/serializers/attribute_test.rb +38 -0
  58. data/test/serializers/attributes_test.rb +63 -0
  59. data/test/serializers/cache_test.rb +138 -0
  60. data/test/serializers/configuration_test.rb +15 -0
  61. data/test/serializers/fieldset_test.rb +26 -0
  62. data/test/serializers/meta_test.rb +107 -0
  63. data/test/serializers/options_test.rb +21 -0
  64. data/test/serializers/serializer_for_test.rb +65 -0
  65. data/test/serializers/urls_test.rb +26 -0
  66. data/test/test_helper.rb +28 -16
  67. metadata +109 -43
  68. data/DESIGN.textile +0 -586
  69. data/Gemfile.edge +0 -9
  70. data/bench/perf.rb +0 -43
  71. data/cruft.md +0 -19
  72. data/lib/active_model/array_serializer.rb +0 -104
  73. data/lib/active_model/serializer/associations.rb +0 -233
  74. data/lib/active_record/serializer_override.rb +0 -16
  75. data/lib/generators/resource_override.rb +0 -13
  76. data/test/association_test.rb +0 -592
  77. data/test/caching_test.rb +0 -96
  78. data/test/no_serialization_scope_test.rb +0 -34
  79. data/test/serialization_test.rb +0 -392
  80. data/test/serializer_support_test.rb +0 -51
  81. data/test/serializer_test.rb +0 -1465
  82. 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, :adapter]
10
+
30
11
  included do
31
12
  class_attribute :_serialization_scope
32
13
  self._serialization_scope = :current_user
@@ -37,21 +18,49 @@ 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
52
53
  end
53
54
  end
54
55
 
56
+ def rescue_with_handler(exception)
57
+ @_serializer = nil
58
+ @_serializer_opts = nil
59
+ @_adapter_opts = nil
60
+
61
+ super(exception)
62
+ end
63
+
55
64
  module ClassMethods
56
65
  def serialization_scope(scope)
57
66
  self._serialization_scope = scope
@@ -0,0 +1,12 @@
1
+ module ActiveModel
2
+ class Serializer
3
+ class Adapter
4
+ class FlattenJson < Json
5
+ def serializable_hash(options = {})
6
+ super
7
+ @result
8
+ end
9
+ end
10
+ end
11
+ end
12
+ 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)
9
+ @options = options
10
+ @adapter = adapter
11
+ @serializer = serializer
12
+ end
13
+
14
+ def fetch
15
+ klass = serializer.class
16
+ # It will split the serializer into two, one that will be cached and other wont
17
+ serializers = fragment_serializer(serializer.object.class.name, klass)
18
+
19
+ # Instanciate both serializers
20
+ cached_serializer = serializers[:cached].constantize.new(serializer.object)
21
+ non_cached_serializer = serializers[:non_cached].constantize.new(serializer.object)
22
+
23
+ cached_adapter = @adapter.class.new(cached_serializer, @options)
24
+ non_cached_adapter = @adapter.class.new(non_cached_serializer, @options)
25
+
26
+ # Get serializable hash from both
27
+ cached_hash = cached_adapter.serializable_hash
28
+ non_cached_hash = non_cached_adapter.serializable_hash
29
+
30
+ # Merge both results
31
+ @adapter.fragment_cache(cached_hash, non_cached_hash)
32
+ end
33
+
34
+ private
35
+
36
+ def cached_attributes(klass, serializers)
37
+ attributes = serializer.class._attributes
38
+ cached_attributes = (klass._cache_only) ? klass._cache_only : attributes.reject {|attr| klass._cache_except.include?(attr) }
39
+ non_cached_attributes = attributes - cached_attributes
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,50 @@
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| FlattenJson.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 && association.object
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
+ { root => @result }
41
+ end
42
+
43
+ def fragment_cache(cached_hash, non_cached_hash)
44
+ Json::FragmentCache.new().fragment_cache(cached_hash, non_cached_hash)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
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].deep_merge(core_non_cached[1]) : core_non_cached[1]
14
+ hash = (root) ? { root => cached_resource } : cached_resource
15
+
16
+ hash.deep_merge no_root_non_cache.deep_merge no_root_cache
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,156 @@
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
+ @hash = { data: [] }
10
+
11
+ if fields = options.delete(:fields)
12
+ @fieldset = ActiveModel::Serializer::Fieldset.new(fields, serializer.json_key)
13
+ else
14
+ @fieldset = options[:fieldset]
15
+ end
16
+ end
17
+
18
+ def serializable_hash(options = {})
19
+ if serializer.respond_to?(:each)
20
+ serializer.each do |s|
21
+ result = self.class.new(s, @options.merge(fieldset: @fieldset)).serializable_hash
22
+ @hash[:data] << result[:data]
23
+
24
+ if result[:included]
25
+ @hash[:included] ||= []
26
+ @hash[:included] |= result[:included]
27
+ end
28
+ end
29
+ else
30
+ @hash[:data] = attributes_for_serializer(serializer, @options)
31
+ add_resource_relationships(@hash[:data], serializer)
32
+ end
33
+ @hash
34
+ end
35
+
36
+ def fragment_cache(cached_hash, non_cached_hash)
37
+ root = false if @options.include?(:include)
38
+ JsonApi::FragmentCache.new().fragment_cache(root, cached_hash, non_cached_hash)
39
+ end
40
+
41
+ private
42
+
43
+ def add_relationships(resource, name, serializers)
44
+ resource[:relationships] ||= {}
45
+ resource[:relationships][name] ||= { data: [] }
46
+ resource[:relationships][name][:data] += serializers.map { |serializer| { type: serializer.type, id: serializer.id.to_s } }
47
+ end
48
+
49
+ def add_relationship(resource, name, serializer, val=nil)
50
+ resource[:relationships] ||= {}
51
+ resource[:relationships][name] = { data: nil }
52
+
53
+ if serializer && serializer.object
54
+ resource[:relationships][name][:data] = { type: serializer.type, id: serializer.id.to_s }
55
+ end
56
+ end
57
+
58
+ def add_included(resource_name, serializers, parent = nil)
59
+ unless serializers.respond_to?(:each)
60
+ return unless serializers.object
61
+ serializers = Array(serializers)
62
+ end
63
+ resource_path = [parent, resource_name].compact.join('.')
64
+ if include_assoc?(resource_path)
65
+ @hash[:included] ||= []
66
+
67
+ serializers.each do |serializer|
68
+ attrs = attributes_for_serializer(serializer, @options)
69
+
70
+ add_resource_relationships(attrs, serializer, add_included: false)
71
+
72
+ @hash[:included].push(attrs) unless @hash[:included].include?(attrs)
73
+ end
74
+ end
75
+
76
+ serializers.each do |serializer|
77
+ serializer.each_association do |name, association, opts|
78
+ add_included(name, association, resource_path) if association
79
+ end if include_nested_assoc? resource_path
80
+ end
81
+ end
82
+
83
+ def attributes_for_serializer(serializer, options)
84
+ if serializer.respond_to?(:each)
85
+ result = []
86
+ serializer.each do |object|
87
+ result << resource_object_for(object, options)
88
+ end
89
+ else
90
+ result = resource_object_for(serializer, options)
91
+ end
92
+ result
93
+ end
94
+
95
+ def resource_object_for(serializer, options)
96
+ options[:fields] = @fieldset && @fieldset.fields_for(serializer)
97
+ options[:required_fields] = [:id, :type]
98
+
99
+ cache_check(serializer) do
100
+ attributes = serializer.attributes(options)
101
+
102
+ result = {
103
+ id: attributes.delete(:id).to_s,
104
+ type: attributes.delete(:type)
105
+ }
106
+
107
+ result[:attributes] = attributes if attributes.any?
108
+ result
109
+ end
110
+ end
111
+
112
+ def include_assoc?(assoc)
113
+ return false unless @options[:include]
114
+ check_assoc("#{assoc}$")
115
+ end
116
+
117
+ def include_nested_assoc?(assoc)
118
+ return false unless @options[:include]
119
+ check_assoc("#{assoc}.")
120
+ end
121
+
122
+ def check_assoc(assoc)
123
+ include_opt = @options[:include]
124
+ include_opt = include_opt.split(',') if include_opt.is_a?(String)
125
+ include_opt.any? do |s|
126
+ s.match(/^#{assoc.gsub('.', '\.')}/)
127
+ end
128
+ end
129
+
130
+ def add_resource_relationships(attrs, serializer, options = {})
131
+ options[:add_included] = options.fetch(:add_included, true)
132
+
133
+ serializer.each_association do |name, association, opts|
134
+ attrs[:relationships] ||= {}
135
+
136
+ if association.respond_to?(:each)
137
+ add_relationships(attrs, name, association)
138
+ else
139
+ if opts[:virtual_value]
140
+ add_relationship(attrs, name, nil, opts[:virtual_value])
141
+ else
142
+ add_relationship(attrs, name, association)
143
+ end
144
+ end
145
+
146
+ if options[:add_included]
147
+ Array(association).each do |association|
148
+ add_included(name, association)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ 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,96 @@
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 :FlattenJson
9
+ autoload :Null
10
+ autoload :JsonApi
11
+
12
+ attr_reader :serializer
13
+
14
+ def initialize(serializer, options = {})
15
+ @serializer = serializer
16
+ @options = options
17
+ end
18
+
19
+ def serializable_hash(options = {})
20
+ raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
21
+ end
22
+
23
+ def as_json(options = {})
24
+ hash = serializable_hash(options)
25
+ include_meta(hash) unless self.class == FlattenJson
26
+ hash
27
+ end
28
+
29
+ def self.create(resource, options = {})
30
+ override = options.delete(:adapter)
31
+ klass = override ? adapter_class(override) : ActiveModel::Serializer.adapter
32
+ klass.new(resource, options)
33
+ end
34
+
35
+ def self.adapter_class(adapter)
36
+ "ActiveModel::Serializer::Adapter::#{adapter.to_s.classify}".safe_constantize
37
+ end
38
+
39
+ def fragment_cache(*args)
40
+ raise NotImplementedError, 'This is an abstract method. Should be implemented at the concrete adapter.'
41
+ end
42
+
43
+ private
44
+
45
+ def cache_check(serializer)
46
+ @cached_serializer = serializer
47
+ @klass = @cached_serializer.class
48
+ if is_cached?
49
+ @klass._cache.fetch(cache_key, @klass._cache_options) do
50
+ yield
51
+ end
52
+ elsif is_fragment_cached?
53
+ FragmentCache.new(self, @cached_serializer, @options).fetch
54
+ else
55
+ yield
56
+ end
57
+ end
58
+
59
+ def is_cached?
60
+ @klass._cache && !@klass._cache_only && !@klass._cache_except
61
+ end
62
+
63
+ def is_fragment_cached?
64
+ @klass._cache_only && !@klass._cache_except || !@klass._cache_only && @klass._cache_except
65
+ end
66
+
67
+ def cache_key
68
+ parts = []
69
+ parts << object_cache_key
70
+ parts << @klass._cache_digest unless @klass._cache_options && @klass._cache_options[:skip_digest]
71
+ parts.join("/")
72
+ end
73
+
74
+ def object_cache_key
75
+ (@klass._cache_key) ? "#{@klass._cache_key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key
76
+ end
77
+
78
+ def meta
79
+ serializer.meta if serializer.respond_to?(:meta)
80
+ end
81
+
82
+ def meta_key
83
+ serializer.meta_key || "meta"
84
+ end
85
+
86
+ def root
87
+ serializer.json_key.to_sym if serializer.json_key
88
+ end
89
+
90
+ def include_meta(json)
91
+ json[meta_key] = meta if meta
92
+ json
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,35 @@
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
+ @resource = objects
11
+ @objects = objects.map do |object|
12
+ serializer_class = options.fetch(
13
+ :serializer,
14
+ ActiveModel::Serializer.serializer_for(object)
15
+ )
16
+ serializer_class.new(object, options.except(:serializer))
17
+ end
18
+ @meta = options[:meta]
19
+ @meta_key = options[:meta_key]
20
+ end
21
+
22
+ def json_key
23
+ if @objects.first
24
+ @objects.first.json_key.pluralize
25
+ else
26
+ @resource.name.downcase.pluralize if @resource.try(:name)
27
+ end
28
+ end
29
+
30
+ def root=(root)
31
+ @objects.first.root = root if @objects.first
32
+ end
33
+ end
34
+ end
35
+ 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 = :flatten_json
10
+ end
11
+ end
12
+ end
13
+ end