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.
Files changed (232) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +29 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. data/.gitignore +17 -0
  5. data/.rubocop.yml +104 -0
  6. data/.rubocop_todo.yml +167 -0
  7. data/.simplecov +110 -0
  8. data/.travis.yml +39 -24
  9. data/CHANGELOG.md +465 -6
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +50 -1
  12. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  13. data/README.md +102 -590
  14. data/Rakefile +93 -8
  15. data/active_model_serializers.gemspec +65 -23
  16. data/appveyor.yml +24 -0
  17. data/bin/bench +171 -0
  18. data/bin/bench_regression +316 -0
  19. data/bin/serve_benchmark +39 -0
  20. data/docs/ARCHITECTURE.md +126 -0
  21. data/docs/README.md +40 -0
  22. data/docs/STYLE.md +58 -0
  23. data/docs/general/adapters.md +245 -0
  24. data/docs/general/caching.md +52 -0
  25. data/docs/general/configuration_options.md +100 -0
  26. data/docs/general/deserialization.md +100 -0
  27. data/docs/general/getting_started.md +133 -0
  28. data/docs/general/instrumentation.md +40 -0
  29. data/docs/general/key_transforms.md +40 -0
  30. data/docs/general/logging.md +14 -0
  31. data/docs/general/rendering.md +255 -0
  32. data/docs/general/serializers.md +372 -0
  33. data/docs/how-open-source-maintained.jpg +0 -0
  34. data/docs/howto/add_pagination_links.md +139 -0
  35. data/docs/howto/add_root_key.md +51 -0
  36. data/docs/howto/outside_controller_use.md +58 -0
  37. data/docs/howto/passing_arbitrary_options.md +27 -0
  38. data/docs/howto/serialize_poro.md +32 -0
  39. data/docs/howto/test.md +152 -0
  40. data/docs/integrations/ember-and-json-api.md +112 -0
  41. data/docs/integrations/grape.md +19 -0
  42. data/docs/jsonapi/errors.md +56 -0
  43. data/docs/jsonapi/schema/schema.json +366 -0
  44. data/docs/jsonapi/schema.md +151 -0
  45. data/docs/rfcs/0000-namespace.md +106 -0
  46. data/docs/rfcs/template.md +15 -0
  47. data/lib/action_controller/serialization.rb +31 -36
  48. data/lib/active_model/serializable_resource.rb +11 -0
  49. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  50. data/lib/active_model/serializer/adapter/base.rb +16 -0
  51. data/lib/active_model/serializer/adapter/json.rb +15 -0
  52. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  53. data/lib/active_model/serializer/adapter/null.rb +15 -0
  54. data/lib/active_model/serializer/adapter.rb +24 -0
  55. data/lib/active_model/serializer/array_serializer.rb +9 -0
  56. data/lib/active_model/serializer/association.rb +19 -0
  57. data/lib/active_model/serializer/associations.rb +87 -220
  58. data/lib/active_model/serializer/attribute.rb +25 -0
  59. data/lib/active_model/serializer/attributes.rb +82 -0
  60. data/lib/active_model/serializer/belongs_to_reflection.rb +10 -0
  61. data/lib/active_model/serializer/caching.rb +333 -0
  62. data/lib/active_model/serializer/collection_reflection.rb +7 -0
  63. data/lib/active_model/serializer/collection_serializer.rb +64 -0
  64. data/lib/active_model/serializer/configuration.rb +35 -0
  65. data/lib/active_model/serializer/error_serializer.rb +10 -0
  66. data/lib/active_model/serializer/errors_serializer.rb +27 -0
  67. data/lib/active_model/serializer/field.rb +90 -0
  68. data/lib/active_model/serializer/fieldset.rb +31 -0
  69. data/lib/active_model/serializer/has_many_reflection.rb +10 -0
  70. data/lib/active_model/serializer/has_one_reflection.rb +10 -0
  71. data/lib/active_model/serializer/include_tree.rb +111 -0
  72. data/lib/active_model/serializer/links.rb +35 -0
  73. data/lib/active_model/serializer/lint.rb +146 -0
  74. data/lib/active_model/serializer/meta.rb +29 -0
  75. data/lib/active_model/serializer/null.rb +17 -0
  76. data/lib/active_model/serializer/reflection.rb +147 -0
  77. data/lib/active_model/serializer/singular_reflection.rb +7 -0
  78. data/lib/active_model/serializer/type.rb +25 -0
  79. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  80. data/lib/active_model/serializer.rb +158 -481
  81. data/lib/active_model_serializers/adapter/attributes.rb +76 -0
  82. data/lib/active_model_serializers/adapter/base.rb +83 -0
  83. data/lib/active_model_serializers/adapter/json.rb +21 -0
  84. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  85. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  86. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  87. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  88. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  89. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +62 -0
  90. data/lib/active_model_serializers/adapter/json_api/relationship.rb +52 -0
  91. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +37 -0
  92. data/lib/active_model_serializers/adapter/json_api.rb +516 -0
  93. data/lib/active_model_serializers/adapter/null.rb +9 -0
  94. data/lib/active_model_serializers/adapter.rb +92 -0
  95. data/lib/active_model_serializers/callbacks.rb +55 -0
  96. data/lib/active_model_serializers/deprecate.rb +55 -0
  97. data/lib/active_model_serializers/deserialization.rb +13 -0
  98. data/lib/active_model_serializers/json_pointer.rb +14 -0
  99. data/lib/active_model_serializers/key_transform.rb +70 -0
  100. data/lib/active_model_serializers/logging.rb +122 -0
  101. data/lib/active_model_serializers/model.rb +49 -0
  102. data/lib/active_model_serializers/railtie.rb +46 -0
  103. data/lib/active_model_serializers/register_jsonapi_renderer.rb +65 -0
  104. data/lib/active_model_serializers/serializable_resource.rb +81 -0
  105. data/lib/active_model_serializers/serialization_context.rb +32 -0
  106. data/lib/active_model_serializers/test/schema.rb +138 -0
  107. data/lib/active_model_serializers/test/serializer.rb +125 -0
  108. data/lib/active_model_serializers/test.rb +7 -0
  109. data/lib/active_model_serializers.rb +32 -89
  110. data/lib/generators/rails/USAGE +6 -0
  111. data/lib/generators/rails/resource_override.rb +10 -0
  112. data/lib/generators/rails/serializer_generator.rb +36 -0
  113. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  114. data/lib/grape/active_model_serializers.rb +14 -0
  115. data/lib/grape/formatters/active_model_serializers.rb +15 -0
  116. data/lib/grape/helpers/active_model_serializers.rb +16 -0
  117. data/test/action_controller/adapter_selector_test.rb +53 -0
  118. data/test/action_controller/explicit_serializer_test.rb +134 -0
  119. data/test/action_controller/json/include_test.rb +167 -0
  120. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  121. data/test/action_controller/json_api/errors_test.rb +41 -0
  122. data/test/action_controller/json_api/linked_test.rb +197 -0
  123. data/test/action_controller/json_api/pagination_test.rb +116 -0
  124. data/test/action_controller/json_api/transform_test.rb +181 -0
  125. data/test/action_controller/serialization_scope_name_test.rb +229 -0
  126. data/test/action_controller/serialization_test.rb +469 -0
  127. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  128. data/test/active_model_serializers/json_pointer_test.rb +20 -0
  129. data/test/active_model_serializers/key_transform_test.rb +263 -0
  130. data/test/active_model_serializers/logging_test.rb +77 -0
  131. data/test/active_model_serializers/model_test.rb +9 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +63 -0
  133. data/test/active_model_serializers/serialization_context_test_isolated.rb +58 -0
  134. data/test/active_model_serializers/test/schema_test.rb +130 -0
  135. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  136. data/test/active_record_test.rb +9 -0
  137. data/test/adapter/deprecation_test.rb +100 -0
  138. data/test/adapter/json/belongs_to_test.rb +45 -0
  139. data/test/adapter/json/collection_test.rb +90 -0
  140. data/test/adapter/json/has_many_test.rb +45 -0
  141. data/test/adapter/json/transform_test.rb +93 -0
  142. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  143. data/test/adapter/json_api/collection_test.rb +95 -0
  144. data/test/adapter/json_api/errors_test.rb +78 -0
  145. data/test/adapter/json_api/fields_test.rb +87 -0
  146. data/test/adapter/json_api/has_many_embed_ids_test.rb +43 -0
  147. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  148. data/test/adapter/json_api/has_many_test.rb +144 -0
  149. data/test/adapter/json_api/has_one_test.rb +80 -0
  150. data/test/adapter/json_api/json_api_test.rb +35 -0
  151. data/test/adapter/json_api/linked_test.rb +392 -0
  152. data/test/adapter/json_api/links_test.rb +93 -0
  153. data/test/adapter/json_api/pagination_links_test.rb +166 -0
  154. data/test/adapter/json_api/parse_test.rb +137 -0
  155. data/test/adapter/json_api/relationship_test.rb +161 -0
  156. data/test/adapter/json_api/relationships_test.rb +199 -0
  157. data/test/adapter/json_api/resource_identifier_test.rb +85 -0
  158. data/test/adapter/json_api/resource_meta_test.rb +100 -0
  159. data/test/adapter/json_api/toplevel_jsonapi_test.rb +82 -0
  160. data/test/adapter/json_api/transform_test.rb +502 -0
  161. data/test/adapter/json_api/type_test.rb +61 -0
  162. data/test/adapter/json_test.rb +45 -0
  163. data/test/adapter/null_test.rb +23 -0
  164. data/test/adapter/polymorphic_test.rb +171 -0
  165. data/test/adapter_test.rb +67 -0
  166. data/test/array_serializer_test.rb +20 -73
  167. data/test/benchmark/app.rb +65 -0
  168. data/test/benchmark/benchmarking_support.rb +67 -0
  169. data/test/benchmark/bm_caching.rb +119 -0
  170. data/test/benchmark/bm_transform.rb +34 -0
  171. data/test/benchmark/config.ru +3 -0
  172. data/test/benchmark/controllers.rb +84 -0
  173. data/test/benchmark/fixtures.rb +219 -0
  174. data/test/cache_test.rb +485 -0
  175. data/test/collection_serializer_test.rb +110 -0
  176. data/test/fixtures/active_record.rb +78 -0
  177. data/test/fixtures/poro.rb +282 -0
  178. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  179. data/test/generators/serializer_generator_test.rb +57 -0
  180. data/test/grape_test.rb +82 -0
  181. data/test/include_tree/from_include_args_test.rb +26 -0
  182. data/test/include_tree/from_string_test.rb +94 -0
  183. data/test/include_tree/include_args_to_hash_test.rb +64 -0
  184. data/test/lint_test.rb +49 -0
  185. data/test/logger_test.rb +18 -0
  186. data/test/poro_test.rb +9 -0
  187. data/test/serializable_resource_test.rb +83 -0
  188. data/test/serializers/association_macros_test.rb +36 -0
  189. data/test/serializers/associations_test.rb +295 -0
  190. data/test/serializers/attribute_test.rb +151 -0
  191. data/test/serializers/attributes_test.rb +52 -0
  192. data/test/serializers/caching_configuration_test_isolated.rb +170 -0
  193. data/test/serializers/configuration_test.rb +32 -0
  194. data/test/serializers/fieldset_test.rb +14 -0
  195. data/test/serializers/meta_test.rb +196 -0
  196. data/test/serializers/options_test.rb +21 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  198. data/test/serializers/root_test.rb +21 -0
  199. data/test/serializers/serialization_test.rb +55 -0
  200. data/test/serializers/serializer_for_test.rb +134 -0
  201. data/test/support/custom_schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  202. data/test/support/isolated_unit.rb +79 -0
  203. data/test/support/rails5_shims.rb +47 -0
  204. data/test/support/rails_app.rb +45 -0
  205. data/test/support/schemas/active_model_serializers/test/schema_test/my/index.json +6 -0
  206. data/test/support/schemas/active_model_serializers/test/schema_test/my/show.json +6 -0
  207. data/test/support/schemas/custom/show.json +7 -0
  208. data/test/support/schemas/hyper_schema.json +93 -0
  209. data/test/support/schemas/render_using_json_api.json +43 -0
  210. data/test/support/schemas/simple_json_pointers.json +10 -0
  211. data/test/support/serialization_testing.rb +53 -0
  212. data/test/test_helper.rb +48 -23
  213. metadata +449 -43
  214. data/DESIGN.textile +0 -586
  215. data/Gemfile.edge +0 -9
  216. data/bench/perf.rb +0 -43
  217. data/cruft.md +0 -19
  218. data/lib/active_model/array_serializer.rb +0 -104
  219. data/lib/active_record/serializer_override.rb +0 -16
  220. data/lib/generators/resource_override.rb +0 -13
  221. data/lib/generators/serializer/USAGE +0 -9
  222. data/lib/generators/serializer/serializer_generator.rb +0 -42
  223. data/lib/generators/serializer/templates/serializer.rb +0 -19
  224. data/test/association_test.rb +0 -592
  225. data/test/caching_test.rb +0 -96
  226. data/test/generators_test.rb +0 -85
  227. data/test/no_serialization_scope_test.rb +0 -34
  228. data/test/serialization_scope_name_test.rb +0 -67
  229. data/test/serialization_test.rb +0 -392
  230. data/test/serializer_support_test.rb +0 -51
  231. data/test/serializer_test.rb +0 -1465
  232. data/test/test_fakes.rb +0 -217
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi
6
+ class TopLevelJsonApiTest < ActiveSupport::TestCase
7
+ def setup
8
+ @author = Author.new(id: 1, name: 'Steve K.')
9
+ @author.bio = nil
10
+ @author.roles = []
11
+ @blog = Blog.new(id: 23, name: 'AMS Blog')
12
+ @post = Post.new(id: 42, title: 'New Post', body: 'Body')
13
+ @anonymous_post = Post.new(id: 43, title: 'Hello!!', body: 'Hello, world!!')
14
+ @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
15
+ @post.comments = [@comment]
16
+ @post.blog = @blog
17
+ @anonymous_post.comments = []
18
+ @anonymous_post.blog = nil
19
+ @comment.post = @post
20
+ @comment.author = nil
21
+ @post.author = @author
22
+ @anonymous_post.author = nil
23
+ @blog = Blog.new(id: 1, name: 'My Blog!!')
24
+ @blog.writer = @author
25
+ @blog.articles = [@post, @anonymous_post]
26
+ @author.posts = []
27
+ end
28
+
29
+ def test_toplevel_jsonapi_defaults_to_false
30
+ assert_equal config.fetch(:jsonapi_include_toplevel_object), false
31
+ end
32
+
33
+ def test_disable_toplevel_jsonapi
34
+ with_config(jsonapi_include_toplevel_object: false) do
35
+ hash = serialize(@post)
36
+ assert_nil(hash[:jsonapi])
37
+ end
38
+ end
39
+
40
+ def test_enable_toplevel_jsonapi
41
+ with_config(jsonapi_include_toplevel_object: true) do
42
+ hash = serialize(@post)
43
+ refute_nil(hash[:jsonapi])
44
+ end
45
+ end
46
+
47
+ def test_default_toplevel_jsonapi_version
48
+ with_config(jsonapi_include_toplevel_object: true) do
49
+ hash = serialize(@post)
50
+ assert_equal('1.0', hash[:jsonapi][:version])
51
+ end
52
+ end
53
+
54
+ def test_toplevel_jsonapi_no_meta
55
+ with_config(jsonapi_include_toplevel_object: true) do
56
+ hash = serialize(@post)
57
+ assert_nil(hash[:jsonapi][:meta])
58
+ end
59
+ end
60
+
61
+ def test_toplevel_jsonapi_meta
62
+ new_config = {
63
+ jsonapi_include_toplevel_object: true,
64
+ jsonapi_toplevel_meta: {
65
+ 'copyright' => 'Copyright 2015 Example Corp.'
66
+ }
67
+ }
68
+ with_config(new_config) do
69
+ hash = serialize(@post)
70
+ assert_equal(new_config[:jsonapi_toplevel_meta], hash.fetch(:jsonapi).fetch(:meta))
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def serialize(resource, options = {})
77
+ serializable(resource, { adapter: :json_api }.merge!(options)).serializable_hash
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,502 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonApi
6
+ class KeyCaseTest < ActiveSupport::TestCase
7
+ Post = Class.new(::Model)
8
+ class PostSerializer < ActiveModel::Serializer
9
+ type 'posts'
10
+ attributes :title, :body, :publish_at
11
+ belongs_to :author
12
+ has_many :comments
13
+
14
+ link(:self) { post_url(object.id) }
15
+ link(:post_authors) { post_authors_url(object.id) }
16
+ link(:subscriber_comments) { post_comments_url(object.id) }
17
+
18
+ meta do
19
+ {
20
+ rating: 5,
21
+ favorite_count: 10
22
+ }
23
+ end
24
+ end
25
+
26
+ Author = Class.new(::Model)
27
+ class AuthorSerializer < ActiveModel::Serializer
28
+ type 'authors'
29
+ attributes :first_name, :last_name
30
+ end
31
+
32
+ Comment = Class.new(::Model)
33
+ class CommentSerializer < ActiveModel::Serializer
34
+ type 'comments'
35
+ attributes :body
36
+ belongs_to :author
37
+ end
38
+
39
+ def mock_request(transform = nil)
40
+ context = Minitest::Mock.new
41
+ context.expect(:request_url, URI)
42
+ context.expect(:query_parameters, {})
43
+ context.expect(:url_helpers, Rails.application.routes.url_helpers)
44
+ @options = {}
45
+ @options[:key_transform] = transform if transform
46
+ @options[:serialization_context] = context
47
+ end
48
+
49
+ def setup
50
+ Rails.application.routes.draw do
51
+ resources :posts do
52
+ resources :authors
53
+ resources :comments
54
+ end
55
+ end
56
+ @publish_at = 1.day.from_now
57
+ @author = Author.new(id: 1, first_name: 'Bob', last_name: 'Jones')
58
+ @comment1 = Comment.new(id: 7, body: 'cool', author: @author)
59
+ @comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
60
+ @post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
61
+ author: @author, comments: [@comment1, @comment2],
62
+ publish_at: @publish_at)
63
+ @comment1.post = @post
64
+ @comment2.post = @post
65
+ end
66
+
67
+ def test_success_document_transform_default
68
+ mock_request
69
+ serializer = PostSerializer.new(@post)
70
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
71
+ result = adapter.serializable_hash
72
+ assert_equal({
73
+ data: {
74
+ id: '1337',
75
+ type: 'posts',
76
+ attributes: {
77
+ title: 'Title 1',
78
+ body: 'Body 1',
79
+ :"publish-at" => @publish_at
80
+ },
81
+ relationships: {
82
+ author: {
83
+ data: { id: '1', type: 'authors' }
84
+ },
85
+ comments: {
86
+ data: [
87
+ { id: '7', type: 'comments' },
88
+ { id: '12', type: 'comments' }
89
+ ] }
90
+ },
91
+ links: {
92
+ self: 'http://example.com/posts/1337',
93
+ :"post-authors" => 'http://example.com/posts/1337/authors',
94
+ :"subscriber-comments" => 'http://example.com/posts/1337/comments'
95
+ },
96
+ meta: { rating: 5, :"favorite-count" => 10 }
97
+ }
98
+ }, result)
99
+ end
100
+
101
+ def test_success_document_transform_global_config
102
+ mock_request
103
+ result = with_config(key_transform: :camel_lower) do
104
+ serializer = PostSerializer.new(@post)
105
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
106
+ adapter.serializable_hash
107
+ end
108
+ assert_equal({
109
+ data: {
110
+ id: '1337',
111
+ type: 'posts',
112
+ attributes: {
113
+ title: 'Title 1',
114
+ body: 'Body 1',
115
+ publishAt: @publish_at
116
+ },
117
+ relationships: {
118
+ author: {
119
+ data: { id: '1', type: 'authors' }
120
+ },
121
+ comments: {
122
+ data: [
123
+ { id: '7', type: 'comments' },
124
+ { id: '12', type: 'comments' }
125
+ ] }
126
+ },
127
+ links: {
128
+ self: 'http://example.com/posts/1337',
129
+ postAuthors: 'http://example.com/posts/1337/authors',
130
+ subscriberComments: 'http://example.com/posts/1337/comments'
131
+ },
132
+ meta: { rating: 5, favoriteCount: 10 }
133
+ }
134
+ }, result)
135
+ end
136
+
137
+ def test_success_doc_transform_serialization_ctx_overrides_global
138
+ mock_request(:camel)
139
+ result = with_config(key_transform: :camel_lower) do
140
+ serializer = PostSerializer.new(@post)
141
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
142
+ adapter.serializable_hash
143
+ end
144
+ assert_equal({
145
+ Data: {
146
+ Id: '1337',
147
+ Type: 'Posts',
148
+ Attributes: {
149
+ Title: 'Title 1',
150
+ Body: 'Body 1',
151
+ PublishAt: @publish_at
152
+ },
153
+ Relationships: {
154
+ Author: {
155
+ Data: { Id: '1', Type: 'Authors' }
156
+ },
157
+ Comments: {
158
+ Data: [
159
+ { Id: '7', Type: 'Comments' },
160
+ { Id: '12', Type: 'Comments' }
161
+ ] }
162
+ },
163
+ Links: {
164
+ Self: 'http://example.com/posts/1337',
165
+ PostAuthors: 'http://example.com/posts/1337/authors',
166
+ SubscriberComments: 'http://example.com/posts/1337/comments'
167
+ },
168
+ Meta: { Rating: 5, FavoriteCount: 10 }
169
+ }
170
+ }, result)
171
+ end
172
+
173
+ def test_success_document_transform_dash
174
+ mock_request(:dash)
175
+ serializer = PostSerializer.new(@post)
176
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
177
+ result = adapter.serializable_hash
178
+ assert_equal({
179
+ data: {
180
+ id: '1337',
181
+ type: 'posts',
182
+ attributes: {
183
+ title: 'Title 1',
184
+ body: 'Body 1',
185
+ :"publish-at" => @publish_at
186
+ },
187
+ relationships: {
188
+ author: {
189
+ data: { id: '1', type: 'authors' }
190
+ },
191
+ comments: {
192
+ data: [
193
+ { id: '7', type: 'comments' },
194
+ { id: '12', type: 'comments' }
195
+ ] }
196
+ },
197
+ links: {
198
+ self: 'http://example.com/posts/1337',
199
+ :"post-authors" => 'http://example.com/posts/1337/authors',
200
+ :"subscriber-comments" => 'http://example.com/posts/1337/comments'
201
+ },
202
+ meta: { rating: 5, :"favorite-count" => 10 }
203
+ }
204
+ }, result)
205
+ end
206
+
207
+ def test_success_document_transform_unaltered
208
+ mock_request(:unaltered)
209
+ serializer = PostSerializer.new(@post)
210
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
211
+ result = adapter.serializable_hash
212
+ assert_equal({
213
+ data: {
214
+ id: '1337',
215
+ type: 'posts',
216
+ attributes: {
217
+ title: 'Title 1',
218
+ body: 'Body 1',
219
+ publish_at: @publish_at
220
+ },
221
+ relationships: {
222
+ author: {
223
+ data: { id: '1', type: 'authors' }
224
+ },
225
+ comments: {
226
+ data: [
227
+ { id: '7', type: 'comments' },
228
+ { id: '12', type: 'comments' }
229
+ ] }
230
+ },
231
+ links: {
232
+ self: 'http://example.com/posts/1337',
233
+ post_authors: 'http://example.com/posts/1337/authors',
234
+ subscriber_comments: 'http://example.com/posts/1337/comments'
235
+ },
236
+ meta: { rating: 5, favorite_count: 10 }
237
+ }
238
+ }, result)
239
+ end
240
+
241
+ def test_success_document_transform_undefined
242
+ mock_request(:zoot)
243
+ serializer = PostSerializer.new(@post)
244
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
245
+ exception = assert_raises NoMethodError do
246
+ adapter.serializable_hash
247
+ end
248
+ assert_match(/undefined method.*zoot/, exception.message)
249
+ end
250
+
251
+ def test_success_document_transform_camel
252
+ mock_request(:camel)
253
+ serializer = PostSerializer.new(@post)
254
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
255
+ result = adapter.serializable_hash
256
+ assert_equal({
257
+ Data: {
258
+ Id: '1337',
259
+ Type: 'Posts',
260
+ Attributes: {
261
+ Title: 'Title 1',
262
+ Body: 'Body 1',
263
+ PublishAt: @publish_at
264
+ },
265
+ Relationships: {
266
+ Author: {
267
+ Data: { Id: '1', Type: 'Authors' }
268
+ },
269
+ Comments: {
270
+ Data: [
271
+ { Id: '7', Type: 'Comments' },
272
+ { Id: '12', Type: 'Comments' }
273
+ ] }
274
+ },
275
+ Links: {
276
+ Self: 'http://example.com/posts/1337',
277
+ PostAuthors: 'http://example.com/posts/1337/authors',
278
+ SubscriberComments: 'http://example.com/posts/1337/comments'
279
+ },
280
+ Meta: { Rating: 5, FavoriteCount: 10 }
281
+ }
282
+ }, result)
283
+ end
284
+
285
+ def test_success_document_transform_camel_lower
286
+ mock_request(:camel_lower)
287
+ serializer = PostSerializer.new(@post)
288
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
289
+ result = adapter.serializable_hash
290
+ assert_equal({
291
+ data: {
292
+ id: '1337',
293
+ type: 'posts',
294
+ attributes: {
295
+ title: 'Title 1',
296
+ body: 'Body 1',
297
+ publishAt: @publish_at
298
+ },
299
+ relationships: {
300
+ author: {
301
+ data: { id: '1', type: 'authors' }
302
+ },
303
+ comments: {
304
+ data: [
305
+ { id: '7', type: 'comments' },
306
+ { id: '12', type: 'comments' }
307
+ ] }
308
+ },
309
+ links: {
310
+ self: 'http://example.com/posts/1337',
311
+ postAuthors: 'http://example.com/posts/1337/authors',
312
+ subscriberComments: 'http://example.com/posts/1337/comments'
313
+ },
314
+ meta: { rating: 5, favoriteCount: 10 }
315
+ }
316
+ }, result)
317
+ end
318
+
319
+ def test_error_document_transform_default
320
+ mock_request
321
+ resource = ModelWithErrors.new
322
+ resource.errors.add(:published_at, 'must be in the future')
323
+ resource.errors.add(:title, 'must be longer')
324
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
325
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
326
+ result = adapter.serializable_hash
327
+ expected_errors_object =
328
+ { :errors =>
329
+ [
330
+ {
331
+ :source => { :pointer => '/data/attributes/published-at' },
332
+ :detail => 'must be in the future' },
333
+ {
334
+ :source => { :pointer => '/data/attributes/title' },
335
+ :detail => 'must be longer'
336
+ }
337
+ ]
338
+ }
339
+ assert_equal expected_errors_object, result
340
+ end
341
+
342
+ def test_error_document_transform_global_config
343
+ mock_request
344
+ result = with_config(key_transform: :camel) do
345
+ resource = ModelWithErrors.new
346
+ resource.errors.add(:published_at, 'must be in the future')
347
+ resource.errors.add(:title, 'must be longer')
348
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
349
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
350
+ adapter.serializable_hash
351
+ end
352
+ expected_errors_object =
353
+ { :Errors =>
354
+ [
355
+ {
356
+ :Source => { :Pointer => '/data/attributes/PublishedAt' },
357
+ :Detail => 'must be in the future'
358
+ },
359
+ {
360
+ :Source => { :Pointer => '/data/attributes/Title' },
361
+ :Detail => 'must be longer'
362
+ }
363
+ ]
364
+ }
365
+ assert_equal expected_errors_object, result
366
+ end
367
+
368
+ def test_error_document_transform_serialization_ctx_overrides_global
369
+ mock_request(:camel)
370
+ result = with_config(key_transform: :camel_lower) do
371
+ resource = ModelWithErrors.new
372
+ resource.errors.add(:published_at, 'must be in the future')
373
+ resource.errors.add(:title, 'must be longer')
374
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
375
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
376
+ adapter.serializable_hash
377
+ end
378
+ expected_errors_object =
379
+ { :Errors =>
380
+ [
381
+ {
382
+ :Source => { :Pointer => '/data/attributes/PublishedAt' },
383
+ :Detail => 'must be in the future'
384
+ },
385
+ {
386
+ :Source => { :Pointer => '/data/attributes/Title' },
387
+ :Detail => 'must be longer'
388
+ }
389
+ ]
390
+ }
391
+ assert_equal expected_errors_object, result
392
+ end
393
+
394
+ def test_error_document_transform_dash
395
+ mock_request(:dash)
396
+
397
+ resource = ModelWithErrors.new
398
+ resource.errors.add(:published_at, 'must be in the future')
399
+ resource.errors.add(:title, 'must be longer')
400
+
401
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
402
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
403
+ result = adapter.serializable_hash
404
+
405
+ expected_errors_object =
406
+ { :errors =>
407
+ [
408
+ {
409
+ :source => { :pointer => '/data/attributes/published-at' },
410
+ :detail => 'must be in the future'
411
+ },
412
+ {
413
+ :source => { :pointer => '/data/attributes/title' },
414
+ :detail => 'must be longer'
415
+ }
416
+ ]
417
+ }
418
+ assert_equal expected_errors_object, result
419
+ end
420
+
421
+ def test_error_document_transform_unaltered
422
+ mock_request(:unaltered)
423
+
424
+ resource = ModelWithErrors.new
425
+ resource.errors.add(:published_at, 'must be in the future')
426
+ resource.errors.add(:title, 'must be longer')
427
+
428
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
429
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
430
+ result = adapter.serializable_hash
431
+
432
+ expected_errors_object =
433
+ { :errors =>
434
+ [
435
+ { :source => { :pointer => '/data/attributes/published_at' }, :detail => 'must be in the future' },
436
+ { :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
437
+ ]
438
+ }
439
+ assert_equal expected_errors_object, result
440
+ end
441
+
442
+ def test_error_document_transform_undefined
443
+ mock_request(:krazy)
444
+
445
+ resource = ModelWithErrors.new
446
+ resource.errors.add(:published_at, 'must be in the future')
447
+ resource.errors.add(:title, 'must be longer')
448
+
449
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
450
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
451
+
452
+ exception = assert_raises NoMethodError do
453
+ adapter.serializable_hash
454
+ end
455
+ assert_match(/undefined method.*krazy/, exception.message)
456
+ end
457
+
458
+ def test_error_document_transform_camel
459
+ mock_request(:camel)
460
+
461
+ resource = ModelWithErrors.new
462
+ resource.errors.add(:published_at, 'must be in the future')
463
+ resource.errors.add(:title, 'must be longer')
464
+
465
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
466
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
467
+ result = adapter.serializable_hash
468
+
469
+ expected_errors_object =
470
+ { :Errors =>
471
+ [
472
+ { :Source => { :Pointer => '/data/attributes/PublishedAt' }, :Detail => 'must be in the future' },
473
+ { :Source => { :Pointer => '/data/attributes/Title' }, :Detail => 'must be longer' }
474
+ ]
475
+ }
476
+ assert_equal expected_errors_object, result
477
+ end
478
+
479
+ def test_error_document_transform_camel_lower
480
+ mock_request(:camel_lower)
481
+
482
+ resource = ModelWithErrors.new
483
+ resource.errors.add(:published_at, 'must be in the future')
484
+ resource.errors.add(:title, 'must be longer')
485
+
486
+ serializer = ActiveModel::Serializer::ErrorSerializer.new(resource)
487
+ adapter = ActiveModelSerializers::Adapter::JsonApi.new(serializer, @options)
488
+ result = adapter.serializable_hash
489
+
490
+ expected_errors_object =
491
+ { :errors =>
492
+ [
493
+ { :source => { :pointer => '/data/attributes/publishedAt' }, :detail => 'must be in the future' },
494
+ { :source => { :pointer => '/data/attributes/title' }, :detail => 'must be longer' }
495
+ ]
496
+ }
497
+ assert_equal expected_errors_object, result
498
+ end
499
+ end
500
+ end
501
+ end
502
+ end
@@ -0,0 +1,61 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModel
4
+ class Serializer
5
+ module Adapter
6
+ class JsonApi
7
+ class TypeTest < ActiveSupport::TestCase
8
+ class StringTypeSerializer < ActiveModel::Serializer
9
+ attribute :name
10
+ type 'profile'
11
+ end
12
+
13
+ class SymbolTypeSerializer < ActiveModel::Serializer
14
+ attribute :name
15
+ type :profile
16
+ end
17
+
18
+ setup do
19
+ @author = Author.new(id: 1, name: 'Steve K.')
20
+ end
21
+
22
+ def test_config_plural
23
+ with_jsonapi_resource_type :plural do
24
+ assert_type(@author, 'authors')
25
+ end
26
+ end
27
+
28
+ def test_config_singular
29
+ with_jsonapi_resource_type :singular do
30
+ assert_type(@author, 'author')
31
+ end
32
+ end
33
+
34
+ def test_explicit_string_type_value
35
+ assert_type(@author, 'profile', serializer: StringTypeSerializer)
36
+ end
37
+
38
+ def test_explicit_symbol_type_value
39
+ assert_type(@author, 'profile', serializer: SymbolTypeSerializer)
40
+ end
41
+
42
+ private
43
+
44
+ def assert_type(resource, expected_type, opts = {})
45
+ opts = opts.reverse_merge(adapter: :json_api)
46
+ hash = serializable(resource, opts).serializable_hash
47
+ assert_equal(expected_type, hash.fetch(:data).fetch(:type))
48
+ end
49
+
50
+ def with_jsonapi_resource_type inflection
51
+ old_inflection = ActiveModelSerializers.config.jsonapi_resource_type
52
+ ActiveModelSerializers.config.jsonapi_resource_type = inflection
53
+ yield
54
+ ensure
55
+ ActiveModelSerializers.config.jsonapi_resource_type = old_inflection
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+
3
+ module ActiveModelSerializers
4
+ module Adapter
5
+ class JsonTest < ActiveSupport::TestCase
6
+ def setup
7
+ ActionController::Base.cache_store.clear
8
+ @author = Author.new(id: 1, name: 'Steve K.')
9
+ @post = Post.new(id: 1, title: 'New Post', body: 'Body')
10
+ @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
11
+ @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
12
+ @post.comments = [@first_comment, @second_comment]
13
+ @first_comment.post = @post
14
+ @second_comment.post = @post
15
+ @post.author = @author
16
+ @blog = Blog.new(id: 1, name: 'My Blog!!')
17
+ @post.blog = @blog
18
+
19
+ @serializer = PostSerializer.new(@post)
20
+ @adapter = ActiveModelSerializers::Adapter::Json.new(@serializer)
21
+ end
22
+
23
+ def test_has_many
24
+ assert_equal([
25
+ { id: 1, body: 'ZOMG A COMMENT' },
26
+ { id: 2, body: 'ZOMG ANOTHER COMMENT' }
27
+ ], @adapter.serializable_hash[:post][:comments])
28
+ end
29
+
30
+ def test_custom_keys
31
+ serializer = PostWithCustomKeysSerializer.new(@post)
32
+ adapter = ActiveModelSerializers::Adapter::Json.new(serializer)
33
+
34
+ assert_equal({
35
+ id: 1,
36
+ reviews: [{ id: 1, body: 'ZOMG A COMMENT' },
37
+ { id: 2, body: 'ZOMG ANOTHER COMMENT' }
38
+ ],
39
+ writer: { id: 1, name: 'Steve K.' },
40
+ site: { id: 1, name: 'My Blog!!' }
41
+ }, adapter.serializable_hash[:post])
42
+ end
43
+ end
44
+ end
45
+ end