moonrope 1.3.3 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +47 -0
  4. data/MIT-LICENCE +20 -0
  5. data/README.md +24 -0
  6. data/bin/moonrope +28 -0
  7. data/docs/authentication.md +114 -0
  8. data/docs/controllers.md +106 -0
  9. data/docs/exceptions.md +27 -0
  10. data/docs/introduction.md +29 -0
  11. data/docs/structures.md +214 -0
  12. data/example/authentication.rb +50 -0
  13. data/example/controllers/meta_controller.rb +14 -0
  14. data/example/controllers/users_controller.rb +92 -0
  15. data/example/structures/pet_structure.rb +12 -0
  16. data/example/structures/user_structure.rb +35 -0
  17. data/lib/moonrope.rb +5 -4
  18. data/lib/moonrope/action.rb +170 -40
  19. data/lib/moonrope/authenticator.rb +42 -0
  20. data/lib/moonrope/base.rb +67 -6
  21. data/lib/moonrope/controller.rb +4 -2
  22. data/lib/moonrope/doc_context.rb +94 -0
  23. data/lib/moonrope/doc_server.rb +123 -0
  24. data/lib/moonrope/dsl/action_dsl.rb +159 -9
  25. data/lib/moonrope/dsl/authenticator_dsl.rb +35 -0
  26. data/lib/moonrope/dsl/base_dsl.rb +21 -18
  27. data/lib/moonrope/dsl/controller_dsl.rb +60 -9
  28. data/lib/moonrope/dsl/filterable_dsl.rb +27 -0
  29. data/lib/moonrope/dsl/structure_dsl.rb +28 -2
  30. data/lib/moonrope/errors.rb +13 -0
  31. data/lib/moonrope/eval_environment.rb +82 -3
  32. data/lib/moonrope/eval_helpers.rb +47 -8
  33. data/lib/moonrope/eval_helpers/filter_helper.rb +82 -0
  34. data/lib/moonrope/guard.rb +35 -0
  35. data/lib/moonrope/html_generator.rb +65 -0
  36. data/lib/moonrope/param_set.rb +11 -1
  37. data/lib/moonrope/rack_middleware.rb +66 -37
  38. data/lib/moonrope/railtie.rb +31 -14
  39. data/lib/moonrope/request.rb +43 -15
  40. data/lib/moonrope/structure.rb +100 -18
  41. data/lib/moonrope/structure_attribute.rb +39 -0
  42. data/lib/moonrope/version.rb +1 -1
  43. data/moonrope.gemspec +21 -0
  44. data/spec/spec_helper.rb +32 -0
  45. data/spec/specs/action_spec.rb +455 -0
  46. data/spec/specs/base_spec.rb +29 -0
  47. data/spec/specs/controller_spec.rb +31 -0
  48. data/spec/specs/param_set_spec.rb +31 -0
  49. data/templates/basic/_action_form.erb +77 -0
  50. data/templates/basic/_errors_table.erb +32 -0
  51. data/templates/basic/_structure_attributes_list.erb +55 -0
  52. data/templates/basic/action.erb +168 -0
  53. data/templates/basic/assets/lock.svg +3 -0
  54. data/templates/basic/assets/reset.css +101 -0
  55. data/templates/basic/assets/style.css +348 -0
  56. data/templates/basic/assets/tool.svg +4 -0
  57. data/templates/basic/assets/try.js +157 -0
  58. data/templates/basic/authenticator.erb +52 -0
  59. data/templates/basic/controller.erb +20 -0
  60. data/templates/basic/index.erb +114 -0
  61. data/templates/basic/layout.erb +46 -0
  62. data/templates/basic/structure.erb +23 -0
  63. data/test/test_helper.rb +81 -0
  64. data/test/tests/action_access_test.rb +63 -0
  65. data/test/tests/actions_test.rb +524 -0
  66. data/test/tests/authenticators_test.rb +87 -0
  67. data/test/tests/base_test.rb +35 -0
  68. data/test/tests/controllers_test.rb +49 -0
  69. data/test/tests/eval_environment_test.rb +136 -0
  70. data/test/tests/evel_helpers_test.rb +60 -0
  71. data/test/tests/examples_test.rb +11 -0
  72. data/test/tests/helpers_test.rb +97 -0
  73. data/test/tests/param_set_test.rb +44 -0
  74. data/test/tests/rack_middleware_test.rb +131 -0
  75. data/test/tests/request_test.rb +232 -0
  76. data/test/tests/structures_param_extensions_test.rb +159 -0
  77. data/test/tests/structures_test.rb +398 -0
  78. metadata +71 -56
