active_model_serializers 0.8.3 → 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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +29 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +104 -0
- data/.rubocop_todo.yml +167 -0
- data/.simplecov +110 -0
- data/.travis.yml +39 -24
- data/CHANGELOG.md +465 -6
- data/CONTRIBUTING.md +105 -0
- data/Gemfile +50 -1
- data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
- data/README.md +102 -590
- data/Rakefile +93 -8
- data/active_model_serializers.gemspec +65 -23
- data/appveyor.yml +24 -0
- data/bin/bench +171 -0
- data/bin/bench_regression +316 -0
- data/bin/serve_benchmark +39 -0
- data/docs/ARCHITECTURE.md +126 -0
- data/docs/README.md +40 -0
- data/docs/STYLE.md +58 -0
- data/docs/general/adapters.md +245 -0
- data/docs/general/caching.md +52 -0
- data/docs/general/configuration_options.md +100 -0
- data/docs/general/deserialization.md +100 -0
- data/docs/general/getting_started.md +133 -0
- data/docs/general/instrumentation.md +40 -0
- data/docs/general/key_transforms.md +40 -0
- data/docs/general/logging.md +14 -0
- data/docs/general/rendering.md +255 -0
- data/docs/general/serializers.md +372 -0
- data/docs/how-open-source-maintained.jpg +0 -0
- data/docs/howto/add_pagination_links.md +139 -0
- data/docs/howto/add_root_key.md +51 -0
- data/docs/howto/outside_controller_use.md +58 -0
- data/docs/howto/passing_arbitrary_options.md +27 -0
- data/docs/howto/serialize_poro.md +32 -0
- data/docs/howto/test.md +152 -0
- data/docs/integrations/ember-and-json-api.md +112 -0
- data/docs/integrations/grape.md +19 -0
- data/docs/jsonapi/errors.md +56 -0
- data/docs/jsonapi/schema/schema.json +366 -0
- data/docs/jsonapi/schema.md +151 -0
- data/docs/rfcs/0000-namespace.md +106 -0
- data/docs/rfcs/template.md +15 -0
- data/lib/action_controller/serialization.rb +31 -36
- data/lib/active_model/serializable_resource.rb +11 -0
- data/lib/active_model/serializer/adapter/attributes.rb +15 -0
- data/lib/active_model/serializer/adapter/base.rb +16 -0
- data/lib/active_model/serializer/adapter/json.rb +15 -0
- data/lib/active_model/serializer/adapter/json_api.rb +15 -0
- data/lib/active_model/serializer/adapter/null.rb +15 -0
- data/lib/active_model/serializer/adapter.rb +24 -0
- data/lib/active_model/serializer/array_serializer.rb +9 -0
- data/lib/active_model/serializer/association.rb +19 -0
- data/lib/active_model/serializer/associations.rb +87 -220
- data/lib/active_model/serializer/attribute.rb +25 -0
- data/lib/active_model/serializer/attributes.rb +82 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
- data/lib/active_model/serializer/caching.rb +333 -0
- data/lib/active_model/serializer/collection_reflection.rb +7 -0
- data/lib/active_model/serializer/collection_serializer.rb +64 -0
- data/lib/active_model/serializer/configuration.rb +35 -0
- data/lib/active_model/serializer/error_serializer.rb +10 -0
- data/lib/active_model/serializer/errors_serializer.rb +27 -0
- data/lib/active_model/serializer/field.rb +90 -0
- data/lib/active_model/serializer/fieldset.rb +31 -0
- data/lib/active_model/serializer/has_many_reflection.rb +10 -0
- data/lib/active_model/serializer/has_one_reflection.rb +10 -0
- data/lib/active_model/serializer/include_tree.rb +111 -0
- data/lib/active_model/serializer/links.rb +35 -0
- data/lib/active_model/serializer/lint.rb +146 -0
- data/lib/active_model/serializer/meta.rb +29 -0
- data/lib/active_model/serializer/null.rb +17 -0
- data/lib/active_model/serializer/reflection.rb +147 -0
- data/lib/active_model/serializer/singular_reflection.rb +7 -0
- data/lib/active_model/serializer/type.rb +25 -0
- data/lib/active_model/{serializers → serializer}/version.rb +1 -1
- data/lib/active_model/serializer.rb +158 -481
- data/lib/active_model_serializers/adapter/attributes.rb +76 -0
- data/lib/active_model_serializers/adapter/base.rb +83 -0
- data/lib/active_model_serializers/adapter/json.rb +21 -0
- data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
- data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
- data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
- data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
- data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
- data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +62 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
- data/lib/active_model_serializers/adapter/json_api.rb +516 -0
- data/lib/active_model_serializers/adapter/null.rb +9 -0
- data/lib/active_model_serializers/adapter.rb +92 -0
- data/lib/active_model_serializers/callbacks.rb +55 -0
- data/lib/active_model_serializers/deprecate.rb +55 -0
- data/lib/active_model_serializers/deserialization.rb +13 -0
- data/lib/active_model_serializers/json_pointer.rb +14 -0
- data/lib/active_model_serializers/key_transform.rb +70 -0
- data/lib/active_model_serializers/logging.rb +122 -0
- data/lib/active_model_serializers/model.rb +49 -0
- data/lib/active_model_serializers/railtie.rb +46 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +65 -0
- data/lib/active_model_serializers/serializable_resource.rb +81 -0
- data/lib/active_model_serializers/serialization_context.rb +32 -0
- data/lib/active_model_serializers/test/schema.rb +138 -0
- data/lib/active_model_serializers/test/serializer.rb +125 -0
- data/lib/active_model_serializers/test.rb +7 -0
- data/lib/active_model_serializers.rb +32 -89
- data/lib/generators/rails/USAGE +6 -0
- data/lib/generators/rails/resource_override.rb +10 -0
- data/lib/generators/rails/serializer_generator.rb +36 -0
- data/lib/generators/rails/templates/serializer.rb.erb +8 -0
- data/lib/grape/active_model_serializers.rb +14 -0
- data/lib/grape/formatters/active_model_serializers.rb +15 -0
- data/lib/grape/helpers/active_model_serializers.rb +16 -0
- 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/include_test.rb +167 -0
- data/test/action_controller/json_api/deserialization_test.rb +112 -0
- data/test/action_controller/json_api/errors_test.rb +41 -0
- data/test/action_controller/json_api/linked_test.rb +197 -0
- data/test/action_controller/json_api/pagination_test.rb +116 -0
- data/test/action_controller/json_api/transform_test.rb +181 -0
- data/test/action_controller/serialization_scope_name_test.rb +229 -0
- data/test/action_controller/serialization_test.rb +469 -0
- data/test/active_model_serializers/adapter_for_test.rb +208 -0
- data/test/active_model_serializers/json_pointer_test.rb +20 -0
- data/test/active_model_serializers/key_transform_test.rb +263 -0
- data/test/active_model_serializers/logging_test.rb +77 -0
- data/test/active_model_serializers/model_test.rb +9 -0
- data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
- data/test/active_model_serializers/test/schema_test.rb +130 -0
- data/test/active_model_serializers/test/serializer_test.rb +62 -0
- data/test/active_record_test.rb +9 -0
- data/test/adapter/deprecation_test.rb +100 -0
- data/test/adapter/json/belongs_to_test.rb +45 -0
- data/test/adapter/json/collection_test.rb +90 -0
- data/test/adapter/json/has_many_test.rb +45 -0
- data/test/adapter/json/transform_test.rb +93 -0
- data/test/adapter/json_api/belongs_to_test.rb +155 -0
- data/test/adapter/json_api/collection_test.rb +95 -0
- data/test/adapter/json_api/errors_test.rb +78 -0
- data/test/adapter/json_api/fields_test.rb +87 -0
- data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
- data/test/adapter/json_api/has_many_test.rb +144 -0
- data/test/adapter/json_api/has_one_test.rb +80 -0
- data/test/adapter/json_api/json_api_test.rb +35 -0
- data/test/adapter/json_api/linked_test.rb +392 -0
- data/test/adapter/json_api/links_test.rb +93 -0
- data/test/adapter/json_api/pagination_links_test.rb +166 -0
- data/test/adapter/json_api/parse_test.rb +137 -0
- data/test/adapter/json_api/relationship_test.rb +161 -0
- data/test/adapter/json_api/relationships_test.rb +199 -0
- data/test/adapter/json_api/resource_identifier_test.rb +85 -0
- data/test/adapter/json_api/resource_meta_test.rb +100 -0
- data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
- data/test/adapter/json_api/transform_test.rb +502 -0
- data/test/adapter/json_api/type_test.rb +61 -0
- data/test/adapter/json_test.rb +45 -0
- data/test/adapter/null_test.rb +23 -0
- data/test/adapter/polymorphic_test.rb +171 -0
- data/test/adapter_test.rb +67 -0
- data/test/array_serializer_test.rb +20 -73
- data/test/benchmark/app.rb +65 -0
- data/test/benchmark/benchmarking_support.rb +67 -0
- data/test/benchmark/bm_caching.rb +119 -0
- data/test/benchmark/bm_transform.rb +34 -0
- data/test/benchmark/config.ru +3 -0
- data/test/benchmark/controllers.rb +84 -0
- data/test/benchmark/fixtures.rb +219 -0
- data/test/cache_test.rb +485 -0
- data/test/collection_serializer_test.rb +110 -0
- data/test/fixtures/active_record.rb +78 -0
- data/test/fixtures/poro.rb +282 -0
- data/test/generators/scaffold_controller_generator_test.rb +24 -0
- data/test/generators/serializer_generator_test.rb +57 -0
- data/test/grape_test.rb +82 -0
- data/test/include_tree/from_include_args_test.rb +26 -0
- data/test/include_tree/from_string_test.rb +94 -0
- data/test/include_tree/include_args_to_hash_test.rb +64 -0
- data/test/lint_test.rb +49 -0
- data/test/logger_test.rb +18 -0
- data/test/poro_test.rb +9 -0
- data/test/serializable_resource_test.rb +83 -0
- data/test/serializers/association_macros_test.rb +36 -0
- data/test/serializers/associations_test.rb +295 -0
- data/test/serializers/attribute_test.rb +151 -0
- data/test/serializers/attributes_test.rb +52 -0
- data/test/serializers/caching_configuration_test_isolated.rb +170 -0
- data/test/serializers/configuration_test.rb +32 -0
- data/test/serializers/fieldset_test.rb +14 -0
- data/test/serializers/meta_test.rb +196 -0
- data/test/serializers/options_test.rb +21 -0
- data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
- data/test/serializers/root_test.rb +21 -0
- data/test/serializers/serialization_test.rb +55 -0
- data/test/serializers/serializer_for_test.rb +134 -0
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/isolated_unit.rb +79 -0
- data/test/support/rails5_shims.rb +47 -0
- data/test/support/rails_app.rb +45 -0
- data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
- data/test/support/schemas/custom/show.json +7 -0
- data/test/support/schemas/hyper_schema.json +93 -0
- data/test/support/schemas/render_using_json_api.json +43 -0
- data/test/support/schemas/simple_json_pointers.json +10 -0
- data/test/support/serialization_testing.rb +53 -0
- data/test/test_helper.rb +48 -23
- metadata +449 -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_record/serializer_override.rb +0 -16
- data/lib/generators/resource_override.rb +0 -13
- data/lib/generators/serializer/USAGE +0 -9
- data/lib/generators/serializer/serializer_generator.rb +0 -42
- data/lib/generators/serializer/templates/serializer.rb +0 -19
- data/test/association_test.rb +0 -592
- data/test/caching_test.rb +0 -96
- data/test/generators_test.rb +0 -85
- data/test/no_serialization_scope_test.rb +0 -34
- data/test/serialization_scope_name_test.rb +0 -67
- 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
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module Adapter
|
|
3
|
+
UnknownAdapterError = Class.new(ArgumentError)
|
|
4
|
+
ADAPTER_MAP = {} # rubocop:disable Style/MutableConstant
|
|
5
|
+
private_constant :ADAPTER_MAP if defined?(private_constant)
|
|
6
|
+
|
|
7
|
+
class << self # All methods are class functions
|
|
8
|
+
def new(*args)
|
|
9
|
+
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
|
|
10
|
+
"Adapter.new called with args: '#{args.inspect}', from" \
|
|
11
|
+
"'caller[0]'."
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def configured_adapter
|
|
15
|
+
lookup(ActiveModelSerializers.config.adapter)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create(resource, options = {})
|
|
19
|
+
override = options.delete(:adapter)
|
|
20
|
+
klass = override ? adapter_class(override) : configured_adapter
|
|
21
|
+
klass.new(resource, options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @see ActiveModelSerializers::Adapter.lookup
|
|
25
|
+
def adapter_class(adapter)
|
|
26
|
+
ActiveModelSerializers::Adapter.lookup(adapter)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Hash<adapter_name, adapter_class>]
|
|
30
|
+
def adapter_map
|
|
31
|
+
ADAPTER_MAP
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Array<Symbol>] list of adapter names
|
|
35
|
+
def adapters
|
|
36
|
+
adapter_map.keys.sort
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
|
|
40
|
+
# Names are stringified and underscored
|
|
41
|
+
# @param name [Symbol, String, Class] name of the registered adapter
|
|
42
|
+
# @param klass [Class] adapter class itself, optional if name is the class
|
|
43
|
+
# @example
|
|
44
|
+
# AMS::Adapter.register(:my_adapter, MyAdapter)
|
|
45
|
+
# @note The registered name strips out 'ActiveModelSerializers::Adapter::'
|
|
46
|
+
# so that registering 'ActiveModelSerializers::Adapter::Json' and
|
|
47
|
+
# 'Json' will both register as 'json'.
|
|
48
|
+
def register(name, klass = name)
|
|
49
|
+
name = name.to_s.gsub(/\AActiveModelSerializers::Adapter::/, ''.freeze)
|
|
50
|
+
adapter_map[name.underscore] = klass
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param adapter [String, Symbol, Class] name to fetch adapter by
|
|
55
|
+
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
|
|
56
|
+
# @raise [UnknownAdapterError]
|
|
57
|
+
def lookup(adapter)
|
|
58
|
+
# 1. return if is a class
|
|
59
|
+
return adapter if adapter.is_a?(Class)
|
|
60
|
+
adapter_name = adapter.to_s.underscore
|
|
61
|
+
# 2. return if registered
|
|
62
|
+
adapter_map.fetch(adapter_name) do
|
|
63
|
+
# 3. try to find adapter class from environment
|
|
64
|
+
adapter_class = find_by_name(adapter_name)
|
|
65
|
+
register(adapter_name, adapter_class)
|
|
66
|
+
adapter_class
|
|
67
|
+
end
|
|
68
|
+
rescue NameError, ArgumentError => e
|
|
69
|
+
failure_message =
|
|
70
|
+
"NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}"
|
|
71
|
+
raise UnknownAdapterError, failure_message, e.backtrace
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @api private
|
|
75
|
+
def find_by_name(adapter_name)
|
|
76
|
+
adapter_name = adapter_name.to_s.classify.tr('API', 'Api')
|
|
77
|
+
"ActiveModelSerializers::Adapter::#{adapter_name}".safe_constantize ||
|
|
78
|
+
"ActiveModelSerializers::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr
|
|
79
|
+
fail UnknownAdapterError
|
|
80
|
+
end
|
|
81
|
+
private :find_by_name
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Gotta be at the bottom to use the code above it :(
|
|
85
|
+
extend ActiveSupport::Autoload
|
|
86
|
+
autoload :Base
|
|
87
|
+
autoload :Null
|
|
88
|
+
autoload :Attributes
|
|
89
|
+
autoload :Json
|
|
90
|
+
autoload :JsonApi
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Adapted from
|
|
2
|
+
# https://github.com/rails/rails/blob/7f18ea14c8/activejob/lib/active_job/callbacks.rb
|
|
3
|
+
require 'active_support/callbacks'
|
|
4
|
+
|
|
5
|
+
module ActiveModelSerializers
|
|
6
|
+
# = ActiveModelSerializers Callbacks
|
|
7
|
+
#
|
|
8
|
+
# ActiveModelSerializers provides hooks during the life cycle of serialization and
|
|
9
|
+
# allow you to trigger logic. Available callbacks are:
|
|
10
|
+
#
|
|
11
|
+
# * <tt>around_render</tt>
|
|
12
|
+
#
|
|
13
|
+
module Callbacks
|
|
14
|
+
extend ActiveSupport::Concern
|
|
15
|
+
include ActiveSupport::Callbacks
|
|
16
|
+
|
|
17
|
+
included do
|
|
18
|
+
define_callbacks :render
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# These methods will be included into any ActiveModelSerializers object, adding
|
|
22
|
+
# callbacks for +render+.
|
|
23
|
+
module ClassMethods
|
|
24
|
+
# Defines a callback that will get called around the render method,
|
|
25
|
+
# whether it is as_json, to_json, or serializable_hash
|
|
26
|
+
#
|
|
27
|
+
# class ActiveModelSerializers::SerializableResource
|
|
28
|
+
# include ActiveModelSerializers::Callbacks
|
|
29
|
+
#
|
|
30
|
+
# around_render do |args, block|
|
|
31
|
+
# tag_logger do
|
|
32
|
+
# notify_render do
|
|
33
|
+
# block.call(args)
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# def as_json
|
|
39
|
+
# run_callbacks :render do
|
|
40
|
+
# adapter.as_json
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
# # Note: So that we can re-use the instrumenter for as_json, to_json, and
|
|
44
|
+
# # serializable_hash, we aren't using the usual format, which would be:
|
|
45
|
+
# # def render(args)
|
|
46
|
+
# # adapter.as_json
|
|
47
|
+
# # end
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
def around_render(*filters, &blk)
|
|
51
|
+
set_callback(:render, :around, *filters, &blk)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
##
|
|
2
|
+
# Provides a single method +deprecate+ to be used to declare when
|
|
3
|
+
# something is going away.
|
|
4
|
+
#
|
|
5
|
+
# class Legacy
|
|
6
|
+
# def self.klass_method
|
|
7
|
+
# # ...
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# def instance_method
|
|
11
|
+
# # ...
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# extend ActiveModelSerializers::Deprecate
|
|
15
|
+
# deprecate :instance_method, "ActiveModelSerializers::NewPlace#new_method"
|
|
16
|
+
#
|
|
17
|
+
# class << self
|
|
18
|
+
# extend ActiveModelSerializers::Deprecate
|
|
19
|
+
# deprecate :klass_method, :none
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# Adapted from https://github.com/rubygems/rubygems/blob/1591331/lib/rubygems/deprecate.rb
|
|
24
|
+
module ActiveModelSerializers
|
|
25
|
+
module Deprecate
|
|
26
|
+
##
|
|
27
|
+
# Simple deprecation method that deprecates +name+ by wrapping it up
|
|
28
|
+
# in a dummy method. It warns on each call to the dummy method
|
|
29
|
+
# telling the user of +replacement+ (unless +replacement+ is :none) that it is planned to go away.
|
|
30
|
+
|
|
31
|
+
def deprecate(name, replacement)
|
|
32
|
+
old = "_deprecated_#{name}"
|
|
33
|
+
alias_method old, name
|
|
34
|
+
class_eval do
|
|
35
|
+
define_method(name) do |*args, &block|
|
|
36
|
+
target = is_a?(Module) ? "#{self}." : "#{self.class}#"
|
|
37
|
+
msg = ["NOTE: #{target}#{name} is deprecated",
|
|
38
|
+
replacement == :none ? ' with no replacement' : "; use #{replacement} instead",
|
|
39
|
+
"\n#{target}#{name} called from #{ActiveModelSerializers.location_of_caller.join(":")}"
|
|
40
|
+
]
|
|
41
|
+
warn "#{msg.join}."
|
|
42
|
+
send old, *args, &block
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def delegate_and_deprecate(method, delegee)
|
|
48
|
+
delegate method, to: delegee
|
|
49
|
+
deprecate method, "#{delegee.name}."
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module_function :deprecate
|
|
53
|
+
module_function :delegate_and_deprecate
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module Deserialization
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
def jsonapi_parse(*args)
|
|
6
|
+
Adapter::JsonApi::Deserialization.parse(*args)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def jsonapi_parse!(*args)
|
|
10
|
+
Adapter::JsonApi::Deserialization.parse!(*args)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module JsonPointer
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
POINTERS = {
|
|
6
|
+
attribute: '/data/attributes/%s'.freeze,
|
|
7
|
+
primary_data: '/data%s'.freeze
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
def new(pointer_type, value = nil)
|
|
11
|
+
format(POINTERS[pointer_type], value)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
module KeyTransform
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Transforms values to UpperCamelCase or PascalCase.
|
|
8
|
+
#
|
|
9
|
+
# @example:
|
|
10
|
+
# "some_key" => "SomeKey",
|
|
11
|
+
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
|
|
12
|
+
def camel(value)
|
|
13
|
+
case value
|
|
14
|
+
when Hash then value.deep_transform_keys! { |key| camel(key) }
|
|
15
|
+
when Symbol then camel(value.to_s).to_sym
|
|
16
|
+
when String then value.underscore.camelize
|
|
17
|
+
else value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Transforms values to camelCase.
|
|
22
|
+
#
|
|
23
|
+
# @example:
|
|
24
|
+
# "some_key" => "someKey",
|
|
25
|
+
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L66-L76 ActiveSupport::Inflector.camelize}
|
|
26
|
+
def camel_lower(value)
|
|
27
|
+
case value
|
|
28
|
+
when Hash then value.deep_transform_keys! { |key| camel_lower(key) }
|
|
29
|
+
when Symbol then camel_lower(value.to_s).to_sym
|
|
30
|
+
when String then value.underscore.camelize(:lower)
|
|
31
|
+
else value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Transforms values to dashed-case.
|
|
36
|
+
# This is the default case for the JsonApi adapter.
|
|
37
|
+
#
|
|
38
|
+
# @example:
|
|
39
|
+
# "some_key" => "some-key",
|
|
40
|
+
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L185-L187 ActiveSupport::Inflector.dasherize}
|
|
41
|
+
def dash(value)
|
|
42
|
+
case value
|
|
43
|
+
when Hash then value.deep_transform_keys! { |key| dash(key) }
|
|
44
|
+
when Symbol then dash(value.to_s).to_sym
|
|
45
|
+
when String then value.underscore.dasherize
|
|
46
|
+
else value
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Transforms values to underscore_case.
|
|
51
|
+
# This is the default case for deserialization in the JsonApi adapter.
|
|
52
|
+
#
|
|
53
|
+
# @example:
|
|
54
|
+
# "some-key" => "some_key",
|
|
55
|
+
# @see {https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L89-L98 ActiveSupport::Inflector.underscore}
|
|
56
|
+
def underscore(value)
|
|
57
|
+
case value
|
|
58
|
+
when Hash then value.deep_transform_keys! { |key| underscore(key) }
|
|
59
|
+
when Symbol then underscore(value.to_s).to_sym
|
|
60
|
+
when String then value.underscore
|
|
61
|
+
else value
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the value unaltered
|
|
66
|
+
def unaltered(value)
|
|
67
|
+
value
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
##
|
|
2
|
+
# ActiveModelSerializers::Logging
|
|
3
|
+
#
|
|
4
|
+
# https://github.com/rails/rails/blob/280654ef88/activejob/lib/active_job/logging.rb
|
|
5
|
+
#
|
|
6
|
+
module ActiveModelSerializers
|
|
7
|
+
module Logging
|
|
8
|
+
RENDER_EVENT = 'render.active_model_serializers'.freeze
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
include ActiveModelSerializers::Callbacks
|
|
13
|
+
extend Macros
|
|
14
|
+
instrument_rendering
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module ClassMethods
|
|
18
|
+
def instrument_rendering
|
|
19
|
+
around_render do |args, block|
|
|
20
|
+
tag_logger do
|
|
21
|
+
notify_render do
|
|
22
|
+
block.call(args)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Macros that can be used to customize the logging of class or instance methods,
|
|
30
|
+
# by extending the class or its singleton.
|
|
31
|
+
#
|
|
32
|
+
# Adapted from:
|
|
33
|
+
# https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb
|
|
34
|
+
#
|
|
35
|
+
# Provides a single method +notify+ to be used to declare when
|
|
36
|
+
# something a method notifies, with the argument +callback_name+ of the notification callback.
|
|
37
|
+
#
|
|
38
|
+
# class Adapter
|
|
39
|
+
# def self.klass_method
|
|
40
|
+
# # ...
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# def instance_method
|
|
44
|
+
# # ...
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# include ActiveModelSerializers::Logging::Macros
|
|
48
|
+
# notify :instance_method, :render
|
|
49
|
+
#
|
|
50
|
+
# class << self
|
|
51
|
+
# extend ActiveModelSerializers::Logging::Macros
|
|
52
|
+
# notify :klass_method, :render
|
|
53
|
+
# end
|
|
54
|
+
# end
|
|
55
|
+
module Macros
|
|
56
|
+
##
|
|
57
|
+
# Simple notify method that wraps up +name+
|
|
58
|
+
# in a dummy method. It notifies on with the +callback_name+ notifier on
|
|
59
|
+
# each call to the dummy method, telling what the current serializer and adapter
|
|
60
|
+
# are being rendered.
|
|
61
|
+
# Adapted from:
|
|
62
|
+
# https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb
|
|
63
|
+
def notify(name, callback_name)
|
|
64
|
+
class_eval do
|
|
65
|
+
old = "_notifying_#{callback_name}_#{name}"
|
|
66
|
+
alias_method old, name
|
|
67
|
+
define_method name do |*args, &block|
|
|
68
|
+
run_callbacks callback_name do
|
|
69
|
+
send old, *args, &block
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def notify_render(*)
|
|
77
|
+
event_name = RENDER_EVENT
|
|
78
|
+
ActiveSupport::Notifications.instrument(event_name, notify_render_payload) do
|
|
79
|
+
yield
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def notify_render_payload
|
|
84
|
+
{
|
|
85
|
+
serializer: serializer || ActiveModel::Serializer::Null,
|
|
86
|
+
adapter: adapter || ActiveModelSerializers::Adapter::Null
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def tag_logger(*tags)
|
|
93
|
+
if ActiveModelSerializers.logger.respond_to?(:tagged)
|
|
94
|
+
tags.unshift 'active_model_serializers'.freeze unless logger_tagged_by_active_model_serializers?
|
|
95
|
+
ActiveModelSerializers.logger.tagged(*tags) { yield }
|
|
96
|
+
else
|
|
97
|
+
yield
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def logger_tagged_by_active_model_serializers?
|
|
102
|
+
ActiveModelSerializers.logger.formatter.current_tags.include?('active_model_serializers'.freeze)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
|
106
|
+
def render(event)
|
|
107
|
+
info do
|
|
108
|
+
serializer = event.payload[:serializer]
|
|
109
|
+
adapter = event.payload[:adapter]
|
|
110
|
+
duration = event.duration.round(2)
|
|
111
|
+
"Rendered #{serializer.name} with #{adapter.class} (#{duration}ms)"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def logger
|
|
116
|
+
ActiveModelSerializers.logger
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
ActiveModelSerializers::Logging::LogSubscriber.attach_to :active_model_serializers
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ActiveModelSerializers::Model is a convenient
|
|
2
|
+
# serializable class to inherit from when making
|
|
3
|
+
# serializable non-activerecord objects.
|
|
4
|
+
module ActiveModelSerializers
|
|
5
|
+
class Model
|
|
6
|
+
include ActiveModel::Model
|
|
7
|
+
include ActiveModel::Serializers::JSON
|
|
8
|
+
|
|
9
|
+
attr_reader :attributes, :errors
|
|
10
|
+
|
|
11
|
+
def initialize(attributes = {})
|
|
12
|
+
@attributes = attributes
|
|
13
|
+
@errors = ActiveModel::Errors.new(self)
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Defaults to the downcased model name.
|
|
18
|
+
def id
|
|
19
|
+
attributes.fetch(:id) { self.class.name.downcase }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Defaults to the downcased model name and updated_at
|
|
23
|
+
def cache_key
|
|
24
|
+
attributes.fetch(:cache_key) { "#{self.class.name.downcase}/#{id}-#{updated_at.strftime("%Y%m%d%H%M%S%9N")}" }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Defaults to the time the serializer file was modified.
|
|
28
|
+
def updated_at
|
|
29
|
+
attributes.fetch(:updated_at) { File.mtime(__FILE__) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def read_attribute_for_serialization(key)
|
|
33
|
+
if key == :id || key == 'id'
|
|
34
|
+
attributes.fetch(key) { id }
|
|
35
|
+
else
|
|
36
|
+
attributes[key]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The following methods are needed to be minimally implemented for ActiveModel::Errors
|
|
41
|
+
def self.human_attribute_name(attr, _options = {})
|
|
42
|
+
attr
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.lookup_ancestors
|
|
46
|
+
[self]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'rails/railtie'
|
|
2
|
+
require 'action_controller'
|
|
3
|
+
require 'action_controller/railtie'
|
|
4
|
+
require 'action_controller/serialization'
|
|
5
|
+
|
|
6
|
+
module ActiveModelSerializers
|
|
7
|
+
class Railtie < Rails::Railtie
|
|
8
|
+
config.to_prepare do
|
|
9
|
+
ActiveModel::Serializer.serializers_cache.clear
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
initializer 'active_model_serializers.action_controller' do
|
|
13
|
+
ActiveSupport.on_load(:action_controller) do
|
|
14
|
+
include(::ActionController::Serialization)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
initializer 'active_model_serializers.prepare_serialization_context' do
|
|
19
|
+
SerializationContext.url_helpers = Rails.application.routes.url_helpers
|
|
20
|
+
SerializationContext.default_url_options = Rails.application.routes.default_url_options
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# This hook is run after the action_controller railtie has set the configuration
|
|
24
|
+
# based on the *environment* configuration and before any config/initializers are run
|
|
25
|
+
# and also before eager_loading (if enabled).
|
|
26
|
+
initializer 'active_model_serializers.set_configs', :after => 'action_controller.set_configs' do
|
|
27
|
+
ActiveModelSerializers.logger = Rails.configuration.action_controller.logger
|
|
28
|
+
ActiveModelSerializers.config.perform_caching = Rails.configuration.action_controller.perform_caching
|
|
29
|
+
# We want this hook to run after the config has been set, even if ActionController has already loaded.
|
|
30
|
+
ActiveSupport.on_load(:action_controller) do
|
|
31
|
+
ActiveModelSerializers.config.cache_store = ActionController::Base.cache_store
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
generators do |app|
|
|
36
|
+
Rails::Generators.configure!(app.config.generators)
|
|
37
|
+
Rails::Generators.hidden_namespaces.uniq!
|
|
38
|
+
require 'generators/rails/resource_override'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if Rails.env.test?
|
|
42
|
+
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Schema)
|
|
43
|
+
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Based on discussion in https://github.com/rails/rails/pull/23712#issuecomment-184977238,
|
|
2
|
+
# the JSON API media type will have its own format/renderer.
|
|
3
|
+
#
|
|
4
|
+
# > We recommend the media type be registered on its own as jsonapi
|
|
5
|
+
# when a jsonapi Renderer and deserializer (Http::Parameters::DEFAULT_PARSERS) are added.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
#
|
|
9
|
+
# ActiveSupport.on_load(:action_controller) do
|
|
10
|
+
# require 'active_model_serializers/register_jsonapi_renderer'
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# And then in controllers, use `render jsonapi: model` rather than `render json: model, adapter: :json_api`.
|
|
14
|
+
#
|
|
15
|
+
# For example, in a controller action, we can:
|
|
16
|
+
# respond_to do |format|
|
|
17
|
+
# format.jsonapi { render jsonapi: model }
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# or
|
|
21
|
+
#
|
|
22
|
+
# render jsonapi: model
|
|
23
|
+
#
|
|
24
|
+
# No wrapper format needed as it does not apply (i.e. no `wrap_parameters format: [jsonapi]`)
|
|
25
|
+
|
|
26
|
+
module ActiveModelSerializers::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
|
|
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
|
|
40
|
+
|
|
41
|
+
# actionpack/lib/action_dispatch/http/mime_types.rb
|
|
42
|
+
Mime::Type.register ActiveModelSerializers::Jsonapi::MEDIA_TYPE, :jsonapi
|
|
43
|
+
|
|
44
|
+
parsers = Rails::VERSION::MAJOR >= 5 ? ActionDispatch::Http::Parameters : ActionDispatch::ParamsParser
|
|
45
|
+
media_type = Mime::Type.lookup(ActiveModelSerializers::Jsonapi::MEDIA_TYPE)
|
|
46
|
+
|
|
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
|
+
parsers::DEFAULT_PARSERS[media_type] = lambda do |body|
|
|
51
|
+
data = JSON.parse(body)
|
|
52
|
+
data = { :_json => data } unless data.is_a?(Hash)
|
|
53
|
+
data.with_indifferent_access
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# ref https://github.com/rails/rails/pull/21496
|
|
57
|
+
ActionController::Renderers.add :jsonapi do |json, options|
|
|
58
|
+
json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String)
|
|
59
|
+
self.content_type ||= media_type
|
|
60
|
+
self.response_body = json
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
ActiveSupport.on_load(:action_controller) do
|
|
64
|
+
include ActiveModelSerializers::Jsonapi::ControllerSupport
|
|
65
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module ActiveModelSerializers
|
|
4
|
+
class SerializableResource
|
|
5
|
+
ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context, :key_transform])
|
|
6
|
+
include ActiveModelSerializers::Logging
|
|
7
|
+
|
|
8
|
+
delegate :serializable_hash, :as_json, :to_json, to: :adapter
|
|
9
|
+
notify :serializable_hash, :render
|
|
10
|
+
notify :as_json, :render
|
|
11
|
+
notify :to_json, :render
|
|
12
|
+
|
|
13
|
+
# Primary interface to composing a resource with a serializer and adapter.
|
|
14
|
+
# @return the serializable_resource, ready for #as_json/#to_json/#serializable_hash.
|
|
15
|
+
def initialize(resource, options = {})
|
|
16
|
+
@resource = resource
|
|
17
|
+
@adapter_opts, @serializer_opts =
|
|
18
|
+
options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def serialization_scope=(scope)
|
|
22
|
+
serializer_opts[:scope] = scope
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def serialization_scope
|
|
26
|
+
serializer_opts[:scope]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def serialization_scope_name=(scope_name)
|
|
30
|
+
serializer_opts[:scope_name] = scope_name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# NOTE: if no adapter is available, returns the resource itself. (i.e. adapter is a no-op)
|
|
34
|
+
def adapter
|
|
35
|
+
@adapter ||= find_adapter
|
|
36
|
+
end
|
|
37
|
+
alias adapter_instance adapter
|
|
38
|
+
|
|
39
|
+
def find_adapter
|
|
40
|
+
return resource unless serializer?
|
|
41
|
+
ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts)
|
|
42
|
+
rescue ActiveModel::Serializer::CollectionSerializer::NoSerializerError
|
|
43
|
+
resource
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def serializer_instance
|
|
47
|
+
@serializer_instance ||= serializer.new(resource, serializer_opts)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Get serializer either explicitly :serializer or implicitly from resource
|
|
51
|
+
# Remove :serializer key from serializer_opts
|
|
52
|
+
# Replace :serializer key with :each_serializer if present
|
|
53
|
+
def serializer
|
|
54
|
+
@serializer ||=
|
|
55
|
+
begin
|
|
56
|
+
@serializer = serializer_opts.delete(:serializer)
|
|
57
|
+
@serializer ||= ActiveModel::Serializer.serializer_for(resource)
|
|
58
|
+
|
|
59
|
+
if serializer_opts.key?(:each_serializer)
|
|
60
|
+
serializer_opts[:serializer] = serializer_opts.delete(:each_serializer)
|
|
61
|
+
end
|
|
62
|
+
@serializer
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
alias serializer_class serializer
|
|
66
|
+
|
|
67
|
+
# True when no explicit adapter given, or explicit appear is truthy (non-nil)
|
|
68
|
+
# False when explicit adapter is falsy (nil or false)
|
|
69
|
+
def use_adapter?
|
|
70
|
+
!(adapter_opts.key?(:adapter) && !adapter_opts[:adapter])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def serializer?
|
|
74
|
+
use_adapter? && !serializer.nil?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
protected
|
|
78
|
+
|
|
79
|
+
attr_reader :resource, :adapter_opts, :serializer_opts
|
|
80
|
+
end
|
|
81
|
+
end
|