media_types-serialization 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d52037ca66b6864b8f20026b58fef6053f12ad870d9e6c2557395244b820d2a
4
- data.tar.gz: 0c01cedd3e14c127a791aeedd78e8674a9fefabd1e3d7768921bbb731ce93c85
3
+ metadata.gz: a5256da16f2721f390a507bea65a937dca96ac7b29760a71c67beb6a805d0b78
4
+ data.tar.gz: 6cb02dd865e5ba545b41b0900f7ec60fdc2f51523795fa9de2c1837188a1d592
5
5
  SHA512:
6
- metadata.gz: 21aecc3516b00a3dcd9c02ac1389211858dedbb1e38876f0416c0fe03e69f76e213e20c282b564b52053fb6f81d71ee3af8091bdf0e371fd65efc01f958be762
7
- data.tar.gz: 772be730a9c94e6ab4201f7ddc11daebfbae8a9368185d2ce5d5a0e6c2e43d13f368bcf2071c4b62ead4b714f4559986066455a7cab3b75eab2f4bbe7bdcaf0e
6
+ metadata.gz: 168f94f9374f1f1c42bfa97dd897108f3c46a20c5a864120c7a4b0d8487563cca4e5bc3203ab6e51e958d8d85a6e2287dc7254b6f6f5c9f20b414ac03e32f5c0
7
+ data.tar.gz: 42f202188e3282b03e130d088b185ea9c60294ee95a9a2c9c21bd9081cee17c90489abf9833f9a08b3c2e8e48f021b850b7562e6ae72eaa63d167ab1c6de1f40
data/.idea/.rakeTasks CHANGED
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <Settings><!--This file was automatically generated by Ruby plugin.
3
- You are allowed to:
4
- 1. Remove rake task
5
- 2. Add existing rake tasks
6
- To add existing rake tasks automatically delete this file and reload the project.
7
- --><RakeGroup description="" fullCmd="" taksId="rake"><RakeTask description="Build media_types-serialization-0.3.1.gem into the pkg directory" fullCmd="build" taksId="build" /><RakeTask description="Remove any temporary products" fullCmd="clean" taksId="clean" /><RakeTask description="Remove any generated files" fullCmd="clobber" taksId="clobber" /><RakeTask description="Build and install media_types-serialization-0.3.1.gem into system gems" fullCmd="install" taksId="install" /><RakeGroup description="" fullCmd="" taksId="install"><RakeTask description="Build and install media_types-serialization-0.3.1.gem into system gems without network access" fullCmd="install:local" taksId="local" /></RakeGroup><RakeTask description="Create tag v0.3.1 and build and push media_types-serialization-0.3.1.gem to rubygems.org" fullCmd="release[remote]" taksId="release[remote]" /><RakeTask description="Run tests" fullCmd="test" taksId="test" /><RakeTask description="" fullCmd="default" taksId="default" /><RakeTask description="" fullCmd="release" taksId="release" /><RakeGroup description="" fullCmd="" taksId="release"><RakeTask description="" fullCmd="release:guard_clean" taksId="guard_clean" /><RakeTask description="" fullCmd="release:rubygem_push" taksId="rubygem_push" /><RakeTask description="" fullCmd="release:source_control_push" taksId="source_control_push" /></RakeGroup></RakeGroup></Settings>
2
+ <Settings><!--This file was automatically generated by Ruby plugin.
3
+ You are allowed to:
4
+ 1. Remove rake task
5
+ 2. Add existing rake tasks
6
+ To add existing rake tasks automatically delete this file and reload the project.
7
+ --><RakeGroup description="" fullCmd="" taksId="rake"><RakeTask description="Build media_types-serialization-0.5.1.gem into the pkg directory" fullCmd="build" taksId="build" /><RakeTask description="Remove any temporary products" fullCmd="clean" taksId="clean" /><RakeTask description="Remove any generated files" fullCmd="clobber" taksId="clobber" /><RakeTask description="Build and install media_types-serialization-0.5.1.gem into system gems" fullCmd="install" taksId="install" /><RakeGroup description="" fullCmd="" taksId="install"><RakeTask description="Build and install media_types-serialization-0.5.1.gem into system gems without network access" fullCmd="install:local" taksId="local" /></RakeGroup><RakeTask description="Create tag v0.5.1 and build and push media_types-serialization-0.5.1.gem to rubygems.org" fullCmd="release[remote]" taksId="release[remote]" /><RakeTask description="Run tests" fullCmd="test" taksId="test" /><RakeTask description="" fullCmd="default" taksId="default" /><RakeTask description="" fullCmd="release" taksId="release" /><RakeGroup description="" fullCmd="" taksId="release"><RakeTask description="" fullCmd="release:guard_clean" taksId="guard_clean" /><RakeTask description="" fullCmd="release:rubygem_push" taksId="rubygem_push" /><RakeTask description="" fullCmd="release:source_control_push" taksId="source_control_push" /></RakeGroup></RakeGroup></Settings>
@@ -26,30 +26,52 @@
26
26
  <content url="file://$MODULE_DIR$" />
27
27
  <orderEntry type="jdk" jdkName="ruby-2.5.3-p105" jdkType="RUBY_SDK" />
28
28
  <orderEntry type="sourceFolder" forTests="false" />
29
- <orderEntry type="library" scope="PROVIDED" name="actionpack (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
29
+ <orderEntry type="library" scope="PROVIDED" name="actioncable (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
30
+ <orderEntry type="library" scope="PROVIDED" name="actionmailer (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
31
+ <orderEntry type="library" scope="PROVIDED" name="actionpack (v5.2.2.1, ruby-2.5.3-p105) [gem]" level="application" />
30
32
  <orderEntry type="library" scope="PROVIDED" name="actionview (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
