active_model_serializers_custom 0.10.90

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.
Files changed (215) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +35 -0
  5. data/.rubocop.yml +109 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +63 -0
  8. data/CHANGELOG.md +727 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +74 -0
  12. data/MIT-LICENSE +22 -0
  13. data/README.md +305 -0
  14. data/Rakefile +76 -0
  15. data/active_model_serializers.gemspec +64 -0
  16. data/appveyor.yml +28 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/rubocop +38 -0
  20. data/bin/serve_benchmark +39 -0
  21. data/docs/README.md +41 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +269 -0
  24. data/docs/general/caching.md +58 -0
  25. data/docs/general/configuration_options.md +185 -0
  26. data/docs/general/deserialization.md +100 -0
  27. data/docs/general/fields.md +31 -0
  28. data/docs/general/getting_started.md +133 -0
  29. data/docs/general/instrumentation.md +40 -0
  30. data/docs/general/key_transforms.md +40 -0
  31. data/docs/general/logging.md +21 -0
  32. data/docs/general/rendering.md +293 -0
  33. data/docs/general/serializers.md +495 -0
  34. data/docs/how-open-source-maintained.jpg +0 -0
  35. data/docs/howto/add_pagination_links.md +138 -0
  36. data/docs/howto/add_relationship_links.md +140 -0
  37. data/docs/howto/add_root_key.md +62 -0
  38. data/docs/howto/grape_integration.md +42 -0
  39. data/docs/howto/outside_controller_use.md +66 -0
  40. data/docs/howto/passing_arbitrary_options.md +27 -0
  41. data/docs/howto/serialize_poro.md +73 -0
  42. data/docs/howto/test.md +154 -0
  43. data/docs/howto/upgrade_from_0_8_to_0_10.md +265 -0
  44. data/docs/integrations/ember-and-json-api.md +147 -0
  45. data/docs/integrations/grape.md +19 -0
  46. data/docs/jsonapi/errors.md +56 -0
  47. data/docs/jsonapi/schema.md +151 -0
  48. data/docs/jsonapi/schema/schema.json +366 -0
  49. data/docs/rfcs/0000-namespace.md +106 -0
  50. data/docs/rfcs/template.md +15 -0
  51. data/lib/action_controller/serialization.rb +76 -0
  52. data/lib/active_model/serializable_resource.rb +13 -0
  53. data/lib/active_model/serializer.rb +418 -0
  54. data/lib/active_model/serializer/adapter.rb +26 -0
  55. data/lib/active_model/serializer/adapter/attributes.rb +17 -0
  56. data/lib/active_model/serializer/adapter/base.rb +20 -0
  57. data/lib/active_model/serializer/adapter/json.rb +17 -0
  58. data/lib/active_model/serializer/adapter/json_api.rb +17 -0
  59. data/lib/active_model/serializer/adapter/null.rb +17 -0
  60. data/lib/active_model/serializer/array_serializer.rb +14 -0
  61. data/lib/active_model/serializer/association.rb +91 -0
  62. data/lib/active_model/serializer/attribute.rb +27 -0
  63. data/lib/active_model/serializer/belongs_to_reflection.rb +13 -0
  64. data/lib/active_model/serializer/collection_serializer.rb +90 -0
  65. data/lib/active_model/serializer/concerns/caching.rb +304 -0
  66. data/lib/active_model/serializer/error_serializer.rb +16 -0
  67. data/lib/active_model/serializer/errors_serializer.rb +34 -0
  68. data/lib/active_model/serializer/field.rb +92 -0
  69. data/lib/active_model/serializer/fieldset.rb +33 -0
  70. data/lib/active_model/serializer/has_many_reflection.rb +12 -0
  71. data/lib/active_model/serializer/has_one_reflection.rb +9 -0
  72. data/lib/active_model/serializer/lazy_association.rb +99 -0
  73. data/lib/active_model/serializer/link.rb +23 -0
  74. data/lib/active_model/serializer/lint.rb +152 -0
  75. data/lib/active_model/serializer/null.rb +19 -0
  76. data/lib/active_model/serializer/reflection.rb +212 -0
  77. data/lib/active_model/serializer/version.rb +7 -0
  78. data/lib/active_model_serializers.rb +63 -0
  79. data/lib/active_model_serializers/adapter.rb +100 -0
  80. data/lib/active_model_serializers/adapter/attributes.rb +15 -0
  81. data/lib/active_model_serializers/adapter/base.rb +85 -0
  82. data/lib/active_model_serializers/adapter/json.rb +23 -0
  83. data/lib/active_model_serializers/adapter/json_api.rb +535 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +215 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +98 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +51 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +85 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +39 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +90 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +106 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +68 -0
  92. data/lib/active_model_serializers/adapter/null.rb +11 -0
  93. data/lib/active_model_serializers/callbacks.rb +57 -0
  94. data/lib/active_model_serializers/deprecate.rb +56 -0
  95. data/lib/active_model_serializers/deserialization.rb +17 -0
  96. data/lib/active_model_serializers/json_pointer.rb +16 -0
  97. data/lib/active_model_serializers/logging.rb +124 -0
  98. data/lib/active_model_serializers/lookup_chain.rb +82 -0
  99. data/lib/active_model_serializers/model.rb +132 -0
  100. data/lib/active_model_serializers/railtie.rb +52 -0
  101. data/lib/active_model_serializers/register_jsonapi_renderer.rb +80 -0
  102. data/lib/active_model_serializers/serializable_resource.rb +84 -0
  103. data/lib/active_model_serializers/serialization_context.rb +41 -0
  104. data/lib/active_model_serializers/test.rb +9 -0
  105. data/lib/active_model_serializers/test/schema.rb +140 -0
  106. data/lib/active_model_serializers/test/serializer.rb +127 -0
  107. data/lib/generators/rails/USAGE +6 -0
  108. data/lib/generators/rails/resource_override.rb +12 -0
  109. data/lib/generators/rails/serializer_generator.rb +38 -0
  110. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  111. data/lib/grape/active_model_serializers.rb +18 -0
  112. data/lib/grape/formatters/active_model_serializers.rb +34 -0
  113. data/lib/grape/helpers/active_model_serializers.rb +19 -0
  114. data/lib/tasks/rubocop.rake +55 -0
  115. data/test/action_controller/adapter_selector_test.rb +64 -0
  116. data/test/action_controller/explicit_serializer_test.rb +137 -0
  117. data/test/action_controller/json/include_test.rb +248 -0
  118. data/test/action_controller/json_api/deserialization_test.rb +114 -0
  119. data/test/action_controller/json_api/errors_test.rb +42 -0
  120. data/test/action_controller/json_api/fields_test.rb +68 -0
  121. data/test/action_controller/json_api/linked_test.rb +204 -0
  122. data/test/action_controller/json_api/pagination_test.rb +126 -0
  123. data/test/action_controller/json_api/transform_test.rb +191 -0
  124. data/test/action_controller/lookup_proc_test.rb +51 -0
  125. data/test/action_controller/namespace_lookup_test.rb +239 -0
  126. data/test/action_controller/serialization_scope_name_test.rb +237 -0
  127. data/test/action_controller/serialization_test.rb +480 -0
  128. data/test/active_model_serializers/adapter_for_test.rb +210 -0
  129. data/test/active_model_serializers/json_pointer_test.rb +24 -0
  130. data/test/active_model_serializers/logging_test.rb +79 -0
  131. data/test/active_model_serializers/model_test.rb +144 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +70 -0
  133. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +163 -0
  134. data/test/active_model_serializers/serialization_context_test_isolated.rb +73 -0
  135. data/test/active_model_serializers/test/schema_test.rb +133 -0
  136. data/test/active_model_serializers/test/serializer_test.rb +64 -0
  137. data/test/active_record_test.rb +11 -0
  138. data/test/adapter/attributes_test.rb +42 -0
  139. data/test/adapter/deprecation_test.rb +102 -0
  140. data/test/adapter/json/belongs_to_test.rb +47 -0
  141. data/test/adapter/json/collection_test.rb +106 -0
  142. data/test/adapter/json/has_many_test.rb +55 -0
  143. data/test/adapter/json/transform_test.rb +95 -0
  144. data/test/adapter/json_api/belongs_to_test.rb +157 -0
  145. data/test/adapter/json_api/collection_test.rb +98 -0
  146. data/test/adapter/json_api/errors_test.rb +78 -0
  147. data/test/adapter/json_api/fields_test.rb +98 -0
  148. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +98 -0
  149. data/test/adapter/json_api/has_many_test.rb +175 -0
  150. data/test/adapter/json_api/has_one_test.rb +82 -0
  151. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +215 -0
  152. data/test/adapter/json_api/json_api_test.rb +35 -0
  153. data/test/adapter/json_api/linked_test.rb +415 -0
  154. data/test/adapter/json_api/links_test.rb +112 -0
  155. data/test/adapter/json_api/pagination_links_test.rb +208 -0
  156. data/test/adapter/json_api/parse_test.rb +139 -0
  157. data/test/adapter/json_api/relationship_test.rb +399 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +102 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +84 -0
  160. data/test/adapter/json_api/transform_test.rb +514 -0
  161. data/test/adapter/json_api/type_test.rb +195 -0
  162. data/test/adapter/json_test.rb +48 -0
  163. data/test/adapter/null_test.rb +24 -0
  164. data/test/adapter/polymorphic_test.rb +220 -0
  165. data/test/adapter_test.rb +69 -0
  166. data/test/array_serializer_test.rb +24 -0
  167. data/test/benchmark/app.rb +67 -0
  168. data/test/benchmark/benchmarking_support.rb +69 -0
  169. data/test/benchmark/bm_active_record.rb +83 -0
  170. data/test/benchmark/bm_adapter.rb +40 -0
  171. data/test/benchmark/bm_caching.rb +121 -0
  172. data/test/benchmark/bm_lookup_chain.rb +85 -0
  173. data/test/benchmark/bm_transform.rb +47 -0
  174. data/test/benchmark/config.ru +3 -0
  175. data/test/benchmark/controllers.rb +85 -0
  176. data/test/benchmark/fixtures.rb +221 -0
  177. data/test/cache_test.rb +717 -0
  178. data/test/collection_serializer_test.rb +129 -0
  179. data/test/fixtures/active_record.rb +115 -0
  180. data/test/fixtures/poro.rb +227 -0
  181. data/test/generators/scaffold_controller_generator_test.rb +26 -0
  182. data/test/generators/serializer_generator_test.rb +77 -0
  183. data/test/grape_test.rb +198 -0
  184. data/test/lint_test.rb +51 -0
  185. data/test/logger_test.rb +22 -0
  186. data/test/poro_test.rb +11 -0
  187. data/test/serializable_resource_test.rb +81 -0
  188. data/test/serializers/association_macros_test.rb +39 -0
  189. data/test/serializers/associations_test.rb +520 -0
  190. data/test/serializers/attribute_test.rb +155 -0
  191. data/test/serializers/attributes_test.rb +54 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +172 -0
  193. data/test/serializers/configuration_test.rb +34 -0
  194. data/test/serializers/fieldset_test.rb +16 -0
  195. data/test/serializers/meta_test.rb +204 -0
  196. data/test/serializers/options_test.rb +34 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +81 -0
  198. data/test/serializers/reflection_test.rb +481 -0
  199. data/test/serializers/root_test.rb +23 -0
  200. data/test/serializers/serialization_test.rb +57 -0
  201. data/test/serializers/serializer_for_test.rb +138 -0
  202. data/test/serializers/serializer_for_with_namespace_test.rb +90 -0
  203. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  204. data/test/support/isolated_unit.rb +86 -0
  205. data/test/support/rails5_shims.rb +55 -0
  206. data/test/support/rails_app.rb +40 -0
  207. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  208. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  209. data/test/support/schemas/custom/show.json +7 -0
  210. data/test/support/schemas/hyper_schema.json +93 -0
  211. data/test/support/schemas/render_using_json_api.json +43 -0
  212. data/test/support/schemas/simple_json_pointers.json +10 -0
  213. data/test/support/serialization_testing.rb +81 -0
  214. data/test/test_helper.rb +72 -0
  215. metadata +622 -0
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module JsonPointer
5
+ module_function
6
+
7
+ POINTERS = {
8
+ attribute: '/data/attributes/%s'.freeze,
9
+ primary_data: '/data%s'.freeze
10
+ }.freeze
11
+
12
+ def new(pointer_type, value = nil)
13
+ format(POINTERS[pointer_type], value)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # ActiveModelSerializers::Logging
5
+ #
6
+ # https://github.com/rails/rails/blob/280654ef88/activejob/lib/active_job/logging.rb
7
+ #
8
+ module ActiveModelSerializers
9
+ module Logging
10
+ RENDER_EVENT = 'render.active_model_serializers'.freeze
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ include ActiveModelSerializers::Callbacks
15
+ extend Macros
16
+ instrument_rendering
17
+ end
18
+
19
+ module ClassMethods
20
+ def instrument_rendering
21
+ around_render do |args, block|
22
+ tag_logger do
23
+ notify_render do
24
+ block.call(args)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ # Macros that can be used to customize the logging of class or instance methods,
32
+ # by extending the class or its singleton.
33
+ #
34
+ # Adapted from:
35
+ # https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb
36
+ #
37
+ # Provides a single method +notify+ to be used to declare when
38
+ # something a method notifies, with the argument +callback_name+ of the notification callback.
39
+ #
40
+ # class Adapter
41
+ # def self.klass_method
42
+ # # ...
43
+ # end
44
+ #
45
+ # def instance_method
46
+ # # ...
47
+ # end
48
+ #
49
+ # include ActiveModelSerializers::Logging::Macros
50
+ # notify :instance_method, :render
51
+ #
52
+ # class << self
53
+ # extend ActiveModelSerializers::Logging::Macros
54
+ # notify :klass_method, :render
55
+ # end
56
+ # end
57
+ module Macros
58
+ ##
59
+ # Simple notify method that wraps up +name+
60
+ # in a dummy method. It notifies on with the +callback_name+ notifier on
61
+ # each call to the dummy method, telling what the current serializer and adapter
62
+ # are being rendered.
63
+ # Adapted from:
64
+ # https://github.com/rubygems/rubygems/blob/cb28f5e991/lib/rubygems/deprecate.rb
65
+ def notify(name, callback_name)
66
+ class_eval do
67
+ old = "_notifying_#{callback_name}_#{name}"
68
+ alias_method old, name
69
+ define_method name do |*args, &block|
70
+ run_callbacks callback_name do
71
+ send old, *args, &block
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def notify_render(*)
79
+ event_name = RENDER_EVENT
80
+ ActiveSupport::Notifications.instrument(event_name, notify_render_payload) do
81
+ yield
82
+ end
83
+ end
84
+
85
+ def notify_render_payload
86
+ {
87
+ serializer: serializer || ActiveModel::Serializer::Null,
88
+ adapter: adapter || ActiveModelSerializers::Adapter::Null
89
+ }
90
+ end
91
+
92
+ private
93
+
94
+ def tag_logger(*tags)
95
+ if ActiveModelSerializers.logger.respond_to?(:tagged)
96
+ tags.unshift 'active_model_serializers'.freeze unless logger_tagged_by_active_model_serializers?
97
+ ActiveModelSerializers.logger.tagged(*tags) { yield }
98
+ else
99
+ yield
100
+ end
101
+ end
102
+
103
+ def logger_tagged_by_active_model_serializers?
104
+ ActiveModelSerializers.logger.formatter.current_tags.include?('active_model_serializers'.freeze)
105
+ end
106
+
107
+ class LogSubscriber < ActiveSupport::LogSubscriber
108
+ def render(event)
109
+ info do
110
+ serializer = event.payload[:serializer]
111
+ adapter = event.payload[:adapter]
112
+ duration = event.duration.round(2)
113
+ "Rendered #{serializer.name} with #{adapter.class} (#{duration}ms)"
114
+ end
115
+ end
116
+
117
+ def logger
118
+ ActiveModelSerializers.logger
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ ActiveModelSerializers::Logging::LogSubscriber.attach_to :active_model_serializers
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module LookupChain
5
+ # Standard appending of Serializer to the resource name.
6
+ #
7
+ # Example:
8
+ # Author => AuthorSerializer
9
+ BY_RESOURCE = lambda do |resource_class, _serializer_class, _namespace|
10
+ serializer_from(resource_class)
11
+ end
12
+
13
+ # Uses the namespace of the resource to find the serializer
14
+ #
15
+ # Example:
16
+ # British::Author => British::AuthorSerializer
17
+ BY_RESOURCE_NAMESPACE = lambda do |resource_class, _serializer_class, _namespace|
18
+ resource_namespace = namespace_for(resource_class)
19
+ serializer_name = serializer_from(resource_class)
20
+
21
+ "#{resource_namespace}::#{serializer_name}"
22
+ end
23
+
24
+ # Uses the controller namespace of the resource to find the serializer
25
+ #
26
+ # Example:
27
+ # Api::V3::AuthorsController => Api::V3::AuthorSerializer
28
+ BY_NAMESPACE = lambda do |resource_class, _serializer_class, namespace|
29
+ resource_name = resource_class_name(resource_class)
30
+ namespace ? "#{namespace}::#{resource_name}Serializer" : nil
31
+ end
32
+
33
+ # Allows for serializers to be defined in parent serializers
34
+ # - useful if a relationship only needs a different set of attributes
35
+ # than if it were rendered independently.
36
+ #
37
+ # Example:
38
+ # class BlogSerializer < ActiveModel::Serializer
39
+ # class AuthorSerialier < ActiveModel::Serializer
40
+ # ...
41
+ # end
42
+ #
43
+ # belongs_to :author
44
+ # ...
45
+ # end
46
+ #
47
+ # The belongs_to relationship would be rendered with
48
+ # BlogSerializer::AuthorSerialier
49
+ BY_PARENT_SERIALIZER = lambda do |resource_class, serializer_class, _namespace|
50
+ return if serializer_class == ActiveModel::Serializer
51
+
52
+ serializer_name = serializer_from(resource_class)
53
+ "#{serializer_class}::#{serializer_name}"
54
+ end
55
+
56
+ DEFAULT = [
57
+ BY_PARENT_SERIALIZER,
58
+ BY_NAMESPACE,
59
+ BY_RESOURCE_NAMESPACE,
60
+ BY_RESOURCE
61
+ ].freeze
62
+
63
+ module_function
64
+
65
+ def namespace_for(klass)
66
+ klass.name.deconstantize
67
+ end
68
+
69
+ def resource_class_name(klass)
70
+ klass.name.demodulize
71
+ end
72
+
73
+ def serializer_from_resource_name(name)
74
+ "#{name}Serializer"
75
+ end
76
+
77
+ def serializer_from(klass)
78
+ name = resource_class_name(klass)
79
+ serializer_from_resource_name(name)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ActiveModelSerializers::Model is a convenient superclass for making your models
4
+ # from Plain-Old Ruby Objects (PORO). It also serves as a reference implementation
5
+ # that satisfies ActiveModel::Serializer::Lint::Tests.
6
+ require 'active_support/core_ext/hash'
7
+ module ActiveModelSerializers
8
+ class Model
9
+ include ActiveModel::Serializers::JSON
10
+ include ActiveModel::Model
11
+
12
+ # Declare names of attributes to be included in +attributes+ hash.
13
+ # Is only available as a class-method since the ActiveModel::Serialization mixin in Rails
14
+ # uses an +attribute_names+ local variable, which may conflict if we were to add instance methods here.
15
+ #
16
+ # @overload attribute_names
17
+ # @return [Array<Symbol>]
18
+ class_attribute :attribute_names, instance_writer: false, instance_reader: false
19
+ # Initialize +attribute_names+ for all subclasses. The array is usually
20
+ # mutated in the +attributes+ method, but can be set directly, as well.
21
+ self.attribute_names = []
22
+
23
+ # Easily declare instance attributes with setters and getters for each.
24
+ #
25
+ # To initialize an instance, all attributes must have setters.
26
+ # However, the hash returned by +attributes+ instance method will ALWAYS
27
+ # be the value of the initial attributes, regardless of what accessors are defined.
28
+ # The only way to change the change the attributes after initialization is
29
+ # to mutate the +attributes+ directly.
30
+ # Accessor methods do NOT mutate the attributes. (This is a bug).
31
+ #
32
+ # @note For now, the Model only supports the notion of 'attributes'.
33
+ # In the tests, there is a special Model that also supports 'associations'. This is
34
+ # important so that we can add accessors for values that should not appear in the
35
+ # attributes hash when modeling associations. It is not yet clear if it
36
+ # makes sense for a PORO to have associations outside of the tests.
37
+ #
38
+ # @overload attributes(names)
39
+ # @param names [Array<String, Symbol>]
40
+ # @param name [String, Symbol]
41
+ def self.attributes(*names)
42
+ self.attribute_names |= names.map(&:to_sym)
43
+ # Silence redefinition of methods warnings
44
+ ActiveModelSerializers.silence_warnings do
45
+ attr_accessor(*names)
46
+ end
47
+ end
48
+
49
+ # Opt-in to breaking change
50
+ def self.derive_attributes_from_names_and_fix_accessors
51
+ unless included_modules.include?(DeriveAttributesFromNamesAndFixAccessors)
52
+ prepend(DeriveAttributesFromNamesAndFixAccessors)
53
+ end
54
+ end
55
+
56
+ module DeriveAttributesFromNamesAndFixAccessors
57
+ def self.included(base)
58
+ # NOTE that +id+ will always be in +attributes+.
59
+ base.attributes :id
60
+ end
61
+
62
+ # Override the +attributes+ method so that the hash is derived from +attribute_names+.
63
+ #
64
+ # The fields in +attribute_names+ determines the returned hash.
65
+ # +attributes+ are returned frozen to prevent any expectations that mutation affects
66
+ # the actual values in the model.
67
+ def attributes
68
+ self.class.attribute_names.each_with_object({}) do |attribute_name, result|
69
+ result[attribute_name] = public_send(attribute_name).freeze
70
+ end.with_indifferent_access.freeze
71
+ end
72
+ end
73
+
74
+ # Support for validation and other ActiveModel::Errors
75
+ # @return [ActiveModel::Errors]
76
+ attr_reader :errors
77
+
78
+ # (see #updated_at)
79
+ attr_writer :updated_at
80
+
81
+ # The only way to change the attributes of an instance is to directly mutate the attributes.
82
+ # @example
83
+ #
84
+ # model.attributes[:foo] = :bar
85
+ # @return [Hash]
86
+ attr_reader :attributes
87
+
88
+ # @param attributes [Hash]
89
+ def initialize(attributes = {})
90
+ attributes ||= {} # protect against nil
91
+ @attributes = attributes.symbolize_keys.with_indifferent_access
92
+ @errors = ActiveModel::Errors.new(self)
93
+ super
94
+ end
95
+
96
+ # Defaults to the downcased model name.
97
+ # This probably isn't a good default, since it's not a unique instance identifier,
98
+ # but that's what is currently implemented \_('-')_/.
99
+ #
100
+ # @note Though +id+ is defined, it will only show up
101
+ # in +attributes+ when it is passed in to the initializer or added to +attributes+,
102
+ # such as <tt>attributes[:id] = 5</tt>.
103
+ # @return [String, Numeric, Symbol]
104
+ def id
105
+ attributes.fetch(:id) do
106
+ defined?(@id) ? @id : self.class.model_name.name && self.class.model_name.name.downcase
107
+ end
108
+ end
109
+
110
+ # When not set, defaults to the time the file was modified.
111
+ #
112
+ # @note Though +updated_at+ and +updated_at=+ are defined, it will only show up
113
+ # in +attributes+ when it is passed in to the initializer or added to +attributes+,
114
+ # such as <tt>attributes[:updated_at] = Time.current</tt>.
115
+ # @return [String, Numeric, Time]
116
+ def updated_at
117
+ attributes.fetch(:updated_at) do
118
+ defined?(@updated_at) ? @updated_at : File.mtime(__FILE__)
119
+ end
120
+ end
121
+
122
+ # To customize model behavior, this method must be redefined. However,
123
+ # there are other ways of setting the +cache_key+ a serializer uses.
124
+ # @return [String]
125
+ def cache_key
126
+ ActiveSupport::Cache.expand_cache_key([
127
+ self.class.model_name.name.downcase,
128
+ "#{id}-#{updated_at.strftime('%Y%m%d%H%M%S%9N')}"
129
+ ].compact)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/railtie'
4
+ require 'action_controller'
5
+ require 'action_controller/railtie'
6
+ require 'action_controller/serialization'
7
+
8
+ module ActiveModelSerializers
9
+ class Railtie < Rails::Railtie
10
+ config.eager_load_namespaces << ActiveModelSerializers
11
+
12
+ config.to_prepare do
13
+ ActiveModel::Serializer.serializers_cache.clear
14
+ end
15
+
16
+ initializer 'active_model_serializers.action_controller' do
17
+ ActiveSupport.on_load(:action_controller) do
18
+ include(::ActionController::Serialization)
19
+ end
20
+ end
21
+
22
+ initializer 'active_model_serializers.prepare_serialization_context' do
23
+ SerializationContext.url_helpers = Rails.application.routes.url_helpers
24
+ SerializationContext.default_url_options = Rails.application.routes.default_url_options
25
+ end
26
+
27
+ # This hook is run after the action_controller railtie has set the configuration
28
+ # based on the *environment* configuration and before any config/initializers are run
29
+ # and also before eager_loading (if enabled).
30
+ initializer 'active_model_serializers.set_configs', after: 'action_controller.set_configs' do
31
+ ActiveModelSerializers.logger = Rails.configuration.action_controller.logger
32
+ ActiveModelSerializers.config.perform_caching = Rails.configuration.action_controller.perform_caching
33
+ # We want this hook to run after the config has been set, even if ActionController has already loaded.
34
+ ActiveSupport.on_load(:action_controller) do
35
+ ActiveModelSerializers.config.cache_store = ActionController::Base.cache_store
36
+ end
37
+ end
38
+
39
+ # :nocov:
40
+ generators do |app|
41
+ Rails::Generators.configure!(app.config.generators)
42
+ Rails::Generators.hidden_namespaces.uniq!
43
+ require 'generators/rails/resource_override'
44
+ end
45
+ # :nocov:
46
+
47
+ if Rails.env.test?
48
+ ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Schema)
49
+ ActionController::TestCase.send(:include, ActiveModelSerializers::Test::Serializer)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Based on discussion in https://github.com/rails/rails/pull/23712#issuecomment-184977238,
4
+ # the JSON API media type will have its own format/renderer.
5
+ #
6
+ # > We recommend the media type be registered on its own as jsonapi
7
+ # when a jsonapi Renderer and deserializer (Http::Parameters::DEFAULT_PARSERS) are added.
8
+ #
9
+ # Usage:
10
+ #
11
+ # ActiveSupport.on_load(:action_controller) do
12
+ # require 'active_model_serializers/register_jsonapi_renderer'
13
+ # end
14
+ #
15
+ # And then in controllers, use `render jsonapi: model` rather than `render json: model, adapter: :json_api`.
16
+ #
17
+ # For example, in a controller action, we can:
18
+ # respond_to do |format|
19
+ # format.jsonapi { render jsonapi: model }
20
+ # end
21
+ #
22
+ # or
23
+ #
24
+ # render jsonapi: model
25
+ #
26
+ # No wrapper format needed as it does not apply (i.e. no `wrap_parameters format: [jsonapi]`)
27
+ module ActiveModelSerializers
28
+ module Jsonapi
29
+ MEDIA_TYPE = 'application/vnd.api+json'.freeze
30
+ HEADERS = {
31
+ response: { 'CONTENT_TYPE'.freeze => MEDIA_TYPE },
32
+ request: { 'ACCEPT'.freeze => MEDIA_TYPE }
33
+ }.freeze
34
+
35
+ def self.install
36
+ # actionpack/lib/action_dispatch/http/mime_types.rb
37
+ Mime::Type.register MEDIA_TYPE, :jsonapi
38
+
39
+ if Rails::VERSION::MAJOR >= 5
40
+ ActionDispatch::Request.parameter_parsers[:jsonapi] = parser
41
+ else
42
+ ActionDispatch::ParamsParser::DEFAULT_PARSERS[Mime[:jsonapi]] = parser
43
+ end
44
+
45
+ # ref https://github.com/rails/rails/pull/21496
46
+ ActionController::Renderers.add :jsonapi do |json, options|
47
+ json = serialize_jsonapi(json, options).to_json(options) unless json.is_a?(String)
48
+ self.content_type ||= Mime[:jsonapi]
49
+ self.response_body = json
50
+ end
51
+ end
52
+
53
+ # Proposal: should actually deserialize the JSON API params
54
+ # to the hash format expected by `ActiveModel::Serializers::JSON`
55
+ # actionpack/lib/action_dispatch/http/parameters.rb
56
+ def self.parser
57
+ lambda do |body|
58
+ data = JSON.parse(body)
59
+ data = { _json: data } unless data.is_a?(Hash)
60
+ data.with_indifferent_access
61
+ end
62
+ end
63
+
64
+ module ControllerSupport
65
+ def serialize_jsonapi(json, options)
66
+ options[:adapter] = :json_api
67
+ options.fetch(:serialization_context) do
68
+ options[:serialization_context] = ActiveModelSerializers::SerializationContext.new(request)
69
+ end
70
+ get_serializer(json, options)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ ActiveModelSerializers::Jsonapi.install
77
+
78
+ ActiveSupport.on_load(:action_controller) do
79
+ include ActiveModelSerializers::Jsonapi::ControllerSupport
80
+ end