cathode 0.0.1 → 0.1.0

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +346 -125
  3. data/Rakefile +1 -0
  4. data/app/controllers/cathode/base_controller.rb +28 -45
  5. data/app/models/cathode/token.rb +21 -0
  6. data/config/routes.rb +16 -0
  7. data/db/migrate/20140425164100_create_cathode_tokens.rb +11 -0
  8. data/lib/cathode.rb +0 -13
  9. data/lib/cathode/_version.rb +2 -1
  10. data/lib/cathode/action.rb +197 -39
  11. data/lib/cathode/action_dsl.rb +60 -0
  12. data/lib/cathode/base.rb +81 -12
  13. data/lib/cathode/create_request.rb +21 -0
  14. data/lib/cathode/custom_request.rb +5 -0
  15. data/lib/cathode/debug.rb +25 -0
  16. data/lib/cathode/destroy_request.rb +13 -0
  17. data/lib/cathode/engine.rb +9 -0
  18. data/lib/cathode/exceptions.rb +20 -0
  19. data/lib/cathode/index_request.rb +40 -0
  20. data/lib/cathode/object_collection.rb +49 -0
  21. data/lib/cathode/query.rb +24 -0
  22. data/lib/cathode/railtie.rb +21 -0
  23. data/lib/cathode/request.rb +139 -7
  24. data/lib/cathode/resource.rb +50 -19
  25. data/lib/cathode/resource_dsl.rb +46 -0
  26. data/lib/cathode/show_request.rb +13 -0
  27. data/lib/cathode/update_request.rb +26 -0
  28. data/lib/cathode/version.rb +112 -23
  29. data/lib/tasks/cathode_tasks.rake +5 -4
  30. data/spec/dummy/app/api/api.rb +0 -0
  31. data/spec/dummy/app/models/payment.rb +3 -0
  32. data/spec/dummy/app/models/product.rb +1 -0
  33. data/spec/dummy/app/models/sale.rb +5 -0
  34. data/spec/dummy/app/models/salesperson.rb +3 -0
  35. data/spec/dummy/db/development.sqlite3 +0 -0
  36. data/spec/dummy/db/migrate/20140409183635_create_sales.rb +11 -0
  37. data/spec/dummy/db/migrate/20140423172419_create_salespeople.rb +11 -0
  38. data/spec/dummy/db/migrate/20140424181343_create_payments.rb +10 -0
  39. data/spec/dummy/db/schema.rb +31 -1
  40. data/spec/dummy/db/test.sqlite3 +0 -0
  41. data/spec/dummy/log/development.log +1167 -0
  42. data/spec/dummy/log/test.log +180602 -0
  43. data/spec/dummy/spec/factories/payments.rb +8 -0
  44. data/spec/dummy/spec/factories/products.rb +1 -1
  45. data/spec/dummy/spec/factories/sales.rb +9 -0
  46. data/spec/dummy/spec/factories/salespeople.rb +7 -0
  47. data/spec/dummy/spec/requests/requests_spec.rb +434 -0
  48. data/spec/lib/cathode/action_spec.rb +136 -0
  49. data/spec/lib/cathode/base_spec.rb +34 -0
  50. data/spec/lib/cathode/create_request_spec.rb +40 -0
  51. data/spec/lib/cathode/custom_request_spec.rb +31 -0
  52. data/spec/lib/cathode/debug_spec.rb +25 -0
  53. data/spec/lib/cathode/destroy_request_spec.rb +28 -0
  54. data/spec/lib/cathode/index_request_spec.rb +62 -0
  55. data/spec/lib/cathode/object_collection_spec.rb +66 -0
  56. data/spec/lib/cathode/query_spec.rb +28 -0
  57. data/spec/lib/cathode/request_spec.rb +58 -0
  58. data/spec/lib/cathode/resource_spec.rb +482 -0
  59. data/spec/lib/cathode/show_request_spec.rb +23 -0
  60. data/spec/lib/cathode/update_request_spec.rb +41 -0
  61. data/spec/lib/cathode/version_spec.rb +416 -0
  62. data/spec/models/cathode/token_spec.rb +62 -0
  63. data/spec/spec_helper.rb +8 -2
  64. data/spec/support/factories/payments.rb +3 -0
  65. data/spec/support/factories/sale.rb +3 -0
  66. data/spec/support/factories/salespeople.rb +3 -0
  67. data/spec/support/factories/token.rb +3 -0
  68. data/spec/support/helpers.rb +13 -2
  69. metadata +192 -47
  70. data/app/helpers/cathode/application_helper.rb +0 -4
  71. data/spec/dummy/app/api/dummy_api.rb +0 -4
  72. data/spec/integration/api_spec.rb +0 -88
  73. data/spec/lib/action_spec.rb +0 -140
  74. data/spec/lib/base_spec.rb +0 -28
  75. data/spec/lib/request_spec.rb +0 -5
  76. data/spec/lib/resources_spec.rb +0 -78
  77. data/spec/lib/versioning_spec.rb +0 -104
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cathode::Request do
4
+ describe '.create' do
5
+ subject do
6
+ Cathode::Request.create(context_stub(
7
+ headers: headers,
8
+ params: ActionController::Parameters.new(params.merge(controller: 'products', action: action)),
9
+ path: try(:path)
10
+ ))
11
+ end
12
+
13
+ before do
14
+ use_api { resources :products, actions: [:index] }
15
+ end
16
+
17
+ context 'without a version header' do
18
+ let(:action) { 'index' }
19
+ let(:headers) { {} }
20
+ let(:params) { {} }
21
+
22
+ it 'sets status as bad request' do
23
+ expect(subject._status).to eq(:bad_request)
24
+ end
25
+
26
+ it 'sets body text' do
27
+ expect(subject._body).to eq('A version number must be passed in the Accept-Version header')
28
+ end
29
+ end
30
+
31
+ context 'with an invalid version' do
32
+ let(:action) { 'index' }
33
+ let(:headers) { { 'HTTP_ACCEPT_VERSION' => '2.0.0' } }
34
+ let(:params) { {} }
35
+
36
+ it 'sets status as bad request' do
37
+ expect(subject._status).to eq(:bad_request)
38
+ end
39
+
40
+ it 'sets body text' do
41
+ expect(subject._body).to eq('Unknown API version: 2.0.0')
42
+ end
43
+ end
44
+
45
+ context 'with a valid version' do
46
+ let(:headers) { { 'HTTP_ACCEPT_VERSION' => '1.0.0' } }
47
+
48
+ context 'with an invalid action' do
49
+ let(:action) { 'invalid' }
50
+ let(:params) { {} }
51
+
52
+ it 'sets status as not found' do
53
+ expect(subject._status).to eq(:not_found)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,482 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cathode::Resource do
4
+ describe '.new' do
5
+ context 'with a nonexistent resource' do
6
+ subject { Cathode::Resource.new(:boxes, :all) }
7
+
8
+ it 'raises an error' do
9
+ expect { subject }.to raise_error(
10
+ Cathode::UnknownResourceError,
11
+ "Could not find constant `Box' for resource `boxes'"
12
+ )
13
+ end
14
+ end
15
+
16
+ context 'with all methods' do
17
+ subject { Cathode::Resource.new(:products, actions: [:index]) }
18
+
19
+ it 'creates a controller' do
20
+ subject
21
+ expect(Cathode::ProductsController).to_not be_nil
22
+ end
23
+ end
24
+
25
+ context 'with subset of methods' do
26
+ subject { Cathode::Resource.new(:products, actions: [:index, :show]) }
27
+
28
+ it 'creates a controller' do
29
+ subject
30
+ expect(Cathode::ProductsController).to_not be_nil
31
+ end
32
+ end
33
+
34
+ context 'with a singular resource' do
35
+ [:index, :create, :update, :show].each do |action|
36
+ context action do
37
+ subject do
38
+ resource = proc do |action, override|
39
+ Cathode::Resource.new(:product, singular: true) do
40
+ if override
41
+ override_action(action) { head :ok }
42
+ else
43
+ action(action)
44
+ end
45
+
46
+ if [:create, :update].include? action
47
+ attributes {}
48
+ end
49
+ end
50
+ end
51
+ resource.call(action, override)
52
+ end
53
+
54
+ context 'with custom behavior' do
55
+ let(:override) { true }
56
+
57
+ it 'creates the action' do
58
+ expect(subject.actions.names).to eq([action])
59
+ end
60
+ end
61
+
62
+ describe 'with default behavior' do
63
+ let(:override) { false }
64
+
65
+ it 'raises an error' do
66
+ expect { subject }.to raise_error(
67
+ Cathode::ActionBehaviorMissingError,
68
+ "Can't use default :#{action} action on singular resource `product'"
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'custom' do
76
+ subject do
77
+ Cathode::Resource.new(:product, singular: true) do
78
+ get :custom do
79
+ head :ok
80
+ end
81
+ end
82
+ end
83
+
84
+ it 'creates the action' do
85
+ expect(subject.actions.names).to eq([:custom])
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'with an attributes block' do
91
+ subject do
92
+ Cathode::Resource.new(:products, actions: actions) do
93
+ attributes do |params|
94
+ params.require(:product).permit(:title)
95
+ end
96
+ end
97
+ end
98
+
99
+ context 'when create is specified' do
100
+ let(:actions) { [:create] }
101
+
102
+ it 'sets the strong params of the action' do
103
+ expect(subject.actions.find(:create).strong_params).to_not be_nil
104
+ end
105
+ end
106
+
107
+ context 'when update is specified' do
108
+ let(:actions) { [:update] }
109
+
110
+ it 'sets the strong params of the action' do
111
+ expect(subject.actions.find(:update).strong_params).to_not be_nil
112
+ end
113
+ end
114
+
115
+ context 'when create and update are specified' do
116
+ let(:actions) { :all }
117
+
118
+ it 'sets the strong params of both actions' do
119
+ expect(subject.actions.find(:create).strong_params).to_not be_nil
120
+ expect(subject.actions.find(:update).strong_params).to_not be_nil
121
+ end
122
+ end
123
+
124
+ context 'when neither create nor update is specified' do
125
+ let(:actions) { [:index] }
126
+
127
+ it 'raises an error' do
128
+ expect { subject }.to raise_error(Cathode::UnknownActionError, 'An attributes block was specified without a :create or :update action')
129
+ end
130
+ end
131
+
132
+ context 'when attributes block is specified after create/update' do
133
+ subject do
134
+ Cathode::Resource.new(:products) do
135
+ attributes do |params|
136
+ params.require(:product).permit(:title)
137
+ end
138
+ action :create
139
+ action :update
140
+ end
141
+ end
142
+
143
+ it 'sets the strong params of both actions' do
144
+ expect(subject.actions.find(:create).strong_params).to_not be_nil
145
+ expect(subject.actions.find(:update).strong_params).to_not be_nil
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'with a nested resource' do
151
+ subject do
152
+ block = proc do |subresource, plural, default, action|
153
+ method = plural ? :resources : :resource
154
+
155
+ proc do
156
+ send method, subresource do
157
+ if default
158
+ action action
159
+ else
160
+ override_action action do
161
+ body 'hello'
162
+ end
163
+ end
164
+ if [:create, :update].include? action
165
+ attributes {}
166
+ end
167
+ end
168
+ end
169
+ end
170
+ Cathode::Resource.new(resource, &block.call(subresource, plural, default, action))
171
+ end
172
+ let(:resource) { :products }
173
+
174
+ context 'with a has_many association' do
175
+ let(:plural) { true }
176
+ let(:subresource) { :sales }
177
+
178
+ context ':index' do
179
+ let(:action) { :index }
180
+
181
+ context 'with default behavior' do
182
+ let(:default) { true }
183
+
184
+ it 'adds the resource' do
185
+ expect(subject._resources.names).to match_array([:sales])
186
+ expect(subject._resources.find(:sales).actions.names).to match_array([:index])
187
+ end
188
+ end
189
+
190
+ context 'with custom behavior' do
191
+ let(:default) { false }
192
+
193
+ it 'adds the resource' do
194
+ expect(subject._resources.names).to match_array([:sales])
195
+ expect(subject._resources.find(:sales).actions.names).to match_array([:index])
196
+ end
197
+ end
198
+ end
199
+
200
+ context ':show' do
201
+ let(:action) { :show }
202
+
203
+ context 'with default behavior' do
204
+ let(:default) { true }
205
+
206
+ it 'raises an error' do
207
+ expect { subject }.to raise_error(
208
+ Cathode::MissingAssociationError,
209
+ "Can't use default :show action on `products' without a has_one `sale' association"
210
+ )
211
+ end
212
+ end
213
+
214
+ context 'with custom behavior' do
215
+ let(:default) { false }
216
+
217
+ it 'adds the resource' do
218
+ expect(subject._resources.names).to match_array([:sales])
219
+ expect(subject._resources.find(:sales).actions.names).to match_array([:show])
220
+ end
221
+ end
222
+ end
223
+
224
+ context ':create' do
225
+ let(:action) { :create }
226
+
227
+ context 'with default behavior' do
228
+ let(:default) { true }
229
+
230
+ it 'adds the resource' do
231
+ expect(subject._resources.names).to match_array([:sales])
232
+ expect(subject._resources.find(:sales).actions.names).to match_array([:create])
233
+ end
234
+ end
235
+
236
+ context 'with custom behavior' do
237
+ let(:default) { false }
238
+
239
+ it 'adds the resource' do
240
+ expect(subject._resources.names).to match_array([:sales])
241
+ expect(subject._resources.find(:sales).actions.names).to match_array([:create])
242
+ end
243
+ end
244
+ end
245
+
246
+ context ':update' do
247
+ let(:action) { :update }
248
+
249
+ context 'with default behavior' do
250
+ let(:default) { true }
251
+
252
+ it 'raises an error' do
253
+ expect { subject }.to raise_error(
254
+ Cathode::MissingAssociationError,
255
+ "Can't use default :update action on `products' without a has_one `sale' association"
256
+ )
257
+ end
258
+ end
259
+
260
+ context 'with custom behavior' do
261
+ let(:default) { false }
262
+
263
+ it 'adds the resource' do
264
+ expect(subject._resources.names).to match_array([:sales])
265
+ expect(subject._resources.find(:sales).actions.names).to match_array([:update])
266
+ end
267
+ end
268
+ end
269
+
270
+ context ':destroy' do
271
+ let(:action) { :destroy }
272
+
273
+ context 'with default behavior' do
274
+ let(:default) { true }
275
+
276
+ it 'raises an error' do
277
+ expect { subject }.to raise_error(
278
+ Cathode::MissingAssociationError,
279
+ "Can't use default :destroy action on `products' without a has_one `sale' association"
280
+ )
281
+ end
282
+ end
283
+
284
+ context 'with custom behavior' do
285
+ let(:default) { false }
286
+
287
+ it 'adds the resource' do
288
+ expect(subject._resources.names).to match_array([:sales])
289
+ expect(subject._resources.find(:sales).actions.names).to match_array([:destroy])
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ context 'with a has_one association' do
296
+ let(:resource) { :sales }
297
+ let(:subresource) { :payment }
298
+ let(:plural) { false }
299
+
300
+ context ':index' do
301
+ let(:action) { :index }
302
+
303
+ context 'with default behavior' do
304
+ let(:default) { true }
305
+
306
+ it 'raises an error' do
307
+ expect { subject }.to raise_error(
308
+ Cathode::MissingAssociationError,
309
+ "Can't use default :index action on `sales' without a has_many or has_and_belongs_to_many `payment' association"
310
+ )
311
+ end
312
+ end
313
+
314
+ context 'with custom behavior' do
315
+ let(:default) { false }
316
+
317
+ it 'adds the resource' do
318
+ expect(subject._resources.names).to match_array([:payment])
319
+ expect(subject._resources.find(:payment).actions.names).to match_array([:index])
320
+ end
321
+ end
322
+ end
323
+
324
+ context ':show' do
325
+ let(:action) { :show }
326
+
327
+ context 'with default behavior' do
328
+ let(:default) { true }
329
+
330
+ it 'adds the resource' do
331
+ expect(subject._resources.names).to match_array([:payment])
332
+ expect(subject._resources.find(:payment).actions.names).to match_array([:show])
333
+ end
334
+ end
335
+
336
+ context 'with custom behavior' do
337
+ let(:default) { false }
338
+
339
+ it 'adds the resource' do
340
+ expect(subject._resources.names).to match_array([:payment])
341
+ expect(subject._resources.find(:payment).actions.names).to match_array([:show])
342
+ end
343
+ end
344
+ end
345
+
346
+ context ':create' do
347
+ let(:action) { :create }
348
+
349
+ context 'with default behavior' do
350
+ let(:default) { true }
351
+
352
+ it 'adds the resource' do
353
+ expect(subject._resources.names).to match_array([:payment])
354
+ expect(subject._resources.find(:payment).actions.names).to match_array([:create])
355
+ end
356
+ end
357
+
358
+ context 'with custom behavior' do
359
+ let(:default) { false }
360
+
361
+ it 'adds the resource' do
362
+ expect(subject._resources.names).to match_array([:payment])
363
+ expect(subject._resources.find(:payment).actions.names).to match_array([:create])
364
+ end
365
+ end
366
+ end
367
+
368
+ context ':update' do
369
+ let(:action) { :update }
370
+
371
+ context 'with default behavior' do
372
+ let(:default) { true }
373
+
374
+ it 'adds the resource' do
375
+ expect(subject._resources.names).to match_array([:payment])
376
+ expect(subject._resources.find(:payment).actions.names).to match_array([:update])
377
+ end
378
+ end
379
+
380
+ context 'with custom behavior' do
381
+ let(:default) { false }
382
+
383
+ it 'adds the resource' do
384
+ expect(subject._resources.names).to match_array([:payment])
385
+ expect(subject._resources.find(:payment).actions.names).to match_array([:update])
386
+ end
387
+ end
388
+ end
389
+
390
+ context ':destroy' do
391
+ let(:action) { :destroy }
392
+
393
+ context 'with default behavior' do
394
+ let(:default) { true }
395
+
396
+ it 'adds the resource' do
397
+ expect(subject._resources.names).to match_array([:payment])
398
+ expect(subject._resources.find(:payment).actions.names).to match_array([:destroy])
399
+ end
400
+ end
401
+
402
+ context 'with custom behavior' do
403
+ let(:default) { false }
404
+
405
+ it 'adds the resource' do
406
+ expect(subject._resources.names).to match_array([:payment])
407
+ expect(subject._resources.find(:payment).actions.names).to match_array([:destroy])
408
+ end
409
+ end
410
+ end
411
+ end
412
+
413
+ context 'with a belongs_to association' do
414
+ let(:resource) { :payments }
415
+ let(:subresource) { :sale }
416
+ let(:plural) { false }
417
+
418
+ context ':show' do
419
+ let(:action) { :show }
420
+
421
+ context 'with default behavior' do
422
+ let(:default) { true }
423
+
424
+ it 'adds the resource' do
425
+ expect(subject._resources.names).to match_array([:sale])
426
+ expect(subject._resources.find(:sale).actions.names).to match_array([:show])
427
+ end
428
+ end
429
+
430
+ context 'with custom behavior' do
431
+ let(:default) { false }
432
+
433
+ it 'adds the resource' do
434
+ expect(subject._resources.names).to match_array([:sale])
435
+ expect(subject._resources.find(:sale).actions.names).to match_array([:show])
436
+ end
437
+ end
438
+ end
439
+ end
440
+
441
+ context 'with a has_many:through association' do pending end
442
+
443
+ context 'with a has_and_belongs_to_many association' do pending end
444
+
445
+ context 'with no association' do pending end
446
+ end
447
+
448
+ context 'with custom action' do
449
+ subject { Cathode::Resource.new(:products, nil) { get :custom } }
450
+
451
+ it 'sets up the action' do
452
+ expect(subject.actions.find(:custom).http_method).to eq(:get)
453
+ end
454
+ end
455
+ end
456
+
457
+ describe 'default and custom actions' do
458
+ let(:resource) do
459
+ Cathode::Resource.new(:products, actions: :all) do
460
+ get :custom
461
+ post :custom2
462
+ attributes {}
463
+ end
464
+ end
465
+
466
+ describe '#default_actions' do
467
+ subject { resource.default_actions }
468
+
469
+ it 'returns the default actions' do
470
+ expect(subject.map(&:name)).to match_array([:index, :show, :create, :update, :destroy])
471
+ end
472
+ end
473
+
474
+ describe '#custom_actions' do
475
+ subject { resource.custom_actions }
476
+
477
+ it 'returns the custom actions' do
478
+ expect(subject.map(&:name)).to match_array([:custom, :custom2])
479
+ end
480
+ end
481
+ end
482
+ end