31
- <orderEntry type="library" scope="PROVIDED" name="activesupport (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
33
+ <orderEntry type="library" scope="PROVIDED" name="activejob (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
34
+ <orderEntry type="library" scope="PROVIDED" name="activemodel (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
35
+ <orderEntry type="library" scope="PROVIDED" name="activerecord (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
36
+ <orderEntry type="library" scope="PROVIDED" name="activestorage (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
37
+ <orderEntry type="library" scope="PROVIDED" name="activesupport (v5.2.2.1, ruby-2.5.3-p105) [gem]" level="application" />
38
+ <orderEntry type="library" scope="PROVIDED" name="arel (v9.0.0, ruby-2.5.3-p105) [gem]" level="application" />
39
+ <orderEntry type="library" scope="PROVIDED" name="awesome_print (v1.8.0, ruby-2.5.3-p105) [gem]" level="application" />
32
40
  <orderEntry type="library" scope="PROVIDED" name="builder (v3.2.3, ruby-2.5.3-p105) [gem]" level="application" />
33
41
  <orderEntry type="library" scope="PROVIDED" name="bundler (v2.0.1, ruby-2.5.3-p105) [gem]" level="application" />
34
42
  <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.4, ruby-2.5.3-p105) [gem]" level="application" />
35
43
  <orderEntry type="library" scope="PROVIDED" name="crass (v1.0.4, ruby-2.5.3-p105) [gem]" level="application" />
36
44
  <orderEntry type="library" scope="PROVIDED" name="erubi (v1.8.0, ruby-2.5.3-p105) [gem]" level="application" />
45
+ <orderEntry type="library" scope="PROVIDED" name="globalid (v0.4.2, ruby-2.5.3-p105) [gem]" level="application" />
37
46
  <orderEntry type="library" scope="PROVIDED" name="http_headers-accept (v0.2.1, ruby-2.5.3-p105) [gem]" level="application" />
38
47
  <orderEntry type="library" scope="PROVIDED" name="http_headers-link (v0.2.1, ruby-2.5.3-p105) [gem]" level="application" />
39
48
  <orderEntry type="library" scope="PROVIDED" name="http_headers-utils (v0.2.0, ruby-2.5.3-p105) [gem]" level="application" />
40
49
  <orderEntry type="library" scope="PROVIDED" name="i18n (v1.6.0, ruby-2.5.3-p105) [gem]" level="application" />
41
50
  <orderEntry type="library" scope="PROVIDED" name="loofah (v2.2.3, ruby-2.5.3-p105) [gem]" level="application" />
42
- <orderEntry type="library" scope="PROVIDED" name="media_types (v0.6.0, ruby-2.5.3-p105) [gem]" level="application" />
51
+ <orderEntry type="library" scope="PROVIDED" name="mail (v2.7.1, ruby-2.5.3-p105) [gem]" level="application" />
52
+ <orderEntry type="library" scope="PROVIDED" name="marcel (v0.3.3, ruby-2.5.3-p105) [gem]" level="application" />
53
+ <orderEntry type="library" scope="PROVIDED" name="media_types (v0.6.1, ruby-2.5.3-p105) [gem]" level="application" />
54
+ <orderEntry type="library" scope="PROVIDED" name="method_source (v0.9.2, ruby-2.5.3-p105) [gem]" level="application" />
55
+ <orderEntry type="library" scope="PROVIDED" name="mimemagic (v0.3.3, ruby-2.5.3-p105) [gem]" level="application" />
56
+ <orderEntry type="library" scope="PROVIDED" name="mini_mime (v1.0.1, ruby-2.5.3-p105) [gem]" level="application" />
43
57
  <orderEntry type="library" scope="PROVIDED" name="mini_portile2 (v2.4.0, ruby-2.5.3-p105) [gem]" level="application" />
44
58
  <orderEntry type="library" scope="PROVIDED" name="minitest (v5.11.3, ruby-2.5.3-p105) [gem]" level="application" />
59
+ <orderEntry type="library" scope="PROVIDED" name="nio4r (v2.3.1, ruby-2.5.3-p105) [gem]" level="application" />
45
60
  <orderEntry type="library" scope="PROVIDED" name="nokogiri (v1.10.1, ruby-2.5.3-p105) [gem]" level="application" />
46
- <orderEntry type="library" scope="PROVIDED" name="oj (v3.7.9, ruby-2.5.3-p105) [gem]" level="application" />
61
+ <orderEntry type="library" scope="PROVIDED" name="oj (v3.7.10, ruby-2.5.3-p105) [gem]" level="application" />
47
62
  <orderEntry type="library" scope="PROVIDED" name="rack (v2.0.6, ruby-2.5.3-p105) [gem]" level="application" />
48
63
  <orderEntry type="library" scope="PROVIDED" name="rack-test (v1.1.0, ruby-2.5.3-p105) [gem]" level="application" />
64
+ <orderEntry type="library" scope="PROVIDED" name="rails (v5.2.2.1, ruby-2.5.3-p105) [gem]" level="application" />
49
65
  <orderEntry type="library" scope="PROVIDED" name="rails-dom-testing (v2.0.3, ruby-2.5.3-p105) [gem]" level="application" />
50
66
  <orderEntry type="library" scope="PROVIDED" name="rails-html-sanitizer (v1.0.4, ruby-2.5.3-p105) [gem]" level="application" />
67
+ <orderEntry type="library" scope="PROVIDED" name="railties (v5.2.2, ruby-2.5.3-p105) [gem]" level="application" />
51
68
  <orderEntry type="library" scope="PROVIDED" name="rake (v10.5.0, ruby-2.5.3-p105) [gem]" level="application" />
