active_model_serializers 0.8.3 → 0.10.8
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/.github/ISSUE_TEMPLATE.md +29 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +105 -0
- data/.simplecov +110 -0
- data/.travis.yml +50 -24
- data/CHANGELOG.md +650 -6
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +105 -0
- data/Gemfile +69 -1
- data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
- data/README.md +195 -545
- data/Rakefile +64 -8
- data/active_model_serializers.gemspec +62 -23
- data/appveyor.yml +28 -0
- data/bin/bench +171 -0
- data/bin/bench_regression +316 -0
- data/bin/rubocop +38 -0
- data/bin/serve_benchmark +39 -0
- data/docs/README.md +41 -0
- data/docs/STYLE.md +58 -0
- data/docs/general/adapters.md +269 -0
- data/docs/general/caching.md +58 -0
- data/docs/general/configuration_options.md +185 -0
- data/docs/general/deserialization.md +100 -0
- data/docs/general/fields.md +31 -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 +21 -0
- data/docs/general/rendering.md +293 -0
- data/docs/general/serializers.md +495 -0
- data/docs/how-open-source-maintained.jpg +0 -0
- data/docs/howto/add_pagination_links.md +138 -0
- data/docs/howto/add_relationship_links.md +140 -0
- data/docs/howto/add_root_key.md +62 -0
- data/docs/howto/grape_integration.md +42 -0
- data/docs/howto/outside_controller_use.md +66 -0
- data/docs/howto/passing_arbitrary_options.md +27 -0
- data/docs/howto/serialize_poro.md +73 -0
- data/docs/howto/test.md +154 -0
- data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
- data/docs/integrations/ember-and-json-api.md +147 -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 +43 -38
- 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 +18 -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 +12 -0
- data/lib/active_model/serializer/association.rb +71 -0
- data/lib/active_model/serializer/attribute.rb +25 -0
- data/lib/active_model/serializer/belongs_to_reflection.rb +11 -0
- data/lib/active_model/serializer/collection_serializer.rb +88 -0
- data/lib/active_model/serializer/concerns/caching.rb +300 -0
- data/lib/active_model/serializer/error_serializer.rb +14 -0
- data/lib/active_model/serializer/errors_serializer.rb +32 -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 +7 -0
- data/lib/active_model/serializer/lazy_association.rb +96 -0
- data/lib/active_model/serializer/link.rb +21 -0
- data/lib/active_model/serializer/lint.rb +150 -0
- data/lib/active_model/serializer/null.rb +17 -0
- data/lib/active_model/serializer/reflection.rb +210 -0
- data/lib/active_model/{serializers → serializer}/version.rb +1 -1
- data/lib/active_model/serializer.rb +343 -442
- data/lib/active_model_serializers/adapter/attributes.rb +13 -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 +88 -0
- data/lib/active_model_serializers/adapter/json_api/relationship.rb +104 -0
- data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +66 -0
- data/lib/active_model_serializers/adapter/json_api.rb +533 -0
- data/lib/active_model_serializers/adapter/null.rb +9 -0
- data/lib/active_model_serializers/adapter.rb +98 -0
- data/lib/active_model_serializers/callbacks.rb +55 -0
- data/lib/active_model_serializers/deprecate.rb +54 -0
- data/lib/active_model_serializers/deserialization.rb +15 -0
- data/lib/active_model_serializers/json_pointer.rb +14 -0
- data/lib/active_model_serializers/logging.rb +122 -0
- data/lib/active_model_serializers/lookup_chain.rb +80 -0
- data/lib/active_model_serializers/model.rb +130 -0
- data/lib/active_model_serializers/railtie.rb +50 -0
- data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
- data/lib/active_model_serializers/serializable_resource.rb +82 -0
- data/lib/active_model_serializers/serialization_context.rb +39 -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 +47 -81
- 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 +16 -0
- data/lib/grape/formatters/active_model_serializers.rb +32 -0
- data/lib/grape/helpers/active_model_serializers.rb +17 -0
- data/lib/tasks/rubocop.rake +53 -0
- data/test/action_controller/adapter_selector_test.rb +62 -0
- data/test/action_controller/explicit_serializer_test.rb +135 -0
- data/test/action_controller/json/include_test.rb +246 -0
- data/test/action_controller/json_api/deserialization_test.rb +112 -0
- data/test/action_controller/json_api/errors_test.rb +40 -0
- data/test/action_controller/json_api/fields_test.rb +66 -0
- data/test/action_controller/json_api/linked_test.rb +202 -0
- data/test/action_controller/json_api/pagination_test.rb +124 -0
- data/test/action_controller/json_api/transform_test.rb +189 -0
- 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 +235 -0
- data/test/action_controller/serialization_test.rb +478 -0
- data/test/active_model_serializers/adapter_for_test.rb +208 -0
- data/test/active_model_serializers/json_pointer_test.rb +22 -0
- data/test/active_model_serializers/logging_test.rb +77 -0
- data/test/active_model_serializers/model_test.rb +142 -0
- data/test/active_model_serializers/railtie_test_isolated.rb +68 -0
- data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
- data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
- data/test/active_model_serializers/test/schema_test.rb +131 -0
- data/test/active_model_serializers/test/serializer_test.rb +62 -0
- data/test/active_record_test.rb +9 -0
- data/test/adapter/attributes_test.rb +40 -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 +104 -0
- data/test/adapter/json/has_many_test.rb +53 -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 +96 -0
- data/test/adapter/json_api/errors_test.rb +76 -0
- data/test/adapter/json_api/fields_test.rb +96 -0
- data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
- data/test/adapter/json_api/has_many_test.rb +173 -0
- data/test/adapter/json_api/has_one_test.rb +80 -0
- data/test/adapter/json_api/include_data_if_sideloaded_test.rb +213 -0
- data/test/adapter/json_api/json_api_test.rb +33 -0
- data/test/adapter/json_api/linked_test.rb +413 -0
- data/test/adapter/json_api/links_test.rb +110 -0
- data/test/adapter/json_api/pagination_links_test.rb +206 -0
- data/test/adapter/json_api/parse_test.rb +137 -0
- data/test/adapter/json_api/relationship_test.rb +397 -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 +512 -0
- data/test/adapter/json_api/type_test.rb +193 -0
- data/test/adapter/json_test.rb +46 -0
- data/test/adapter/null_test.rb +22 -0
- data/test/adapter/polymorphic_test.rb +218 -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_active_record.rb +81 -0
- data/test/benchmark/bm_adapter.rb +38 -0
- data/test/benchmark/bm_caching.rb +119 -0
- data/test/benchmark/bm_lookup_chain.rb +83 -0
- data/test/benchmark/bm_transform.rb +45 -0
- data/test/benchmark/config.ru +3 -0
- data/test/benchmark/controllers.rb +83 -0
- data/test/benchmark/fixtures.rb +219 -0
- data/test/cache_test.rb +651 -0
- data/test/collection_serializer_test.rb +127 -0
- data/test/fixtures/active_record.rb +113 -0
- data/test/fixtures/poro.rb +225 -0
- data/test/generators/scaffold_controller_generator_test.rb +24 -0
- data/test/generators/serializer_generator_test.rb +75 -0
- data/test/grape_test.rb +196 -0
- data/test/lint_test.rb +49 -0
- data/test/logger_test.rb +20 -0
- data/test/poro_test.rb +9 -0
- data/test/serializable_resource_test.rb +79 -0
- data/test/serializers/association_macros_test.rb +37 -0
- data/test/serializers/associations_test.rb +518 -0
- data/test/serializers/attribute_test.rb +153 -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 +202 -0
- data/test/serializers/options_test.rb +32 -0
- data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
- data/test/serializers/reflection_test.rb +479 -0
- data/test/serializers/root_test.rb +21 -0
- data/test/serializers/serialization_test.rb +55 -0
- data/test/serializers/serializer_for_test.rb +136 -0
- data/test/serializers/serializer_for_with_namespace_test.rb +88 -0
- data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
- data/test/support/isolated_unit.rb +84 -0
- data/test/support/rails5_shims.rb +53 -0
- data/test/support/rails_app.rb +38 -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 +79 -0
- data/test/test_helper.rb +59 -21
- metadata +529 -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_model/serializer/associations.rb +0 -233
- 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,98 @@
|
|
|
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
|
+
# :nocov:
|
|
9
|
+
def new(*args)
|
|
10
|
+
fail ArgumentError, 'Adapters inherit from Adapter::Base.' \
|
|
11
|
+
"Adapter.new called with args: '#{args.inspect}', from" \
|
|
12
|
+
"'caller[0]'."
|
|
13
|
+
end
|
|
14
|
+
# :nocov:
|
|
15
|
+
|
|
16
|
+
def configured_adapter
|
|
17
|
+
lookup(ActiveModelSerializers.config.adapter)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create(resource, options = {})
|
|
21
|
+
override = options.delete(:adapter)
|
|
22
|
+
klass = override ? adapter_class(override) : configured_adapter
|
|
23
|
+
klass.new(resource, options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @see ActiveModelSerializers::Adapter.lookup
|
|
27
|
+
def adapter_class(adapter)
|
|
28
|
+
ActiveModelSerializers::Adapter.lookup(adapter)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Hash<adapter_name, adapter_class>]
|
|
32
|
+
def adapter_map
|
|
33
|
+
ADAPTER_MAP
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Array<Symbol>] list of adapter names
|
|
37
|
+
def adapters
|
|
38
|
+
adapter_map.keys.sort
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Adds an adapter 'klass' with 'name' to the 'adapter_map'
|
|
42
|
+
# Names are stringified and underscored
|
|
43
|
+
# @param name [Symbol, String, Class] name of the registered adapter
|
|
44
|
+
# @param klass [Class] adapter class itself, optional if name is the class
|
|
45
|
+
# @example
|
|
46
|
+
# AMS::Adapter.register(:my_adapter, MyAdapter)
|
|
47
|
+
# @note The registered name strips out 'ActiveModelSerializers::Adapter::'
|
|
48
|
+
# so that registering 'ActiveModelSerializers::Adapter::Json' and
|
|
49
|
+
# 'Json' will both register as 'json'.
|
|
50
|
+
def register(name, klass = name)
|
|
51
|
+
name = name.to_s.gsub(/\AActiveModelSerializers::Adapter::/, ''.freeze)
|
|
52
|
+
adapter_map[name.underscore] = klass
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def registered_name(adapter_class)
|
|
57
|
+
ADAPTER_MAP.key adapter_class
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @param adapter [String, Symbol, Class] name to fetch adapter by
|
|
61
|
+
# @return [ActiveModelSerializers::Adapter] subclass of Adapter
|
|
62
|
+
# @raise [UnknownAdapterError]
|
|
63
|
+
def lookup(adapter)
|
|
64
|
+
# 1. return if is a class
|
|
65
|
+
return adapter if adapter.is_a?(Class)
|
|
66
|
+
adapter_name = adapter.to_s.underscore
|
|
67
|
+
# 2. return if registered
|
|
68
|
+
adapter_map.fetch(adapter_name) do
|
|
69
|
+
# 3. try to find adapter class from environment
|
|
70
|
+
adapter_class = find_by_name(adapter_name)
|
|
71
|
+
register(adapter_name, adapter_class)
|
|
72
|
+
adapter_class
|
|
73
|
+
end
|
|
74
|
+
rescue NameError, ArgumentError => e
|
|
75
|
+
failure_message =
|
|
76
|
+
"NameError: #{e.message}. Unknown adapter: #{adapter.inspect}. Valid adapters are: #{adapters}"
|
|
77
|
+
raise UnknownAdapterError, failure_message, e.backtrace
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api private
|
|
81
|
+
def find_by_name(adapter_name)
|
|
82
|
+
adapter_name = adapter_name.to_s.classify.tr('API', 'Api')
|
|
83
|
+
"ActiveModelSerializers::Adapter::#{adapter_name}".safe_constantize ||
|
|
84
|
+
"ActiveModelSerializers::Adapter::#{adapter_name.pluralize}".safe_constantize or # rubocop:disable Style/AndOr
|
|
85
|
+
fail UnknownAdapterError
|
|
86
|
+
end
|
|
87
|
+
private :find_by_name
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Gotta be at the bottom to use the code above it :(
|
|
91
|
+
extend ActiveSupport::Autoload
|
|
92
|
+
autoload :Base
|
|
93
|
+
autoload :Null
|
|
94
|
+
autoload :Attributes
|
|
95
|
+
autoload :Json
|
|
96
|
+
autoload :JsonApi
|
|
97
|
+
end
|
|
98
|
+
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,54 @@
|
|
|
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
|
+
warn "#{msg.join}."
|
|
41
|
+
send old, *args, &block
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def delegate_and_deprecate(method, delegee)
|
|
47
|
+
delegate method, to: delegee
|
|
48
|
+
deprecate method, "#{delegee.name}."
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
module_function :deprecate
|
|
52
|
+
module_function :delegate_and_deprecate
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
# :nocov:
|
|
10
|
+
def jsonapi_parse!(*args)
|
|
11
|
+
Adapter::JsonApi::Deserialization.parse!(*args)
|
|
12
|
+
end
|
|
13
|
+
# :nocov:
|
|
14
|
+
end
|
|
15
|
+
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,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,80 @@
|
|
|
1
|
+
module ActiveModelSerializers
|
|
2
|
+
module LookupChain
|
|
3
|
+
# Standard appending of Serializer to the resource name.
|
|
4
|
+
#
|
|
5
|
+
# Example:
|
|
6
|
+
# Author => AuthorSerializer
|
|
7
|
+
BY_RESOURCE = lambda do |resource_class, _serializer_class, _namespace|
|
|
8
|
+
serializer_from(resource_class)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Uses the namespace of the resource to find the serializer
|
|
12
|
+
#
|
|
13
|
+
# Example:
|
|
14
|
+
# British::Author => British::AuthorSerializer
|
|
15
|
+
BY_RESOURCE_NAMESPACE = lambda do |resource_class, _serializer_class, _namespace|
|
|
16
|
+
resource_namespace = namespace_for(resource_class)
|
|
17
|
+
serializer_name = serializer_from(resource_class)
|
|
18
|
+
|
|
19
|
+
"#{resource_namespace}::#{serializer_name}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Uses the controller namespace of the resource to find the serializer
|
|
23
|
+
#
|
|
24
|
+
# Example:
|
|
25
|
+
# Api::V3::AuthorsController => Api::V3::AuthorSerializer
|
|
26
|
+
BY_NAMESPACE = lambda do |resource_class, _serializer_class, namespace|
|
|
27
|
+
resource_name = resource_class_name(resource_class)
|
|
28
|
+
namespace ? "#{namespace}::#{resource_name}Serializer" : nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Allows for serializers to be defined in parent serializers
|
|
32
|
+
# - useful if a relationship only needs a different set of attributes
|
|
33
|
+
# than if it were rendered independently.
|
|
34
|
+
#
|
|
35
|
+
# Example:
|
|
36
|
+
# class BlogSerializer < ActiveModel::Serializer
|
|
37
|
+
# class AuthorSerialier < ActiveModel::Serializer
|
|
38
|
+
# ...
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# belongs_to :author
|
|
42
|
+
# ...
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# The belongs_to relationship would be rendered with
|
|
46
|
+
# BlogSerializer::AuthorSerialier
|
|
47
|
+
BY_PARENT_SERIALIZER = lambda do |resource_class, serializer_class, _namespace|
|
|
48
|
+
return if serializer_class == ActiveModel::Serializer
|
|
49
|
+
|
|
50
|
+
serializer_name = serializer_from(resource_class)
|
|
51
|
+
"#{serializer_class}::#{serializer_name}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
DEFAULT = [
|
|
55
|
+
BY_PARENT_SERIALIZER,
|
|
56
|
+
BY_NAMESPACE,
|
|
57
|
+
BY_RESOURCE_NAMESPACE,
|
|
58
|
+
BY_RESOURCE
|
|
59
|
+
].freeze
|
|
60
|
+
|
|
61
|
+
module_function
|
|
62
|
+
|
|
63
|
+
def namespace_for(klass)
|
|
64
|
+
klass.name.deconstantize
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def resource_class_name(klass)
|
|
68
|
+
klass.name.demodulize
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def serializer_from_resource_name(name)
|
|
72
|
+
"#{name}Serializer"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def serializer_from(klass)
|
|
76
|
+
name = resource_class_name(klass)
|
|
77
|
+
serializer_from_resource_name(name)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
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'
|
|
5
|
+
module ActiveModelSerializers
|
|
6
|
+
class Model
|
|
7
|
+
include ActiveModel::Serializers::JSON
|
|
8
|
+
include ActiveModel::Model
|
|
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 = []
|
|
20
|
+
|
|
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
|
|
45
|
+
end
|
|
46
|
+
|
|
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
|
|
52
|
+
end
|
|
53
|
+
|
|
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
|
|
70
|
+
end
|
|
71
|
+
|
|
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
|
|
92
|
+
end
|
|
93
|
+
|
|
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
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
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
|
|
118
|
+
end
|
|
119
|
+
|
|
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)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
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.eager_load_namespaces << ActiveModelSerializers
|
|
9
|
+
|
|
10
|
+
config.to_prepare do
|
|
11
|
+
ActiveModel::Serializer.serializers_cache.clear
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
initializer 'active_model_serializers.action_controller' do
|
|
15
|
+
ActiveSupport.on_load(:action_controller) do
|
|
16
|
+
include(::ActionController::Serialization)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
initializer 'active_model_serializers.prepare_serialization_context' do
|
|
21
|
+
SerializationContext.url_helpers = Rails.application.routes.url_helpers
|
|
22
|
+
SerializationContext.default_url_options = Rails.application.routes.default_url_options
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# This hook is run after the action_controller railtie has set the configuration
|
|
26
|
+
# based on the *environment* configuration and before any config/initializers are run
|
|
27
|
+
# and also before eager_loading (if enabled).
|
|
28
|
+
initializer 'active_model_serializers.set_configs', after: 'action_controller.set_configs' do
|
|
29
|
+
ActiveModelSerializers.logger = Rails.configuration.action_controller.logger
|
|
30
|
+
ActiveModelSerializers.config.perform_caching = Rails.configuration.action_controller.perform_caching
|
|
31
|
+
# We want this hook to run after the config has been set, even if ActionController has already loaded.
|
|
32
|
+
ActiveSupport.on_load(:action_controller) do
|
|
33
|
+
ActiveModelSerializers.config.cache_store = ActionController::Base.cache_store
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# :nocov:
|
|
38
|
+
generators do |app|
|
|
39
|
+
Rails::Generators.configure!(app.config.generators)
|
|
40
|
+
Rails::Generators.hidden_namespaces.uniq!
|
|
41
|
+
require 'generators/rails/resource_override'
|
|
42
|
+
end
|
|
43
|
+
# :nocov:
|
|
44
|
+
|
|
45
|
+
if Rails.env.test?
|
|
46
|
+
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Schema)
|
|
47
|
+
ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|