active_model_serializers 0.8.3 → 0.10.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. checksums.yaml +5 -5
  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 +105 -0
  6. data/.simplecov +110 -0
  7. data/.travis.yml +50 -24
  8. data/CHANGELOG.md +650 -6
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/CONTRIBUTING.md +105 -0
  11. data/Gemfile +69 -1
  12. data/{MIT-LICENSE.txt → MIT-LICENSE} +3 -2
  13. data/README.md +195 -545
  14. data/Rakefile +64 -8
  15. data/active_model_serializers.gemspec +62 -23
  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/schema.json +366 -0
  48. data/docs/jsonapi/schema.md +151 -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 +43 -38
  52. data/lib/active_model/serializable_resource.rb +11 -0
  53. data/lib/active_model/serializer/adapter/attributes.rb +15 -0
  54. data/lib/active_model/serializer/adapter/base.rb +18 -0
  55. data/lib/active_model/serializer/adapter/json.rb +15 -0
  56. data/lib/active_model/serializer/adapter/json_api.rb +15 -0
  57. data/lib/active_model/serializer/adapter/null.rb +15 -0
  58. data/lib/active_model/serializer/adapter.rb +24 -0
  59. data/lib/active_model/serializer/array_serializer.rb +12 -0
  60. data/lib/active_model/serializer/association.rb +71 -0
  61. data/lib/active_model/serializer/attribute.rb +25 -0
  62. data/lib/active_model/serializer/belongs_to_reflection.rb +11 -0
  63. data/lib/active_model/serializer/collection_serializer.rb +88 -0
  64. data/lib/active_model/serializer/concerns/caching.rb +300 -0
  65. data/lib/active_model/serializer/error_serializer.rb +14 -0
  66. data/lib/active_model/serializer/errors_serializer.rb +32 -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 +7 -0
  71. data/lib/active_model/serializer/lazy_association.rb +96 -0
  72. data/lib/active_model/serializer/link.rb +21 -0
  73. data/lib/active_model/serializer/lint.rb +150 -0
  74. data/lib/active_model/serializer/null.rb +17 -0
  75. data/lib/active_model/serializer/reflection.rb +210 -0
  76. data/lib/active_model/{serializers → serializer}/version.rb +1 -1
  77. data/lib/active_model/serializer.rb +343 -442
  78. data/lib/active_model_serializers/adapter/attributes.rb +13 -0
  79. data/lib/active_model_serializers/adapter/base.rb +83 -0
  80. data/lib/active_model_serializers/adapter/json.rb +21 -0
  81. data/lib/active_model_serializers/adapter/json_api/deserialization.rb +213 -0
  82. data/lib/active_model_serializers/adapter/json_api/error.rb +96 -0
  83. data/lib/active_model_serializers/adapter/json_api/jsonapi.rb +49 -0
  84. data/lib/active_model_serializers/adapter/json_api/link.rb +83 -0
  85. data/lib/active_model_serializers/adapter/json_api/meta.rb +37 -0
  86. data/lib/active_model_serializers/adapter/json_api/pagination_links.rb +88 -0
  87. data/lib/active_model_serializers/adapter/json_api/relationship.rb +104 -0
  88. data/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +66 -0
  89. data/lib/active_model_serializers/adapter/json_api.rb +533 -0
  90. data/lib/active_model_serializers/adapter/null.rb +9 -0
  91. data/lib/active_model_serializers/adapter.rb +98 -0
  92. data/lib/active_model_serializers/callbacks.rb +55 -0
  93. data/lib/active_model_serializers/deprecate.rb +54 -0
  94. data/lib/active_model_serializers/deserialization.rb +15 -0
  95. data/lib/active_model_serializers/json_pointer.rb +14 -0
  96. data/lib/active_model_serializers/logging.rb +122 -0
  97. data/lib/active_model_serializers/lookup_chain.rb +80 -0
  98. data/lib/active_model_serializers/model.rb +130 -0
  99. data/lib/active_model_serializers/railtie.rb +50 -0
  100. data/lib/active_model_serializers/register_jsonapi_renderer.rb +78 -0
  101. data/lib/active_model_serializers/serializable_resource.rb +82 -0
  102. data/lib/active_model_serializers/serialization_context.rb +39 -0
  103. data/lib/active_model_serializers/test/schema.rb +138 -0
  104. data/lib/active_model_serializers/test/serializer.rb +125 -0
  105. data/lib/active_model_serializers/test.rb +7 -0
  106. data/lib/active_model_serializers.rb +47 -81
  107. data/lib/generators/rails/USAGE +6 -0
  108. data/lib/generators/rails/resource_override.rb +10 -0
  109. data/lib/generators/rails/serializer_generator.rb +36 -0
  110. data/lib/generators/rails/templates/serializer.rb.erb +8 -0
  111. data/lib/grape/active_model_serializers.rb +16 -0
  112. data/lib/grape/formatters/active_model_serializers.rb +32 -0
  113. data/lib/grape/helpers/active_model_serializers.rb +17 -0
  114. data/lib/tasks/rubocop.rake +53 -0
  115. data/test/action_controller/adapter_selector_test.rb +62 -0
  116. data/test/action_controller/explicit_serializer_test.rb +135 -0
  117. data/test/action_controller/json/include_test.rb +246 -0
  118. data/test/action_controller/json_api/deserialization_test.rb +112 -0
  119. data/test/action_controller/json_api/errors_test.rb +40 -0
  120. data/test/action_controller/json_api/fields_test.rb +66 -0
  121. data/test/action_controller/json_api/linked_test.rb +202 -0
  122. data/test/action_controller/json_api/pagination_test.rb +124 -0
  123. data/test/action_controller/json_api/transform_test.rb +189 -0
  124. data/test/action_controller/lookup_proc_test.rb +49 -0
  125. data/test/action_controller/namespace_lookup_test.rb +232 -0
  126. data/test/action_controller/serialization_scope_name_test.rb +235 -0
  127. data/test/action_controller/serialization_test.rb +478 -0
  128. data/test/active_model_serializers/adapter_for_test.rb +208 -0
  129. data/test/active_model_serializers/json_pointer_test.rb +22 -0
  130. data/test/active_model_serializers/logging_test.rb +77 -0
  131. data/test/active_model_serializers/model_test.rb +142 -0
  132. data/test/active_model_serializers/railtie_test_isolated.rb +68 -0
  133. data/test/active_model_serializers/register_jsonapi_renderer_test_isolated.rb +161 -0
  134. data/test/active_model_serializers/serialization_context_test_isolated.rb +71 -0
  135. data/test/active_model_serializers/test/schema_test.rb +131 -0
  136. data/test/active_model_serializers/test/serializer_test.rb +62 -0
  137. data/test/active_record_test.rb +9 -0
  138. data/test/adapter/attributes_test.rb +40 -0
  139. data/test/adapter/deprecation_test.rb +100 -0
  140. data/test/adapter/json/belongs_to_test.rb +45 -0
  141. data/test/adapter/json/collection_test.rb +104 -0
  142. data/test/adapter/json/has_many_test.rb +53 -0
  143. data/test/adapter/json/transform_test.rb +93 -0
  144. data/test/adapter/json_api/belongs_to_test.rb +155 -0
  145. data/test/adapter/json_api/collection_test.rb +96 -0
  146. data/test/adapter/json_api/errors_test.rb +76 -0
  147. data/test/adapter/json_api/fields_test.rb +96 -0
  148. data/test/adapter/json_api/has_many_explicit_serializer_test.rb +96 -0
  149. data/test/adapter/json_api/has_many_test.rb +173 -0
  150. data/test/adapter/json_api/has_one_test.rb +80 -0
  151. data/test/adapter/json_api/include_data_if_sideloaded_test.rb +213 -0
  152. data/test/adapter/json_api/json_api_test.rb +33 -0
  153. data/test/adapter/json_api/linked_test.rb +413 -0
  154. data/test/adapter/json_api/links_test.rb +110 -0
  155. data/test/adapter/json_api/pagination_links_test.rb +206 -0
  156. data/test/adapter/json_api/parse_test.rb +137 -0
  157. data/test/adapter/json_api/relationship_test.rb +397 -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 +512 -0
  161. data/test/adapter/json_api/type_test.rb +193 -0
  162. data/test/adapter/json_test.rb +46 -0
  163. data/test/adapter/null_test.rb +22 -0
  164. data/test/adapter/polymorphic_test.rb +218 -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_active_record.rb +81 -0
  170. data/test/benchmark/bm_adapter.rb +38 -0
  171. data/test/benchmark/bm_caching.rb +119 -0
  172. data/test/benchmark/bm_lookup_chain.rb +83 -0
  173. data/test/benchmark/bm_transform.rb +45 -0
  174. data/test/benchmark/config.ru +3 -0
  175. data/test/benchmark/controllers.rb +83 -0
  176. data/test/benchmark/fixtures.rb +219 -0
  177. data/test/cache_test.rb +651 -0
  178. data/test/collection_serializer_test.rb +127 -0
  179. data/test/fixtures/active_record.rb +113 -0
  180. data/test/fixtures/poro.rb +225 -0
  181. data/test/generators/scaffold_controller_generator_test.rb +24 -0
  182. data/test/generators/serializer_generator_test.rb +75 -0
  183. data/test/grape_test.rb +196 -0
  184. data/test/lint_test.rb +49 -0
  185. data/test/logger_test.rb +20 -0
  186. data/test/poro_test.rb +9 -0
  187. data/test/serializable_resource_test.rb +79 -0
  188. data/test/serializers/association_macros_test.rb +37 -0
  189. data/test/serializers/associations_test.rb +518 -0
  190. data/test/serializers/attribute_test.rb +153 -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 +202 -0
  196. data/test/serializers/options_test.rb +32 -0
  197. data/test/serializers/read_attribute_for_serialization_test.rb +79 -0
  198. data/test/serializers/reflection_test.rb +479 -0
  199. data/test/serializers/root_test.rb +21 -0
  200. data/test/serializers/serialization_test.rb +55 -0
  201. data/test/serializers/serializer_for_test.rb +136 -0
  202. data/test/serializers/serializer_for_with_namespace_test.rb +88 -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 +84 -0
  205. data/test/support/rails5_shims.rb +53 -0
  206. data/test/support/rails_app.rb +38 -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 +79 -0
  214. data/test/test_helper.rb +59 -21
  215. metadata +529 -43
  216. data/DESIGN.textile +0 -586
  217. data/Gemfile.edge +0 -9
  218. data/bench/perf.rb +0 -43
  219. data/cruft.md +0 -19
  220. data/lib/active_model/array_serializer.rb +0 -104
  221. data/lib/active_model/serializer/associations.rb +0 -233
  222. data/lib/active_record/serializer_override.rb +0 -16
  223. data/lib/generators/resource_override.rb +0 -13
  224. data/lib/generators/serializer/USAGE +0 -9
  225. data/lib/generators/serializer/serializer_generator.rb +0 -42
  226. data/lib/generators/serializer/templates/serializer.rb +0 -19
  227. data/test/association_test.rb +0 -592
  228. data/test/caching_test.rb +0 -96
  229. data/test/generators_test.rb +0 -85
  230. data/test/no_serialization_scope_test.rb +0 -34
  231. data/test/serialization_scope_name_test.rb +0 -67
  232. data/test/serialization_test.rb +0 -392
  233. data/test/serializer_support_test.rb +0 -51
  234. data/test/serializer_test.rb +0 -1465
  235. data/test/test_fakes.rb +0 -217