69
+ <orderEntry type="library" scope="PROVIDED" name="sprockets (v3.7.2, ruby-2.5.3-p105) [gem]" level="application" />
70
+ <orderEntry type="library" scope="PROVIDED" name="sprockets-rails (v3.2.1, ruby-2.5.3-p105) [gem]" level="application" />
71
+ <orderEntry type="library" scope="PROVIDED" name="thor (v0.20.3, ruby-2.5.3-p105) [gem]" level="application" />
52
72
  <orderEntry type="library" scope="PROVIDED" name="thread_safe (v0.3.6, ruby-2.5.3-p105) [gem]" level="application" />
53
73
  <orderEntry type="library" scope="PROVIDED" name="tzinfo (v1.2.5, ruby-2.5.3-p105) [gem]" level="application" />
74
+ <orderEntry type="library" scope="PROVIDED" name="websocket-driver (v0.7.0, ruby-2.5.3-p105) [gem]" level="application" />
75
+ <orderEntry type="library" scope="PROVIDED" name="websocket-extensions (v0.1.3, ruby-2.5.3-p105) [gem]" level="application" />
54
76
  </component>
55
77
  </module>
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ - Add `accept_api_viewer` which is on by default
6
+ - Add `overwrite` parameter to `accept_html`
7
+ - Add `api_viewer_layout` configuration option
8
+ - Add generator to initialize the gem and copy the API Viewer
9
+
3
10
  ## 0.5.1
4
11
 
5
12
  - Correctly expose `current_media_type` and `current_view`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- media_types-serialization (0.5.1)
4
+ media_types-serialization (0.6.0)
5
5
  actionpack (>= 4.0.0)
6
6
  activesupport (>= 4.0.0)
7
7
  http_headers-accept (< 1.0.0)
@@ -12,6 +12,16 @@ PATH
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
+ actioncable (5.2.2)
16
+ actionpack (= 5.2.2)
17
+ nio4r (~> 2.0)
18
+ websocket-driver (>= 0.6.1)
19
+ actionmailer (5.2.2)
20
+ actionpack (= 5.2.2)
21
+ actionview (= 5.2.2)
22
+ activejob (= 5.2.2)
23
+ mail (~> 2.5, >= 2.5.4)
24
+ rails-dom-testing (~> 2.0)
15
25
  actionpack (5.2.2)
16
26
  actionview (= 5.2.2)
17
27
  activesupport (= 5.2.2)
@@ -25,15 +35,32 @@ GEM
25
35
  erubi (~> 1.4)
26
36
  rails-dom-testing (~> 2.0)
27
37
  rails-html-sanitizer (~> 1.0, >= 1.0.3)
38
+ activejob (5.2.2)
39
+ activesupport (= 5.2.2)
40
+ globalid (>= 0.3.6)
41
+ activemodel (5.2.2)
42
+ activesupport (= 5.2.2)
43
+ activerecord (5.2.2)
44
+ activemodel (= 5.2.2)
45
+ activesupport (= 5.2.2)
46
+ arel (>= 9.0)
47
+ activestorage (5.2.2)
48
+ actionpack (= 5.2.2)
49
+ activerecord (= 5.2.2)
50
+ marcel (~> 0.3.1)
28
51
  activesupport (5.2.2)
29
52
  concurrent-ruby (~> 1.0, >= 1.0.2)
30
53
  i18n (>= 0.7, < 2)
31
54
  minitest (~> 5.1)
32
55
  tzinfo (~> 1.1)
56
+ arel (9.0.0)
57
+ awesome_print (1.8.0)
33
58
  builder (3.2.3)
34
59
  concurrent-ruby (1.1.4)
35
60
  crass (1.0.4)
36
61
  erubi (1.8.0)
62
+ globalid (0.4.2)
63
+ activesupport (>= 4.2.0)
37
64
  http_headers-accept (0.2.1)
38
65
  http_headers-utils (>= 0.2.0, < 1.0.0)
39
66
  http_headers-link (0.2.1)
@@ -44,32 +71,72 @@ GEM
44
71
  loofah (2.2.3)
45
72
  crass (~> 1.0.2)
46
73
  nokogiri (>= 1.5.9)
47
- media_types (0.6.0)
74
+ mail (2.7.1)
75
+ mini_mime (>= 0.1.1)
76
+ marcel (0.3.3)
77
+ mimemagic (~> 0.3.2)
78
+ media_types (0.6.1)
79
+ method_source (0.9.2)
80
+ mimemagic (0.3.3)
81
+ mini_mime (1.0.1)
48
82
  mini_portile2 (2.4.0)
49
83
  minitest (5.11.3)
84
+ nio4r (2.3.1)
50
85
  nokogiri (1.10.1-x64-mingw32)
51
86
  mini_portile2 (~> 2.4.0)
52
- oj (3.7.9)
87
+ oj (3.7.10)
53
88
  rack (2.0.6)
54
89
  rack-test (1.1.0)
55
90
  rack (>= 1.0, < 3)
91
+ rails (5.2.2)
92
+ actioncable (= 5.2.2)
93
+ actionmailer (= 5.2.2)
94
+ actionpack (= 5.2.2)
95
+ actionview (= 5.2.2)
96
+ activejob (= 5.2.2)
97
+ activemodel (= 5.2.2)
98
+ activerecord (= 5.2.2)
99
+ activestorage (= 5.2.2)
100
+ activesupport (= 5.2.2)
101
+ bundler (>= 1.3.0)
102
+ railties (= 5.2.2)
103
+ sprockets-rails (>= 2.0.0)
56
104
  rails-dom-testing (2.0.3)
