active_model_serializers 0.10.0.rc5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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