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,139 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ # How to add pagination links
4
+
5
+ ### JSON API adapter
6
+
7
+ Pagination links will be included in your response automatically as long as
8
+ the resource is paginated and if you are using the ```JsonApi``` adapter.
9
+
10
+ If you want pagination links in your response, use [Kaminari](https://github.com/amatsuda/kaminari)
11
+ or [WillPaginate](https://github.com/mislav/will_paginate).
12
+
13
+ Although the others adapters does not have this feature, it is possible to
14
+ implement pagination links to `JSON` adapter. For more information about it,
15
+ please see in our docs
16
+
17
+ ###### Kaminari examples
18
+
19
+ ```ruby
20
+ #array
21
+ @posts = Kaminari.paginate_array([1, 2, 3]).page(3).per(1)
22
+ render json: @posts
23
+
24
+ #active_record
25
+ @posts = Post.page(3).per(1)
26
+ render json: @posts
27
+ ```
28
+
29
+ ###### WillPaginate examples
30
+
31
+ ```ruby
32
+ #array
33
+ @posts = [1,2,3].paginate(page: 3, per_page: 1)
34
+ render json: @posts
35
+
36
+ #active_record
37
+ @posts = Post.page(3).per_page(1)
38
+ render json: @posts
39
+ ```
40
+
41
+ ```ruby
42
+ ActiveModelSerializers.config.adapter = :json_api
43
+ ```
44
+
45
+ ex:
46
+ ```json
47
+ {
48
+ "data": [
49
+ {
50
+ "type": "articles",
51
+ "id": "3",
52
+ "attributes": {
53
+ "title": "JSON API paints my bikeshed!",
54
+ "body": "The shortest article. Ever.",
55
+ "created": "2015-05-22T14:56:29.000Z",
56
+ "updated": "2015-05-22T14:56:28.000Z"
57
+ }
58
+ }
59
+ ],
60
+ "links": {
61
+ "self": "http://example.com/articles?page[number]=3&page[size]=1",
62
+ "first": "http://example.com/articles?page[number]=1&page[size]=1",
63
+ "prev": "http://example.com/articles?page[number]=2&page[size]=1",
64
+ "next": "http://example.com/articles?page[number]=4&page[size]=1",
65
+ "last": "http://example.com/articles?page[number]=13&page[size]=1"
66
+ }
67
+ }
68
+ ```
69
+
70
+ ActiveModelSerializers pagination relies on a paginated collection with the methods `current_page`, `total_pages`, and `size`, such as are supported by both [Kaminari](https://github.com/amatsuda/kaminari) or [WillPaginate](https://github.com/mislav/will_paginate).
71
+
72
+
73
+ ### JSON adapter
74
+
75
+ If you are using `JSON` adapter, pagination links will not be included automatically, but it is possible to do so using `meta` key.
76
+
77
+ Add this method to your base API controller.
78
+
79
+ ```ruby
80
+ def pagination_dict(object)
81
+ {
82
+ current_page: object.current_page,
83
+ next_page: object.next_page,
84
+ prev_page: object.prev_page,
85
+ total_pages: object.total_pages,
86
+ total_count: object.total_count
87
+ }
88
+ end
89
+ ```
90
+
91
+ Then, use it on your render method.
92
+
93
+ ```ruby
94
+ render json: posts, meta: pagination_dict(posts)
95
+ ```
96
+
97
+ ex.
98
+ ```json
99
+ {
100
+ "posts": [
101
+ {
102
+ "id": 2,
103
+ "title": "JSON API paints my bikeshed!",
104
+ "body": "The shortest article. Ever."
105
+ }
106
+ ],
107
+ "meta": {
108
+ "current_page": 3,
109
+ "next_page": 4,
110
+ "prev_page": 2,
111
+ "total_pages": 10,
112
+ "total_count": 10
113
+ }
114
+ }
115
+ ```
116
+
117
+ You can also achieve the same result if you have a helper method that adds the pagination info in the meta tag. For instance, in your action specify a custom serializer.
118
+
119
+ ```ruby
120
+ render json: @posts, each_serializer: PostPreviewSerializer, meta: meta_attributes(@post)
121
+ ```
122
+
123
+ ```ruby
124
+ #expects pagination!
125
+ def meta_attributes(resource, extra_meta = {})
126
+ {
127
+ current_page: resource.current_page,
128
+ next_page: resource.next_page,
129
+ prev_page: resource.prev_page,
130
+ total_pages: resource.total_pages,
131
+ total_count: resource.total_count
132
+ }.merge(extra_meta)
133
+ end
134
+ ```
135
+
136
+
137
+ ### Attributes adapter
138
+
139
+ This adapter does not allow us to use `meta` key, due to that it is not possible to add pagination links.
@@ -0,0 +1,51 @@
1
+ # How to add root key
2
+
3
+ Add the root key to your API is quite simple with ActiveModelSerializers. The **Adapter** is what determines the format of your JSON response. The default adapter is the ```Attributes``` which doesn't have the root key, so your response is something similar to:
4
+
5
+ ```json
6
+ {
7
+ "id": 1,
8
+ "title": "Awesome Post Tile",
9
+ "content": "Post content"
10
+ }
11
+ ```
12
+
13
+ In order to add the root key you need to use the ```JSON``` Adapter, you can change this in an initializer:
14
+
15
+ ```ruby
16
+ ActiveModelSerializers.config.adapter = :json
17
+ ```
18
+
19
+ You can also specify a class as adapter, as long as it complies with the ActiveModelSerializers adapters interface.
20
+ It will add the root key to all your serialized endpoints.
21
+
22
+ ex:
23
+
24
+ ```json
25
+ {
26
+ "post": {
27
+ "id": 1,
28
+ "title": "Awesome Post Tile",
29
+ "content": "Post content"
30
+ }
31
+ }
32
+ ```
33
+
34
+ or if it returns a collection:
35
+
36
+ ```json
37
+ {
38
+ "posts": [
39
+ {
40
+ "id": 1,
41
+ "title": "Awesome Post Tile",
42
+ "content": "Post content"
43
+ },
44
+ {
45
+ "id": 2,
46
+ "title": "Another Post Tile",
47
+ "content": "Another post content"
48
+ }
49
+ ]
50
+ }
51
+ ```
@@ -0,0 +1,58 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ ## Using ActiveModelSerializers Outside Of A Controller
4
+
5
+ ### Serializing a resource
6
+
7
+ In ActiveModelSerializers versions 0.10 or later, serializing resources outside of the controller context is fairly simple:
8
+
9
+ ```ruby
10
+ # Create our resource
11
+ post = Post.create(title: "Sample post", body: "I love Active Model Serializers!")
12
+
13
+ # Optional options parameters
14
+ options = {}
15
+
16
+ # Create a serializable resource instance
17
+ serializable_resource = ActiveModelSerializers::SerializableResource.new(post, options)
18
+
19
+ # Convert your resource into json
20
+ model_json = serializable_resource.as_json
21
+ ```
22
+
23
+ ### Looking up the Serializer for a Resource
24
+
25
+ If you want to retrieve a serializer for a specific resource, you can do the following:
26
+
27
+ ```ruby
28
+ # Create our resource
29
+ post = Post.create(title: "Another Example", body: "So much fun.")
30
+
31
+ # Optional options parameters
32
+ options = {}
33
+
34
+ # Retrieve the default serializer for posts
35
+ serializer = ActiveModel::Serializer.serializer_for(post, options)
36
+ ```
37
+
38
+ You could also retrieve the serializer via:
39
+
40
+ ```ruby
41
+ ActiveModelSerializers::SerializableResource.new(post, options).serializer
42
+ ```
43
+
44
+ Both approaches will return an instance, if any, of the resource's serializer.
45
+
46
+ ## Serializing before controller render
47
+
48
+ At times, you might want to use a serializer without rendering it to the view. For those cases, you can create an instance of `ActiveModelSerializers::SerializableResource` with
49
+ the resource you want to be serialized and call `.as_json`.
50
+
51
+ ```ruby
52
+ def create
53
+ message = current_user.messages.create!(message_params)
54
+ message_json = ActiveModelSerializers::SerializableResource.new(message).as_json
55
+ MessageCreationWorker.perform(message_json)
56
+ head 204
57
+ end
58
+ ```
@@ -0,0 +1,27 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ # Passing Arbitrary Options To A Serializer
4
+
5
+ In addition to the [`serialization_scope`](../general/serializers.md#scope), any options passed to `render`
6
+ that are not reserved for the [adapter](../general/rendering.md#adapter_opts)
7
+ are available in the serializer as [instance_options](../general/serializers.md#instance_options).
8
+
9
+ For example, we could pass in a field, such as `user_id` into our serializer.
10
+
11
+ ```ruby
12
+ # posts_controller.rb
13
+ class PostsController < ApplicationController
14
+ def dashboard
15
+ render json: @post, user_id: 12
16
+ end
17
+ end
18
+
19
+ # post_serializer.rb
20
+ class PostSerializer < ActiveModel::Serializer
21
+ attributes :id, :title, :body
22
+
23
+ def comments_by_me
24
+ Comments.where(user_id: instance_options[:user_id], post_id: object.id)
25
+ end
26
+ end
27
+ ```
@@ -0,0 +1,32 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ # How to serialize a Plain-Old Ruby Object (PORO)
4
+
5
+ When you are first getting started with ActiveModelSerializers, it may seem only `ActiveRecord::Base` objects can be serializable, but pretty much any object can be serializable with ActiveModelSerializers. Here is an example of a PORO that is serializable:
6
+ ```ruby
7
+ # my_model.rb
8
+ class MyModel
9
+ alias :read_attribute_for_serialization :send
10
+ attr_accessor :id, :name, :level
11
+
12
+ def initialize(attributes)
13
+ @id = attributes[:id]
14
+ @name = attributes[:name]
15
+ @level = attributes[:level]
16
+ end
17
+
18
+ def self.model_name
19
+ @_model_name ||= ActiveModel::Name.new(self)
20
+ end
21
+ end
22
+ ```
23
+
24
+ Fortunately, ActiveModelSerializers provides a [`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb) which you can use in production code that will make your PORO a lot cleaner. The above code now becomes:
25
+ ```ruby
26
+ # my_model.rb
27
+ class MyModel < ActiveModelSerializers::Model
28
+ attr_accessor :id, :name, :level
29
+ end
30
+ ```
31
+
32
+ The default serializer would be `MyModelSerializer`.
@@ -0,0 +1,152 @@
1
+ # How to test
2
+
3
+ ## Controller Serializer Usage
4
+
5
+ ActiveModelSerializers provides a `assert_serializer` method to be used on your controller tests to
6
+ assert that a specific serializer was used.
7
+
8
+ ```ruby
9
+ class PostsControllerTest < ActionController::TestCase
10
+ test "should render post serializer" do
11
+ get :index
12
+ assert_serializer "PostSerializer"
13
+ end
14
+ end
15
+ ```
16
+
17
+ See [ActiveModelSerializers::Test::Serializer](../../lib/active_model_serializers/test/serializer.rb)
18
+ for more examples and documentation.
19
+
20
+ ## Serialization against a schema
21
+
22
+ ### Dependencies
23
+
24
+ To use the `assert_response_schema` you need to have the
25
+ [`json_schema`](https://github.com/brandur/json_schema) on your Gemfile. Please
26
+ add it to your Gemfile and run `$ bundle install`.
27
+
28
+ ### Minitest test helpers
29
+
30
+ ActiveModelSerializers provides a `assert_response_schema` method to be used on your controller tests to
31
+ assert the response against a [JSON Schema](http://json-schema.org/). Let's take
32
+ a look in an example.
33
+
34
+ ```ruby
35
+ class PostsController < ApplicationController
36
+ def show
37
+ @post = Post.find(params[:id])
38
+
39
+ render json: @post
40
+ end
41
+ end
42
+ ```
43
+
44
+ To test the `posts#show` response of this controller we need to create a file
45
+ named `test/support/schemas/posts/show.json`. The helper uses a naming convention
46
+ to locate the file.
47
+
48
+ This file is a JSON Schema representation of our response.
49
+
50
+ ```json
51
+ {
52
+ "properties": {
53
+ "title" : { "type" : "string" },
54
+ "content" : { "type" : "string" }
55
+ }
56
+ }
57
+ ```
58
+
59
+ With all in place we can go to our test and use the helper.
60
+
61
+ ```ruby
62
+ class PostsControllerTest < ActionController::TestCase
63
+ test "should render right response" do
64
+ get :index
65
+ assert_response_schema
66
+ end
67
+ end
68
+ ```
69
+
70
+ #### Load a custom schema
71
+
72
+ If we need to use another schema, for example when we have a namespaced API that
73
+ shows the same response, we can pass the path of the schema.
74
+
75
+ ```ruby
76
+ module V1
77
+ class PostsController < ApplicationController
78
+ def show
79
+ @post = Post.find(params[:id])
80
+
81
+ render json: @post
82
+ end
83
+ end
84
+ end
85
+ ```
86
+
87
+ ```ruby
88
+ class V1::PostsControllerTest < ActionController::TestCase
89
+ test "should render right response" do
90
+ get :index
91
+ assert_response_schema('posts/show.json')
92
+ end
93
+ end
94
+ ```
95
+
96
+ #### Change the schema path
97
+
98
+ By default all schemas are created at `test/support/schemas`. If we are using
99
+ RSpec for example we can change this to `spec/support/schemas` defining the
100
+ default schema path in an initializer.
101
+
102
+ ```ruby
103
+ ActiveModelSerializers.config.schema_path = 'spec/support/schemas'
104
+ ```
105
+
106
+ #### Using with the Heroku’s JSON Schema-based tools
107
+
108
+ To use the test helper with the [prmd](https://github.com/interagent/prmd) and
109
+ [committee](https://github.com/interagent/committee).
110
+
111
+ We need to change the schema path to the recommended by prmd:
112
+
113
+ ```ruby
114
+ ActiveModelSerializers.config.schema_path = 'docs/schema/schemata'
115
+ ```
116
+
117
+ We also need to structure our schemata according to Heroku's conventions
118
+ (e.g. including
119
+ [required metadata](https://github.com/interagent/prmd/blob/master/docs/schemata.md#meta-data)
120
+ and [links](https://github.com/interagent/prmd/blob/master/docs/schemata.md#links).
121
+
122
+ #### JSON Pointers
123
+
124
+ If we plan to use [JSON
125
+ Pointers](http://spacetelescope.github.io/understanding-json-schema/UnderstandingJSONSchema.pdf) we need to define the `id` attribute on the schema. Example:
126
+
127
+ ```js
128
+ // attributes.json
129
+
130
+ {
131
+ "id": "file://attributes.json#",
132
+ "properties": {
133
+ "name" : { "type" : "string" },
134
+ "description" : { "type" : "string" }
135
+ }
136
+ }
137
+ ```
138
+
139
+ ```js
140
+ // show.json
141
+
142
+ {
143
+ "properties": {
144
+ "name": {
145
+ "$ref": "file://attributes.json#/properties/name"
146
+ },
147
+ "description": {
148
+ "$ref": "file://attributes.json#/properties/description"
149
+ }
150
+ }
151
+ }
152
+ ```
@@ -0,0 +1,112 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ # Integrating with Ember and JSON API
4
+
5
+ - [Preparation](./ember-and-json-api.md#preparation)
6
+ - [Server-Side Changes](./ember-and-json-api.md#server-side-changes)
7
+ - [Adapter Changes](./ember-and-json-api.md#adapter-changes)
8
+ - [Serializer Changes](./ember-and-json-api.md#serializer-changes)
9
+ - [Including Nested Resources](./ember-and-json-api.md#including-nested-resources)
10
+
11
+ ## Preparation
12
+
13
+ Note: This guide assumes that `ember-cli` is used for your ember app.
14
+
15
+ The JSON API specification calls for hyphens for multi-word separators. ActiveModelSerializers uses underscores.
16
+ To solve this, in Ember, both the adapter and the serializer will need some modifications:
17
+
18
+ ### Server-Side Changes
19
+
20
+ there are multiple mimetypes for json that should all be parsed similarly, so
21
+ in `config/initializers/mime_types.rb`:
22
+ ```ruby
23
+ api_mime_types = %W(
24
+ application/vnd.api+json
25
+ text/x-json
26
+ application/json
27
+ )
28
+
29
+ Mime::Type.unregister :json
30
+ Mime::Type.register 'application/json', :json, api_mime_types
31
+ ```
32
+
33
+ ### Adapter Changes
34
+
35
+ ```javascript
36
+ // app/adapters/application.js
37
+ import DS from 'ember-data';
38
+ import ENV from "../config/environment";
39
+
40
+ export default DS.JSONAPIAdapter.extend({
41
+ namespace: 'api',
42
+ // if your rails app is on a different port from your ember app
43
+ // this can be helpful for development.
44
+ // in production, the host for both rails and ember should be the same.
45
+ host: ENV.host,
46
+
47
+ // allows the multiword paths in urls to be underscored
48
+ pathForType: function(type) {
49
+ let underscored = Ember.String.underscore(type);
50
+ return Ember.String.pluralize(underscored);
51
+ },
52
+
53
+ // allows queries to be sent along with a findRecord
54
+ // hopefully Ember / EmberData will soon have this built in
55
+ // ember-data issue tracked here:
56
+ // https://github.com/emberjs/data/issues/3596
57
+ urlForFindRecord(id, modelName, snapshot) {
58
+ let url = this._super(...arguments);
59
+ let query = Ember.get(snapshot, 'adapterOptions.query');
60
+ if(query) {
61
+ url += '?' + Ember.$.param(query);
62
+ }
63
+ return url;
64
+ }
65
+ });
66
+ ```
67
+
68
+ ### Serializer Changes
69
+
70
+ ```javascript
71
+ // app/serializers/application.js
72
+ import Ember from 'ember';
73
+ import DS from 'ember-data';
74
+ var underscore = Ember.String.underscore;
75
+
76
+ export default DS.JSONAPISerializer.extend({
77
+ keyForAttribute: function(attr) {
78
+ return underscore(attr);
79
+ },
80
+
81
+ keyForRelationship: function(rawKey) {
82
+ return underscore(rawKey);
83
+ }
84
+ });
85
+
86
+ ```
87
+
88
+ ## Including Nested Resources
89
+
90
+ Previously, `store.find` and `store.findRecord` did not allow specification of any query params.
91
+ The ActiveModelSerializers default for the `include` parameter is to be `nil` meaning that if any associations are defined in your serializer, only the `id` and `type` will be in the `relationships` structure of the JSON API response.
92
+ For more on `include` usage, see: [The JSON API include examples](./../general/adapters.md#JSON-API)
93
+
94
+ With the above modifications, you can execute code as below in order to include nested resources while doing a find query.
95
+
96
+ ```javascript
97
+ store.findRecord('post', postId, { adapterOptions: { query: { include: 'comments' } } });
98
+ ```
99
+ will generate the path `/posts/{postId}?include='comments'`
100
+
101
+ So then in your controller, you'll want to be sure to have something like:
102
+ ```ruby
103
+ render json: @post, include: params[:include]
104
+ ```
105
+
106
+ If you want to use `include` on a collection, you'd write something like this:
107
+
108
+ ```javascript
109
+ store.query('post', { include: 'comments' });
110
+ ```
111
+
112
+ which will generate the path `/posts?include='comments'`
@@ -0,0 +1,19 @@
1
+ # Integration with Grape
2
+
3
+ [Grape](https://github.com/ruby-grape/grape) is an opinionated micro-framework for creating REST-like APIs in ruby.
4
+
5
+ ActiveModelSerializers currently supports Grape >= 0.13, < 1.0
6
+
7
+ To add [Grape](https://github.com/ruby-grape/grape) support, enable the formatter and helper functions by including `Grape::ActiveModelSerializers` in your base endpoint. For example:
8
+
9
+ ```ruby
10
+ module Example
11
+ class Dummy < Grape::API
12
+ require 'grape/active_model_serializers'
13
+ include Grape::ActiveModelSerializers
14
+ mount Example::V1::Base
15
+ end
16
+ end
17
+ ```
18
+
19
+ Aside from this, [configuration](../general/configuration_options.md) of ActiveModelSerializers is exactly the same.
@@ -0,0 +1,56 @@
1
+ [Back to Guides](../README.md)
2
+
3
+ # [JSON API Errors](http://jsonapi.org/format/#errors)
4
+
5
+ Rendering error documents requires specifying the error serializer(s):
6
+
7
+ - Serializer:
8
+ - For a single resource: `serializer: ActiveModel::Serializer::ErrorSerializer`.
9
+ - For a collection: `serializer: ActiveModel::Serializer::ErrorsSerializer`, `each_serializer: ActiveModel::Serializer::ErrorSerializer`.
10
+
11
+ The resource **MUST** have a non-empty associated `#errors` object.
12
+ The `errors` object must have a `#messages` method that returns a hash of error name to array of
13
+ descriptions.
14
+
15
+ ## Use in controllers
16
+
17
+ ```ruby
18
+ resource = Profile.new(name: 'Name 1',
19
+ description: 'Description 1',
20
+ comments: 'Comments 1')
21
+ resource.errors.add(:name, 'cannot be nil')
22
+ resource.errors.add(:name, 'must be longer')
23
+ resource.errors.add(:id, 'must be a uuid')
24
+
25
+ render json: resource, status: 422, adapter: :json_api, serializer: ActiveModel::Serializer::ErrorSerializer
26
+ # #=>
27
+ # { :errors =>
28
+ # [
29
+ # { :source => { :pointer => '/data/attributes/name' }, :detail => 'cannot be nil' },
30
+ # { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be longer' },
31
+ # { :source => { :pointer => '/data/attributes/id' }, :detail => 'must be a uuid' }
32
+ # ]
33
+ # }.to_json
34
+ ```
35
+
36
+ ## Direct error document generation
37
+
38
+ ```ruby
39
+ options = nil
40
+ resource = ModelWithErrors.new
41
+ resource.errors.add(:name, 'must be awesome')
42
+
43
+ serializable_resource = ActiveModelSerializers::SerializableResource.new(
44
+ resource, {
45
+ serializer: ActiveModel::Serializer::ErrorSerializer,
46
+ adapter: :json_api
47
+ })
48
+ serializable_resource.as_json(options)
49
+ # #=>
50
+ # {
51
+ # :errors =>
52
+ # [
53
+ # { :source => { :pointer => '/data/attributes/name' }, :detail => 'must be awesome' }
54
+ # ]
55
+ # }
56
+ ```