57
105
  activesupport (>= 4.2.0)
58
106
  nokogiri (>= 1.6)
59
107
  rails-html-sanitizer (1.0.4)
60
108
  loofah (~> 2.2, >= 2.2.2)
109
+ railties (5.2.2)
110
+ actionpack (= 5.2.2)
111
+ activesupport (= 5.2.2)
112
+ method_source
113
+ rake (>= 0.8.7)
114
+ thor (>= 0.19.0, < 2.0)
61
115
  rake (10.5.0)
116
+ sprockets (3.7.2)
117
+ concurrent-ruby (~> 1.0)
118
+ rack (> 1, < 3)
119
+ sprockets-rails (3.2.1)
120
+ actionpack (>= 4.0)
121
+ activesupport (>= 4.0)
122
+ sprockets (>= 3.0.0)
123
+ thor (0.20.3)
62
124
  thread_safe (0.3.6)
63
125
  tzinfo (1.2.5)
64
126
  thread_safe (~> 0.1)
127
+ websocket-driver (0.7.0)
128
+ websocket-extensions (>= 0.1.0)
129
+ websocket-extensions (0.1.3)
65
130
 
66
131
  PLATFORMS
67
132
  x64-mingw32
68
133
 
69
134
  DEPENDENCIES
135
+ awesome_print
70
136
  bundler (~> 2.0)
71
137
  media_types-serialization!
72
138
  minitest (~> 5.0)
139
+ rails (~> 5.2)
73
140
  rake (~> 10.0)
74
141
 
75
142
  BUNDLED WITH
data/README.md CHANGED
@@ -21,6 +21,19 @@ And then execute:
21
21
  Or install it yourself as:
22
22
 
23
23
  $ gem install media_types-serialization
24
+
25
+ If you have not done this before, and you're using `rails`, install the necessary parts using:
26
+
27
+ ```bash
28
+ rails g media_types:serialization:api_viewer
29
+ ```
30
+
31
+ This will:
32
+
33
+ - Add the default `html_wrapper` layout which is an API Viewer used as fallback or the `.api_viewer` format
34
+ - Add the default `template_controller` which allows the API Viewer to post templated links
35
+ - Add the `route` for these templated link forms
36
+ - Add an initializer that registers the `media` renderer and `api_viewer` media type
24
37
 
25
38
  ## Usage
26
39
 
@@ -28,7 +41,7 @@ In order to use media type serialization you only need to do 2 things:
28
41
 
29
42
  ### Serializer
30
43
 
31
- Add a serializer that can serialize a certain media type. The `to_hash` function will be called _explicitely_ in your
44
+ Add a serializer that can serialize a certain media type. The `to_hash` function will be called _explicitly_ in your
32
45
  controller, so you can always use your own, favourite serializer here to do the hefty work. This gem does provide some
33
46
  easy tools, usually enough to do most serialization.
34
47
 
