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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +18 -20
- data/CHANGELOG.md +8 -67
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +14 -1
- data/{MIT-LICENSE.txt → LICENSE.txt} +3 -2
- data/README.md +169 -495
- data/Rakefile +6 -12
- data/active_model_serializers.gemspec +21 -19
- data/lib/action_controller/serialization.rb +36 -27
- data/lib/active_model/serializer/adapter/flatten_json.rb +12 -0
- data/lib/active_model/serializer/adapter/fragment_cache.rb +78 -0
- data/lib/active_model/serializer/adapter/json/fragment_cache.rb +15 -0
- data/lib/active_model/serializer/adapter/json.rb +50 -0
- data/lib/active_model/serializer/adapter/json_api/fragment_cache.rb +23 -0
- data/lib/active_model/serializer/adapter/json_api.rb +156 -0
- data/lib/active_model/serializer/adapter/null.rb +11 -0
- data/lib/active_model/serializer/adapter.rb +96 -0
- data/lib/active_model/serializer/array_serializer.rb +35 -0
- data/lib/active_model/serializer/configuration.rb +13 -0
- data/lib/active_model/serializer/fieldset.rb +40 -0
- data/lib/active_model/serializer/railtie.rb +8 -0
- data/lib/active_model/{serializers → serializer}/version.rb +1 -1
- data/lib/active_model/serializer.rb +177 -440
- data/lib/active_model_serializers.rb +10 -86
- data/lib/generators/serializer/USAGE +0 -3
- data/lib/generators/serializer/resource_override.rb +12 -0
- data/lib/generators/serializer/serializer_generator.rb +1 -6
- data/lib/generators/serializer/templates/serializer.rb +2 -13
- data/test/action_controller/adapter_selector_test.rb +53 -0
- data/test/action_controller/explicit_serializer_test.rb +134 -0
- data/test/action_controller/json_api_linked_test.rb +179 -0
- data/test/action_controller/rescue_from_test.rb +32 -0
- data/test/{serialization_scope_name_test.rb → action_controller/serialization_scope_name_test.rb} +7 -11
- data/test/action_controller/serialization_test.rb +383 -0
- data/test/adapter/fragment_cache_test.rb +27 -0
- data/test/adapter/json/belongs_to_test.rb +48 -0
- data/test/adapter/json/collection_test.rb +73 -0
- data/test/adapter/json/has_many_test.rb +36 -0
- data/test/adapter/json_api/belongs_to_test.rb +157 -0
- data/test/adapter/json_api/collection_test.rb +96 -0
- data/test/adapter/json_api/has_many_embed_ids_test.rb +45 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
- data/test/adapter/json_api/has_many_test.rb +110 -0
- data/test/adapter/json_api/has_one_test.rb +61 -0
- data/test/adapter/json_api/linked_test.rb +283 -0
- data/test/adapter/json_test.rb +34 -0
- data/test/adapter/null_test.rb +25 -0
- data/test/adapter_test.rb +43 -0
- data/test/array_serializer_test.rb +31 -63
- data/test/fixtures/poro.rb +230 -0
- data/test/generators/scaffold_controller_generator_test.rb +24 -0
- data/test/{generators_test.rb → generators/serializer_generator_test.rb} +2 -36
- data/test/serializers/adapter_for_test.rb +50 -0
- data/test/serializers/associations_test.rb +127 -0
- data/test/serializers/attribute_test.rb +38 -0
- data/test/serializers/attributes_test.rb +63 -0
- data/test/serializers/cache_test.rb +138 -0
- data/test/serializers/configuration_test.rb +15 -0
- data/test/serializers/fieldset_test.rb +26 -0
- data/test/serializers/meta_test.rb +107 -0
- data/test/serializers/options_test.rb +21 -0
- data/test/serializers/serializer_for_test.rb +65 -0
- data/test/serializers/urls_test.rb +26 -0
- data/test/test_helper.rb +28 -16
- metadata +109 -43
- data/DESIGN.textile +0 -586
- data/Gemfile.edge +0 -9
- data/bench/perf.rb +0 -43
- data/cruft.md +0 -19
- data/lib/active_model/array_serializer.rb +0 -104
- data/lib/active_model/serializer/associations.rb +0 -233
- data/lib/active_record/serializer_override.rb +0 -16
- data/lib/generators/resource_override.rb +0 -13
- data/test/association_test.rb +0 -592
- data/test/caching_test.rb +0 -96
- data/test/no_serialization_scope_test.rb +0 -34
- data/test/serialization_test.rb +0 -392
- data/test/serializer_support_test.rb +0 -51
- data/test/serializer_test.rb +0 -1465
- 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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
#
|
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
|
-
|
4
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
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
|
-
|
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
|
-
|
48
|
-
|
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,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,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,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
|