jsonapionify 0.0.1.pre

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 (124) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +29 -0
  3. data/.csslintrc +2 -0
  4. data/.gitignore +11 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +1171 -0
  7. data/.ruby-version +1 -0
  8. data/.travis.yml +10 -0
  9. data/CODE_OF_CONDUCT.md +13 -0
  10. data/Gemfile +4 -0
  11. data/Guardfile +14 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +43 -0
  14. data/Rakefile +34 -0
  15. data/TODO +13 -0
  16. data/bin/console +14 -0
  17. data/bin/setup +7 -0
  18. data/config.ru +15 -0
  19. data/fixtures/documentation.json +364 -0
  20. data/jsonapionify.gemspec +50 -0
  21. data/lib/core_ext/boolean.rb +3 -0
  22. data/lib/jsonapionify/api/action.rb +211 -0
  23. data/lib/jsonapionify/api/attribute.rb +67 -0
  24. data/lib/jsonapionify/api/base/app_builder.rb +33 -0
  25. data/lib/jsonapionify/api/base/class_methods.rb +73 -0
  26. data/lib/jsonapionify/api/base/delegation.rb +15 -0
  27. data/lib/jsonapionify/api/base/doc_helper.rb +47 -0
  28. data/lib/jsonapionify/api/base/reloader.rb +10 -0
  29. data/lib/jsonapionify/api/base/resource_definitions.rb +39 -0
  30. data/lib/jsonapionify/api/base.rb +25 -0
  31. data/lib/jsonapionify/api/context.rb +14 -0
  32. data/lib/jsonapionify/api/context_delegate.rb +42 -0
  33. data/lib/jsonapionify/api/errors.rb +6 -0
  34. data/lib/jsonapionify/api/errors_object.rb +66 -0
  35. data/lib/jsonapionify/api/header_options.rb +13 -0
  36. data/lib/jsonapionify/api/param_options.rb +46 -0
  37. data/lib/jsonapionify/api/relationship/blocks.rb +41 -0
  38. data/lib/jsonapionify/api/relationship/many.rb +61 -0
  39. data/lib/jsonapionify/api/relationship/one.rb +36 -0
  40. data/lib/jsonapionify/api/relationship.rb +89 -0
  41. data/lib/jsonapionify/api/resource/builders.rb +81 -0
  42. data/lib/jsonapionify/api/resource/class_methods.rb +82 -0
  43. data/lib/jsonapionify/api/resource/defaults/actions.rb +11 -0
  44. data/lib/jsonapionify/api/resource/defaults/errors.rb +99 -0
  45. data/lib/jsonapionify/api/resource/defaults/request_contexts.rb +96 -0
  46. data/lib/jsonapionify/api/resource/defaults/response_contexts.rb +31 -0
  47. data/lib/jsonapionify/api/resource/defaults.rb +10 -0
  48. data/lib/jsonapionify/api/resource/definitions/actions.rb +196 -0
  49. data/lib/jsonapionify/api/resource/definitions/attributes.rb +51 -0
  50. data/lib/jsonapionify/api/resource/definitions/contexts.rb +16 -0
  51. data/lib/jsonapionify/api/resource/definitions/helpers.rb +9 -0
  52. data/lib/jsonapionify/api/resource/definitions/pagination.rb +79 -0
  53. data/lib/jsonapionify/api/resource/definitions/params.rb +49 -0
  54. data/lib/jsonapionify/api/resource/definitions/relationships.rb +42 -0
  55. data/lib/jsonapionify/api/resource/definitions/request_headers.rb +103 -0
  56. data/lib/jsonapionify/api/resource/definitions/response_headers.rb +22 -0
  57. data/lib/jsonapionify/api/resource/definitions/scopes.rb +50 -0
  58. data/lib/jsonapionify/api/resource/definitions/sorting.rb +85 -0
  59. data/lib/jsonapionify/api/resource/definitions.rb +14 -0
  60. data/lib/jsonapionify/api/resource/error_handling.rb +108 -0
  61. data/lib/jsonapionify/api/resource/http.rb +11 -0
  62. data/lib/jsonapionify/api/resource/includer.rb +4 -0
  63. data/lib/jsonapionify/api/resource.rb +35 -0
  64. data/lib/jsonapionify/api/response.rb +47 -0
  65. data/lib/jsonapionify/api/server/mock_response.rb +37 -0
  66. data/lib/jsonapionify/api/server/request.rb +78 -0
  67. data/lib/jsonapionify/api/server.rb +50 -0
  68. data/lib/jsonapionify/api/test_helper.rb +52 -0
  69. data/lib/jsonapionify/api.rb +9 -0
  70. data/lib/jsonapionify/autoload.rb +52 -0
  71. data/lib/jsonapionify/callbacks.rb +49 -0
  72. data/lib/jsonapionify/character_range.rb +41 -0
  73. data/lib/jsonapionify/continuation.rb +26 -0
  74. data/lib/jsonapionify/documentation/template.erb +487 -0
  75. data/lib/jsonapionify/documentation.rb +40 -0
  76. data/lib/jsonapionify/enumerable_observer.rb +91 -0
  77. data/lib/jsonapionify/indented_string.rb +27 -0
  78. data/lib/jsonapionify/inherited_attributes.rb +125 -0
  79. data/lib/jsonapionify/structure/collections/base.rb +104 -0
  80. data/lib/jsonapionify/structure/collections/errors.rb +7 -0
  81. data/lib/jsonapionify/structure/collections/included_resources.rb +39 -0
  82. data/lib/jsonapionify/structure/collections/resource_identifiers.rb +7 -0
  83. data/lib/jsonapionify/structure/collections/resources.rb +7 -0
  84. data/lib/jsonapionify/structure/helpers/errors.rb +71 -0
  85. data/lib/jsonapionify/structure/helpers/inherits_origin.rb +17 -0
  86. data/lib/jsonapionify/structure/helpers/member_names.rb +37 -0
  87. data/lib/jsonapionify/structure/helpers/meta_delegate.rb +16 -0
  88. data/lib/jsonapionify/structure/helpers/object_defaults.rb +123 -0
  89. data/lib/jsonapionify/structure/helpers/object_setters.rb +21 -0
  90. data/lib/jsonapionify/structure/helpers/pagination_links.rb +10 -0
  91. data/lib/jsonapionify/structure/helpers/validations.rb +296 -0
  92. data/lib/jsonapionify/structure/maps/base.rb +25 -0
  93. data/lib/jsonapionify/structure/maps/error_links.rb +7 -0
  94. data/lib/jsonapionify/structure/maps/links.rb +21 -0
  95. data/lib/jsonapionify/structure/maps/relationship_links.rb +11 -0
  96. data/lib/jsonapionify/structure/maps/relationships.rb +23 -0
  97. data/lib/jsonapionify/structure/maps/resource_links.rb +7 -0
  98. data/lib/jsonapionify/structure/maps/top_level_links.rb +10 -0
  99. data/lib/jsonapionify/structure/objects/attributes.rb +29 -0
  100. data/lib/jsonapionify/structure/objects/base.rb +166 -0
  101. data/lib/jsonapionify/structure/objects/error.rb +16 -0
  102. data/lib/jsonapionify/structure/objects/included_resource.rb +14 -0
  103. data/lib/jsonapionify/structure/objects/jsonapi.rb +7 -0
  104. data/lib/jsonapionify/structure/objects/link.rb +18 -0
  105. data/lib/jsonapionify/structure/objects/meta.rb +7 -0
  106. data/lib/jsonapionify/structure/objects/relationship.rb +20 -0
  107. data/lib/jsonapionify/structure/objects/resource.rb +45 -0
  108. data/lib/jsonapionify/structure/objects/resource_identifier.rb +40 -0
  109. data/lib/jsonapionify/structure/objects/source.rb +10 -0
  110. data/lib/jsonapionify/structure/objects/top_level.rb +105 -0
  111. data/lib/jsonapionify/structure.rb +27 -0
  112. data/lib/jsonapionify/types/array_type.rb +32 -0
  113. data/lib/jsonapionify/types/boolean_type.rb +22 -0
  114. data/lib/jsonapionify/types/date_string_type.rb +28 -0
  115. data/lib/jsonapionify/types/float_type.rb +8 -0
  116. data/lib/jsonapionify/types/integer_type.rb +9 -0
  117. data/lib/jsonapionify/types/object_type.rb +22 -0
  118. data/lib/jsonapionify/types/string_type.rb +66 -0
  119. data/lib/jsonapionify/types/time_string_type.rb +28 -0
  120. data/lib/jsonapionify/types.rb +49 -0
  121. data/lib/jsonapionify/unstrict_proc.rb +28 -0
  122. data/lib/jsonapionify/version.rb +3 -0
  123. data/lib/jsonapionify.rb +37 -0
  124. metadata +530 -0