@@ -0,0 +1,348 @@
1
+ html {
2
+ font-family: Lato, Arial, sans-serif;
3
+ font-size:16px;
4
+ }
5
+
6
+ a {
7
+ text-decoration: none;
8
+ }
9
+
10
+ .sidebarBackground {
11
+ background:#2f3e4d;
12
+ width:200px;
13
+ position:fixed;
14
+ top:0;
15
+ bottom:0;
16
+ z-index:1;
17
+ }
18
+
19
+ .sidebar {
20
+ width:200px;
21
+ float:left;
22
+ z-index:10;
23
+ position:absolute;
24
+ }
25
+ .sidebar a {
26
+ display:block;
27
+ padding:7px 15px;
28
+ border-bottom:1px solid #394959;
29
+ color:#889db1;
30
+ font-size:85%;
31
+ }
32
+
33
+ .sidebar a:hover {
34
+ background-color:#26323f;
35
+ }
36
+
37
+ .sidebar a.active {
38
+ background-color:#1d2630;
39
+ color:#fff;
40
+ }
41
+
42
+ .footer {
43
+ margin-left:200px;
44
+ border-top:3px solid #efefef;
45
+ padding:20px 40px 100px 40px;
46
+ font-size:90%;
47
+ color:#999;
48
+ }
49
+
50
+ .content {
51
+ margin-left:200px;
52
+ padding:40px;
53
+ padding-right:15%;
54
+ }
55
+
56
+
57
+ .content h1 {
58
+ font-size:32px;
59
+ font-weight:900;
60
+ margin-bottom:15px;
61
+ }
62
+
63
+ .content h2 {
64
+ font-size:24px;
65
+ font-weight:700;
66
+ color:#f65552;
67
+ margin-bottom:10px;
68
+ margin-top:30px;
69
+ }
70
+
71
+ .content h3 {
72
+ font-size:18px;
73
+ font-weight:700;
74
+ color:#888;
75
+ margin-bottom:10px;
76
+ margin-top:30px;
77
+ }
78
+
79
+ .content p.text {
80
+ line-height:1.5;
81
+ margin-bottom:15px;
82
+ }
83
+
84
+
85
+ .content .standardList {
86
+ margin-bottom:15px;
87
+ line-height:1.5;
88
+ }
89
+
90
+ .content .standardList li {
91
+ list-style:square;
92
+ margin-bottom:15px;
93
+ margin-left:35px;
94
+ }
95
+
96
+ .content code {
97
+ font-family:'Droid Sans Mono', fixed;
98
+ color:#f65552;
99
+ padding:1px 2px;
100
+ font-size:90%;
101
+ }
102
+
103
+ .content pre.code {
104
+ font-family:'Droid Sans Mono', fixed;
105
+ white-space: pre;
106
+ background:#efefef;
107
+ padding:10px 10px 10px;
108
+ font-size:90%;
109
+ margin-bottom:15px;
110
+ border-radius:7px 7px 7px 7px;
111
+ }
112
+
113
+ .content pre.code span.jsonKey {
114
+ color:#7f55ad;
115
+ font-weight:bold;
116
+ }
117
+
118
+ .content pre.code span.jsonString {
119
+ color:#9eb503;
120
+ }
121
+
122
+ .content pre.code span.jsonComment {
123
+ color:#bbb;
124
+ font-style:italic;
125
+ }
126
+
127
+ .content a.link {
128
+ color:#2d80c8;
129
+ font-weight:700;
130
+ border-bottom:1px solid #2d80c8;
131
+ }
132
+
133
+ .content p.apiURL span {
134
+ color:#aaa;
135
+ }
136
+
137
+ .table {
138
+ width:100%;
139
+ }
140
+
141
+ .table th, .table td {
142
+ border-bottom:1px solid #ccc;
143
+ text-align: left;
144
+ padding:7px 0;
145
+ }
146
+
147
+ .table th {
148
+ border-bottom:2px solid #666;
149
+ font-size:90%;
150
+ }
151
+
152
+ .paramTable__req {
153
+ background:#ea4c89;
154
+ color:#fff;
155
+ font-weight:bold;
156
+ font-size:10px;
157
+ text-transform: uppercase;
158
+ padding:1px 3px;
159
+ border-radius: 2px;
160
+ vertical-align: 1px;
161
+ margin-left:5px;
162
+ }
163
+
164
+ .paramTable__field {
165
+ width:100%;
166
+ border:0;
167
+ padding:0;
168
+ font:inherit;
169
+ color:#7f55ad;
170
+ }
171
+
172
+ .paramTable__description {
173
+ font-size:90%;
174
+ color:#999;
175
+ margin-top:3px;
176
+ }
177
+
178
+ .paramTable__name {
179
+ font-family:'Droid Sans Mono', fixed;
180
+ font-size:90%;
181
+ }
182
+
183
+ .paramTable__restrictedList {
184
+ font-size:80%;
185
+ margin-top:7px;
186
+ background:url(lock.svg) no-repeat 0 1px;
187
+ background-size:14px;
188
+ color:#ed4300;
189
+ padding-left:20px;
190
+ }
191
+
192
+ .paramTable__nil {
193
+ color:#ccc;
194
+ font-size:90%;
195
+ }
196
+
197
+ .apiURLFull {
198
+ font-size:110%;
199
+ font-weight:bold;
200
+ }
201
+
202
+ .apiURLFull span {
203
+ color:#aaaaaa;
204
+ font-weight:normal;
205
+ }
206
+
207
+ .errorAttributeList {
208
+ font-size:80%;
209
+ }
210
+
211
+ .errorAttributeList li {
212
+ margin-bottom:8px;
213
+ list-style:square;
214
+ margin-left:16px;
215
+ }
216
+ .errorAttributeList li:last-child {
217
+ margin-bottom:0;
218
+ }
219
+
220
+ .errorAttributeList__name {
221
+ font-family:'Droid Sans Mono', fixed;
222
+ }
223
+
224
+ .errorAttributeList__desc {
225
+ color:#999;
226
+ }
227
+
228
+ /* Try Action Form */
229
+
230
+ .tryForm {
231
+ display:none;
232
+ border:2px solid #ccc;
233
+ background:#f7f7f7;
234
+ border-radius:7px;
235
+ overflow: hidden;
236
+ }
237
+
238
+ .tryForm__header {
239
+ background:#ddd;
240
+ padding:15px 25px;
241
+ font-weight:700;
242
+ color:#999;
243
+
244
+ }
245
+
246
+ .tryForm__header input {
247
+ background:none;
248
+ border:0;
249
+ padding:0;
250
+ vertical-align:0px;
251
+ font:inherit;
252
+ font-weight:700;
253
+ color:#000;
254
+ width:auto;
255
+ }
256
+
257
+ .tryForm__output {
258
+ display:none;
259
+ background:#111;
260
+ color:#fff;
261
+ padding:15px;
262
+ font-family:'Droid Sans Mono', fixed;
263
+ white-space: pre;
264
+ word-wrap:normal;
265
+ font-size:90%;
266
+ overflow-x:auto;
267
+ }
268
+
269
+ .tryForm__output--success {
270
+ border-top:7px solid #68be28;
271
+ }
272
+
273
+ .tryForm__output--error {
274
+ border-top:7px solid #d4172e;
275
+ }
276
+
277
+ .tryForm__heading {
278
+ border-bottom:1px solid #ddd;
279
+ border-top:4px solid #aaa;
280
+ margin-bottom:15px;
281
+ padding:8px 25px;
282
+ background:#efefef;
283
+ font-weight:700;
284
+ font-size:80%;
285
+ text-transform: uppercase;
286
+ color:#888;
287
+ }
288
+
289
+ .tryForm__table {
290
+ font-size:90%;
291
+ width:calc(100% - 50px);
292
+ margin:0 25px;
293
+ margin-bottom:15px;
294
+ }
295
+
296
+ .tryForm__table td {
297
+ padding:5px 0;
298
+ background:#f8f8f8;
299
+ }
300
+
301
+ .tryForm__tableField {
302
+ border:0;
303
+ background:none;
304
+ border-bottom:1px solid #ccc;
305
+ padding:0;
306
+ padding-bottom:4px;
307
+ width:100%;
308
+ font:inherit;
309
+ color:#7f55ad;
310
+ font-weight:700;
311
+ }
312
+
313
+ .tryForm__tableField:focus {
314
+ border-color:#7f55ad;
315
+ }
316
+
317
+ .tryForm__button {
318
+ margin:15px 25px;
319
+ text-align: right;
320
+ }
321
+
322
+ .tryFormActivate {
323
+ margin:20px 0;
324
+ }
325
+ .tryFormActivate__button {
326
+ background:url(tool.svg) #fffce0 no-repeat 12px 7px;
327
+ background-size:12px;
328
+ border:1px solid #dad7bb;
329
+ color:#6c6a55;
330
+ border-radius:4px;
331
+ padding:6px 12px 6px 30px;
332
+ font-size:12px;
333
+ font-weight:700;
334
+ }
335
+
336
+ .tryForm__checkbox {
337
+ margin-bottom:4px;
338
+ }
339
+
340
+ .tryForm__checkbox:last-child {
341
+ margin-bottom:0;
342
+ }
343
+
344
+ .tryForm__checkbox input {
345
+ float:left;
346
+ margin-right:4px;
347
+ margin-top:4px;
348
+ }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
2
+ <path fill="#6c6a55" d="M5.5 0c-1.38 0-2.5 1.12-2.5 2.5 0 .32.08.62.19.91l-2.91 2.88c-.39.39-.39 1.05 0 1.44.2.2.46.28.72.28.26 0 .52-.09.72-.28l2.88-2.91c.28.11.58.19.91.19 1.38 0 2.5-1.12 2.5-2.5 0-.16 0-.32-.03-.47l-.97.97h-2v-2l.97-.97c-.15-.03-.31-.03-.47-.03zm-4.5 6.5c.28 0 .5.22.5.5s-.22.5-.5.5-.5-.22-.5-.5.22-.5.5-.5z"
3
+ />
4
+ </svg>
@@ -0,0 +1,157 @@
1
+ $(document).ready(function() {
2
+
3
+ $form = $('form.tryForm')
4
+
5
+ // Get all fields which will be added as headers
6
+ var headerFields = $('input.headerField', $form)
7
+
8
+ // Add stored values to header fields
9
+ if(typeof(Storage) !== "undefined") {
10
+ headerFields.each(function() {
11
+ $field = $(this)
12
+ $field.val(localStorage.getItem("header__" + $(this).attr('name')))
13
+ })
14
+
15
+ }
16
+
17
+
18
+ //
19
+ // Form submission
20
+ //
21
+ $form.on('submit', function() {
22
+
23
+ // Gets values used to make up the URL which should be
24
+ // requested for this request.
25
+ var host = $("input[name=host]", $(this)).val()
26
+ var version = $("input[name=version]", $(this)).val()
27
+ var controller = $("input[name=controller]", $(this)).val()
28
+ var action = $("input[name=action]", $(this)).val()
29
+ var url = host + "/api/" + version + "/" + controller + "/" + action
30
+ // Get the output box ready for use
31
+ var outputBox = $('.tryForm__output', $(this))
32
+ // Create a hash fo all parameters which will be submitted
33
+ var parameters = {}
34
+ $('input.paramField').each(function(){
35
+ $this = $(this)
36
+ value = $this.val()
37
+ type = $this.data('type')
38
+ name = $this.attr('name')
39
+ if(value.length) {
40
+ if(type == 'Integer') {
41
+ parameters[name] = parseInt(value);
42
+ } else if (type == "Hash" || type == "Array") {
43
+ parameters[name] = JSON.parse(value);
44
+ } else {
45
+ parameters[name] = value;
46
+ }
47
+ }
48
+ });
49
+
50
+ // Include/exclude full attributes as needed
51
+ var fullAttrsCheckbox = $('#full_attrs')
52
+ if(fullAttrsCheckbox.length) {
53
+ parameters['_full'] = !!fullAttrsCheckbox.prop('checked')
54
+ }
55
+
56
+ // Include/exclude expansions
57
+ var expansionCheckboxes = $('.tryForm__expansions')
58
+ if(expansionCheckboxes.length) {
59
+ parameters['_expansions'] = []
60
+ expansionCheckboxes.each(function() {
61
+ $this = $(this)
62
+ name = $(this).attr('name')
63
+ if($(this).prop('checked')) {
64
+ parameters['_expansions'].push(name)
65
+ }
66
+ })
67
+ }
68
+
69
+ // Make the AJAX request
70
+ $.ajax({
71
+ url: url,
72
+ method: 'POST',
73
+ contentType: 'application/json',
74
+ data: JSON.stringify(parameters),
75
+ beforeSend: function(xhr) {
76
+ // Add any headers which have been added
77
+ headerFields.each(function() {
78
+ $field = $(this)
79
+ name = $field.attr('name')
80
+ value = $field.val()
81
+ if(typeof(Storage) !== "undefined") {
82
+ localStorage.setItem("header__" + name, value)
83
+ }
84
+ if(value.length) {
85
+ xhr.setRequestHeader(name, $field.val())
86
+ }
87
+ })
88
+ },
89
+ success: function(data) {
90
+ // Success means that we got a 200 OK which means we can be pretty
91
+ // sure that we've got a moonrope response.
92
+ if(data.status == "success") {
93
+ outputBox.addClass('tryForm__output--success').removeClass('tryForm__output--error')
94
+ } else {
95
+ outputBox.addClass('tryForm__output--error').removeClass('tryForm__output--success')
96
+ }
97
+ outputBox.text(JSON.stringify(data, null, 4))
98
+ outputBox.show()
99
+ },
100
+ error: function(xhr) {
101
+ // Errors which occurr aren't very well reported at the moment.
102
+ // They should be.
103
+ if(xhr.getResponseHeader('content-type') == 'application/json') {
104
+ var text = JSON.stringify(JSON.parse(xhr.responseText), null, 4)
105
+ } else {
106
+ var text = "Failed to make request."
107
+ }
108
+
109
+ outputBox.show()
110
+ outputBox.text(text)
111
+ outputBox.addClass('tryForm__output--error').removeClass('tryForm__output--success')
112
+ }
113
+ })
114
+ return false
115
+ });
116
+
117
+ //
118
+ // Open the try form
119
+ //
120
+ $('p.tryFormActivate a').on('click', function() {
121
+ $form = $('form.tryForm')
122
+ $parent = $(this).parents('p')
123
+ $form.show('fast')
124
+ $parent.hide()
125
+ return false
126
+ });
127
+
128
+ //
129
+ // Close the try form
130
+ //
131
+ $('button.tryFormCancel').on('click', function() {
132
+ $form = $('form.tryForm')
133
+ $parent = $('p.tryFormActivate')
134
+ $form.hide()
135
+ $parent.show()
136
+ return false
137
+ });
138
+
139
+ // http://stackoverflow.com/questions/8100770/auto-scaling-inputtype-text-to-width-of-value
140
+ // http://jsfiddle.net/MqM76/217/
141
+ $.fn.textWidth = function(text, font) {
142
+ if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('<span>').hide().appendTo(document.body);
143
+ $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font'));
144
+ return $.fn.textWidth.fakeEl.width();
145
+ };
146
+
147
+ // Automatically ensure that the size for the header inputs is
148
+ // correct
149
+ function resizeInput() {
150
+ $this = $(this)
151
+ $this.css('width', $this.textWidth() + "px")
152
+ $this.attr('size', $this.val().length)
153
+ }
154
+
155
+ $('form.tryForm .tryForm__header input').on('input', resizeInput).trigger('input')
156
+
157
+ });