data/README.md CHANGED
@@ -1,655 +1,305 @@
1
- [![Build Status](https://api.travis-ci.org/rails-api/active_model_serializers.png)](https://travis-ci.org/rails-api/active_model_serializers) [![Code Climate](https://codeclimate.com/github/rails-api/active_model_serializers.png)](https://codeclimate.com/github/rails-api/active_model_serializers)
1
+ # ActiveModelSerializers
2
2
 
3
- # Purpose
3
+ <table>
4
+ <tr>
5
+ <td>Build Status</td>
6
+ <td>
7
+ <a href="https://travis-ci.org/rails-api/active_model_serializers"><img src="https://travis-ci.org/rails-api/active_model_serializers.svg?branch=master" alt="Build Status" ></a>
8
+ <a href="https://ci.appveyor.com/project/joaomdmoura/active-model-serializers/branch/master"><img src="https://ci.appveyor.com/api/projects/status/x6xdjydutm54gvyt/branch/master?svg=true" alt="Build status"></a>
9
+ </td>
10
+ </tr>
11
+ <tr>
12
+ <td>Code Quality</td>
13
+ <td>
14
+ <a href="https://codeclimate.com/github/rails-api/active_model_serializers"><img src="https://codeclimate.com/github/rails-api/active_model_serializers/badges/gpa.svg" alt="Code Quality"></a>
15
+ <a href="https://codebeat.co/projects/github-com-rails-api-active_model_serializers"><img src="https://codebeat.co/badges/a9ab35fa-8b5a-4680-9d4e-a81f9a55ebcd" alt="codebeat" ></a>
16
+ <a href="https://codeclimate.com/github/rails-api/active_model_serializers/coverage"><img src="https://codeclimate.com/github/rails-api/active_model_serializers/badges/coverage.svg" alt="Test Coverage"></a>
17
+ </td>
18
+ </tr>
19
+ <tr>
20
+ <td>Issue Stats</td>
21
+ <td>
22
+ <a href="https://github.com/rails-api/active_model_serializers/pulse/monthly">Pulse</a>
23
+ </td>
24
+ </tr>
25
+ </table>
4
26
 
5
- The purpose of `ActiveModel::Serializers` is to provide an object to
6
- encapsulate serialization of `ActiveModel` objects, including `ActiveRecord`
7
- objects.
27
+ ## About
8
28
 
9
- Serializers know about both a model and the `current_user`, so you can
10
- customize serialization based upon whether a user is authorized to see the
11
- content.
29
+ ActiveModelSerializers brings convention over configuration to your JSON generation.
12
30
 
13
- In short, **serializers replace hash-driven development with object-oriented
14
- development.**
31
+ ActiveModelSerializers works through two components: **serializers** and **adapters**.
15
32
 
16
- # Installing Serializers
33
+ Serializers describe _which_ attributes and relationships should be serialized.
17
34
 
18
- The easiest way to install `ActiveModel::Serializers` is to add it to your
19
- `Gemfile`:
35
+ Adapters describe _how_ attributes and relationships should be serialized.
20
36
 
21
- ```ruby
22
- gem "active_model_serializers", "~> 0.8.0"
23
- ```
37
+ SerializableResource co-ordinates the resource, Adapter and Serializer to produce the
38
+ resource serialization. The serialization has the `#as_json`, `#to_json` and `#serializable_hash`
39
+ methods used by the Rails JSON Renderer. (SerializableResource actually delegates
40
+ these methods to the adapter.)
24
41
 
25
- Then, install it on the command line:
42
+ By default ActiveModelSerializers will use the **Attributes Adapter** (no JSON root).
43
+ But we strongly advise you to use **JsonApi Adapter**, which
44
+ follows 1.0 of the format specified in [jsonapi.org/format](http://jsonapi.org/format).
45
+ Check how to change the adapter in the sections below.
26
46
 
27
- ```
28
- $ bundle install
29
- ```
47
+ `0.10.x` is **not** backward compatible with `0.9.x` nor `0.8.x`.
30
48
 
31
- # Creating a Serializer
49
+ `0.10.x` is based on the `0.8.0` code, but with a more flexible
50
+ architecture. We'd love your help. [Learn how you can help here.](CONTRIBUTING.md)
32
51
 
33
- The easiest way to create a new serializer is to generate a new resource, which
34
- will generate a serializer at the same time:
52
+ ## Installation
35
53
 
36
- ```
37
- $ rails g resource post title:string body:string
38
- ```
39
-
40
- This will generate a serializer in `app/serializers/post_serializer.rb` for
41
- your new model. You can also generate a serializer for an existing model with
42
- the serializer generator:
54
+ Add this line to your application's Gemfile:
43
55
 
44
56
  ```
45
- $ rails g serializer post
57
+ gem 'active_model_serializers', '~> 0.10.0'
46
58
  ```
47
59
 
48
- ### Support for PORO's and other ORM's.
49
-
50
- Currently `ActiveModel::Serializers` adds serialization support to all models
51
- that descend from `ActiveRecord` or include `Mongoid::Document`. If you are
52
- using another ORM, or if you are using objects that are `ActiveModel`
53
- compliant but do not descend from `ActiveRecord` or include
54
- `Mongoid::Document`, you must add an include statement for
55
- `ActiveModel::SerializerSupport` to make models serializable. If you
56
- also want to make collections serializable, you should include
57
- `ActiveModel::ArraySerializerSupport` into your ORM's
58
- relation/criteria class.
59
-
60
- # ActiveModel::Serializer
60
+ And then execute:
61
61
 
62
- All new serializers descend from ActiveModel::Serializer
63
-
64
- # render :json
65
-
66
- In your controllers, when you use `render :json`, Rails will now first search
67
- for a serializer for the object and use it if available.
68
-
69
- ```ruby
70
- class PostsController < ApplicationController
71
- def show
72
- @post = Post.find(params[:id])
73
- render :json => @post
74
- end
75
- end
76
62
  ```
77
-
78
- In this case, Rails will look for a serializer named `PostSerializer`, and if
79
- it exists, use it to serialize the `Post`.
80
-
81
- This also works with `respond_with`, which uses `to_json` under the hood. Also
82
- note that any options passed to `render :json` will be passed to your
83
- serializer and available as `@options` inside.
84
-
85
- To specify a custom serializer for an object, there are 2 options:
86
-
87
- #### 1. Specify the serializer in your model:
88
-
89
- ```ruby
90
- class Post < ActiveRecord::Base
91
- def active_model_serializer
92
- FancyPostSerializer
93
- end
94
- end
63
+ $ bundle
95
64
  ```
96
65
 
97
- #### 2. Specify the serializer when you render the object:
66
+ ## Getting Started
98
67
 
99
- ```ruby
100
- render :json => @post, :serializer => FancyPostSerializer
101
- ```
68
+ See [Getting Started](docs/general/getting_started.md) for the nuts and bolts.
102
69
 
103
- ## Arrays
70
+ More information is available in the [Guides](docs) and
71
+ [High-level behavior](README.md#high-level-behavior).
104
72
 
105
- In your controllers, when you use `render :json` for an array of objects, AMS will
106
- use `ActiveModel::ArraySerializer` (included in this project) as the base serializer,
107
- and the individual `Serializer` for the objects contained in that array.
73
+ ## Getting Help
108
74
 
109
- ```ruby
110
- class PostSerializer < ActiveModel::Serializer
111
- attributes :title, :body
112
- end
75
+ If you find a bug, please report an [Issue](https://github.com/rails-api/active_model_serializers/issues/new)
76
+ and see our [contributing guide](CONTRIBUTING.md).
113
77
 
114
- class PostsController < ApplicationController
115
- def index
116
- @posts = Post.all
117
- render :json => @posts
118
- end
119
- end
120
- ```
121
-
122
- Given the example above, the index action will return
78
+ If you have a question, please [post to Stack Overflow](http://stackoverflow.com/questions/tagged/active-model-serializers).
123
79
 
124
- ```json
125
- {
126
- "posts":
127
- [
128
- { "title": "Post 1", "body": "Hello!" },
129
- { "title": "Post 2", "body": "Goodbye!" }
130
- ]
131
- }
132
- ```
80
+ If you'd like to chat, we have a [community slack](http://amserializers.herokuapp.com).
133
81
 
134
- By default, the root element is the name of the controller. For example, `PostsController`
135
- generates a root element "posts". To change it:
82
+ Thanks!
136
83
 
137
- ```ruby
138
- render :json => @posts, :root => "some_posts"
139
- ```
84
+ ## Documentation
140
85
 
141
- You may disable the root element for arrays at the top level, which will result in
142
- more concise json. See the next section for ways on how to do this. Disabling the
143
- root element of the array with any of those methods will produce
86
+ If you're reading this at https://github.com/rails-api/active_model_serializers you are
87
+ reading documentation for our `master`, which may include features that have not
88
+ been released yet. Please see below for the documentation relevant to you.
144
89
 
145
- ```json
146
- [
147
- { "title": "Post 1", "body": "Hello!" },
148
- { "title": "Post 2", "body": "Goodbye!" }
149
- ]
150
- ```
90
+ - [0.10 (master) Documentation](https://github.com/rails-api/active_model_serializers/tree/master)
91
+ - [0.10.6 (latest release) Documentation](https://github.com/rails-api/active_model_serializers/tree/v0.10.6)
92
+ - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/gems/active_model_serializers/0.10.6)
93
+ - [Guides](docs)
94
+ - [0.9 (0-9-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-9-stable)
95
+ - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-9-stable)
96
+ - [0.8 (0-8-stable) Documentation](https://github.com/rails-api/active_model_serializers/tree/0-8-stable)
97
+ - [![API Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://www.rubydoc.info/github/rails-api/active_model_serializers/0-8-stable)
151
98
 
152
- To specify a custom serializer for the items within an array:
153
99
 
154
- ```ruby
155
- render :json => @posts, :each_serializer => FancyPostSerializer
156
- ```
100
+ ## High-level behavior
157
101
 
158
- ## Disabling the root element
102
+ Choose an adapter from [adapters](lib/active_model_serializers/adapter):
159
103
 
160
- You have 4 options to disable the root element, each with a slightly different scope:
161
-
162
- #### 1. Disable root globally for all, or per class
163
-
164
- In an initializer:
165
-
166
- ```ruby
167
- ActiveSupport.on_load(:active_model_serializers) do
168
- # Disable for all serializers (except ArraySerializer)
169
- ActiveModel::Serializer.root = false
170
-
171
- # Disable for ArraySerializer
172
- ActiveModel::ArraySerializer.root = false
173
- end
174
- ```
175
-
176
- #### 2. Disable root per render call in your controller
177
-
178
- ```ruby
179
- render :json => @posts, :root => false
180
- ```
181
-
182
- #### 3. Subclass the serializer, and specify using it
183
-
184
- ```ruby
185
- class CustomArraySerializer < ActiveModel::ArraySerializer
186
- self.root = false
187
- end
188
-
189
- # controller:
190
- render :json => @posts, :serializer => CustomArraySerializer
191
- ```
192
-
193
- #### 4. Define default_serializer_options in your controller
194
-
195
- If you define `default_serializer_options` method in your controller,
196
- all serializers in actions of this controller and it's children will use them.
197
- One of the options may be `root: false`
198
-
199
- ```ruby
200
- def default_serializer_options
201
- {
202
- root: false
203
- }
204
- end
104
+ ``` ruby
105
+ ActiveModelSerializers.config.adapter = :json_api # Default: `:attributes`
205
106
  ```
206
107
 
207
- ## Getting the old version
208
-
209
- If you find that your project is already relying on the old rails to_json
210
- change `render :json` to `render :json => @your_object.to_json`.
211
-
212
- # Attributes and Associations
213
-
214
- Once you have a serializer, you can specify which attributes and associations
215
- you would like to include in the serialized form.
108
+ Given a [serializable model](lib/active_model/serializer/lint.rb):
216
109
 
217
110
  ```ruby
218
- class PostSerializer < ActiveModel::Serializer
219
- attributes :id, :title, :body
220
- has_many :comments
111
+ # either
112
+ class SomeResource < ActiveRecord::Base
113
+ # columns: title, body
221
114
  end
222
- ```
223
-
224
- ## Attributes
225
-
226
- For specified attributes, a serializer will look up the attribute on the
227
- object you passed to `render :json`. It uses
228
- `read_attribute_for_serialization`, which `ActiveRecord` objects implement as a
229
- regular attribute lookup.
230
-
231
- Before looking up the attribute on the object, a serializer will check for the
232
- presence of a method with the name of the attribute. This allows serializers to
233
- include properties beyond the simple attributes of the model. For example:
234
-
235
- ```ruby
236
- class PersonSerializer < ActiveModel::Serializer
237
- attributes :first_name, :last_name, :full_name
238
-
239
- def full_name
240
- "#{object.first_name} #{object.last_name}"
241
- end
242
- end
243
- ```
244
-
245
- Within a serializer's methods, you can access the object being
246
- serialized as `object`.
247
-
248
- You can also access the `current_user` method, which provides an
249
- authorization context to your serializer. By default, the context
250
- is the current user of your application, but this
251
- [can be customized](#customizing-scope).
252
-
253
- Serializers will check for the presence of a method named
254
- `include_[ATTRIBUTE]?` to determine whether a particular attribute should be
255
- included in the output. This is typically used to customize output
256
- based on `current_user`. For example:
257
-
258
- ```ruby
259
- class PostSerializer < ActiveModel::Serializer
260
- attributes :id, :title, :body, :author
261
-
262
- def include_author?
263
- current_user.admin?
264
- end
115
+ # or
116
+ class SomeResource < ActiveModelSerializers::Model
117
+ attributes :title, :body
265
118
  end
266
119
  ```
267
120
 
268
- The type of a computed attribute (like :full_name above) is not easily
269
- calculated without some sophisticated static code analysis. To specify the
270
- type of a computed attribute:
121
+ And initialized as:
271
122
 
272
123
  ```ruby
273
- class PersonSerializer < ActiveModel::Serializer
274
- attributes :first_name, :last_name, {:full_name => :string}
275
-
276
- def full_name
277
- "#{object.first_name} #{object.last_name}"
278
- end
279
- end
124
+ resource = SomeResource.new(title: 'ActiveModelSerializers', body: 'Convention over configuration')
280
125
  ```
281
126
 
282
- If you would like the key in the outputted JSON to be different from its name
283
- in ActiveRecord, you can use the `:key` option to customize it:
127
+ Given a serializer for the serializable model:
284
128
 
285
129
  ```ruby
286
- class PostSerializer < ActiveModel::Serializer
287
- attributes :id, :body
288
-
289
- # look up :subject on the model, but use +title+ in the JSON
290
- attribute :subject, :key => :title
291
- has_many :comments
130
+ class SomeSerializer < ActiveModel::Serializer
131
+ attribute :title, key: :name
132
+ attributes :body
292
133
  end
293
134
  ```
294
135
 
295
- If you would like to add meta information to the outputted JSON, use the `:meta`
296
- option:
297
-
298
- ```ruby
299
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}
300
- ```
301
-
302
- The above usage of `:meta` will produce the following:
303
-
304
- ```json
305
- {
306
- "meta": { "total": 10 },
307
- "posts": [
308
- { "title": "Post 1", "body": "Hello!" },
309
- { "title": "Post 2", "body": "Goodbye!" }
310
- ]
311
- }
312
- ```
313
-
314
- If you would like to change the meta key name you can use the `:meta_key` option:
315
-
316
- ```ruby
317
- render :json => @posts, :serializer => CustomArraySerializer, :meta => {:total => 10}, :meta_key => 'meta_object'
318
- ```
319
-
320
- The above usage of `:meta_key` will produce the following:
321
-
322
- ```json
323
- {
324
- "meta_object": { "total": 10 },
325
- "posts": [
326
- { "title": "Post 1", "body": "Hello!" },
327
- { "title": "Post 2", "body": "Goodbye!" }
328
- ]
329
- }
330
- ```
331
-
332
- If you would like direct, low-level control of attribute serialization, you can
333
- completely override the `attributes` method to return the hash you need:
136
+ The model can be serialized as:
334
137
 
335
138
  ```ruby
336
- class PersonSerializer < ActiveModel::Serializer
337
- attributes :first_name, :last_name
338
-
339
- def attributes
340
- hash = super
341
- if current_user.admin?
342
- hash["ssn"] = object.ssn
343
- hash["secret"] = object.mothers_maiden_name
344
- end
345
- hash
346
- end
347
- end
139
+ options = {}
140
+ serialization = ActiveModelSerializers::SerializableResource.new(resource, options)
141
+ serialization.to_json
142
+ serialization.as_json
348
143
  ```
349
144
 
350
- ## Associations
351
-
352
- For specified associations, the serializer will look up the association and
353
- then serialize each element of the association. For instance, a `has_many
354
- :comments` association will create a new `CommentSerializer` for each comment
355
- and use it to serialize the comment.
356
-
357
- By default, serializers simply look up the association on the original object.
358
- You can customize this behavior by implementing a method with the name of the
359
- association and returning a different Array. Often, you will do this to
360
- customize the objects returned based on the current user.
145
+ SerializableResource delegates to the adapter, which it builds as:
361
146
 
362
147
  ```ruby
363
- class PostSerializer < ActiveModel::Serializer
364
- attributes :id, :title, :body
365
- has_many :comments
366
-
367
- # only let the user see comments he created.
368
- def comments
369
- object.comments.where(:created_by => current_user)
370
- end
371
- end
148
+ adapter_options = {}
149
+ adapter = ActiveModelSerializers::Adapter.create(serializer, adapter_options)
150
+ adapter.to_json
151
+ adapter.as_json
152
+ adapter.serializable_hash
372
153
  ```
373
154
 
374
- As with attributes, you can change the JSON key that the serializer should
375
- use for a particular association.
155
+ The adapter formats the serializer's attributes and associations (a.k.a. includes):
376
156
 
377
157
  ```ruby
378
- class PostSerializer < ActiveModel::Serializer
379
- attributes :id, :title, :body
380
-
381
- # look up comments, but use +my_comments+ as the key in JSON
382
- has_many :comments, :key => :my_comments
383
- end
158
+ serializer_options = {}
159
+ serializer = SomeSerializer.new(resource, serializer_options)
160
+ serializer.attributes
161
+ serializer.associations
384
162
  ```
385
163
 
386
- Also, as with attributes, serializers will check for the presence
387
- of a method named `include_[ASSOCIATION]?` to determine whether a particular association
388
- should be included in the output. For example:
164
+ ## Architecture
389
165
 
390
- ```ruby
391
- class PostSerializer < ActiveModel::Serializer
392
- attributes :id, :title, :body
393
- has_many :comments
166
+ This section focuses on architecture the 0.10.x version of ActiveModelSerializers. If you are interested in the architecture of the 0.8 or 0.9 versions,
167
+ please refer to the [0.8 README](https://github.com/rails-api/active_model_serializers/blob/0-8-stable/README.md) or
168
+ [0.9 README](https://github.com/rails-api/active_model_serializers/blob/0-9-stable/README.md).
394
169
 
395
- def include_comments?
396
- !object.comments_disabled?
397
- end
398
- end
399
- ```
170
+ The original design is also available [here](https://github.com/rails-api/active_model_serializers/blob/d72b66d4c5355b0ff0a75a04895fcc4ea5b0c65e/README.textile).
400
171
 
401
- If you would like lower-level control of association serialization, you can
402
- override `include_associations!` to specify which associations should be included:
172
+ ### ActiveModel::Serializer
403
173
 
404
- ```ruby
405
- class PostSerializer < ActiveModel::Serializer
406
- attributes :id, :title, :body
407
- has_one :author
408
- has_many :comments
409
-
410
- def include_associations!
411
- include! :author if current_user.admin?
412
- include! :comments unless object.comments_disabled?
413
- end
414
- end
415
- ```
174
+ An **`ActiveModel::Serializer`** wraps a [serializable resource](https://github.com/rails/rails/blob/4-2-stable/activemodel/lib/active_model/serialization.rb)
175
+ and exposes an `attributes` method, among a few others.
176
+ It allows you to specify which attributes and associations should be represented in the serializatation of the resource.
177
+ It requires an adapter to transform its attributes into a JSON document; it cannot be serialized itself.
178
+ It may be useful to think of it as a
179
+ [presenter](http://blog.steveklabnik.com/posts/2011-09-09-better-ruby-presenters).
416
180
 
417
- You may also use the `:serializer` option to specify a custom serializer class and the `:polymorphic` option to specify an association that is polymorphic (STI), e.g.:
181
+ #### ActiveModel::CollectionSerializer
418
182
 
419
- ```ruby
420
- has_many :comments, :serializer => CommentShortSerializer
421
- has_one :reviewer, :polymorphic => true
422
- ```
183
+ The **`ActiveModel::CollectionSerializer`** represents a collection of resources as serializers
184
+ and, if there is no serializer, primitives.
423
185
 
424
- Serializers are only concerned with multiplicity, and not ownership. `belongs_to` ActiveRecord associations can be included using `has_one` in your serializer.
186
+ ### ActiveModelSerializers::Adapter::Base
425
187
 
426
- ## Embedding Associations
188
+ The **`ActiveModelSerializers::Adapter::Base`** describes the structure of the JSON document generated from a
189
+ serializer. For example, the `Attributes` example represents each serializer as its
190
+ unmodified attributes. The `JsonApi` adapter represents the serializer as a [JSON
191
+ API](http://jsonapi.org/) document.
427
192
 
428
- By default, associations will be embedded inside the serialized object. So if
429
- you have a post, the outputted JSON will look like:
193
+ ### ActiveModelSerializers::SerializableResource
430
194
 
431
- ```json
432
- {
433
- "post": {
434
- "id": 1,
435
- "title": "New post",
436
- "body": "A body!",
437
- "comments": [
438
- { "id": 1, "body": "what a dumb post" }
439
- ]
440
- }
441
- }
442
- ```
195
+ The **`ActiveModelSerializers::SerializableResource`** acts to coordinate the serializer(s) and adapter
196
+ to an object that responds to `to_json`, and `as_json`. It is used in the controller to
197
+ encapsulate the serialization resource when rendered. However, it can also be used on its own
198
+ to serialize a resource outside of a controller, as well.
443
199
 
444
- This is convenient for simple use-cases, but for more complex clients, it is
445
- better to supply an Array of IDs for the association. This makes your API more
446
- flexible from a performance standpoint and avoids wasteful duplication.
200
+ ### Primitive handling
447
201
 
448
- To embed IDs instead of associations, simply use the `embed` class method:
202
+ Definitions: A primitive is usually a String or Array. There is no serializer
203
+ defined for them; they will be serialized when the resource is converted to JSON (`as_json` or
204
+ `to_json`). (The below also applies for any object with no serializer.)
449
205
 
450
- ```ruby
451
- class PostSerializer < ActiveModel::Serializer
452
- embed :ids
206
+ - ActiveModelSerializers doesn't handle primitives passed to `render json:` at all.
453
207
 
454
- attributes :id, :title, :body
455
- has_many :comments
456
- end
457
- ```
208
+ Internally, if no serializer can be found in the controller, the resource is not decorated by
209
+ ActiveModelSerializers.
458
210
 
459
- Now, any associations will be supplied as an Array of IDs:
460
-
461
- ```json
462
- {
463
- "post": {
464
- "id": 1,
465
- "title": "New post",
466
- "body": "A body!",
467
- "comment_ids": [ 1, 2, 3 ]
468
- }
469
- }
470
- ```
211
+ - However, when a primitive value is an attribute or in a collection, it is not modified.
471
212
 
472
- Alternatively, you can choose to embed only the ids or the associated objects per association:
213
+ When serializing a collection and the collection serializer (CollectionSerializer) cannot
214
+ identify a serializer for a resource in its collection, it throws [`:no_serializer`](https://github.com/rails-api/active_model_serializers/issues/1191#issuecomment-142327128).
215
+ For example, when caught by `Reflection#build_association`, and the association value is set directly:
473
216
 
474
217
  ```ruby
475
- class PostSerializer < ActiveModel::Serializer
476
- attributes :id, :title, :body
477
-
478
- has_many :comments, embed: :objects
479
- has_many :tags, embed: :ids
480
- end
218
+ reflection_options[:virtual_value] = association_value.try(:as_json) || association_value
481
219
  ```
482
220
 
483
- The JSON will look like this:
484
-
485
- ```json
486
- {
487
- "post": {
488
- "id": 1,
489
- "title": "New post",
490
- "body": "A body!",
491
- "comments": [
492
- { "id": 1, "body": "what a dumb post" }
493
- ],
494
- "tag_ids": [ 1, 2, 3 ]
495
- }
496
- }
497
- ```
221
+ (which is called by the adapter as `serializer.associations(*)`.)
498
222
 
499
- In addition to supplying an Array of IDs, you may want to side-load the data
500
- alongside the main object. This makes it easier to process the entire package
501
- of data without having to recursively scan the tree looking for embedded
502
- information. It also ensures that associations that are shared between several
503
- objects (like tags), are only delivered once for the entire payload.
223
+ ### How options are parsed
504
224
 
505
- You can specify that the data be included like this:
225
+ High-level overview:
506
226
 
507
- ```ruby
508
- class PostSerializer < ActiveModel::Serializer
509
- embed :ids, :include => true
227
+ - For a **collection**
228
+ - `:serializer` specifies the collection serializer and
229
+ - `:each_serializer` specifies the serializer for each resource in the collection.
230
+ - For a **single resource**, the `:serializer` option is the resource serializer.
231
+ - Options are partitioned in serializer options and adapter options. Keys for adapter options are specified by
232
+ [`ADAPTER_OPTION_KEYS`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/serializable_resource.rb#L5).
233
+ The remaining options are serializer options.
510
234
 
511
- attributes :id, :title, :body
512
- has_many :comments
513
- end
514
- ```
235
+ Details:
515
236
 
516
- Assuming that the comments also `has_many :tags`, you will get a JSON like
517
- this:
518
-
519
- ```json
520
- {
521
- "post": {
522
- "id": 1,
523
- "title": "New post",
524
- "body": "A body!",
525
- "comment_ids": [ 1, 2 ]
526
- },
527
- "comments": [
528
- { "id": 1, "body": "what a dumb post", "tag_ids": [ 1, 2 ] },
529
- { "id": 2, "body": "i liked it", "tag_ids": [ 1, 3 ] },
530
- ],
531
- "tags": [
532
- { "id": 1, "name": "short" },
533
- { "id": 2, "name": "whiny" },
534
- { "id": 3, "name": "happy" }
535
- ]
536
- }
537
- ```
237
+ 1. **ActionController::Serialization**
238
+ 1. `serializable_resource = ActiveModelSerializers::SerializableResource.new(resource, options)`
239
+ 1. `options` are partitioned into `adapter_opts` and everything else (`serializer_opts`).
240
+ The `adapter_opts` keys are defined in [`ActiveModelSerializers::SerializableResource::ADAPTER_OPTION_KEYS`](lib/active_model_serializers/serializable_resource.rb#L5).
241
+ 1. **ActiveModelSerializers::SerializableResource**
242
+ 1. `if serializable_resource.serializer?` (there is a serializer for the resource, and an adapter is used.)
243
+ - Where `serializer?` is `use_adapter? && !!(serializer)`
244
+ - Where `use_adapter?`: 'True when no explicit adapter given, or explicit value is truthy (non-nil);
245
+ False when explicit adapter is falsy (nil or false)'
246
+ - Where `serializer`:
247
+ 1. from explicit `:serializer` option, else
248
+ 2. implicitly from resource `ActiveModel::Serializer.serializer_for(resource)`
249
+ 1. A side-effect of checking `serializer` is:
250
+ - The `:serializer` option is removed from the serializer_opts hash
251
+ - If the `:each_serializer` option is present, it is removed from the serializer_opts hash and set as the `:serializer` option
252
+ 1. The serializer and adapter are created as
253
+ 1. `serializer_instance = serializer.new(resource, serializer_opts)`
254
+ 2. `adapter_instance = ActiveModel::Serializer::Adapter.create(serializer_instance, adapter_opts)`
255
+ 1. **ActiveModel::Serializer::CollectionSerializer#new**
256
+ 1. If the `serializer_instance` was a `CollectionSerializer` and the `:serializer` serializer_opts
257
+ is present, then [that serializer is passed into each resource](https://github.com/rails-api/active_model_serializers/blob/a54d237e2828fe6bab1ea5dfe6360d4ecc8214cd/lib/active_model/serializer/array_serializer.rb#L14-L16).
258
+ 1. **ActiveModel::Serializer#attributes** is used by the adapter to get the attributes for
259
+ resource as defined by the serializer.
538
260
 
539
- You can also specify a different root for the embedded objects than the key
540
- used to reference them:
261
+ (In Rails, the `options` are also passed to the `as_json(options)` or `to_json(options)`
262
+ methods on the resource serialization by the Rails JSON renderer. They are, therefore, important
263
+ to know about, but not part of ActiveModelSerializers.)
541
264
 
542
- ```ruby
543
- class PostSerializer < ActiveModel::Serializer
544
- embed :ids, :include => true
265
+ ### What does a 'serializable resource' look like?
545
266
 
546
- attributes :id, :title, :body
547
- has_many :comments, :key => :comment_ids, :root => :comment_objects
548
- end
549
- ```
267
+ - An `ActiveRecord::Base` object.
268
+ - Any Ruby object that passes the
269
+ [Lint](https://www.rubydoc.info/gems/active_model_serializers/ActiveModel/Serializer/Lint/Tests)
270
+ [(code)](lib/active_model/serializer/lint.rb).
550
271
 
551
- This would generate JSON that would look like this:
552
-
553
- ```json
554
- {
555
- "post": {
556
- "id": 1,
557
- "title": "New post",
558
- "body": "A body!",
559
- "comment_ids": [ 1 ]
560
- },
561
- "comment_objects": [
562
- { "id": 1, "body": "what a dumb post" }
563
- ]
564
- }
565
- ```
272
+ ActiveModelSerializers provides a
273
+ [`ActiveModelSerializers::Model`](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model_serializers/model.rb),
274
+ which is a simple serializable PORO (Plain-Old Ruby Object).
566
275
 
567
- You can also specify a different attribute to use rather than the ID of the
568
- objects:
276
+ `ActiveModelSerializers::Model` may be used either as a reference implementation, or in production code.
569
277
 
570
278
  ```ruby
571
- class PostSerializer < ActiveModel::Serializer
572
- embed :ids, :include => true
573
-
574
- attributes :id, :title, :body
575
- has_many :comments, :embed_key => :external_id
279
+ class MyModel < ActiveModelSerializers::Model
280
+ attributes :id, :name, :level
576
281
  end
577
282
  ```
578
283
 
579
- This would generate JSON that would look like this:
580
-
581
- ```json
582
- {
583
- "post": {
584
- "id": 1,
585
- "title": "New post",
586
- "body": "A body!",
587
- "comment_ids": [ "COMM001" ]
588
- },
589
- "comments": [
590
- { "id": 1, "external_id": "COMM001", "body": "what a dumb post" }
591
- ]
592
- }
593
- ```
594
-
595
- **NOTE**: The `embed :ids` mechanism is primary useful for clients that process
596
- data in bulk and load it into a local store. For these clients, the ability to
597
- easily see all of the data per type, rather than having to recursively scan the
598
- data looking for information, is extremely useful.
284
+ The default serializer for `MyModel` would be `MyModelSerializer` whether MyModel is an
285
+ ActiveRecord::Base object or not.
599
286
 
600
- If you are mostly working with the data in simple scenarios and manually making
601
- Ajax requests, you probably just want to use the default embedded behavior.
602
-
603
- ## Customizing Scope
604
-
605
- In a serializer, `current_user` is the current authorization scope which the controller
606
- provides to the serializer when you call `render :json`. By default, this is
607
- `current_user`, but can be customized in your controller by calling
608
- `serialization_scope`:
287
+ Outside of the controller the rules are **exactly** the same as for records. For example:
609
288
 
610
289
  ```ruby
611
- class ApplicationController < ActionController::Base
612
- serialization_scope :current_admin
613
- end
290
+ render json: MyModel.new(level: 'awesome'), adapter: :json
614
291
  ```
615
292
 
616
- The above example will also change the scope name from `current_user` to
617
- `current_admin`.
618
-
619
- Please note that, until now, `serialization_scope` doesn't accept a second
620
- object with options for specifying which actions should or should not take a
621
- given scope in consideration.
622
-
623
- To be clear, it's not possible, yet, to do something like this:
293
+ would be serialized the same as
624
294
 
625
295
  ```ruby
626
- class SomeController < ApplicationController
627
- serialization_scope :current_admin, :except => [:index, :show]
628
- end
296
+ ActiveModelSerializers::SerializableResource.new(MyModel.new(level: 'awesome'), adapter: :json).as_json
629
297
  ```
630
298
 
631
- So, in order to have a fine grained control of what each action should take in
632
- consideration for its scope, you may use something like this:
633
-
634
- ```ruby
635
- class CitiesController < ApplicationController
636
- serialization_scope nil
637
-
638
- def index
639
- @cities = City.all
640
-
641
- render :json => @cities, :each_serializer => CitySerializer
642
- end
299
+ ## Semantic Versioning
643
300
 
644
- def show
645
- @city = City.find(params[:id])
301
+ This project adheres to [semver](http://semver.org/)
646
302
 
647
- render :json => @city, :scope => current_admin, :scope_name => :current_admin
648
- end
649
- end
650
- ```
303
+ ## Contributing
651
304
 
652
- Assuming that the `current_admin` method needs to make a query in the database
653
- for the current user, the advantage of this approach is that, by setting
654
- `serialization_scope` to `nil`, the `index` action no longer will need to make
655
- that query, only the `show` action will.
305
+ See [CONTRIBUTING.md](CONTRIBUTING.md)