active_model_serializers 0.10.0 → 0.10.7
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 +5 -5
- data/.rubocop.yml +6 -5
- data/.travis.yml +30 -21
- data/CHANGELOG.md +172 -2
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +23 -4
- data/README.md +166 -28
- data/Rakefile +3 -32
- data/active_model_serializers.gemspec +22 -25
- data/appveyor.yml +10 -6
- data/bin/rubocop +38 -0
- data/docs/README.md +2 -1
- data/docs/general/adapters.md +35 -11
- data/docs/general/caching.md +7 -1
- data/docs/general/configuration_options.md +86 -1
- data/docs/general/deserialization.md +1 -1
- data/docs/general/fields.md +31 -0
- data/docs/general/getting_started.md +1 -1
- data/docs/general/logging.md +7 -0
- data/docs/general/rendering.md +63 -25
- data/docs/general/serializers.md +125 -14
- data/docs/howto/add_pagination_links.md +16 -17
- data/docs/howto/add_relationship_links.md +140 -0
- data/docs/howto/add_root_key.md +11 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +12 -4
- data/docs/howto/passing_arbitrary_options.md +2 -2
- data/docs/howto/serialize_poro.md +46 -5
- data/docs/howto/test.md +2 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +67 -32
- data/docs/jsonapi/schema.md +1 -1
- data/lib/action_controller/serialization.rb +13 -3
- data/lib/active_model/serializer/adapter/base.rb +2 -0
- data/lib/active_model/serializer/array_serializer.rb +8 -5
- data/lib/active_model/serializer/association.rb +62 -10
- data/lib/active_model/serializer/belongs_to_reflection.rb +4 -3
- data/lib/active_model/serializer/collection_serializer.rb +39 -13
- data/lib/active_model/serializer/{caching.rb → concerns/caching.rb} +82 -115
- 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/has_many_reflection.rb +3 -3
- data/lib/active_model/serializer/has_one_reflection.rb +1 -4
- data/lib/active_model/serializer/lazy_association.rb +95 -0
- data/lib/active_model/serializer/lint.rb +134 -130
- data/lib/active_model/serializer/reflection.rb +127 -67
- data/lib/active_model/serializer/version.rb +1 -1
- data/lib/active_model/serializer.rb +297 -79
- data/lib/active_model_serializers/adapter/attributes.rb +3 -66
- data/lib/active_model_serializers/adapter/base.rb +39 -39
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +2 -2
- data/lib/active_model_serializers/adapter/json_api/link.rb +1 -1
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +47 -21
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +75 -23
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +39 -10
- data/lib/active_model_serializers/adapter/json_api.rb +71 -57
- data/lib/active_model_serializers/adapter.rb +6 -0
- data/lib/active_model_serializers/deprecate.rb +1 -2
- data/lib/active_model_serializers/deserialization.rb +2 -0
- data/lib/active_model_serializers/lookup_chain.rb +80 -0
- data/lib/active_model_serializers/model.rb +109 -28
- data/lib/active_model_serializers/railtie.rb +3 -1
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +44 -31
- data/lib/active_model_serializers/serializable_resource.rb +6 -5
- data/lib/active_model_serializers/serialization_context.rb +10 -3
- data/lib/active_model_serializers/test/schema.rb +2 -2
- data/lib/active_model_serializers.rb +16 -1
- data/lib/generators/rails/resource_override.rb +1 -1
- data/lib/generators/rails/serializer_generator.rb +4 -4
- data/lib/grape/active_model_serializers.rb +7 -5
- data/lib/grape/formatters/active_model_serializers.rb +19 -2
- data/lib/grape/helpers/active_model_serializers.rb +1 -0
- data/lib/tasks/rubocop.rake +53 -0
- data/test/action_controller/adapter_selector_test.rb +14 -5
- data/test/action_controller/explicit_serializer_test.rb +5 -4
- data/test/action_controller/json/include_test.rb +106 -27
- data/test/action_controller/json_api/deserialization_test.rb +1 -1
- data/test/action_controller/json_api/errors_test.rb +8 -9
- data/test/action_controller/json_api/fields_test.rb +66 -0
- data/test/action_controller/json_api/linked_test.rb +29 -24
- data/test/action_controller/json_api/pagination_test.rb +31 -23
- data/test/action_controller/json_api/transform_test.rb +11 -3
- data/test/action_controller/lookup_proc_test.rb +49 -0
- data/test/action_controller/namespace_lookup_test.rb +232 -0
- data/test/action_controller/serialization_scope_name_test.rb +12 -6
- data/test/action_controller/serialization_test.rb +12 -9
- data/test/active_model_serializers/json_pointer_test.rb +15 -13
- data/test/active_model_serializers/model_test.rb +137 -4
- data/test/active_model_serializers/railtie_test_isolated.rb +12 -7
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +23 -10
- data/test/active_model_serializers/test/schema_test.rb +3 -2
- data/test/adapter/attributes_test.rb +40 -0
- data/test/adapter/json/collection_test.rb +14 -0
- data/test/adapter/json/has_many_test.rb +10 -2
- data/test/adapter/json/transform_test.rb +15 -15
- data/test/adapter/json_api/collection_test.rb +4 -3
- data/test/adapter/json_api/errors_test.rb +17 -19
- data/test/adapter/json_api/fields_test.rb +12 -3
- data/test/adapter/json_api/has_many_test.rb +49 -20
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +213 -0
- data/test/adapter/json_api/json_api_test.rb +5 -7
- data/test/adapter/json_api/linked_test.rb +33 -12
- data/test/adapter/json_api/links_test.rb +4 -2
- data/test/adapter/json_api/pagination_links_test.rb +53 -13
- data/test/adapter/json_api/parse_test.rb +1 -1
- data/test/adapter/json_api/relationship_test.rb +309 -73
- data/test/adapter/json_api/resource_meta_test.rb +3 -3
- data/test/adapter/json_api/transform_test.rb +263 -253
- data/test/adapter/json_api/type_test.rb +168 -36
- data/test/adapter/json_test.rb +8 -7
- data/test/adapter/null_test.rb +1 -2
- data/test/adapter/polymorphic_test.rb +52 -5
- data/test/adapter_test.rb +1 -1
- data/test/benchmark/app.rb +1 -1
- data/test/benchmark/benchmarking_support.rb +1 -1
- data/test/benchmark/bm_active_record.rb +81 -0
- data/test/benchmark/bm_adapter.rb +38 -0
- data/test/benchmark/bm_caching.rb +16 -16
- data/test/benchmark/bm_lookup_chain.rb +83 -0
- data/test/benchmark/bm_transform.rb +21 -10
- data/test/benchmark/controllers.rb +16 -17
- data/test/benchmark/fixtures.rb +72 -72
- data/test/cache_test.rb +235 -69
- data/test/collection_serializer_test.rb +31 -14
- data/test/fixtures/active_record.rb +45 -10
- data/test/fixtures/poro.rb +124 -181
- data/test/generators/serializer_generator_test.rb +23 -5
- data/test/grape_test.rb +170 -56
- data/test/lint_test.rb +1 -1
- data/test/logger_test.rb +13 -11
- data/test/serializable_resource_test.rb +18 -22
- data/test/serializers/association_macros_test.rb +3 -2
- data/test/serializers/associations_test.rb +222 -49
- data/test/serializers/attribute_test.rb +5 -3
- data/test/serializers/attributes_test.rb +1 -1
- data/test/serializers/caching_configuration_test_isolated.rb +6 -6
- data/test/serializers/fieldset_test.rb +1 -1
- data/test/serializers/meta_test.rb +12 -6
- data/test/serializers/options_test.rb +17 -6
- data/test/serializers/read_attribute_for_serialization_test.rb +3 -3
- data/test/serializers/reflection_test.rb +427 -0
- data/test/serializers/root_test.rb +1 -1
- data/test/serializers/serialization_test.rb +2 -2
- data/test/serializers/serializer_for_test.rb +12 -10
- data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
- data/test/support/isolated_unit.rb +9 -4
- data/test/support/rails5_shims.rb +8 -2
- data/test/support/rails_app.rb +2 -9
- data/test/support/serialization_testing.rb +31 -5
- data/test/test_helper.rb +13 -0
- metadata +130 -71
- data/.rubocop_todo.yml +0 -167
- data/docs/ARCHITECTURE.md +0 -126
- data/lib/active_model/serializer/associations.rb +0 -100
- data/lib/active_model/serializer/attributes.rb +0 -82
- data/lib/active_model/serializer/collection_reflection.rb +0 -7
- data/lib/active_model/serializer/configuration.rb +0 -35
- data/lib/active_model/serializer/include_tree.rb +0 -111
- data/lib/active_model/serializer/links.rb +0 -35
- data/lib/active_model/serializer/meta.rb +0 -29
- data/lib/active_model/serializer/singular_reflection.rb +0 -7
- data/lib/active_model/serializer/type.rb +0 -25
- data/lib/active_model_serializers/key_transform.rb +0 -70
- data/test/active_model_serializers/key_transform_test.rb +0 -263
- data/test/adapter/json_api/has_many_embed_ids_test.rb +0 -43
- data/test/adapter/json_api/relationships_test.rb +0 -199
- data/test/adapter/json_api/resource_identifier_test.rb +0 -85
- data/test/include_tree/from_include_args_test.rb +0 -26
- data/test/include_tree/from_string_test.rb +0 -94
- data/test/include_tree/include_args_to_hash_test.rb +0 -64
|
@@ -1,49 +1,130 @@
|
|
|
1
|
-
# ActiveModelSerializers::Model is a convenient
|
|
2
|
-
#
|
|
3
|
-
#
|
|
1
|
+
# ActiveModelSerializers::Model is a convenient superclass for making your models
|
|
2
|
+
# from Plain-Old Ruby Objects (PORO). It also serves as a reference implementation
|
|
3
|
+
# that satisfies ActiveModel::Serializer::Lint::Tests.
|
|
4
|
+
require 'active_support/core_ext/hash'
|
|
4
5
|
module ActiveModelSerializers
|
|
5
6
|
class Model
|
|
6
|
-
include ActiveModel::Model
|
|
7
7
|
include ActiveModel::Serializers::JSON
|
|
8
|
+
include ActiveModel::Model
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
# Declare names of attributes to be included in +attributes+ hash.
|
|
11
|
+
# Is only available as a class-method since the ActiveModel::Serialization mixin in Rails
|
|
12
|
+
# uses an +attribute_names+ local variable, which may conflict if we were to add instance methods here.
|
|
13
|
+
#
|
|
14
|
+
# @overload attribute_names
|
|
15
|
+
# @return [Array<Symbol>]
|
|
16
|
+
class_attribute :attribute_names, instance_writer: false, instance_reader: false
|
|
17
|
+
# Initialize +attribute_names+ for all subclasses. The array is usually
|
|
18
|
+
# mutated in the +attributes+ method, but can be set directly, as well.
|
|
19
|
+
self.attribute_names = []
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
# Easily declare instance attributes with setters and getters for each.
|
|
22
|
+
#
|
|
23
|
+
# To initialize an instance, all attributes must have setters.
|
|
24
|
+
# However, the hash returned by +attributes+ instance method will ALWAYS
|
|
25
|
+
# be the value of the initial attributes, regardless of what accessors are defined.
|
|
26
|
+
# The only way to change the change the attributes after initialization is
|
|
27
|
+
# to mutate the +attributes+ directly.
|
|
28
|
+
# Accessor methods do NOT mutate the attributes. (This is a bug).
|
|
29
|
+
#
|
|
30
|
+
# @note For now, the Model only supports the notion of 'attributes'.
|
|
31
|
+
# In the tests, there is a special Model that also supports 'associations'. This is
|
|
32
|
+
# important so that we can add accessors for values that should not appear in the
|
|
33
|
+
# attributes hash when modeling associations. It is not yet clear if it
|
|
34
|
+
# makes sense for a PORO to have associations outside of the tests.
|
|
35
|
+
#
|
|
36
|
+
# @overload attributes(names)
|
|
37
|
+
# @param names [Array<String, Symbol>]
|
|
38
|
+
# @param name [String, Symbol]
|
|
39
|
+
def self.attributes(*names)
|
|
40
|
+
self.attribute_names |= names.map(&:to_sym)
|
|
41
|
+
# Silence redefinition of methods warnings
|
|
42
|
+
ActiveModelSerializers.silence_warnings do
|
|
43
|
+
attr_accessor(*names)
|
|
44
|
+
end
|
|
15
45
|
end
|
|
16
46
|
|
|
17
|
-
#
|
|
18
|
-
def
|
|
19
|
-
|
|
47
|
+
# Opt-in to breaking change
|
|
48
|
+
def self.derive_attributes_from_names_and_fix_accessors
|
|
49
|
+
unless included_modules.include?(DeriveAttributesFromNamesAndFixAccessors)
|
|
50
|
+
prepend(DeriveAttributesFromNamesAndFixAccessors)
|
|
51
|
+
end
|
|
20
52
|
end
|
|
21
53
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
54
|
+
module DeriveAttributesFromNamesAndFixAccessors
|
|
55
|
+
def self.included(base)
|
|
56
|
+
# NOTE that +id+ will always be in +attributes+.
|
|
57
|
+
base.attributes :id
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Override the +attributes+ method so that the hash is derived from +attribute_names+.
|
|
61
|
+
#
|
|
62
|
+
# The fields in +attribute_names+ determines the returned hash.
|
|
63
|
+
# +attributes+ are returned frozen to prevent any expectations that mutation affects
|
|
64
|
+
# the actual values in the model.
|
|
65
|
+
def attributes
|
|
66
|
+
self.class.attribute_names.each_with_object({}) do |attribute_name, result|
|
|
67
|
+
result[attribute_name] = public_send(attribute_name).freeze
|
|
68
|
+
end.with_indifferent_access.freeze
|
|
69
|
+
end
|
|
25
70
|
end
|
|
26
71
|
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
72
|
+
# Support for validation and other ActiveModel::Errors
|
|
73
|
+
# @return [ActiveModel::Errors]
|
|
74
|
+
attr_reader :errors
|
|
75
|
+
|
|
76
|
+
# (see #updated_at)
|
|
77
|
+
attr_writer :updated_at
|
|
78
|
+
|
|
79
|
+
# The only way to change the attributes of an instance is to directly mutate the attributes.
|
|
80
|
+
# @example
|
|
81
|
+
#
|
|
82
|
+
# model.attributes[:foo] = :bar
|
|
83
|
+
# @return [Hash]
|
|
84
|
+
attr_reader :attributes
|
|
85
|
+
|
|
86
|
+
# @param attributes [Hash]
|
|
87
|
+
def initialize(attributes = {})
|
|
88
|
+
attributes ||= {} # protect against nil
|
|
89
|
+
@attributes = attributes.symbolize_keys.with_indifferent_access
|
|
90
|
+
@errors = ActiveModel::Errors.new(self)
|
|
91
|
+
super
|
|
30
92
|
end
|
|
31
93
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
94
|
+
# Defaults to the downcased model name.
|
|
95
|
+
# This probably isn't a good default, since it's not a unique instance identifier,
|
|
96
|
+
# but that's what is currently implemented \_('-')_/.
|
|
97
|
+
#
|
|
98
|
+
# @note Though +id+ is defined, it will only show up
|
|
99
|
+
# in +attributes+ when it is passed in to the initializer or added to +attributes+,
|
|
100
|
+
# such as <tt>attributes[:id] = 5</tt>.
|
|
101
|
+
# @return [String, Numeric, Symbol]
|
|
102
|
+
def id
|
|
103
|
+
attributes.fetch(:id) do
|
|
104
|
+
defined?(@id) ? @id : self.class.model_name.name && self.class.model_name.name.downcase
|
|
37
105
|
end
|
|
38
106
|
end
|
|
39
107
|
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
-
|
|
108
|
+
# When not set, defaults to the time the file was modified.
|
|
109
|
+
#
|
|
110
|
+
# @note Though +updated_at+ and +updated_at=+ are defined, it will only show up
|
|
111
|
+
# in +attributes+ when it is passed in to the initializer or added to +attributes+,
|
|
112
|
+
# such as <tt>attributes[:updated_at] = Time.current</tt>.
|
|
113
|
+
# @return [String, Numeric, Time]
|
|
114
|
+
def updated_at
|
|
115
|
+
attributes.fetch(:updated_at) do
|
|
116
|
+
defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
|
|
117
|
+
end
|
|
43
118
|
end
|
|
44
119
|
|
|
45
|
-
|
|
46
|
-
|
|
120
|
+
# To customize model behavior, this method must be redefined. However,
|
|
121
|
+
# there are other ways of setting the +cache_key+ a serializer uses.
|
|
122
|
+
# @return [String]
|
|
123
|
+
def cache_key
|
|
124
|
+
ActiveSupport::Cache.expand_cache_key([
|
|
125
|
+
self.class.model_name.name.downcase,
|
|
126
|
+
"#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
|
|
127
|
+
].compact)
|
|
47
128
|
end
|
|
48
129
|
end
|
|
49
130
|
end
|
|
@@ -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.
|
|
@@ -32,11 +32,13 @@ module ActiveModelSerializers
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# :nocov:
|
|
35
36
|
generators do |app|
|
|
36
37
|
Rails::Generators.configure!(app.config.generators)
|
|
37
38
|
Rails::Generators.hidden_namespaces.uniq!
|
|
38
39
|
require 'generators/rails/resource_override'
|
|
39
40
|
end
|
|
41
|
+
# :nocov:
|
|
40
42
|
|
|
41
43
|
if Rails.env.test?
|
|
42
44
|
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Schema)
|
|
@@ -22,44 +22,57 @@
|
|
|
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
|
+
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
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
response: { 'CONTENT_TYPE'.freeze => MEDIA_TYPE },
|
|
30
|
-
request: { 'ACCEPT'.freeze => MEDIA_TYPE }
|
|
31
|
-
}.freeze
|
|
32
|
-
module ControllerSupport
|
|
33
|
-
def serialize_jsonapi(json, options)
|
|
34
|
-
options[:adapter] = :json_api
|
|
35
|
-
options.fetch(:serialization_context) { options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request) }
|
|
36
|
-
get_serializer(json, options)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
33
|
+
def self.install
|
|
34
|
+
# actionpack/lib/action_dispatch/http/mime_types.rb
|
|
35
|
+
Mime::Type.register MEDIA_TYPE, :jsonapi
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
49
|
+
end
|
|
46
50
|
|
|
47
|
-
# Proposal: should actually deserialize the JSON API params
|
|
48
|
-
# to the hash format expected by `ActiveModel::Serializers::JSON`
|
|
49
|
-
# actionpack/lib/action_dispatch/http/parameters.rb
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
60
|
+
end
|
|
55
61
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
61
72
|
end
|
|
62
73
|
|
|
74
|
+
ActiveModelSerializers::Jsonapi.install
|
|
75
|
+
|
|
63
76
|
ActiveSupport.on_load(:action_controller) do
|
|
64
77
|
include ActiveModelSerializers::Jsonapi::ControllerSupport
|
|
65
78
|
end
|
|
@@ -38,9 +38,10 @@ module ActiveModelSerializers
|
|
|
38
38
|
|
|
39
39
|
def find_adapter
|
|
40
40
|
return resource unless serializer?
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
adapter = catch :no_serializer do
|
|
42
|
+
ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts)
|
|
43
|
+
end
|
|
44
|
+
adapter || resource
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def serializer_instance
|
|
@@ -49,12 +50,12 @@ module ActiveModelSerializers
|
|
|
49
50
|
|
|
50
51
|
# Get serializer either explicitly :serializer or implicitly from resource
|
|
51
52
|
# Remove :serializer key from serializer_opts
|
|
52
|
-
#
|
|
53
|
+
# Remove :each_serializer if present and set as :serializer key
|
|
53
54
|
def serializer
|
|
54
55
|
@serializer ||=
|
|
55
56
|
begin
|
|
56
57
|
@serializer = serializer_opts.delete(:serializer)
|
|
57
|
-
@serializer ||= ActiveModel::Serializer.serializer_for(resource)
|
|
58
|
+
@serializer ||= ActiveModel::Serializer.serializer_for(resource, serializer_opts)
|
|
58
59
|
|
|
59
60
|
if serializer_opts.key?(:each_serializer)
|
|
60
61
|
serializer_opts[:serializer] = serializer_opts.delete(:each_serializer)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'active_support/core_ext/array/extract_options'
|
|
1
2
|
module ActiveModelSerializers
|
|
2
3
|
class SerializationContext
|
|
3
4
|
class << self
|
|
@@ -22,9 +23,15 @@ module ActiveModelSerializers
|
|
|
22
23
|
|
|
23
24
|
attr_reader :request_url, :query_parameters, :key_transform
|
|
24
25
|
|
|
25
|
-
def initialize(
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
def initialize(*args)
|
|
27
|
+
options = args.extract_options!
|
|
28
|
+
if args.size == 1
|
|
29
|
+
request = args.pop
|
|
30
|
+
options[:request_url] = request.original_url[/\A[^?]+/]
|
|
31
|
+
options[:query_parameters] = request.query_parameters
|
|
32
|
+
end
|
|
33
|
+
@request_url = options.delete(:request_url)
|
|
34
|
+
@query_parameters = options.delete(:query_parameters)
|
|
28
35
|
@url_helpers = options.delete(:url_helpers) || self.class.url_helpers
|
|
29
36
|
@default_url_options = options.delete(:default_url_options) || self.class.default_url_options
|
|
30
37
|
end
|
|
@@ -60,11 +60,11 @@ module ActiveModelSerializers
|
|
|
60
60
|
attr_reader :document_store
|
|
61
61
|
|
|
62
62
|
def controller_path
|
|
63
|
-
request.filtered_parameters[:controller]
|
|
63
|
+
request.filtered_parameters.with_indifferent_access[:controller]
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def action
|
|
67
|
-
request.filtered_parameters[:action]
|
|
67
|
+
request.filtered_parameters.with_indifferent_access[:action]
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def schema_directory
|
|
@@ -14,6 +14,7 @@ module ActiveModelSerializers
|
|
|
14
14
|
autoload :Adapter
|
|
15
15
|
autoload :JsonPointer
|
|
16
16
|
autoload :Deprecate
|
|
17
|
+
autoload :LookupChain
|
|
17
18
|
|
|
18
19
|
class << self; attr_accessor :logger; end
|
|
19
20
|
self.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT))
|
|
@@ -31,8 +32,22 @@ module ActiveModelSerializers
|
|
|
31
32
|
[file, lineno]
|
|
32
33
|
end
|
|
33
34
|
|
|
35
|
+
# Memoized default include directive
|
|
36
|
+
# @return [JSONAPI::IncludeDirective]
|
|
37
|
+
def self.default_include_directive
|
|
38
|
+
@default_include_directive ||= JSONAPI::IncludeDirective.new(config.default_includes, allow_wildcard: true)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.silence_warnings
|
|
42
|
+
original_verbose = $VERBOSE
|
|
43
|
+
$VERBOSE = nil
|
|
44
|
+
yield
|
|
45
|
+
ensure
|
|
46
|
+
$VERBOSE = original_verbose
|
|
47
|
+
end
|
|
48
|
+
|
|
34
49
|
require 'active_model/serializer/version'
|
|
35
50
|
require 'active_model/serializer'
|
|
36
51
|
require 'active_model/serializable_resource'
|
|
37
|
-
require 'active_model_serializers/railtie' if defined?(::Rails)
|
|
52
|
+
require 'active_model_serializers/railtie' if defined?(::Rails::Railtie)
|
|
38
53
|
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")
|
|
@@ -25,7 +25,7 @@ module Rails
|
|
|
25
25
|
def parent_class_name
|
|
26
26
|
if options[:parent]
|
|
27
27
|
options[:parent]
|
|
28
|
-
elsif
|
|
28
|
+
elsif 'ApplicationSerializer'.safe_constantize
|
|
29
29
|
'ApplicationSerializer'
|
|
30
30
|
else
|
|
31
31
|
'ActiveModel::Serializer'
|
|
@@ -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
|
|
@@ -2,14 +2,31 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Serializer options can be passed as a hash from your Grape endpoint using env[:active_model_serializer_options],
|
|
4
4
|
# or better yet user the render helper in Grape::Helpers::ActiveModelSerializers
|
|
5
|
+
|
|
6
|
+
require 'active_model_serializers/serialization_context'
|
|
7
|
+
|
|
5
8
|
module Grape
|
|
6
9
|
module Formatters
|
|
7
10
|
module ActiveModelSerializers
|
|
8
11
|
def self.call(resource, env)
|
|
9
|
-
serializer_options =
|
|
10
|
-
serializer_options.merge!(env[:active_model_serializer_options]) if env[:active_model_serializer_options]
|
|
12
|
+
serializer_options = build_serializer_options(env)
|
|
11
13
|
::ActiveModelSerializers::SerializableResource.new(resource, serializer_options).to_json
|
|
12
14
|
end
|
|
15
|
+
|
|
16
|
+
def self.build_serializer_options(env)
|
|
17
|
+
ams_options = env[:active_model_serializer_options] || {}
|
|
18
|
+
|
|
19
|
+
# Add serialization context
|
|
20
|
+
ams_options.fetch(:serialization_context) do
|
|
21
|
+
request = env['grape.request']
|
|
22
|
+
ams_options[:serialization_context] = ::ActiveModelSerializers::SerializationContext.new(
|
|
23
|
+
request_url: request.url[/\A[^?]+/],
|
|
24
|
+
query_parameters: request.params
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ams_options
|
|
29
|
+
end
|
|
13
30
|
end
|
|
14
31
|
end
|
|
15
32
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'rubocop'
|
|
3
|
+
require 'rubocop/rake_task'
|
|
4
|
+
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
|
5
|
+
else
|
|
6
|
+
require 'rbconfig'
|
|
7
|
+
# https://github.com/bundler/bundler/blob/1b3eb2465a/lib/bundler/constants.rb#L2
|
|
8
|
+
windows_platforms = /(msdos|mswin|djgpp|mingw)/
|
|
9
|
+
if RbConfig::CONFIG['host_os'] =~ windows_platforms
|
|
10
|
+
desc 'No-op rubocop on Windows-- unsupported platform'
|
|
11
|
+
task :rubocop do
|
|
12
|
+
puts 'Skipping rubocop on Windows'
|
|
13
|
+
end
|
|
14
|
+
elsif defined?(::Rubinius)
|
|
15
|
+
desc 'No-op rubocop to avoid rbx segfault'
|
|
16
|
+
task :rubocop do
|
|
17
|
+
puts 'Skipping rubocop on rbx due to segfault'
|
|
18
|
+
puts 'https://github.com/rubinius/rubinius/issues/3499'
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
Rake::Task[:rubocop].clear if Rake::Task.task_defined?(:rubocop)
|
|
22
|
+
patterns = [
|
|
23
|
+
'Gemfile',
|
|
24
|
+
'Rakefile',
|
|
25
|
+
'lib/**/*.{rb,rake}',
|
|
26
|
+
'config/**/*.rb',
|
|
27
|
+
'app/**/*.rb',
|
|
28
|
+
'test/**/*.rb'
|
|
29
|
+
]
|
|
30
|
+
desc 'Execute rubocop'
|
|
31
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
32
|
+
task.options = ['--rails', '--display-cop-names', '--display-style-guide']
|
|
33
|
+
task.formatters = ['progress']
|
|
34
|
+
task.patterns = patterns
|
|
35
|
+
task.fail_on_error = true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
namespace :rubocop do
|
|
39
|
+
desc 'Auto-gen rubocop config'
|
|
40
|
+
task :auto_gen_config do
|
|
41
|
+
options = ['--auto-gen-config'].concat patterns
|
|
42
|
+
require 'benchmark'
|
|
43
|
+
result = 0
|
|
44
|
+
cli = RuboCop::CLI.new
|
|
45
|
+
time = Benchmark.realtime do
|
|
46
|
+
result = cli.run(options)
|
|
47
|
+
end
|
|
48
|
+
puts "Finished in #{time} seconds" if cli.options[:debug]
|
|
49
|
+
abort('RuboCop failed!') if result.nonzero?
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -3,19 +3,28 @@ require 'test_helper'
|
|
|
3
3
|
module ActionController
|
|
4
4
|
module Serialization
|
|
5
5
|
class AdapterSelectorTest < ActionController::TestCase
|
|
6
|
+
class Profile < Model
|
|
7
|
+
attributes :id, :name, :description
|
|
8
|
+
associations :comments
|
|
9
|
+
end
|
|
10
|
+
class ProfileSerializer < ActiveModel::Serializer
|
|
11
|
+
type 'profiles'
|
|
12
|
+
attributes :name, :description
|
|
13
|
+
end
|
|
14
|
+
|
|
6
15
|
class AdapterSelectorTestController < ActionController::Base
|
|
7
16
|
def render_using_default_adapter
|
|
8
|
-
@profile = Profile.new(
|
|
17
|
+
@profile = Profile.new(name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
|
9
18
|
render json: @profile
|
|
10
19
|
end
|
|
11
20
|
|
|
12
21
|
def render_using_adapter_override
|
|
13
|
-
@profile = Profile.new(
|
|
22
|
+
@profile = Profile.new(id: 'render_using_adapter_override', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
|
14
23
|
render json: @profile, adapter: :json_api
|
|
15
24
|
end
|
|
16
25
|
|
|
17
26
|
def render_skipping_adapter
|
|
18
|
-
@profile = Profile.new(
|
|
27
|
+
@profile = Profile.new(id: 'render_skipping_adapter_id', name: 'Name 1', description: 'Description 1', comments: 'Comments 1')
|
|
19
28
|
render json: @profile, adapter: false
|
|
20
29
|
end
|
|
21
30
|
end
|
|
@@ -32,7 +41,7 @@ module ActionController
|
|
|
32
41
|
|
|
33
42
|
expected = {
|
|
34
43
|
data: {
|
|
35
|
-
id:
|
|
44
|
+
id: 'render_using_adapter_override',
|
|
36
45
|
type: 'profiles',
|
|
37
46
|
attributes: {
|
|
38
47
|
name: 'Name 1',
|
|
@@ -46,7 +55,7 @@ module ActionController
|
|
|
46
55
|
|
|
47
56
|
def test_render_skipping_adapter
|
|
48
57
|
get :render_skipping_adapter
|
|
49
|
-
assert_equal '{"
|
|
58
|
+
assert_equal '{"id":"render_skipping_adapter_id","name":"Name 1","description":"Description 1"}', response.body
|
|
50
59
|
end
|
|
51
60
|
end
|
|
52
61
|
end
|
|
@@ -100,11 +100,12 @@ module ActionController
|
|
|
100
100
|
get :render_array_using_explicit_serializer_and_custom_serializers
|
|
101
101
|
|
|
102
102
|
expected = [
|
|
103
|
-
{
|
|
103
|
+
{
|
|
104
|
+
'title' => 'New Post',
|
|
104
105
|
'body' => 'Body',
|
|
105
|
-
'id' =>
|
|
106
|
+
'id' => @controller.instance_variable_get(:@post).id,
|
|
106
107
|
'comments' => [{ 'id' => 1 }, { 'id' => 2 }],
|
|
107
|
-
'author' => { 'id' =>
|
|
108
|
+
'author' => { 'id' => @controller.instance_variable_get(:@author).id }
|
|
108
109
|
}
|
|
109
110
|
]
|
|
110
111
|
|
|
@@ -122,7 +123,7 @@ module ActionController
|
|
|
122
123
|
id: 42,
|
|
123
124
|
lat: '-23.550520',
|
|
124
125
|
lng: '-46.633309',
|
|
125
|
-
|
|
126
|
+
address: 'Nowhere' # is a virtual attribute on LocationSerializer
|
|
126
127
|
}
|
|
127
128
|
]
|
|
128
129
|
}
|