active_model_serializers 0.10.0.rc5 → 0.10.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -0
  3. data/.travis.yml +0 -8
  4. data/CHANGELOG.md +23 -0
  5. data/CONTRIBUTING.md +14 -4
  6. data/Gemfile +1 -2
  7. data/README.md +3 -3
  8. data/active_model_serializers.gemspec +1 -1
  9. data/appveyor.yml +6 -10
  10. data/docs/ARCHITECTURE.md +1 -1
  11. data/docs/README.md +1 -0
  12. data/docs/general/deserialization.md +19 -19
  13. data/docs/general/serializers.md +33 -0
  14. data/docs/howto/serialize_poro.md +32 -0
  15. data/lib/active_model/serializer.rb +2 -0
  16. data/lib/active_model/serializer/caching.rb +185 -3
  17. data/lib/active_model/serializer/field.rb +36 -2
  18. data/lib/active_model/serializer/lint.rb +8 -18
  19. data/lib/active_model/serializer/version.rb +1 -1
  20. data/lib/active_model_serializers.rb +0 -2
  21. data/lib/active_model_serializers/adapter/attributes.rb +20 -38
  22. data/lib/active_model_serializers/adapter/base.rb +8 -15
  23. data/lib/active_model_serializers/adapter/json.rb +12 -2
  24. data/lib/active_model_serializers/adapter/json_api.rb +33 -30
  25. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +10 -5
  26. data/lib/active_model_serializers/adapter/null.rb +1 -2
  27. data/lib/active_model_serializers/register_jsonapi_renderer.rb +3 -2
  28. data/lib/active_model_serializers/serializable_resource.rb +1 -1
  29. data/lib/active_model_serializers/test/schema.rb +44 -9
  30. data/test/action_controller/json_api/transform_test.rb +2 -1
  31. data/test/action_controller/serialization_test.rb +3 -1
  32. data/test/active_model_serializers/test/schema_test.rb +5 -3
  33. data/test/active_model_serializers/test/serializer_test.rb +1 -2
  34. data/test/adapter/json/transform_test.rb +14 -14
  35. data/test/adapter/json_api/has_many_test.rb +3 -2
  36. data/test/adapter/json_api/has_one_test.rb +3 -2
  37. data/test/adapter/json_api/pagination_links_test.rb +39 -21
  38. data/test/adapter/json_api/transform_test.rb +36 -34
  39. data/test/adapter/polymorphic_test.rb +111 -12
  40. data/test/adapter_test.rb +27 -0
  41. data/test/array_serializer_test.rb +10 -25
  42. data/test/benchmark/bm_caching.rb +17 -15
  43. data/test/benchmark/controllers.rb +9 -2
  44. data/test/benchmark/fixtures.rb +56 -4
  45. data/test/cache_test.rb +103 -6
  46. data/test/fixtures/active_record.rb +10 -0
  47. data/test/fixtures/poro.rb +31 -3
  48. data/test/serializers/associations_test.rb +43 -15
  49. data/test/serializers/attribute_test.rb +44 -16
  50. data/test/serializers/meta_test.rb +5 -7
  51. data/test/support/isolated_unit.rb +0 -1
  52. data/test/test_helper.rb +19 -21
  53. metadata +7 -12
  54. data/lib/active_model_serializers/cached_serializer.rb +0 -87
  55. data/lib/active_model_serializers/fragment_cache.rb +0 -118
  56. data/test/active_model_serializers/cached_serializer_test.rb +0 -80
  57. data/test/active_model_serializers/fragment_cache_test.rb +0 -34
@@ -92,7 +92,7 @@ module ActiveModel
92
92
  assert_equal(expected, actual)
93
93
  end
94
94
 
95
- def test_meta_key_is_used_with_json_api
95
+ def test_meta_key_is_not_used_with_json_api
96
96
  actual = ActiveModelSerializers::SerializableResource.new(
97
97
  @blog,
98
98
  adapter: :json_api,
@@ -105,18 +105,17 @@ module ActiveModel
105
105
  type: 'blogs',
106
106
  attributes: { title: 'AMS Hints' }
107
107
  },
108
- 'haha_meta' => { total: 10 }
108
+ meta: { total: 10 }
109
109
  }
110
110
  assert_equal(expected, actual)
111
111
  end
112
112
 
