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,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cathode::ShowRequest do
4
+ subject do
5
+ Cathode::ShowRequest.new(context_stub(params: ActionController::Parameters.new(params.merge(controller: 'products', action: 'show'))))
6
+ end
7
+ before do
8
+ use_api do
9
+ resources :products, actions: [:show]
10
+ end
11
+ end
12
+
13
+ let(:params) { { id: 3 } }
14
+ let!(:products) { create_list(:product, 3) }
15
+
16
+ it 'sets status as ok' do
17
+ expect(subject._status).to eq(:ok)
18
+ end
19
+
20
+ it 'sets body as the resource' do
21
+ expect(subject._body).to eq(products[2])
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cathode::UpdateRequest do
4
+ subject do
5
+ Cathode::UpdateRequest.new(context_stub(params: ActionController::Parameters.new(params.merge(controller: 'products', action: 'update'))))
6
+ end
7
+ before do
8
+ use_api do
9
+ resources :products, actions: [:update] do
10
+ attributes do
11
+ params.require(:product).permit(:title)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ let!(:product) { create(:product, title: 'cool product') }
17
+
18
+ context 'with valid attributes' do
19
+ let(:params) { { id: product.id, product: { title: 'cooler product' } } }
20
+
21
+ it 'sets status as ok' do
22
+ expect(subject._status).to eq(:ok)
23
+ end
24
+
25
+ it 'sets body as the updated record' do
26
+ expect(subject._body.title).to eq('cooler product')
27
+ end
28
+ end
29
+
30
+ context 'with invalid attributes' do
31
+ let(:params) { { id: product.id, title: 'cooler product' } }
32
+
33
+ it 'sets status as bad request' do
34
+ expect(subject._status).to eq(:bad_request)
35
+ end
36
+
37
+ it 'sets body as error message' do
38
+ expect(subject._body).to eq('param is missing or the value is empty: product')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,416 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cathode::Version do
4
+ describe '.standardize' do
5
+ subject { Cathode::Version.standardize(version) }
6
+
7
+ context 'with integer' do
8
+ let(:version) { 1 }
9
+
10
+ it 'sets the semantic version' do
11
+ expect(subject).to eq('1.0.0')
12
+ end
13
+ end
14
+
15
+ context 'with float' do
16
+ let(:version) { 1.5 }
17
+
18
+ it 'sets the semantic version' do
19
+ expect(subject).to eq('1.5.0')
20
+ end
21
+ end
22
+
23
+ context 'with string' do
24
+ let(:version) { '1.5.1' }
25
+
26
+ it 'sets the semantic version' do
27
+ expect(subject).to eq('1.5.1')
28
+ end
29
+ end
30
+
31
+ context 'with prerelease' do
32
+ let(:version) { '1.6.0-pre' }
33
+
34
+ it 'sets the semantic version' do
35
+ expect(subject).to eq('1.6.0-pre')
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '.define' do
41
+ subject { Cathode::Version.define('1.0.0') { resources :sales } }
42
+
43
+ context 'when version already exists' do
44
+ before do
45
+ use_api do
46
+ resources :products, actions: [:index]
47
+ end
48
+ end
49
+
50
+ it 'adds to the version' do
51
+ expect(subject._resources.names).to match_array([:products, :sales])
52
+ end
53
+ end
54
+
55
+ context 'when version does not exist' do
56
+ it 'creates a new version' do
57
+ expect(subject._resources.names).to match_array([:sales])
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '.new' do
63
+ subject { Cathode::Version.new(version, &block) }
64
+ let(:version) { 1 }
65
+ before(:each) do
66
+ Cathode::Version.all.clear
67
+ end
68
+
69
+ context 'with bad version number' do
70
+ let(:version) { 'a' }
71
+ let(:block) { nil }
72
+
73
+ it 'raises an error' do
74
+ expect { subject }.to raise_error(ArgumentError)
75
+ end
76
+ end
77
+
78
+ context 'with no params or block' do
79
+ let(:block) do
80
+ proc do
81
+ resources :products
82
+ end
83
+ end
84
+
85
+ it 'creates the resource' do
86
+ expect(Cathode::Resource).to receive(:new) do |resource, params, &block|
87
+ expect(resource).to eq(:products)
88
+ expect(params).to eq(singular: false)
89
+ expect(block).to be_nil
90
+ end
91
+ subject
92
+ end
93
+ end
94
+
95
+ context 'with params' do
96
+ let(:block) do
97
+ proc do
98
+ resources :sales, actions: [:index, :create]
99
+ end
100
+ end
101
+
102
+ it 'creates the resource' do
103
+ expect(Cathode::Resource).to receive(:new) do |resource, params, &block|
104
+ expect(resource).to eq(:sales)
105
+ expect(params).to eq(actions: [:index, :create], singular: false)
106
+ expect(block).to be_nil
107
+ end
108
+ subject
109
+ end
110
+ end
111
+
112
+ context 'with params and block' do
113
+ let(:block) do
114
+ proc do
115
+ get :custom
116
+ resources :sales, actions: [:index] do
117
+ action :show
118
+ end
119
+ end
120
+ end
121
+
122
+ it 'creates the resource' do
123
+ expect(subject._resources.names).to match_array([:sales])
124
+ end
125
+
126
+ it 'creates the action' do
127
+ expect(subject.actions.names).to match_array([:custom])
128
+ end
129
+ end
130
+
131
+ context 'with duplicate resource' do
132
+ let(:block) do
133
+ proc do
134
+ resources :products, actions: [:index]
135
+ resources :products, actions: [:show]
136
+ end
137
+ end
138
+
139
+ it 'combines the actions' do
140
+ expect(subject._resources.names).to match_array([:products])
141
+ expect(subject._resources.find(:products).actions.names).to match_array([:index, :show])
142
+ end
143
+ end
144
+
145
+ context 'with inherited version' do
146
+ before do
147
+ Cathode::Version.new 1 do
148
+ resources :sales, actions: [:index, :show]
149
+ get :status
150
+ end
151
+ end
152
+ let(:version) { 1.5 }
153
+
154
+ context 'with a new action' do
155
+ let(:block) do
156
+ proc do
157
+ resources :sales do
158
+ action :destroy
159
+ end
160
+ end
161
+ end
162
+
163
+ it 'inherits the actions from the previous version' do
164
+ expect(subject._resources.names).to match_array([:sales])
165
+ expect(subject._resources.find(:sales).actions.names).to match_array([:index, :show, :destroy])
166
+ expect(subject.actions.names).to eq([:status])
167
+ end
168
+
169
+ it 'leaves the previous version intact' do
170
+ subject
171
+ previous_version = Cathode::Version.find('1.0.0')
172
+ expect(previous_version._resources.find(:sales).actions.names).to match_array([:index, :show])
173
+ end
174
+ end
175
+
176
+ context 'with a new resource' do
177
+ let(:block) { proc { resources :products, actions: [:index] } }
178
+
179
+ it 'inherits the resources from the previous version' do
180
+ expect(subject._resources.names).to match_array([:products, :sales])
181
+ expect(subject._resources.find(:sales).actions.names).to match_array([:index, :show])
182
+ expect(subject._resources.find(:products).actions.names).to match_array([:index])
183
+ end
184
+ end
185
+
186
+ context 'with a removed resource' do
187
+ context 'with an unkown resource' do
188
+ let(:block) { proc { remove_resources :factories } }
189
+
190
+ it 'raises an error' do
191
+ expect { subject }.to raise_error(Cathode::UnknownResourceError)
192
+ end
193
+ end
194
+
195
+ context 'with a single resource' do
196
+ let(:block) do
197
+ proc do
198
+ resources :products, actions: [:index]
199
+ remove_resources :sales
200
+ end
201
+ end
202
+
203
+ it 'does not use the resource' do
204
+ expect(subject._resources.names).to match_array([:products])
205
+ end
206
+ end
207
+
208
+ context 'with an array of resources' do
209
+ before do
210
+ Cathode::Version.new 1.2 do
211
+ resources :products, actions: [:index]
212
+ end
213
+ end
214
+
215
+ let(:block) { proc { remove_resources [:sales, :products] } }
216
+
217
+ it 'does not use the resource' do
218
+ expect(subject._resources.names).to be_empty
219
+ end
220
+ end
221
+ end
222
+
223
+ context 'with a removed action' do
224
+ context 'within a resource' do
225
+ context 'with an unknown resource' do
226
+ let(:block) { proc { remove_action :destroy, from: :unknown } }
227
+
228
+ it 'raises an error' do
229
+ expect { subject }.to raise_error(
230
+ Cathode::UnknownResourceError,
231
+ "Unknown resource `unknown' on ancestor version 1.0.0"
232
+ )
233
+ end
234
+ end
235
+
236
+ context 'with an unkown action' do
237
+ let(:block) { proc { remove_action :destroy, from: :sales } }
238
+
239
+ it 'raises an error' do
240
+ expect { subject }.to raise_error(
241
+ Cathode::UnknownActionError,
242
+ "Unknown action `destroy' on resource `sales'"
243
+ )
244
+ end
245
+ end
246
+
247
+ context 'with a single action' do
248
+ let(:block) do
249
+ proc do
250
+ resources :products, actions: [:index]
251
+ remove_action :index, from: :sales
252
+ end
253
+ end
254
+
255
+ it 'does not use the action' do
256
+ subject
257
+ expect(subject._resources.find(:sales).actions.names).to match_array([:show])
258
+ expect(subject._resources.find(:products).actions.names).to match_array([:index])
259
+ end
260
+ end
261
+
262
+ context 'with multiple actions' do
263
+ let(:block) do
264
+ proc do
265
+ resources :products, actions: [:index]
266
+ remove_actions :index, :show, from: :sales
267
+ end
268
+ end
269
+
270
+ it 'does not use the actions' do
271
+ subject
272
+ expect(subject._resources.find(:sales).actions.names).to match_array([])
273
+ expect(subject._resources.find(:products).actions.names).to match_array([:index])
274
+ end
275
+ end
276
+ end
277
+
278
+ context 'outside of a resource' do
279
+ before do
280
+ Cathode::Version.new 1.5 do
281
+ get :custom
282
+ delete :custom2
283
+ end
284
+ end
285
+ let(:version) { 2 }
286
+
287
+ context 'with an unkown action' do
288
+ let(:block) { proc { remove_action :unknown } }
289
+
290
+ it 'raises an error' do
291
+ expect { subject }.to raise_error(
292
+ Cathode::UnknownActionError,
293
+ "Unknown action `unknown' on ancestor version 1.5.0"
294
+ )
295
+ end
296
+ end
297
+
298
+ context 'with a single action' do
299
+ let(:block) { proc { remove_action :custom } }
300
+
301
+ it 'removes the action' do
302
+ expect(subject.actions.names).to match_array([:status, :custom2])
303
+ end
304
+ end
305
+
306
+ context 'with multiple actions' do
307
+ let(:block) { proc { remove_actions :custom, :custom2 } }
308
+
309
+ it 'removes the action' do
310
+ expect(subject.actions.names).to eq([:status])
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end
317
+
318
+ describe '.find' do
319
+ subject { Cathode::Version.find(version_number) }
320
+ before do
321
+ use_api do
322
+ resources :products
323
+ version 1.5 do
324
+ resources :sales
325
+ end
326
+ end
327
+ end
328
+
329
+ context 'with a valid SemVer number' do
330
+ let(:version_number) { '1.5.0' }
331
+
332
+ it 'returns the version matching the version number' do
333
+ expect(subject.version).to eq('1.5.0')
334
+ end
335
+ end
336
+
337
+ context 'with a non-standard SemVer number' do
338
+ let(:version_number) { '1.5' }
339
+
340
+ it 'returns the version matching the standardized number' do
341
+ expect(subject.version).to eq('1.5.0')
342
+ end
343
+ end
344
+
345
+ context 'with an invalid SemVer number' do
346
+ let(:version_number) { '1.x' }
347
+
348
+ it 'returns nil' do
349
+ expect(subject).to be_nil
350
+ end
351
+ end
352
+ end
353
+
354
+ describe '#resource?' do
355
+ subject { Cathode::Version.find('1.0.0').resource?(resource) }
356
+ before do
357
+ use_api do
358
+ resources :products
359
+ end
360
+ end
361
+
362
+ context 'when the resource is present' do
363
+ let(:resource) { 'products' }
364
+
365
+ it 'is true' do
366
+ expect(subject).to be_true
367
+ end
368
+ end
369
+
370
+ context 'when the resource is not present' do
371
+ let(:resource) { 'sales' }
372
+
373
+ it 'is false' do
374
+ expect(subject).to be_false
375
+ end
376
+ end
377
+ end
378
+
379
+ describe '#action?' do
380
+ subject { Cathode::Version.find('1.0.0').action?(resource, action) }
381
+ before do
382
+ use_api do
383
+ resources :products, actions: [:index]
384
+ end
385
+ end
386
+
387
+ context 'when the resource is not present' do
388
+ let(:resource) { 'sales' }
389
+ let(:action) { 'index' }
390
+
391
+ it 'is false' do
392
+ expect(subject).to be_false
393
+ end
394
+ end
395
+
396
+ context 'when the resource is present' do
397
+ let(:resource) { 'products' }
398
+
399
+ context 'when the action is present' do
400
+ let(:action) { 'index' }
401
+
402
+ it 'is true' do
403
+ expect(subject).to be_true
404
+ end
405
+ end
406
+
407
+ context 'when the action is not present' do
408
+ let(:action) { 'show' }
409
+
410
+ it 'is false' do
411
+ expect(subject).to be_false
412
+ end
413
+ end
414
+ end
415
+ end
416
+ end