@@ -74,6 +87,7 @@ current `view` (e.g. `create`, `index`, `collection` or ` `). This means that by
74
87
  the latest version you `MediaType` is reporting. The best way to supply your media type is via the [`media_types`](https://github.com/SleeplessByte/media-types-ruby) gem.
75
88
 
76
89
  #### Multiple suffixes, one serializer
90
+
77
91
  By default, the media renderer will automatically detect and inject the following:
78
92
  - suffix `+json` if you define `to_json`
79
93
  - suffix `+xml` if you define `to_xml`
@@ -86,6 +100,7 @@ If you don't define `to_html`, but try to make a serializer output `html`, it wi
86
100
  `serializers/wrapper/html_wrapper.html.erb` (or any other templating extension).
87
101
 
88
102
  #### Migrations (versions)
103
+
89
104
  If the serializer can serialize multiple _versions_, you can supply them through `additional_versions: [2, 3]`. A way to
90
105
  handle this is via backward migrations, meaning you'll migrate from the current version back to an older version.
91
106
 
@@ -129,7 +144,7 @@ end
129
144
  ### Controller
130
145
 
131
146
  In your base controller, or wherever you'd like, include the `MediaTypes::Serialization` concern. In the controller that
132
- uses the serialization, you need to explicitely `accept` it if you want to use the built-in lookups.
147
+ uses the serialization, you need to explicitly `accept` it if you want to use the built-in lookups.
133
148
 
134
149
  ```ruby
135
150
  require 'media_types/serialization'
@@ -155,7 +170,8 @@ class BookController < ApiController
155
170
  end
156
171
  ```
157
172
 
158
- If you have normalized your resources (e.g. into `@resource`), you may render resources like so:
173
+ If you have normalized your resources (e.g. into `@resource`), you can add a `render_media` method to your
174
+ `BaseController` and render resources like so:
159
175
 
160
176
  ```ruby
161
177
  class ApiController < ActionController::API
@@ -165,13 +181,13 @@ class ApiController < ActionController::API
165
181
  end
166
182
  ```
167
183
 
168
- And then call `render_media` whenever you're ready to render
184
+ And then call `render_media` whenever you're ready to render.
169
185
 
170
186
  ### HTML output
171
187
 
172
188
  You can define HTML outputs for example by creating a serializer that accepts `text/html`. At this moment, there may
173
- only be one (1) active HTML serializer for each action; a single controller can have multiple registered, but never for
174
- the same preconditions in `before_action` (because how else would it know which one to pick?).
189
+ only be one (1) active `text/html` serializer for each action; a single controller can have multiple registered, but
190
+ never for the same preconditions in `before_action` (because how else would it know which one to pick?).
175
191
 
176
192
  Use the `render` method to generate your HTML:
177
193
  ```ruby
@@ -197,10 +213,28 @@ end
197
213
  ```
198
214
 
199
215
  You can change the default `wrapper` / `to_html` implementation by setting:
216
+
200
217
  ```ruby
201
218
  ::MediaTypes::Serialization.html_wrapper_layout = '/path/to/wrapper/layout'
202
219
  ```
203
220
 
221
+ ### API viewer
222
+
223
+ There is a special media type exposed by this gem at `::MediaTypes::Serialization::MEDIA_TYPE_API_VIEWER`. If you're
224
+ using `rails` you'll want to register it. You can do so manually, or by `require`ing:
225
+
226
+ ```ruby
227
+ require 'media_types/serialization/media_type/register'
228
+ ```
229
+
230
+ If you do so, the `.api_viewer` format becomes available for all actions that call into `render media:`.
231
+
232
+ You can change the default `wrapper` implementation by setting:
233
+
234
+ ```ruby
235
+ ::MediaTypes::Serialization.api_viewer_layout = '/path/to/wrapper/layout'
236
+ ```
237
+
204
238
  ### Wrapping output
205
239
 
206
240
  By convention, `index` views are wrapped in `_index: [items]`, `collection` views are wrapped in `_embedded: [items]`
@@ -0,0 +1,25 @@
1
+ require 'rails/generators/base'
2
+
3
+ module MediaTypes
4
+ module Serialization
5
+ class ApiViewerGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def copy_controllers
9
+ copy_file "template_controller.rb", "app/controllers/api/template_controller.rb"
10
+ end
11
+
12
+ def copy_views
13
+ copy_file "api_viewer.html.erb", "app/views/serializers/wrapper/html_wrapper.html.erb"
14
+ end
15
+
16
+ def copy_initializer
17
+ copy_file "initializer.rb", "config/initializers/media_types_serialization.rb"
18
+ end
19
+
20
+ def add_route
21
+ route "namespace :api do\n match '/template', controller: :template, action: :create, via: %i[get post], as: :template\nend\n\n"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,94 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>API Viewer</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <% # The following expects webpacker to be active, but you can replace these if you use the sprockets pipeline %>
9
+ <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
10
+ <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
11
+ </head>
12
+
13
+ <body class="container h-100" style="overflow: auto; justify-content: start">
14
+
15
+ <%# Remove the # below to show a logo %>
16
+ <%# link_to Rails.application.routes.url_helpers.root_path do %>
17
+ <%# render 'shared/logo', assigns: { width: 300, height: 63, style: "flex-shrink: 0; margin: 20px 0 40px" } %>
18
+ <%# end %>
19
+
20
+ <div class="h-auto w-100 mb-4">
21
+ <% links = @serializer.send(:header_links) %>
22
+ <% if links.present? %>
23
+ <nav class="card w-100 mb-4 card--inverse">
24
+ <div class="card-header">
25
+ <h5 class="card-title my-auto">HTTP Links</h5>
26
+ </div>
27
+ <ul class="list-group list-group-flush">
28
+ <% links.each do |key, link| %>
29
+ <% href = link && link[:href] || next %>
30
+ <li class="list-group-item">
31
+ <% if link[:templated] %>
32
+ <%# You need to add this route to your routes file; Use the provided generator %>
33
+ <%# rails g api_viewer %>
34
+ <%= form_tag Rails.application.routes.url_helpers.api_template_path, { method: :get, class: 'link-template-form' } do %>
35
+ <%= hidden_field_tag 'template[href]', href %>
36
+ <button type="submit"><strong><%= key %></strong></button>
37
+ <code><%= href.gsub(/:([a-z][^\/]*)|(?:{|%7B)([a-z][^\/{}]*)(%7D|})/) do |match|
38
+ tag = match.tr(':{}', '').delete('%7B').delete('%7D')
39
+ text_field_tag('template[%s]' % tag, nil, placeholder: tag)
40
+ end.html_safe %></code>
41
+ <% end %>
42
+ <% else %>
43
+ <%= link_to href do %>
44
+ <strong><%= key %></strong>
45
+ <code><%= href %></code>
46
+ <% end %>
47
+ <% end %>
48
+ </li>
49
+ <% end %>
50
+ </ul>
51
+ </nav>
52
+ <% end %>
53
+
54
+ <article class="card w-100 mb-4 card--inverse">
55
+ <div class="card-body">
56
+ <h5 class="card-title"><%= @mime_type %></h5>
57
+ <small class="card-subtitle mb-2 text-muted">Serialized by: <%= @serializer.class.name %></small>
58
+ <pre><code class="code" style="
59
+ white-space: pre-wrap;
60
+ white-space: -moz-pre-wrap;
61
+ white-space: -o-pre-wrap;
62
+ word-wrap: break-word;"><% code = JSON.pretty_generate(@serializer.to_hash).truncate(50_000)
63
+ .gsub('</h1>', "</h1>\n")
64
+ .gsub('</div>', "</div>\n")
65
+ .gsub('<', '&lt;')
66
+ .gsub('>', '&gt;')
67
+ .gsub('\n', "<br>")%>
68
+ <%= code.gsub(/"href": "(http(?:s?:\/\/.*?))"/) { |full_match|
69
+ href = full_match[9...-1]
70
+ '"href": "' + link_to(href.gsub('%7B', '{').gsub('%7D', '}'), href).html_safe + '"' rescue full_match
71
+ }.html_safe %></code></pre>
72
+ </div>
73
+ </article>
74
+
75
+ <nav class="card w-100 mb-4 card--inverse">
76
+ <div class="card-header">
77
+ <h5 class="card-title my-auto">Endpoint representations</h5>
78
+ </div>
79
+ <ul class="list-group list-group-flush">
80
+ <% @representations.each do |representation| %>
81
+ <% mime_type = Mime::Type.lookup(representation) || next %>
82
+ <li class="list-group-item">
83
+ <%= link_to (@url_context + ".#{mime_type.symbol}").sub('/.', '.') do %>
84
+ <strong><%= representation %></strong>
85
+ <code><%= mime_type.symbol %></code>
86
+ <% end %>
87
+ </li>
88
+ <% end %>
89
+ </ul>
90
+ </nav>
91
+ </div>
92
+ </body>
93
+ </html>
94
+
@@ -0,0 +1,33 @@
1
+ require 'media_types/serialization'
2
+
3
+ # This registers the renderer as side-effect
4
+ require 'media_types/serialization/renderer/register'
5
+
6
+ # This registers the media type as side-effect
7
+ require 'media_types/serialization/media_type/register'
8
+
9
+ ##
10
+ # The following options are breaking and therefore disabled by default.
11
+ #
12
+ # When these are true, the +header_links+ and +extract_links+ methods is called
13
+ # when dealing with a .collection or .index view, respectively. It allows you
14
+ # to define +_links+ for the root level from your serializer.
15
+ #
16
+ #
17
+ # MediaTypes::Serialization.collect_links_for_collection = true
18
+ # MediaTypes::Serialization.collect_links_for_index = true
19
+
20
+ ##
21
+ # The API Viewer template is provided if you used the generator. You can change
22
+ # the view it renders by changing the path below.
23
+ #
24
+ #
25
+ # ::MediaTypes::Serialization.api_viewer_layout = '/path/to/wrapper/layout'
26
+
27
+ ##
28
+ # When .to_html is not provided by a serializer, it will fall back to render
29
+ # the API Viewer, but this template can be changed by changing the path
30
+ # below.
31
+ #
32
+ #
33
+ # ::MediaTypes::Serialization.html_wrapper_layout = '/path/to/wrapper/layout'
@@ -0,0 +1,23 @@
1
+ module Api
2
+ class TemplateController < ApiController
3
+ def create
4
+ href = params[:template].delete(:href)
5
+ templated_values = params[:template].permit!.to_h
6
+
7
+ response["Location"] = templated_values.reduce(href) do |result, (key, value)|
8
+ result.sub!(%r{:#{key}|{#{key}}|%7B#{key}%7D}, value) || invalid_parameter(key, href)
9
+ end
10
+ head :temporary_redirect
11
+ end
12
+
13
+ private
14
+
15
+ def invalid_parameter(key, href)
16
+ raise ActionController::BadRequest, format(
17
+ 'Received templated value for "%<key>s" which does not exist in templated link "%<href>s"',
18
+ key: key,
19
+ href: href.gsub('%7B', '{').gsub('%7D', '}')
20
+ )
21
+ end
22
+ end
23
+ end
@@ -13,35 +13,62 @@ require 'media_types/serialization/no_serializer_for_content_type'
13
13
  require 'media_types/serialization/base'
14
14
  require 'media_types/serialization/wrapper/html_wrapper'
15
15
 
16
+ require 'awesome_print'
17
+
16
18
  module MediaTypes
17
19
  module Serialization
18
20
 
19
- mattr_accessor :common_suffix, :collect_links_for_collection, :collect_links_for_index, :html_wrapper_layout
21
+ mattr_accessor :common_suffix, :collect_links_for_collection, :collect_links_for_index,
22
+ :html_wrapper_layout, :api_viewer_layout
20
23
 
21
24
  extend ActiveSupport::Concern
22
25
 
23
- HEADER_ACCEPT = 'HTTP_ACCEPT'
24
- MEDIA_TYPE_HTML = 'text/html'
26
+ HEADER_ACCEPT = 'HTTP_ACCEPT'
27
+
28
+ MEDIA_TYPE_HTML = 'text/html'
29
+ MEDIA_TYPE_API_VIEWER = 'application/vnd.xpbytes.api-viewer.v1'
25
30
 
26
31
  # rubocop:disable Metrics/BlockLength
27
32
  class_methods do
33
+
34
+ ##
35
+ # Accept serialization using the passed in +serializer+ for the given +view+
36
+ #
28
37
  # @see #freeze_accepted_media!
29
38
  #
30
- def accept_serialization(serializer, view: [nil], accept_html: true, **filter_opts)
39
+ # @param serializer the serializer to use for serialization. Needs to respond to #to_body, but may respond to
40
+ # #to_json if the type accepted is ...+json, or #to_xml if the type accepted is ...+xml or #to_html if the type
41
+ # accepted is text/html
42
+ # @param [(String | NilClass|)[]] view the views it should serializer for. Use nil for no view
43
+ # @param [Boolean] accept_api_viewer if true, accepts this serializer as base for the api viewer
44
+ # @param [Boolean] accept_html if true, accepts this serializer as the html fallback
45
+ #
46
+ def accept_serialization(serializer, view: [nil], accept_api_viewer: true, accept_html: accept_api_viewer, **filter_opts)
31
47
  before_action(**filter_opts) do
32
- self.serializers = resolved_media_types(serializer, view: view) do |media_type, media_view, res|
48
+ resolved_media_types(serializer, view: view) do |media_type, media_view, _, register|
33
49
  opts = { media_type: media_type, media_view: media_view }
50
+ register.call(String(media_type), wrap_media(serializer, **opts))
51
+ end
52
+ end
53
+
54
+ accept_html(serializer, view: view, overwrite: false, **filter_opts) if accept_html
55
+ accept_api_viewer(serializer, view: view, overwrite: false, **filter_opts) if accept_api_viewer
56
+ end
34
57
 
35
- res[MEDIA_TYPE_HTML] = wrap_html(serializer, **opts) if accept_html && !res[MEDIA_TYPE_HTML]
36
- res[String(media_type)] = wrap_media(serializer, **opts) if media_type != MEDIA_TYPE_HTML
58
+ def accept_html(serializer, view: [nil], overwrite: true, **filter_opts)
59
+ before_action(**filter_opts) do
60
+ resolved_media_types(serializer, view: view) do |_, media_view, registered, register|
61
+ break if registered.call(MEDIA_TYPE_HTML) && !overwrite
62
+ register.call(MEDIA_TYPE_HTML, wrap_html(serializer, media_view: media_view, media_type: MEDIA_TYPE_HTML))
37
63
  end
38
64
  end
39
65
  end
40
66
 
41
- def accept_html(serializer, **filter_opts)
67
+ def accept_api_viewer(serializer, view: [nil], overwrite: true, **filter_opts)
42
68
  before_action(**filter_opts) do
43
- self.serializers = resolved_media_types(serializer, view: nil) do |_, media_view, res|
44
- res[MEDIA_TYPE_HTML] = wrap_html(serializer, media_view: media_view, media_type: MEDIA_TYPE_HTML)
69
+ resolved_media_types(serializer, view: view) do |_, media_view, registered, register|
70
+ break if registered.call(MEDIA_TYPE_API_VIEWER) && !overwrite
71
+ register.call(MEDIA_TYPE_API_VIEWER, wrap_html(serializer, media_view: media_view, media_type: MEDIA_TYPE_API_VIEWER))
45
72
  break
46
73
  end
47
74
  end
@@ -76,6 +103,7 @@ module MediaTypes
76
103
  if self.class.respond_to?(:respond_to)
77
104
  self.class.respond_to(*Hash(serializers).keys.map { |type| Mime::Type.lookup(type) })
78
105
  end
106
+
79
107
  serializers.freeze
80
108
  end
81
109
  end
@@ -156,10 +184,15 @@ module MediaTypes
156
184
  end
157
185
 
158
186
  def resolved_media_types(serializer, view:)
159
- Array(view).each_with_object(Hash(serializers)) do |media_view, res|
187
+ self.serializers = Hash(serializers)
188
+
189
+ registered = serializers.method(:key)
190
+ register = serializers.method(:[]=)
191
+
192
+ Array(view).each do |media_view|
160
193
  media_view = String(media_view)
161
194
  Array(serializer.media_type(view: media_view)).each do |media_type|
162
- yield media_type, media_view, res
195
+ yield media_type, media_view, registered, register
163
196
  end
164
197
  end
165
198
  end
@@ -0,0 +1,4 @@
1
+ require 'action_dispatch/http/mime_type'
2
+ require 'media_types/serialization'
3
+
4
+ Mime::Type.register(MediaTypes::Serialization::MEDIA_TYPE_API_VIEWER, :api_viewer)
@@ -38,11 +38,11 @@ module MediaTypes
38
38
  end
39
39
 
40
40
  [media_type_view].concat(
41
- media_type_versions.map { |version| media_type_view&.version(version) },
42
- media_type_versions.flat_map do |version|
43
- (suffixes).map { |suffix| media_type_view&.suffix(suffix)&.version(version) }
44
- end,
45
- additionals
41
+ media_type_versions.map { |version| media_type_view&.version(version) },
42
+ media_type_versions.flat_map do |version|
43
+ (suffixes).map { |suffix| media_type_view&.suffix(suffix)&.version(version) }
44
+ end,
45
+ additionals
46
46
  ).compact.uniq
47
47
  end
48
48
 
@@ -2,11 +2,17 @@ require 'media_types/serialization/no_content_type_given'
2
2
  require 'active_support/core_ext/object/blank'
3
3
  require 'active_support/core_ext/hash/conversions'
4
4
 
5
+ require 'media_types/serialization'
6
+
5
7
  module MediaTypes
6
8
  module Serialization
7
9
  # noinspection RubyConstantNamingConvention
8
10
  Renderer = lambda do |obj, options|
9
- content_type = options[:content_type] || options[:mime_type]&.to_s || self.content_type&.to_s || obj.current_media_type.to_s
11
+ content_type = options[:content_type] ||
12
+ options[:mime_type]&.to_s ||
13
+ self.content_type&.to_s ||
14
+ obj.current_media_type.to_s
15
+
10
16
  raise NoContentTypeGiven if content_type.blank?
11
17
 
12
18
  self.content_type ||= content_type
@@ -15,8 +21,11 @@ module MediaTypes
15
21
  obj.respond_to?(:to_json) ? obj.to_json(options) : obj.to_hash.to_json(options)
16
22
  elsif content_type.ends_with?('+xml') || Mime::Type.lookup(content_type) == Mime[:xml]
17
23
  obj.respond_to?(:to_xml) ? obj.to_xml(options) : obj.to_hash.to_xml(options)
18
- elsif Mime::Type.lookup(content_type) == Mime[:html]
19
- obj.respond_to?(:to_html) ? obj.to_html : obj.to_s
24
+ elsif Mime::Type.lookup(content_type) == Mime[:html] && obj.respond_to?(:to_html)
25
+ obj.to_html
26
+ elsif content_type === MEDIA_TYPE_API_VIEWER && obj.respond_to?(:to_api_viewer)
27
+ self.content_type = 'text/html'
28
+ obj.to_api_viewer
20
29
  else
21
30
  obj.to_body(content_type: options.delete(:content_type) || content_type, **options)
22
31
  end
@@ -1,5 +1,5 @@
1
1
  module MediaTypes
2
2
  module Serialization
3
- VERSION = '0.5.1'
3
+ VERSION = '0.6.0'
4
4
  end
5
5
  end
@@ -21,9 +21,12 @@ module MediaTypes
21
21
 
22
22
  def to_html
23
23
  return super if __getobj__.respond_to?(:to_html)
24
+ to_api_viewer(layout: ::MediaTypes::Serialization.html_wrapper_layout)
25
+ end
24
26
 
27
+ def to_api_viewer(layout: ::MediaTypes::Serialization.api_viewer_layout)
25
28
  ActionController::Base.render(
26
- ::MediaTypes::Serialization.html_wrapper_layout || 'serializers/wrapper/html_wrapper',
29
+ layout || 'serializers/wrapper/html_wrapper',
27
30
  assigns: {
28
31
  serializer: self,
29
32
  view: view,
@@ -1,13 +1,13 @@
1
1
 
2
- lib = File.expand_path("../lib", __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "media_types/serialization/version"
4
+ require 'media_types/serialization/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "media_types-serialization"
7
+ spec.name = 'media_types-serialization'
8
8
  spec.version = MediaTypes::Serialization::VERSION
9
- spec.authors = ["Derk-Jan Karrenbeld"]
10
- spec.email = ["derk-jan@xpbytes.com"]
9
+ spec.authors = ['Derk-Jan Karrenbeld']
10
+ spec.email = ['derk-jan@xpbytes.com']
11
11
 
12
12
  spec.summary = 'Add media types supported serialization using your favourite serializer'
13
13
  spec.homepage = 'https://github.com/XPBytes/media_types-serialization'
@@ -16,14 +16,14 @@ Gem::Specification.new do |spec|
16
16
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
17
  # to allow pushing to a single host or delete this section to allow pushing to any host.
18
18
  if spec.respond_to?(:metadata)
19
- # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
19
+ # spec.metadata['allowed_push_host'] = 'TODO: Set to 'http://mygemserver.com''
20
20
 
21
- spec.metadata["homepage_uri"] = spec.homepage
22
- spec.metadata["source_code_uri"] = spec.homepage
23
- spec.metadata["changelog_uri"] = spec.homepage + '/CHANGELOG.md'
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = spec.homepage
23
+ spec.metadata['changelog_uri'] = spec.homepage + '/CHANGELOG.md'
24
24
  else
25
- raise "RubyGems 2.0 or newer is required to protect against " \
26
- "public gem pushes."
25
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
26
+ 'public gem pushes.'
27
27
  end
28
28
 
29
29
  # Specify which files should be added to the gem when it is released.
@@ -31,9 +31,9 @@ Gem::Specification.new do |spec|
31
31
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
32
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
33
  end
34
- spec.bindir = "exe"
34
+ spec.bindir = 'exe'
35
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
- spec.require_paths = ["lib"]
36
+ spec.require_paths = ['lib']
37
37
 
38
38
  spec.add_dependency 'actionpack', '>= 4.0.0'
39
39
  spec.add_dependency 'activesupport', '>= 4.0.0'
@@ -42,7 +42,9 @@ Gem::Specification.new do |spec|
42
42
  spec.add_dependency 'http_headers-accept', '< 1.0.0'
43
43
  spec.add_dependency 'http_headers-link', '< 1.0.0'
44
44
 
45
- spec.add_development_dependency "bundler", "~> 2.0"
46
- spec.add_development_dependency "rake", "~> 10.0"
47
- spec.add_development_dependency "minitest", "~> 5.0"
45
+ spec.add_development_dependency 'awesome_print'
46
+ spec.add_development_dependency 'bundler', '~> 2.0'
47
+ spec.add_development_dependency 'rails', '~> 5.2'
48
+ spec.add_development_dependency 'rake', '~> 10.0'
49
+ spec.add_development_dependency 'minitest', '~> 5.0'
48
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: media_types-serialization
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derk-Jan Karrenbeld
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-07 00:00:00.000000000 Z
11
+ date: 2019-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "<"
95
95
  - !ruby/object:Gem::Version
96
96
  version: 1.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: awesome_print
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: bundler
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +122,20 @@ dependencies:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: '2.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rails
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '5.2'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '5.2'
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: rake
113
141
  requirement: !ruby/object:Gem::Requirement
@@ -163,9 +191,14 @@ files:
163
191
  - Rakefile
164
192
  - bin/console
165
193
  - bin/setup
194
+ - lib/generators/media_types/serialization/api_viewer/api_viewer_generator.rb
195
+ - lib/generators/media_types/serialization/api_viewer/templates/api_viewer.html.erb
196
+ - lib/generators/media_types/serialization/api_viewer/templates/initializer.rb
197
+ - lib/generators/media_types/serialization/api_viewer/templates/template_controller.rb
166
198
  - lib/media_types/serialization.rb
167
199
  - lib/media_types/serialization/base.rb
168
200
  - lib/media_types/serialization/error.rb
201
+ - lib/media_types/serialization/media_type/register.rb
169
202
  - lib/media_types/serialization/migrations_command.rb
170
203
  - lib/media_types/serialization/migrations_support.rb
171
204
  - lib/media_types/serialization/mime_type_support.rb
@@ -204,8 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
237
  - !ruby/object:Gem::Version
205
238
  version: '0'
206
239
  requirements: []
207
- rubyforge_project:
208
- rubygems_version: 2.7.6
240
+ rubygems_version: 3.0.3
209
241
  signing_key:
210
242
  specification_version: 4
211
243
  summary: Add media types supported serialization using your favourite serializer