cathode 0.0.1 → 0.1.0

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