@@ -0,0 +1,26 @@
1
+ module JSONAPIonify
2
+ class Continuation
3
+ using UnstrictProc
4
+
5
+ def initialize(**options)
6
+ @options = options
7
+ end
8
+
9
+ def check(*arguments)
10
+ yield if check_if(*arguments) && check_unless(*arguments)
11
+ end
12
+
13
+ private
14
+
15
+ def check_if(*arguments)
16
+ return true unless @options[:if]
17
+ @options[:if].unstrict.call(*arguments)
18
+ end
19
+
20
+ def check_unless(*arguments)
21
+ return true unless @options[:unless]
22
+ not @options[:unless].unstrict.call(*arguments)
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,487 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
8
+ <title><%= api.title %>></title>
9
+
10
+ <link href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABU1BMVEUAAAAzerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerczerfsspsqAAAAcHRSTlMAAQIDBAYHCAkKCwwNDg8QERIUFRcYGxweICIjJSgqLC0vMDEyNjc6PD5AQUJDREVGR05PVFVXWVthYmZnaGltcHN4fH+AgoiPkZSXmJqdnqKoq62wtbm8vsHDxcfOz9HV19rc4Obo6evt8fX3+fv9oZ9KRwAAAeFJREFUWMPt1NdT1FAYhvE3YRFWLAsWxIYoIkWlCHZQmiCoIIIFFFFAl/78/1deGJ1NOCV4Zrza7y7fOc9vZjczkapTnZxzGXbvhwBjwNcQYBMYCegbAVoCgAFgPwoAloDXuW8XFt/EmQ1ApySp9G3Y23+Cd2mhFaAoSaUyjPp7GE/tJoBVSYp+gEf43XMvtSwDj//+GU4h6Z+klk0AzZKkmvduwdhrENhNXqJbMPf6ALz88+ASLH0tQIf8gqXXDYA6eYXCsrnXFPC5cmEWrL22gYfyCfb+LMA5eQR7rwfAdnaZFQor1l4rwJTcgqs/BtAup+DqdROgVi4hWnb0mgY+Gk8SYVgnANgrGm9FO8CQ2e4CYE3qB+B7venSeYAzxr4NgHJJTuERsOXrXcIXYNLbS30WoR7gmr+3CrcACjl6qdcozAJL1v50amkSoj1g4FB/3dRLdwFYrxQuADTm7E3CU2Azdy/dyQprwJjpC2fuDwnHAa4coZdup4RugJqj9BlhDlgw/P6f9l7qAWBGkuJ9oL/yMD7w9olwVZIuApxKHU76e6kb2iRJz4CNzOELfy+1JB/xdeB59rC9qLxzEuCS/n16gYM4AHgLzAf0MUBPANAA0BAAqHlk9ZWqU53/Nb8AOJAKnq/8xU4AAAAASUVORK5CYII=" rel="icon" type="image/x-icon"/>
11
+
12
+ <!-- Bootstrap -->
13
+ <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
14
+ <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/styles/default.min.css">
15
+ <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/styles/agate.min.css">
16
+ <link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,200,300,500,600,700' rel='stylesheet' type='text/css'>
17
+ <link href='//fonts.googleapis.com/css?family=Source+Code+Pro:400,200,300,500,600,700' rel='stylesheet' type='text/css'>
18
+ <style>
19
+ body {
20
+ font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
21
+ color: #4c555a;
22
+ }
23
+
24
+ .affix {
25
+ top: 20px;
26
+ }
27
+
28
+ .navbar-default {
29
+ border-radius: 0px;
30
+ background-color: #4c555a;
31
+ border: 0px;
32
+ }
33
+
34
+ .navbar-default .navbar-collapse, .navbar-default .navbar-form {
35
+ border-color: transparent;
36
+ }
37
+
38
+ .navbar-default .navbar-brand {
39
+ color: #e1e8ed;
40
+ }
41
+
42
+ .navbar-default .navbar-brand:hover {
43
+ color: white;
44
+ }
45
+
46
+ .navbar-default .navbar-nav > li > span {
47
+ color: #e1e8ed;
48
+ display: inline-block;
49
+ padding: 15px;
50
+ font-size: 14px;
51
+ font-weight: 300;
52
+ letter-spacing: 0.3px;
53
+ }
54
+
55
+ /* sidebar */
56
+ #sidebar {
57
+ padding-left: 20px;
58
+ margin-top: 20px;
59
+ margin-bottom: 20px;
60
+ }
61
+
62
+ #sidebar li.nav-H2 {
63
+ padding-left: 20px;
64
+ padding-top: 10px;
65
+ }
66
+
67
+ #sidebar li.nav-H2 > a {
68
+ color: #939da3;
69
+ font-weight: 300;
70
+ text-transform: uppercase;
71
+ letter-spacing: 1px;
72
+ font-size: 15px;
73
+ }
74
+
75
+ #sidebar li.nav-H2 > ul {
76
+ margin-left: 10px;
77
+ margin-top: 10px;
78
+ margin-bottom: 10px;
79
+ }
80
+
81
+ #sidebar li.nav-H3 > ul {
82
+ margin-left: 10px;
83
+ margin-bottom: 5px;
84
+ margin-top: 5px;
85
+ }
86
+
87
+ #sidebar li.nav-H4 > a {
88
+ font-size: 0.9em;
89
+ }
90
+
91
+ /* all links */
92
+ #sidebar li > a {
93
+ padding: 0 5px;
94
+ color: #4c555a;
95
+ text-decoration: none;
96
+ border-left: 2px solid transparent;
97
+ font-size: 15px;
98
+ font-weight: 400;
99
+ }
100
+
101
+ /* active & hover links */
102
+ #sidebar .nav-H2.active > a,
103
+ .bs-docs-sidebar .nav li > a:hover,
104
+ .bs-docs-sidebar .nav li > a:focus {
105
+ color: #337ab7;
106
+ text-decoration: none;
107
+ background-color: transparent;
108
+ border-left-color: #337ab7;
109
+ }
110
+
111
+ /* all active links */
112
+ .bs-docs-sidebar .nav > .active > a,
113
+ .bs-docs-sidebar .nav > .active:hover > a,
114
+ .bs-docs-sidebar .nav > .active:focus > a {
115
+ font-weight: 700;
116
+ }
117
+
118
+ /* nested active links */
119
+ .bs-docs-sidebar .nav .nav > .active > a,
120
+ .bs-docs-sidebar .nav .nav > .active:hover > a,
121
+ .bs-docs-sidebar .nav .nav > .active:focus > a {
122
+ font-weight: 500;
123
+ }
124
+
125
+ /* hide inactive nested list */
126
+ #sidebar .nav-H3 ul {
127
+ display: none;
128
+ }
129
+
130
+ #sidebar .nav-H3:before, .nav-H4:before {
131
+ content: '>';
132
+ font-family: "Source Code Pro", Monaco, monospace;
133
+ position: absolute;
134
+ left: -5px;
135
+ color: #e1e8ed;
136
+ }
137
+
138
+ #sidebar .nav-H3.active:before, .nav-H4.active:before {
139
+ color: #6f7579;
140
+ content: '>';
141
+ font-family: "Source Code Pro", Monaco, monospace;
142
+ position: absolute;
143
+ left: -5px;
144
+ }
145
+
146
+ /* show active nested list */
147
+ #sidebar .nav-H3.active > ul {
148
+ display: block;
149
+ }
150
+
151
+ section {
152
+ margin-top: 40px;
153
+ padding-bottom: 40px;
154
+ border-bottom: 2px solid #e1e8ed;
155
+ }
156
+
157
+ section:first-child {
158
+ margin-top: 0px;
159
+ }
160
+
161
+ section:last-child {
162
+ border-bottom: 0px;
163
+ }
164
+
165
+ section.group h3 {
166
+ margin-bottom: 15px;
167
+ margin-top: 30px;
168
+ }
169
+
170
+ section.group h4 {
171
+ margin-top: 30px;
172
+ font-weight: 300;
173
+ color: #939da3;
174
+ text-transform: uppercase;
175
+ font-size: 13px;
176
+ letter-spacing: 1px;
177
+ padding-bottom: 10px;
178
+ margin-bottom: 0px;
179
+ border-bottom: 1px solid #e1e8ed;
180
+ }
181
+
182
+ ul.attributes {
183
+ list-style-type: none;
184
+ font-size: 14px;
185
+ padding-left: 0px;
186
+ }
187
+
188
+ ul.attributes .row-attribute {
189
+ border-bottom: 1px solid #f0f4f7;
190
+ padding: 15px 0px;
191
+ }
192
+
193
+ ul.relationships {
194
+ margin-top: 10px;
195
+ list-style-type: none;
196
+ padding-left: 0px;
197
+ }
198
+
199
+ ul.attributes > li:last-child > p {
200
+ margin-bottom: 0px;
201
+ }
202
+
203
+ .attribute-name strong {
204
+ font-family: 'Source Code Pro', Helvetica, Arial, sans-serif;
205
+ font-weight: 600;
206
+ font-size: 13px;
207
+ }
208
+
209
+ .attribute-description {
210
+ font-size: 14px;
211
+ }
212
+
213
+ ul.attributes .optional, ul.attributes .allowed {,
214
+ color: #939da3;
215
+ font-size: 13px;
216
+ font-weight: 300;
217
+ margin-bottom: 0px;
218
+ }
219
+
220
+ ul.attributes .allowed {
221
+ font-style: italic;
222
+ }
223
+
224
+ ul.attributes .optional {
225
+ margin-top: 5px;
226
+ }
227
+
228
+ footer {
229
+ margin-top: 60px;
230
+ padding: 10px 0px;
231
+ border-top: 1px solid #e7e7e7;
232
+ background-color: #4c555a;
233
+ text-align: center;
234
+ }
235
+
236
+ footer a {
237
+ color: #e1e8ed;
238
+ }
239
+
240
+ footer a:hover {
241
+ color: white;
242
+ }
243
+
244
+ code.inline {
245
+ display: inline-block;
246
+ padding: 0 5px;
247
+ background: whiteSmoke;
248
+ border: 1px solid #e7e7e7;
249
+ -webkit-border-radius: 4px;
250
+ border-radius: 4px;
251
+ color: #b93d6a;
252
+ font-family: 'Source Code Pro', Menlo, monospace;
253
+ font-size: 12px;
254
+ line-height: 20px;
255
+ }
256
+
257
+ pre.nostyle {
258
+ padding: 0px;
259
+ margin: 0px;
260
+ border-radius: 0px;
261
+ position: relative;
262
+ margin: 10px 0px;
263
+ padding: 0px;
264
+ border: 1px solid #e1e8ed;
265
+ font-size: 12px;
266
+ line-height: 16px;
267
+ }
268
+
269
+ .attribute-type {
270
+ color: #939da3
271
+ }
272
+
273
+ pre.nostyle code {
274
+ padding: 10px 15px;
275
+ }
276
+
277
+ .hljs {
278
+ background-color: #4c555a;
279
+ }
280
+
281
+ </style>
282
+
283
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
284
+ <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
285
+ <!--[if lt IE 9]>
286
+ <script
287
+ src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
288
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
289
+ <![endif]-->
290
+ </head>
291
+ <body>
292
+ <nav class="navbar navbar-default" role="navigation">
293
+ <div class="container">
294
+ <!-- Brand and toggle get grouped for better mobile display -->
295
+ <div class="navbar-header">
296
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
297
+ <span class="sr-only">Toggle Description</span>
298
+ <span class="icon-bar"></span>
299
+ <span class="icon-bar"></span>
300
+ <span class="icon-bar"></span>
301
+ </button>
302
+ <a class="navbar-brand" href="<%= api.base_url %>">
303
+ <%= api.title %>
304
+ </a>
305
+ </div>
306
+ </div>
307
+ </nav>
308
+
309
+ <div class="container">
310
+ <div class="row">
311
+
312
+ <!--Main Content -->
313
+ <div class="col-md-9" id="_main">
314
+ <section id="api">
315
+ <h1><%= api.title %></h1>
316
+ <p><%= api.description %></p>
317
+ </section>
318
+
319
+ <h2>Resources</h2>
320
+ <% api.resources.each do |resource| %>
321
+ <section id="<%= resource.name %>" class="group">
322
+ <h3><%= resource.name.titleize %></h3>
323
+
324
+ <p><%= resource.description %></p>
325
+
326
+ <div class="details">
327
+ <h4>Attributes</h4>
328
+ <ul class="attributes">
329
+ <% resource.attributes.each do |attribute| %>
330
+ <li>
331
+ <div class="row-attribute">
332
+ <div class="col-xs-4 attribute-name">
333
+ <strong><%= attribute.name %></strong>
334
+ <i class="attribute-type"><%= attribute.type %></i>
335
+ <% if attribute.required %>
336
+ <p class="optional">required</p>
337
+ <% else %>
338
+ <p class="optional">optional</p>
339
+ <% end %>
340
+ </div>
341
+ <div class="col-xs-8 attribute-description">
342
+ <%= attribute.description.gsub(/`(.*?)`/, "<code class='inline'>\\1</code>") %>
343
+ <p class="allowed">Allowed: <%= "#{attribute.allow.to_sentence}" %></p>
344
+ </div>
345
+ <div style="clear: both;"></div>
346
+ </div>
347
+ </li>
348
+ <% end %>
349
+ </ul>
350
+
351
+ <% resource.actions.each do |action| %>
352
+ <h4><%= action.name.titleize %> <%= action.name == 'list' ? resource.name.pluralize : resource.name.singularize %></h4>
353
+ <% action.sample_requests.each do |sample_request| %>
354
+ <h5>Request</h5>
355
+ <pre><code class="http"><%= sample_request.request %></code></pre>
356
+ <h5>Response</h5>
357
+ <pre><code class="http"><%= sample_request.response %></code></pre>
358
+ <% end %>
359
+ <% end %>
360
+ <h4 id="<%= resource.name %>-relationships">Relationships</h4>
361
+ <ul class="relationships">
362
+ <% resource.relationships.each do |relationship| %>
363
+ <li id="<%= resource.name %>-<%= relationship.name %>" class="subgroup">
364
+ <strong><a href="#<%= relationship.resource %>"><%= relationship.name %></a></strong>
365
+ </li>
366
+ <% end %>
367
+ </ul>
368
+ </div>
369
+ </section>
370
+ <% end %>
371
+
372
+ </div>
373
+
374
+ <!--Side Bar -->
375
+ <nav class="col-md-3 bs-docs-sidebar hidden-sm hidden-xs scrollspy">
376
+ <ul id="sidebar" class="nav" data-spy="affix"></ul>
377
+ </nav>
378
+
379
+ </div>
380
+ </div>
381
+
382
+ <footer>
383
+ <div class="container">
384
+ <div class="row">
385
+ <div class="col-xs-12">
386
+ <a href="https://github.com/brandfolder/jsonapionify">
387
+ Powered by JSONAPIonify
388
+ </a>
389
+ </div>
390
+ </div>
391
+ </div>
392
+ </footer>
393
+
394
+ <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
395
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
396
+ <!-- Include all compiled plugins (below), or include individual files as needed -->
397
+ <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha256-KXn5puMvxCw+dAYznun+drMdG1IFl3agK0p/pqT9KAo= sha512-2e8qq0ETcfWRI4HJBzQiA3UoyFk6tbNyG+qSaIBZLyW9Xf3sWZHN/lxe9fTh1U45DpPf07yj94KsUHHWe4Yk1A==" crossorigin="anonymous"></script>
398
+ <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js"></script>
399
+ <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/languages/http.min.js"></script>
400
+ <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
401
+ <script>
402
+ var $sidebar = $('#sidebar');
403
+
404
+ var nav = function () {
405
+ var $e, $prevE;
406
+ var currentH2 = {}, currentH3 = {}, currentH4 = {};
407
+ var nav = {};
408
+ $('#_main h2, #_main h3, #_main h4').each(function (i, e) {
409
+ var key = 'nav-' + i;
410
+ $prevE = $e;
411
+ $e = $(e);
412
+ switch (e.tagName) {
413
+ case 'H2':
414
+ nav[key] = {};
415
+ nav[key]['_element'] = $e;
416
+ currentH2 = nav[key];
417
+ break;
418
+ case 'H3':
419
+ currentH2[key] = {};
420
+ currentH2[key]['_element'] = $e;
421
+ currentH3 = currentH2[key];
422
+ break;
423
+ case 'H4':
424
+ currentH3[key] = {};
425
+ currentH3[key]['_element'] = $e;
426
+ currentH4 = currentH3[key];
427
+ break;
428
+ }
429
+ });
430
+ return nav
431
+ }();
432
+
433
+ function buildNavigation(map, $list) {
434
+ $.each(map, function (id, sublist) {
435
+ if (id == '_element') {
436
+ return
437
+ }
438
+ $e = $(sublist._element);
439
+ $e.attr('id', id);
440
+ var $listItem = $('<li class="nav-' + $e[0].tagName + '"></li>');
441
+ $listItem.append($('<a href="#' + id + '">' + $e.text() + '</a>'));
442
+ var $subList = $('<ul class="nav"></ul>');
443
+ buildNavigation(sublist, $subList);
444
+ if ($subList.find('li').length) {
445
+ $listItem.append($subList)
446
+ }
447
+ $list.append($listItem)
448
+ })
449
+ }
450
+ buildNavigation(nav, $sidebar);
451
+ $sidebar.find('.nav-H3 ul').addClass('nav-stacked');
452
+
453
+ $('body').scrollspy({
454
+ target: '.bs-docs-sidebar',
455
+ offset: 40
456
+ });
457
+
458
+ $sidebar.affix({
459
+ offset: {
460
+ top: $sidebar.offset().top
461
+ }
462
+ });
463
+
464
+ hljs.initHighlightingOnLoad();
465
+
466
+ $("a[href^='#']").on('click', function (e) {
467
+
468
+ // prevent default anchor click behavior
469
+ e.preventDefault();
470
+
471
+ // store hash
472
+ var hash = this.hash;
473
+
474
+ // animate
475
+ $('html, body').animate({
476
+ scrollTop: $(hash).offset().top - 20
477
+ }, 300, function () {
478
+
479
+ // when done, add hash to url
480
+ // (default click behaviour)
481
+ window.location.hash = hash;
482
+ });
483
+
484
+ });
485
+ </script>
486
+ </body>
487
+ </html>
@@ -0,0 +1,40 @@
1
+ require 'erb'
2
+ require 'redcarpet'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'active_support/core_ext/array'
5
+
6
+ module JSONAPIonify
7
+ class Documentation
8
+ using JSONAPIonify::IndentedString
9
+ RENDERER = Redcarpet::Markdown.new(
10
+ Redcarpet::Render::HTML,
11
+ autolink: true,
12
+ tables: true,
13
+ fenced_code_blocks: true,
14
+ strikethrough: true,
15
+ disable_indented_code_blocks: true,
16
+ no_intra_emphasis: true,
17
+ space_after_headers: true,
18
+ underline: true,
19
+ highlight: true,
20
+ quote: true
21
+ )
22
+
23
+ def self.render_markdown(string)
24
+ RENDERER.render(string.deindent)
25
+ end
26
+
27
+ attr_reader :api
28
+
29
+ def initialize(api, template: nil)
30
+ template ||= File.join(__dir__, 'documentation/template.erb')
31
+ @api = api
32
+ @erb = ERB.new File.read(template)
33
+ end
34
+
35
+ def result
36
+ @erb.result(binding)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,91 @@
1
+ require 'jsonapionify/unstrict_proc'
2
+
3
+ module JSONAPIonify
4
+ module EnumerableObserver
5
+ using UnstrictProc
6
+
7
+ def self.observe(obj)
8
+ Observer.new(obj)
9
+ end
10
+
11
+ def observe(obj = self)
12
+ Observer.new(obj)
13
+ end
14
+
15
+ class Observer
16
+ using UnstrictProc
17
+
18
+ UNSAFE_METHODS = %i{
19
+ instance_variable_set
20
+ remove_instance_variable
21
+ define_singleton_method
22
+ instance_eval
23
+ instance_exec
24
+ }
25
+ SAFE_METHODS = (%i{each} + Object.instance_methods) - UNSAFE_METHODS
26
+
27
+ private def observer_method_proc(existing = [])
28
+ array = [self, *existing].freeze
29
+ lambda do
30
+ array
31
+ end
32
+ end
33
+
34
+ def initialize(obj)
35
+ @object = obj
36
+ observers_proc =
37
+ observer_method_proc(obj.respond_to?(:observers) ? obj.observers : [])
38
+ obj.define_singleton_method(:observers, &observers_proc)
39
+ obj.extend mod
40
+ end
41
+
42
+ def mod
43
+ @mod ||= begin
44
+ obj = @object
45
+ blocks = self.blocks
46
+ Module.new do
47
+ (obj.methods - SAFE_METHODS).each do |meth|
48
+ old = obj.method(meth).unbind.bind(obj)
49
+ define_method(meth) do |*args, &block|
50
+ before = each.to_a
51
+ val = old.call(*args, &block)
52
+ after = each.to_a
53
+ added = after - before
54
+ removed = before - after
55
+ blocks[:add].unstrict.call(added) unless added.empty? || !blocks[:add]
56
+ blocks[:remove].unstrict.call(removed) unless removed.empty? || !blocks[:remove]
57
+ val
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def unobserve
65
+ mod.instance_methods.each do |method_name|
66
+ mod.module_eval do
67
+ remove_method method_name
68
+ end
69
+ end
70
+ @unobserved = true
71
+ end
72
+
73
+ def added(&block)
74
+ blocks[:add] = block
75
+ self
76
+ end
77
+
78
+ def removed(&block)
79
+ blocks[:remove] = block
80
+ self
81
+ end
82
+
83
+ protected
84
+
85
+ def blocks
86
+ @blocks ||= {}
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,27 @@
1
+ module JSONAPIonify
2
+ module IndentedString
3
+ extend self
4
+
5
+ refine String do
6
+ def deindent
7
+ shortest_line_length = lines.each_with_object([]) do |line, ary|
8
+ ary << line.match(/^\s*/)[0].length if line.rstrip.present?
9
+ end.sort.first
10
+
11
+ lines.map do |string|
12
+ if string.length && !string.match(/^\s*$/)
13
+ string.rstrip[shortest_line_length..-1]
14
+ else
15
+ string
16
+ end
17
+ end.join("\n")
18
+ end
19
+ end
20
+
21
+ using self
22
+
23
+ def deindent_string(string)
24
+ string.deindent
25
+ end
26
+ end
27
+ end