jsonapionify 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
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