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,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module ActiveModelSerializers
6
+ class SerializableResource
7
+ ADAPTER_OPTION_KEYS = Set.new([:include, :fields, :adapter, :meta, :meta_key, :links, :serialization_context, :key_transform])
8
+ include ActiveModelSerializers::Logging
9
+
10
+ delegate :serializable_hash, :as_json, :to_json, to: :adapter
11
+ notify :serializable_hash, :render
12
+ notify :as_json, :render
13
+ notify :to_json, :render
14
+
15
+ # Primary interface to composing a resource with a serializer and adapter.
16
+ # @return the serializable_resource, ready for #as_json/#to_json/#serializable_hash.
17
+ def initialize(resource, options = {})
18
+ @resource = resource
19
+ @adapter_opts = options.select { |k, _| ADAPTER_OPTION_KEYS.include? k }
20
+ @serializer_opts = options.reject { |k, _| ADAPTER_OPTION_KEYS.include? k }
21
+ end
22
+
23
+ def serialization_scope=(scope)
24
+ serializer_opts[:scope] = scope
25
+ end
26
+
27
+ def serialization_scope
28
+ serializer_opts[:scope]
29
+ end
30
+
31
+ def serialization_scope_name=(scope_name)
32
+ serializer_opts[:scope_name] = scope_name
33
+ end
34
+
35
+ # NOTE: if no adapter is available, returns the resource itself. (i.e. adapter is a no-op)
36
+ def adapter
37
+ @adapter ||= find_adapter
38
+ end
39
+ alias adapter_instance adapter
40
+
41
+ def find_adapter
42
+ return resource unless serializer?
43
+ adapter = catch :no_serializer do
44
+ ActiveModelSerializers::Adapter.create(serializer_instance, adapter_opts)
45
+ end
46
+ adapter || resource
47
+ end
48
+
49
+ def serializer_instance
50
+ @serializer_instance ||= serializer.new(resource, serializer_opts)
51
+ end
52
+
53
+ # Get serializer either explicitly :serializer or implicitly from resource
54
+ # Remove :serializer key from serializer_opts
55
+ # Remove :each_serializer if present and set as :serializer key
56
+ def serializer
57
+ @serializer ||=
58
+ begin
59
+ @serializer = serializer_opts.delete(:serializer)
60
+ @serializer ||= ActiveModel::Serializer.serializer_for(resource, serializer_opts)
61
+
62
+ if serializer_opts.key?(:each_serializer)
63
+ serializer_opts[:serializer] = serializer_opts.delete(:each_serializer)
64
+ end
65
+ @serializer
66
+ end
67
+ end
68
+ alias serializer_class serializer
69
+
70
+ # True when no explicit adapter given, or explicit appear is truthy (non-nil)
71
+ # False when explicit adapter is falsy (nil or false)
72
+ def use_adapter?
73
+ !(adapter_opts.key?(:adapter) && !adapter_opts[:adapter])
74
+ end
75
+
76
+ def serializer?
77
+ use_adapter? && !serializer.nil?
78
+ end
79
+
80
+ protected
81
+
82
+ attr_reader :resource, :adapter_opts, :serializer_opts
83
+ end
84
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/array/extract_options'
4
+ module ActiveModelSerializers
5
+ class SerializationContext
6
+ class << self
7
+ attr_writer :url_helpers, :default_url_options
8
+ def url_helpers
9
+ @url_helpers ||= Module.new
10
+ end
11
+
12
+ def default_url_options
13
+ @default_url_options ||= {}
14
+ end
15
+ end
16
+ module UrlHelpers
17
+ def self.included(base)
18
+ base.send(:include, SerializationContext.url_helpers)
19
+ end
20
+
21
+ def default_url_options
22
+ SerializationContext.default_url_options
23
+ end
24
+ end
25
+
26
+ attr_reader :request_url, :query_parameters, :key_transform
27
+
28
+ def initialize(*args)
29
+ options = args.extract_options!
30
+ if args.size == 1
31
+ request = args.pop
32
+ options[:request_url] = request.original_url[/\A[^?]+/]
33
+ options[:query_parameters] = request.query_parameters
34
+ end
35
+ @request_url = options.delete(:request_url)
36
+ @query_parameters = options.delete(:query_parameters)
37
+ @url_helpers = options.delete(:url_helpers) || self.class.url_helpers
38
+ @default_url_options = options.delete(:default_url_options) || self.class.default_url_options
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Test
5
+ extend ActiveSupport::Autoload
6
+ autoload :Serializer
7
+ autoload :Schema
8
+ end
9
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveModelSerializers
4
+ module Test
5
+ module Schema
6
+ # A Minitest Assertion that test the response is valid against a schema.
7
+ # @param schema_path [String] a custom schema path
8
+ # @param message [String] a custom error message
9
+ # @return [Boolean] true when the response is valid
10
+ # @return [Minitest::Assertion] when the response is invalid
11
+ # @example
12
+ # get :index
13
+ # assert_response_schema
14
+ def assert_response_schema(schema_path = nil, message = nil)
15
+ matcher = AssertResponseSchema.new(schema_path, request, response, message)
16
+ assert(matcher.call, matcher.message)
17
+ end
18
+
19
+ def assert_request_schema(schema_path = nil, message = nil)
20
+ matcher = AssertRequestSchema.new(schema_path, request, response, message)
21
+ assert(matcher.call, matcher.message)
22
+ end
23
+
24
+ # May be renamed
25
+ def assert_request_response_schema(schema_path = nil, message = nil)
26
+ assert_request_schema(schema_path, message)
27
+ assert_response_schema(schema_path, message)
28
+ end
29
+
30
+ def assert_schema(payload, schema_path = nil, message = nil)
31
+ matcher = AssertSchema.new(schema_path, request, response, message, payload)
32
+ assert(matcher.call, matcher.message)
33
+ end
34
+
35
+ MissingSchema = Class.new(Minitest::Assertion)
36
+ InvalidSchemaError = Class.new(Minitest::Assertion)
37
+
38
+ class AssertSchema
39
+ attr_reader :schema_path, :request, :response, :message, :payload
40
+
41
+ # Interface may change.
42
+ def initialize(schema_path, request, response, message, payload = nil)
43
+ require_json_schema!
44
+ @request = request
45
+ @response = response
46
+ @payload = payload
47
+ @schema_path = schema_path || schema_path_default
48
+ @message = message
49
+ @document_store = JsonSchema::DocumentStore.new
50
+ add_schema_to_document_store
51
+ end
52
+
53
+ def call
54
+ json_schema.expand_references!(store: document_store)
55
+ status, errors = json_schema.validate(response_body)
56
+ @message = [message, errors.map(&:to_s).to_sentence].compact.join(': ')
57
+ status
58
+ end
59
+
60
+ protected
61
+
62
+ attr_reader :document_store
63
+
64
+ def controller_path
65
+ request.filtered_parameters.with_indifferent_access[:controller]
66
+ end
67
+
68
+ def action
69
+ request.filtered_parameters.with_indifferent_access[:action]
70
+ end
71
+
72
+ def schema_directory
73
+ ActiveModelSerializers.config.schema_path
74
+ end
75
+
76
+ def schema_full_path
77
+ "#{schema_directory}/#{schema_path}"
78
+ end
79
+
80
+ def schema_path_default
81
+ "#{controller_path}/#{action}.json"
82
+ end
83
+
84
+ def schema_data
85
+ load_json_file(schema_full_path)
86
+ end
87
+
88
+ def response_body
89
+ load_json(response.body)
90
+ end
91
+
92
+ def request_params
93
+ request.env['action_dispatch.request.request_parameters']
94
+ end
95
+
96
+ def json_schema
97
+ @json_schema ||= JsonSchema.parse!(schema_data)
98
+ end
99
+
100
+ def add_schema_to_document_store
101
+ Dir.glob("#{schema_directory}/**/*.json").each do |path|
102
+ schema_data = load_json_file(path)
103
+ extra_schema = JsonSchema.parse!(schema_data)
104
+ document_store.add_schema(extra_schema)
105
+ end
106
+ end
107
+
108
+ def load_json(json)
109
+ JSON.parse(json)
110
+ rescue JSON::ParserError => ex
111
+ raise InvalidSchemaError, ex.message
112
+ end
113
+
114
+ def load_json_file(path)
115
+ load_json(File.read(path))
116
+ rescue Errno::ENOENT
117
+ raise MissingSchema, "No Schema file at #{schema_full_path}"
118
+ end
119
+
120
+ def require_json_schema!
121
+ require 'json_schema'
122
+ rescue LoadError
123
+ raise LoadError, "You don't have json_schema installed in your application. Please add it to your Gemfile and run bundle install"
124
+ end
125
+ end
126
+ class AssertResponseSchema < AssertSchema
127
+ def initialize(*)
128
+ super
129
+ @payload = response_body
130
+ end
131
+ end
132
+ class AssertRequestSchema < AssertSchema
133
+ def initialize(*)
134
+ super
135
+ @payload = request_params
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ module ActiveModelSerializers
5
+ module Test
6
+ module Serializer
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ setup :setup_serialization_subscriptions
11
+ teardown :teardown_serialization_subscriptions
12
+ end
13
+
14
+ # Asserts that the request was rendered with the appropriate serializers.
15
+ #
16
+ # # assert that the "PostSerializer" serializer was rendered
17
+ # assert_serializer "PostSerializer"
18
+ #
19
+ # # return a custom error message
20
+ # assert_serializer "PostSerializer", "PostSerializer not rendered"
21
+ #
22
+ # # assert that the instance of PostSerializer was rendered
23
+ # assert_serializer PostSerializer
24
+ #
25
+ # # assert that the "PostSerializer" serializer was rendered
26
+ # assert_serializer :post_serializer
27
+ #
28
+ # # assert that the rendered serializer starts with "Post"
29
+ # assert_serializer %r{\APost.+\Z}
30
+ #
31
+ # # assert that no serializer was rendered
32
+ # assert_serializer nil
33
+ #
34
+ def assert_serializer(expectation, message = nil)
35
+ @assert_serializer.expectation = expectation
36
+ @assert_serializer.message = message
37
+ @assert_serializer.response = response
38
+ assert(@assert_serializer.matches?, @assert_serializer.message)
39
+ end
40
+
41
+ class AssertSerializer
42
+ attr_reader :serializers, :message
43
+ attr_accessor :response, :expectation
44
+
45
+ def initialize
46
+ @serializers = Set.new
47
+ @_subscribers = []
48
+ end
49
+
50
+ def message=(message)
51
+ @message = message || "expecting <#{expectation.inspect}> but rendering with <#{serializers.to_a}>"
52
+ end
53
+
54
+ def matches?
55
+ # Force body to be read in case the template is being streamed.
56
+ response.body
57
+
58
+ case expectation
59
+ when a_serializer? then matches_class?
60
+ when Symbol then matches_symbol?
61
+ when String then matches_string?
62
+ when Regexp then matches_regexp?
63
+ when NilClass then matches_nil?
64
+ else fail ArgumentError, 'assert_serializer only accepts a String, Symbol, Regexp, ActiveModel::Serializer, or nil'
65
+ end
66
+ end
67
+
68
+ def subscribe
69
+ @_subscribers << ActiveSupport::Notifications.subscribe(event_name) do |_name, _start, _finish, _id, payload|
70
+ serializer = payload[:serializer].name
71
+ serializers << serializer
72
+ end
73
+ end
74
+
75
+ def unsubscribe
76
+ @_subscribers.each do |subscriber|
77
+ ActiveSupport::Notifications.unsubscribe(subscriber)
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def matches_class?
84
+ serializers.include?(expectation.name)
85
+ end
86
+
87
+ def matches_symbol?
88
+ camelize_expectation = expectation.to_s.camelize
89
+ serializers.include?(camelize_expectation)
90
+ end
91
+
92
+ def matches_string?
93
+ !expectation.empty? && serializers.include?(expectation)
94
+ end
95
+
96
+ def matches_regexp?
97
+ serializers.any? do |serializer|
98
+ serializer.match(expectation)
99
+ end
100
+ end
101
+
102
+ def matches_nil?
103
+ serializers.empty?
104
+ end
105
+
106
+ def a_serializer?
107
+ ->(exp) { exp.is_a?(Class) && exp < ActiveModel::Serializer }
108
+ end
109
+
110
+ def event_name
111
+ ::ActiveModelSerializers::Logging::RENDER_EVENT
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def setup_serialization_subscriptions
118
+ @assert_serializer = AssertSerializer.new
119
+ @assert_serializer.subscribe
120
+ end
121
+
122
+ def teardown_serialization_subscriptions
123
+ @assert_serializer.unsubscribe
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,6 @@
1
+ Description:
2
+ Generates a serializer for the given resource.
3
+
4
+ Example:
5
+ `rails generate serializer Account name created_at`
6
+
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/rails/resource/resource_generator'
5
+
6
+ module Rails
7
+ module Generators
8
+ class ResourceGenerator
9
+ hook_for :serializer, default: true, type: :boolean
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module Generators
5
+ class SerializerGenerator < NamedBase
6
+ source_root File.expand_path('../templates', __FILE__)
7
+ check_class_collision suffix: 'Serializer'
8
+
9
+ argument :attributes, type: :array, default: [], banner: 'field:type field:type'
10
+
11
+ class_option :parent, type: :string, desc: 'The parent class for the generated serializer'
12
+
13
+ def create_serializer_file
14
+ template 'serializer.rb.erb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb")
15
+ end
16
+
17
+ private
18
+
19
+ def attributes_names
20
+ [:id] + attributes.reject(&:reference?).map! { |a| a.name.to_sym }
21
+ end
22
+
23
+ def association_names
24
+ attributes.select(&:reference?).map! { |a| a.name.to_sym }
25
+ end
26
+
27
+ def parent_class_name
28
+ if options[:parent]
29
+ options[:parent]
30
+ elsif 'ApplicationSerializer'.safe_constantize
31
+ 'ApplicationSerializer'
32
+ else
33
+ 'ActiveModel::Serializer'
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end