113
- def test_meta_key_is_not_present_when_blank_object_with_json_api
113
+ def test_meta_key_is_not_present_when_empty_hash_with_json_api
114
114
  actual = ActiveModelSerializers::SerializableResource.new(
115
115
  @blog,
116
116
  adapter: :json_api,
117
117
  serializer: AlternateBlogSerializer,
118
- meta: {},
119
- meta_key: 'haha_meta'
118
+ meta: {}
120
119
  ).as_json
121
120
  expected = {
122
121
  data: {
@@ -133,8 +132,7 @@ module ActiveModel
133
132
  @blog,
134
133
  adapter: :json_api,
135
134
  serializer: AlternateBlogSerializer,
136
- meta: '',
137
- meta_key: 'haha_meta'
135
+ meta: ''
138
136
  ).as_json
139
137
  expected = {
140
138
  data: {
@@ -41,7 +41,6 @@ require 'active_support/core_ext/string/access'
41
41
 
42
42
  # These files do not require any others and are needed
43
43
  # to run the tests
44
- require 'active_support/testing/autorun'
45
44
  require 'active_support/testing/isolation'
46
45
 
47
46
  module TestHelpers
@@ -15,31 +15,29 @@ require 'action_controller'
15
15
  require 'action_controller/test_case'
16
16
  require 'action_controller/railtie'
17
17
  require 'active_model_serializers'
18
+ # For now, we only restrict the options to serializable_hash/as_json/to_json
19
+ # in tests, to ensure developers don't add any unsupported options.
20
+ # There's no known benefit, at this time, to having the filtering run in
21
+ # production when the excluded options would simply not be used.
22
+ #
23
+ # However, for documentation purposes, the constant
24
+ # ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS is defined
25
+ # in the Serializer.
26
+ ActiveModelSerializers::Adapter::Base.class_eval do
27
+ alias_method :original_serialization_options, :serialization_options
28
+
29
+ def serialization_options(options)
30
+ original_serialization_options(options)
31
+ .slice(*ActiveModel::Serializer::SERIALIZABLE_HASH_VALID_KEYS)
32
+ end
33
+ end
18
34
  require 'fileutils'
19
35
  FileUtils.mkdir_p(File.expand_path('../../tmp/cache', __FILE__))
20
36
 
21
37
  gem 'minitest'
22
- begin
23
- require 'minitest'
24
- rescue LoadError
25
- # Minitest 4
26
- require 'minitest/autorun'
27
- $minitest_version = 4
28
- # https://github.com/seattlerb/minitest/blob/644a52fd0/lib/minitest/autorun.rb
29
- # https://github.com/seattlerb/minitest/blob/644a52fd0/lib/minitest/unit.rb#L768-L787
30
- # Ensure backward compatibility with Minitest 4
31
- Minitest = MiniTest unless defined?(Minitest)
32
- Minitest::Test = MiniTest::Unit::TestCase
33
- else
34
- # Minitest 5
35
- require 'minitest/autorun'
36
- $minitest_version = 5
37
- # https://github.com/seattlerb/minitest/blob/e21fdda9d/lib/minitest/autorun.rb
38
- # https://github.com/seattlerb/minitest/blob/e21fdda9d/lib/minitest.rb#L45-L59
39
- # Filter out Minitest backtrace while allowing backtrace from other libraries
40
- # to be shown.
41
- Minitest.backtrace_filter = Minitest::BacktraceFilter.new
42
- end
38
+ require 'minitest'
39
+ require 'minitest/autorun'
40
+ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
43
41
 
44
42
  require 'support/rails_app'
45
43
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_model_serializers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0.rc5
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Klabnik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-04 00:00:00.000000000 Z
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -240,6 +240,7 @@ files:
240
240
  - docs/howto/add_root_key.md
241
241
  - docs/howto/outside_controller_use.md
242
242
  - docs/howto/passing_arbitrary_options.md
243
+ - docs/howto/serialize_poro.md
243
244
  - docs/howto/test.md
244
245
  - docs/integrations/ember-and-json-api.md
245
246
  - docs/integrations/grape.md
@@ -297,11 +298,9 @@ files:
297
298
  - lib/active_model_serializers/adapter/json_api/relationship.rb
298
299
  - lib/active_model_serializers/adapter/json_api/resource_identifier.rb
299
300
  - lib/active_model_serializers/adapter/null.rb
300
- - lib/active_model_serializers/cached_serializer.rb
301
301
  - lib/active_model_serializers/callbacks.rb
302
302
  - lib/active_model_serializers/deprecate.rb
303
303
  - lib/active_model_serializers/deserialization.rb
304
- - lib/active_model_serializers/fragment_cache.rb
305
304
  - lib/active_model_serializers/json_pointer.rb
306
305
  - lib/active_model_serializers/key_transform.rb
307
306
  - lib/active_model_serializers/logging.rb
@@ -331,8 +330,6 @@ files:
331
330
  - test/action_controller/serialization_scope_name_test.rb
332
331
  - test/action_controller/serialization_test.rb
333
332
  - test/active_model_serializers/adapter_for_test.rb
334
- - test/active_model_serializers/cached_serializer_test.rb
335
- - test/active_model_serializers/fragment_cache_test.rb
336
333
  - test/active_model_serializers/json_pointer_test.rb
337
334
  - test/active_model_serializers/key_transform_test.rb
338
335
  - test/active_model_serializers/logging_test.rb
@@ -424,7 +421,7 @@ licenses:
424
421
  metadata: {}
425
422
  post_install_message: |
426
423
  NOTE: The default key case for the JsonApi adapter has changed to dashed.
427
- See https://github.com/rails-api/active_model_serializers/blob/master/docs/general/key_transform.md
424
+ See https://github.com/rails-api/active_model_serializers/blob/master/docs/general/key_transforms.md
428
425
  for more information on configuring this behavior.
429
426
  rdoc_options: []
430
427
  require_paths:
@@ -436,12 +433,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
436
433
  version: 2.0.0
437
434
  required_rubygems_version: !ruby/object:Gem::Requirement
438
435
  requirements:
439
- - - ">"
436
+ - - ">="
440
437
  - !ruby/object:Gem::Version
441
- version: 1.3.1
438
+ version: '0'
442
439
  requirements: []
443
440
  rubyforge_project:
444
- rubygems_version: 2.4.8
441
+ rubygems_version: 2.4.5
445
442
  signing_key:
446
443
  specification_version: 4
447
444
  summary: Conventions-based JSON generation for Rails.
@@ -457,8 +454,6 @@ test_files:
457
454
  - test/action_controller/serialization_scope_name_test.rb
458
455
  - test/action_controller/serialization_test.rb
459
456
  - test/active_model_serializers/adapter_for_test.rb
460
- - test/active_model_serializers/cached_serializer_test.rb
461
- - test/active_model_serializers/fragment_cache_test.rb
462
457
  - test/active_model_serializers/json_pointer_test.rb
463
458
  - test/active_model_serializers/key_transform_test.rb
464
459
  - test/active_model_serializers/logging_test.rb
@@ -1,87 +0,0 @@
1
- module ActiveModelSerializers
2
- class CachedSerializer
3
- UndefinedCacheKey = Class.new(StandardError)
4
-
5
- def initialize(serializer)
6
- @cached_serializer = serializer
7
- @klass = @cached_serializer.class
8
- end
9
-
10
- def cache_check(adapter_instance)
11
- if cached?
12
- @klass._cache.fetch(cache_key(adapter_instance), @klass._cache_options) do
13
- yield
14
- end
15
- elsif fragment_cached?
16
- FragmentCache.new(adapter_instance, @cached_serializer, adapter_instance.instance_options).fetch
17
- else
18
- yield
19
- end
20
- end
21
-
22
- def cached?
23
- @klass.cache_enabled?
24
- end
25
-
26
- def fragment_cached?
27
- @klass.fragment_cache_enabled?
28
- end
29
-
30
- def cache_key(adapter_instance)
31
- return @cache_key if defined?(@cache_key)
32
-
33
- parts = []
34
- parts << object_cache_key
35
- parts << adapter_instance.cached_name
36
- parts << @klass._cache_digest unless @klass._skip_digest?
37
- @cache_key = parts.join('/')
38
- end
39
-
40
- # Use object's cache_key if available, else derive a key from the object
41
- # Pass the `key` option to the `cache` declaration or override this method to customize the cache key
42
- def object_cache_key
43
- if @cached_serializer.object.respond_to?(:cache_key)
44
- @cached_serializer.object.cache_key
45
- elsif (cache_key = (@klass._cache_key || @klass._cache_options[:key]))
46
- object_time_safe = @cached_serializer.object.updated_at
47
- object_time_safe = object_time_safe.strftime('%Y%m%d%H%M%S%9N') if object_time_safe.respond_to?(:strftime)
48
- "#{cache_key}/#{@cached_serializer.object.id}-#{object_time_safe}"
49
- else
50
- fail UndefinedCacheKey, "#{@cached_serializer.object.class} must define #cache_key, or the 'key:' option must be passed into '#{@klass}.cache'"
51
- end
52
- end
53
-
54
- # find all cache_key for the collection_serializer
55
- # @param serializers [ActiveModel::Serializer::CollectionSerializer]
56
- # @param adapter_instance [ActiveModelSerializers::Adapter::Base]
57
- # @param include_tree [ActiveModel::Serializer::IncludeTree]
58
- # @return [Array] all cache_key of collection_serializer
59
- def self.object_cache_keys(serializers, adapter_instance, include_tree)
60
- cache_keys = []
61
-
62
- serializers.each do |serializer|
63
- cache_keys << object_cache_key(serializer, adapter_instance)
64
-
65
- serializer.associations(include_tree).each do |association|
66
- if association.serializer.respond_to?(:each)
67
- association.serializer.each do |sub_serializer|
68
- cache_keys << object_cache_key(sub_serializer, adapter_instance)
69
- end
70
- else
71
- cache_keys << object_cache_key(association.serializer, adapter_instance)
72
- end
73
- end
74
- end
75
-
76
- cache_keys.compact.uniq
77
- end
78
-
79
- # @return [String, nil] the cache_key of the serializer or nil
80
- def self.object_cache_key(serializer, adapter_instance)
81
- return unless serializer.present? && serializer.object.present?
82
-
83
- cached_serializer = new(serializer)
84
- cached_serializer.cached? ? cached_serializer.cache_key(adapter_instance) : nil
85
- end
86
- end
87
- end
@@ -1,118 +0,0 @@
1
- module ActiveModelSerializers
2
- class FragmentCache
3
- attr_reader :serializer
4
-
5
- def initialize(adapter, serializer, options)
6
- @instance_options = options
7
- @adapter = adapter
8
- @serializer = serializer
9
- end
10
-
11
- # 1. Create a CachedSerializer and NonCachedSerializer from the serializer class
12
- # 2. Serialize the above two with the given adapter
13
- # 3. Pass their serializations to the adapter +::fragment_cache+
14
- def fetch
15
- object = serializer.object
16
-
17
- # It will split the serializer into two, one that will be cached and one that will not
18
- serializers = fragment_serializer
19
-
20
- # Get serializable hash from both
21
- cached_hash = serialize(object, serializers[:cached])
22
- non_cached_hash = serialize(object, serializers[:non_cached])
23
-
24
- # Merge both results
25
- adapter.fragment_cache(cached_hash, non_cached_hash)
26
- end
27
-
28
- protected
29
-
30
- attr_reader :instance_options, :adapter
31
-
32
- private
33
-
34
- def serialize(object, serializer_class)
35
- SerializableResource.new(
36
- object,
37
- serializer: serializer_class,
38
- adapter: adapter.class
39
- ).serializable_hash
40
- end
41
-
42
- # Given a hash of its cached and non-cached serializers
43
- # 1. Determine cached attributes from serializer class options
44
- # 2. Add cached attributes to cached Serializer
45
- # 3. Add non-cached attributes to non-cached Serializer
46
- def cache_attributes(serializers)
47
- klass = serializer.class
48
- attributes = klass._attributes
49
- cache_only = klass._cache_only
50
- cached_attributes = cache_only ? cache_only : attributes - klass._cache_except
51
- non_cached_attributes = attributes - cached_attributes
52
- attributes_keys = klass._attributes_keys
53
-
54
- add_attributes_to_serializer(serializers[:cached], cached_attributes, attributes_keys)
55
- add_attributes_to_serializer(serializers[:non_cached], non_cached_attributes, attributes_keys)
56
- end
57
-
58
- def add_attributes_to_serializer(serializer, attributes, attributes_keys)
59
- attributes.each do |attribute|
60
- options = attributes_keys[attribute] || {}
61
- serializer.attribute(attribute, options)
62
- end
63
- end
64
-
65
- # Given a resource name
66
- # 1. Dynamically creates a CachedSerializer and NonCachedSerializer
67
- # for a given class 'name'
68
- # 2. Call
69
- # CachedSerializer.cache(serializer._cache_options)
70
- # CachedSerializer.fragmented(serializer)
71
- # NonCachedSerializer.cache(serializer._cache_options)
72
- # 3. Build a hash keyed to the +cached+ and +non_cached+ serializers
73
- # 4. Call +cached_attributes+ on the serializer class and the above hash
74
- # 5. Return the hash
75
- #
76
- # @example
77
- # When +name+ is <tt>User::Admin</tt>
78
- # creates the Serializer classes (if they don't exist).
79
- # CachedUser_AdminSerializer
80
- # NonCachedUser_AdminSerializer
81
- #
82
- def fragment_serializer
83
- klass = serializer.class
84
- serializer_class_name = to_valid_const_name(klass.name)
85
-
86
- cached = "Cached#{serializer_class_name}"
87
- non_cached = "NonCached#{serializer_class_name}"
88
-
89
- cached_serializer = get_or_create_serializer(cached)
90
- non_cached_serializer = get_or_create_serializer(non_cached)
91
-
92
- klass._cache_options ||= {}
93
- cache_key = klass._cache_key
94
- klass._cache_options[:key] = cache_key if cache_key
95
- cached_serializer.cache(klass._cache_options)
96
-
97
- type = klass._type
98
- cached_serializer.type(type)
99
- non_cached_serializer.type(type)
100
-
101
- non_cached_serializer.fragmented(serializer)
102
- cached_serializer.fragmented(serializer)
103
-
104
- serializers = { cached: cached_serializer, non_cached: non_cached_serializer }
105
- cache_attributes(serializers)
106
- serializers
107
- end
108
-
109
- def get_or_create_serializer(name)
110
- return Object.const_get(name) if Object.const_defined?(name)
111
- Object.const_set(name, Class.new(ActiveModel::Serializer))
112
- end
113
-
114
- def to_valid_const_name(name)
115
- name.gsub('::', '_')
116
- end
117
- end
118
- end
@@ -1,80 +0,0 @@
1
- require 'test_helper'
2
- module ActiveModelSerializers
3
- module Adapter
4
- class CachedSerializerTest < ActiveSupport::TestCase
5
- def test_cached_false_without_cache_store
6
- cached_serializer = build do |serializer|
7
- serializer._cache = nil
8
- end
9
- refute cached_serializer.cached?
10
- end
11
-
12
- def test_cached_true_with_cache_store_and_without_cache_only_and_cache_except
13
- cached_serializer = build do |serializer|
14
- serializer._cache = Object
15
- end
16
- assert cached_serializer.cached?
17
- end
18
-
19
- def test_cached_false_with_cache_store_and_with_cache_only
20
- cached_serializer = build do |serializer|
21
- serializer._cache = Object
22
- serializer._cache_only = [:name]
23
- end
24
- refute cached_serializer.cached?
25
- end
26
-
27
- def test_cached_false_with_cache_store_and_with_cache_except
28
- cached_serializer = build do |serializer|
29
- serializer._cache = Object
30
- serializer._cache_except = [:content]
31
- end
32
- refute cached_serializer.cached?
33
- end
34
-
35
- def test_fragment_cached_false_without_cache_store
36
- cached_serializer = build do |serializer|
37
- serializer._cache = nil
38
- serializer._cache_only = [:name]
39
- end
40
- refute cached_serializer.fragment_cached?
41
- end
42
-
43
- def test_fragment_cached_true_with_cache_store_and_cache_only
44
- cached_serializer = build do |serializer|
45
- serializer._cache = Object
46
- serializer._cache_only = [:name]
47
- end
48
- assert cached_serializer.fragment_cached?
49
- end
50
-
51
- def test_fragment_cached_true_with_cache_store_and_cache_except
52
- cached_serializer = build do |serializer|
53
- serializer._cache = Object
54
- serializer._cache_except = [:content]
55
- end
56
- assert cached_serializer.fragment_cached?
57
- end
58
-
59
- def test_fragment_cached_false_with_cache_store_and_cache_except_and_cache_only
60
- cached_serializer = build do |serializer|
61
- serializer._cache = Object
62
- serializer._cache_except = [:content]
63
- serializer._cache_only = [:name]
64
- end
65
- refute cached_serializer.fragment_cached?
66
- end
67
-
68
- private
69
-
70
- def build
71
- serializer = Class.new(ActiveModel::Serializer)
72
- serializer._cache_key = nil
73
- serializer._cache_options = nil
74
- yield serializer if block_given?
75
- serializer_instance = serializer.new(Object)
76
- CachedSerializer.new(serializer_instance)
77
- end
78
- end
79
- end
80
- end