active_model_serializers 0.10.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +9 -1
- data/Rakefile +3 -3
- data/active_model_serializers.gemspec +15 -15
- data/docs/general/fields.md +31 -0
- data/docs/general/rendering.md +7 -2
- data/docs/general/serializers.md +32 -0
- data/docs/howto/add_pagination_links.md +2 -3
- data/docs/integrations/ember-and-json-api.md +25 -10
- data/lib/action_controller/serialization.rb +4 -3
- data/lib/active_model/serializer.rb +3 -4
- data/lib/active_model/serializer/array_serializer.rb +8 -5
- data/lib/active_model/serializer/associations.rb +2 -2
- data/lib/active_model/serializer/caching.rb +11 -8
- data/lib/active_model/serializer/error_serializer.rb +11 -7
- data/lib/active_model/serializer/errors_serializer.rb +25 -20
- data/lib/active_model/serializer/lint.rb +134 -130
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model_serializers/deprecate.rb +1 -1
- data/lib/active_model_serializers/model.rb +1 -1
- data/lib/active_model_serializers/railtie.rb +1 -1
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +37 -35
- data/lib/generators/rails/serializer_generator.rb +3 -3
- data/lib/grape/active_model_serializers.rb +7 -5
- data/test/action_controller/adapter_selector_test.rb +3 -3
- data/test/action_controller/json_api/errors_test.rb +5 -6
- data/test/action_controller/json_api/linked_test.rb +4 -4
- data/test/action_controller/json_api/pagination_test.rb +19 -19
- data/test/action_controller/serialization_test.rb +1 -1
- data/test/active_model_serializers/json_pointer_test.rb +15 -13
- data/test/active_model_serializers/key_transform_test.rb +254 -252
- data/test/active_model_serializers/model_test.rb +6 -4
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +2 -2
- data/test/adapter/json/transform_test.rb +14 -14
- data/test/adapter/json_api/errors_test.rb +9 -9
- data/test/adapter/json_api/has_many_test.rb +18 -18
- data/test/adapter/json_api/json_api_test.rb +5 -7
- data/test/adapter/json_api/linked_test.rb +1 -1
- data/test/adapter/json_api/pagination_links_test.rb +6 -6
- data/test/adapter/json_api/resource_meta_test.rb +3 -3
- data/test/adapter/json_api/transform_test.rb +218 -218
- data/test/adapter/json_api/type_test.rb +1 -1
- data/test/adapter/json_test.rb +8 -8
- data/test/adapter/null_test.rb +1 -2
- data/test/adapter/polymorphic_test.rb +5 -5
- data/test/adapter_test.rb +1 -1
- data/test/benchmark/bm_caching.rb +1 -1
- data/test/cache_test.rb +29 -1
- data/test/collection_serializer_test.rb +2 -2
- data/test/fixtures/poro.rb +2 -2
- data/test/grape_test.rb +130 -128
- data/test/lint_test.rb +1 -1
- data/test/logger_test.rb +13 -11
- data/test/serializable_resource_test.rb +12 -16
- data/test/serializers/associations_test.rb +10 -10
- data/test/serializers/attribute_test.rb +1 -1
- data/test/serializers/attributes_test.rb +1 -1
- data/test/serializers/fieldset_test.rb +1 -1
- data/test/serializers/root_test.rb +1 -1
- data/test/serializers/serializer_for_test.rb +3 -1
- data/test/support/isolated_unit.rb +4 -2
- data/test/support/serialization_testing.rb +7 -5
- metadata +3 -3
- data/.rubocop_todo.yml +0 -167
@@ -1,146 +1,150 @@
|
|
1
|
-
module ActiveModel
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
1
|
+
module ActiveModel
|
2
|
+
class Serializer
|
3
|
+
module Lint
|
4
|
+
# == Active \Model \Serializer \Lint \Tests
|
5
|
+
#
|
6
|
+
# You can test whether an object is compliant with the Active \Model \Serializers
|
7
|
+
# API by including <tt>ActiveModel::Serializer::Lint::Tests</tt> in your TestCase.
|
8
|
+
# It will include tests that tell you whether your object is fully compliant,
|
9
|
+
# or if not, which aspects of the API are not implemented.
|
10
|
+
#
|
11
|
+
# Note an object is not required to implement all APIs in order to work
|
12
|
+
# with Active \Model \Serializers. This module only intends to provide guidance in case
|
13
|
+
# you want all features out of the box.
|
14
|
+
#
|
15
|
+
# These tests do not attempt to determine the semantic correctness of the
|
16
|
+
# returned values. For instance, you could implement <tt>serializable_hash</tt> to
|
17
|
+
# always return +{}+, and the tests would pass. It is up to you to ensure
|
18
|
+
# that the values are semantically meaningful.
|
19
|
+
module Tests
|
20
|
+
# Passes if the object responds to <tt>serializable_hash</tt> and if it takes
|
21
|
+
# zero or one arguments.
|
22
|
+
# Fails otherwise.
|
23
|
+
#
|
24
|
+
# <tt>serializable_hash</tt> returns a hash representation of a object's attributes.
|
25
|
+
# Typically, it is implemented by including ActiveModel::Serialization.
|
26
|
+
def test_serializable_hash
|
27
|
+
assert_respond_to resource, :serializable_hash, 'The resource should respond to serializable_hash'
|
28
|
+
resource.serializable_hash
|
29
|
+
resource.serializable_hash(nil)
|
30
|
+
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
# Passes if the object responds to <tt>read_attribute_for_serialization</tt>
|
33
|
+
# and if it requires one argument (the attribute to be read).
|
34
|
+
# Fails otherwise.
|
35
|
+
#
|
36
|
+
# <tt>read_attribute_for_serialization</tt> gets the attribute value for serialization
|
37
|
+
# Typically, it is implemented by including ActiveModel::Serialization.
|
38
|
+
def test_read_attribute_for_serialization
|
39
|
+
assert_respond_to resource, :read_attribute_for_serialization, 'The resource should respond to read_attribute_for_serialization'
|
40
|
+
actual_arity = resource.method(:read_attribute_for_serialization).arity
|
41
|
+
# using absolute value since arity is:
|
42
|
+
# 1 for def read_attribute_for_serialization(name); end
|
43
|
+
# -1 for alias :read_attribute_for_serialization :send
|
44
|
+
assert_equal 1, actual_arity.abs, "expected #{actual_arity.inspect}.abs to be 1 or -1"
|
45
|
+
end
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
47
|
+
# Passes if the object responds to <tt>as_json</tt> and if it takes
|
48
|
+
# zero or one arguments.
|
49
|
+
# Fails otherwise.
|
50
|
+
#
|
51
|
+
# <tt>as_json</tt> returns a hash representation of a serialized object.
|
52
|
+
# It may delegate to <tt>serializable_hash</tt>
|
53
|
+
# Typically, it is implemented either by including ActiveModel::Serialization
|
54
|
+
# which includes ActiveModel::Serializers::JSON.
|
55
|
+
# or by the JSON gem when required.
|
56
|
+
def test_as_json
|
57
|
+
assert_respond_to resource, :as_json
|
58
|
+
resource.as_json
|
59
|
+
resource.as_json(nil)
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
# Passes if the object responds to <tt>to_json</tt> and if it takes
|
63
|
+
# zero or one arguments.
|
64
|
+
# Fails otherwise.
|
65
|
+
#
|
66
|
+
# <tt>to_json</tt> returns a string representation (JSON) of a serialized object.
|
67
|
+
# It may be called on the result of <tt>as_json</tt>.
|
68
|
+
# Typically, it is implemented on all objects when the JSON gem is required.
|
69
|
+
def test_to_json
|
70
|
+
assert_respond_to resource, :to_json
|
71
|
+
resource.to_json
|
72
|
+
resource.to_json(nil)
|
73
|
+
end
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
75
|
+
# Passes if the object responds to <tt>cache_key</tt>
|
76
|
+
# Fails otherwise.
|
77
|
+
#
|
78
|
+
# <tt>cache_key</tt> returns a (self-expiring) unique key for the object,
|
79
|
+
# and is part of the (self-expiring) cache_key, which is used by the
|
80
|
+
# adapter. It is not required unless caching is enabled.
|
81
|
+
def test_cache_key
|
82
|
+
assert_respond_to resource, :cache_key
|
83
|
+
actual_arity = resource.method(:cache_key).arity
|
84
|
+
assert_includes [-1, 0], actual_arity, "expected #{actual_arity.inspect} to be 0 or -1"
|
85
|
+
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
87
|
+
# Passes if the object responds to <tt>updated_at</tt> and if it takes no
|
88
|
+
# arguments.
|
89
|
+
# Fails otherwise.
|
90
|
+
#
|
91
|
+
# <tt>updated_at</tt> returns a Time object or iso8601 string and
|
92
|
+
# is part of the (self-expiring) cache_key, which is used by the adapter.
|
93
|
+
# It is not required unless caching is enabled.
|
94
|
+
def test_updated_at
|
95
|
+
assert_respond_to resource, :updated_at
|
96
|
+
actual_arity = resource.method(:updated_at).arity
|
97
|
+
assert_equal 0, actual_arity
|
98
|
+
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
# Passes if the object responds to <tt>id</tt> and if it takes no
|
101
|
+
# arguments.
|
102
|
+
# Fails otherwise.
|
103
|
+
#
|
104
|
+
# <tt>id</tt> returns a unique identifier for the object.
|
105
|
+
# It is not required unless caching is enabled.
|
106
|
+
def test_id
|
107
|
+
assert_respond_to resource, :id
|
108
|
+
assert_equal 0, resource.method(:id).arity
|
109
|
+
end
|
108
110
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
111
|
+
# Passes if the object's class responds to <tt>model_name</tt> and if it
|
112
|
+
# is in an instance of +ActiveModel::Name+.
|
113
|
+
# Fails otherwise.
|
114
|
+
#
|
115
|
+
# <tt>model_name</tt> returns an ActiveModel::Name instance.
|
116
|
+
# It is used by the serializer to identify the object's type.
|
117
|
+
# It is not required unless caching is enabled.
|
118
|
+
def test_model_name
|
119
|
+
resource_class = resource.class
|
120
|
+
assert_respond_to resource_class, :model_name
|
121
|
+
assert_instance_of resource_class.model_name, ActiveModel::Name
|
122
|
+
end
|
121
123
|
|
122
|
-
|
123
|
-
|
124
|
-
|
124
|
+
def test_active_model_errors
|
125
|
+
assert_respond_to resource, :errors
|
126
|
+
end
|
125
127
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
128
|
+
def test_active_model_errors_human_attribute_name
|
129
|
+
assert_respond_to resource.class, :human_attribute_name
|
130
|
+
assert_equal(-2, resource.class.method(:human_attribute_name).arity)
|
131
|
+
end
|
130
132
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
133
|
+
def test_active_model_errors_lookup_ancestors
|
134
|
+
assert_respond_to resource.class, :lookup_ancestors
|
135
|
+
assert_equal 0, resource.class.method(:lookup_ancestors).arity
|
136
|
+
end
|
135
137
|
|
136
|
-
|
138
|
+
private
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
-
|
140
|
+
def resource
|
141
|
+
@resource or fail "'@resource' must be set as the linted object"
|
142
|
+
end
|
141
143
|
|
142
|
-
|
143
|
-
|
144
|
+
def assert_instance_of(result, name)
|
145
|
+
assert result.instance_of?(name), "#{result} should be an instance of #{name}"
|
146
|
+
end
|
147
|
+
end
|
144
148
|
end
|
145
149
|
end
|
146
150
|
end
|
@@ -36,7 +36,7 @@ module ActiveModelSerializers
|
|
36
36
|
target = is_a?(Module) ? "#{self}." : "#{self.class}#"
|
37
37
|
msg = ["NOTE: #{target}#{name} is deprecated",
|
38
38
|
replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
|
39
|
-
"\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(
|
39
|
+
"\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(':')}"]
|
40
40
|
warn "#{msg.join}."
|
41
41
|
send old, *args, &block
|
42
42
|
end
|
@@ -21,7 +21,7 @@ module ActiveModelSerializers
|
|
21
21
|
|
22
22
|
# Defaults to the downcased model name and updated_at
|
23
23
|
def cache_key
|
24
|
-
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime(
|
24
|
+
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}" }
|
25
25
|
end
|
26
26
|
|
27
27
|
# Defaults to the time the serializer file was modified.
|
@@ -23,7 +23,7 @@ module ActiveModelSerializers
|
|
23
23
|
# This hook is run after the action_controller railtie has set the configuration
|
24
24
|
# based on the *environment* configuration and before any config/initializers are run
|
25
25
|
# and also before eager_loading (if enabled).
|
26
|
-
initializer 'active_model_serializers.set_configs', :
|
26
|
+
initializer 'active_model_serializers.set_configs', after: 'action_controller.set_configs' do
|
27
27
|
ActiveModelSerializers.logger = Rails.configuration.action_controller.logger
|
28
28
|
ActiveModelSerializers.config.perform_caching = Rails.configuration.action_controller.perform_caching
|
29
29
|
# We want this hook to run after the config has been set, even if ActionController has already loaded.
|
@@ -22,49 +22,51 @@
|
|
22
22
|
# render jsonapi: model
|
23
23
|
#
|
24
24
|
# No wrapper format needed as it does not apply (i.e. no `wrap_parameters format: [jsonapi]`)
|
25
|
-
module ActiveModelSerializers
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
module ActiveModelSerializers
|
26
|
+
module Jsonapi
|
27
|
+
MEDIA_TYPE = 'application/vnd.api+json'.freeze
|
28
|
+
HEADERS = {
|
29
|
+
response: { 'CONTENT_TYPE'.freeze => MEDIA_TYPE },
|
30
|
+
request: { 'ACCEPT'.freeze => MEDIA_TYPE }
|
31
|
+
}.freeze
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def self.install
|
34
|
+
# actionpack/lib/action_dispatch/http/mime_types.rb
|
35
|
+
Mime::Type.register MEDIA_TYPE, :jsonapi
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
if Rails::VERSION::MAJOR >= 5
|
38
|
+
ActionDispatch::Request.parameter_parsers[:jsonapi] = parser
|
39
|
+
else
|
40
|
+
ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime[:jsonapi]] = parser
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
# ref https://github.com/rails/rails/pull/21496
|
44
|
+
ActionController::Renderers.add :jsonapi do |json, options|
|
45
|
+
json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String)
|
46
|
+
self.content_type ||= Mime[:jsonapi]
|
47
|
+
self.response_body = json
|
48
|
+
end
|
47
49
|
end
|
48
|
-
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
# Proposal: should actually deserialize the JSON API params
|
52
|
+
# to the hash format expected by `ActiveModel::Serializers::JSON`
|
53
|
+
# actionpack/lib/action_dispatch/http/parameters.rb
|
54
|
+
def self.parser
|
55
|
+
lambda do |body|
|
56
|
+
data = JSON.parse(body)
|
57
|
+
data = { _json: data } unless data.is_a?(Hash)
|
58
|
+
data.with_indifferent_access
|
59
|
+
end
|
58
60
|
end
|
59
|
-
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
module ControllerSupport
|
63
|
+
def serialize_jsonapi(json, options)
|
64
|
+
options[:adapter] = :json_api
|
65
|
+
options.fetch(:serialization_context) do
|
66
|
+
options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request)
|
67
|
+
end
|
68
|
+
get_serializer(json, options)
|
66
69
|
end
|
67
|
-
get_serializer(json, options)
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
@@ -2,11 +2,11 @@ module Rails
|
|
2
2
|
module Generators
|
3
3
|
class SerializerGenerator < NamedBase
|
4
4
|
source_root File.expand_path('../templates', __FILE__)
|
5
|
-
check_class_collision :
|
5
|
+
check_class_collision suffix: 'Serializer'
|
6
6
|
|
7
|
-
argument :attributes, :
|
7
|
+
argument :attributes, type: :array, default: [], banner: 'field:type field:type'
|
8
8
|
|
9
|
-
class_option :parent, :
|
9
|
+
class_option :parent, type: :string, desc: 'The parent class for the generated serializer'
|
10
10
|
|
11
11
|
def create_serializer_file
|
12
12
|
template 'serializer.rb.erb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb")
|
@@ -4,11 +4,13 @@ require 'active_model_serializers'
|
|
4
4
|
require 'grape/formatters/active_model_serializers'
|
5
5
|
require 'grape/helpers/active_model_serializers'
|
6
6
|
|
7
|
-
module Grape
|
8
|
-
|
7
|
+
module Grape
|
8
|
+
module ActiveModelSerializers
|
9
|
+
extend ActiveSupport::Concern
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
included do
|
12
|
+
formatter :json, Grape::Formatters::ActiveModelSerializers
|
13
|
+
helpers Grape::Helpers::ActiveModelSerializers
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
@@ -5,17 +5,17 @@ module ActionController
|
|
5
5
|
class AdapterSelectorTest < ActionController::TestCase
|
6
6
|
class AdapterSelectorTestController < ActionController::Base
|
7
7
|
def render_using_default_adapter
|
8
|
-
@profile = Profile.new(
|
8
|
+
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
9
9
|
render json: @profile
|
10
10
|
end
|
11
11
|
|
12
12
|
def render_using_adapter_override
|
13
|
-
@profile = Profile.new(
|
13
|
+
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
14
14
|
render json: @profile, adapter: :json_api
|
15
15
|
end
|
16
16
|
|
17
17
|
def render_skipping_adapter
|
18
|
-
@profile = Profile.new(
|
18
|
+
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
19
19
|
render json: @profile, adapter: false
|
20
20
|
end
|
21
21
|
end
|