daylight 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +113 -0
  3. data/app/controllers/daylight_documentation/documentation_controller.rb +27 -0
  4. data/app/helpers/daylight_documentation/documentation_helper.rb +57 -0
  5. data/app/views/daylight_documentation/documentation/_header.haml +4 -0
  6. data/app/views/daylight_documentation/documentation/index.haml +12 -0
  7. data/app/views/daylight_documentation/documentation/model.haml +114 -0
  8. data/app/views/layouts/documentation.haml +22 -0
  9. data/config/routes.rb +8 -0
  10. data/doc/actions.md +70 -0
  11. data/doc/benchmarks.md +17 -0
  12. data/doc/contribute.md +80 -0
  13. data/doc/develop.md +1205 -0
  14. data/doc/environment.md +109 -0
  15. data/doc/example.md +3 -0
  16. data/doc/framework.md +31 -0
  17. data/doc/install.md +128 -0
  18. data/doc/principles.md +42 -0
  19. data/doc/testing.md +107 -0
  20. data/doc/usage.md +970 -0
  21. data/lib/daylight/api.rb +293 -0
  22. data/lib/daylight/associations.rb +247 -0
  23. data/lib/daylight/client_reloader.rb +45 -0
  24. data/lib/daylight/collection.rb +161 -0
  25. data/lib/daylight/errors.rb +94 -0
  26. data/lib/daylight/inflections.rb +7 -0
  27. data/lib/daylight/mock.rb +282 -0
  28. data/lib/daylight/read_only.rb +88 -0
  29. data/lib/daylight/refinements.rb +63 -0
  30. data/lib/daylight/reflection_ext.rb +67 -0
  31. data/lib/daylight/resource_proxy.rb +226 -0
  32. data/lib/daylight/version.rb +10 -0
  33. data/lib/daylight.rb +27 -0
  34. data/rails/daylight/api_controller.rb +354 -0
  35. data/rails/daylight/documentation.rb +13 -0
  36. data/rails/daylight/helpers.rb +32 -0
  37. data/rails/daylight/params.rb +23 -0
  38. data/rails/daylight/refiners.rb +186 -0
  39. data/rails/daylight/server.rb +29 -0
  40. data/rails/daylight/tasks.rb +37 -0
  41. data/rails/extensions/array_ext.rb +9 -0
  42. data/rails/extensions/autosave_association_fix.rb +49 -0
  43. data/rails/extensions/has_one_serializer_ext.rb +111 -0
  44. data/rails/extensions/inflections.rb +6 -0
  45. data/rails/extensions/nested_attributes_ext.rb +94 -0
  46. data/rails/extensions/read_only_attributes.rb +35 -0
  47. data/rails/extensions/render_json_meta.rb +99 -0
  48. data/rails/extensions/route_options.rb +47 -0
  49. data/rails/extensions/versioned_url_for.rb +22 -0
  50. data/spec/config/dependencies.rb +2 -0
  51. data/spec/config/factory_girl.rb +4 -0
  52. data/spec/config/simplecov_rcov.rb +26 -0
  53. data/spec/config/test_api.rb +1 -0
  54. data/spec/controllers/documentation_controller_spec.rb +24 -0
  55. data/spec/dummy/README.rdoc +28 -0
  56. data/spec/dummy/Rakefile +6 -0
  57. data/spec/dummy/app/assets/images/.keep +0 -0
  58. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  59. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  60. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  61. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  62. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  63. data/spec/dummy/app/mailers/.keep +0 -0
  64. data/spec/dummy/app/models/.keep +0 -0
  65. data/spec/dummy/app/models/concerns/.keep +0 -0
  66. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  67. data/spec/dummy/bin/bundle +3 -0
  68. data/spec/dummy/bin/rails +4 -0
  69. data/spec/dummy/bin/rake +4 -0
  70. data/spec/dummy/config/application.rb +24 -0
  71. data/spec/dummy/config/boot.rb +5 -0
  72. data/spec/dummy/config/database.yml +25 -0
  73. data/spec/dummy/config/environment.rb +5 -0
  74. data/spec/dummy/config/environments/development.rb +29 -0
  75. data/spec/dummy/config/environments/production.rb +80 -0
  76. data/spec/dummy/config/environments/test.rb +36 -0
  77. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  78. data/spec/dummy/config/initializers/daylight.rb +1 -0
  79. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  80. data/spec/dummy/config/initializers/inflections.rb +16 -0
  81. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  82. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  83. data/spec/dummy/config/initializers/session_store.rb +3 -0
  84. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  85. data/spec/dummy/config/locales/en.yml +23 -0
  86. data/spec/dummy/config/routes.rb +59 -0
  87. data/spec/dummy/config.ru +4 -0
  88. data/spec/dummy/lib/assets/.keep +0 -0
  89. data/spec/dummy/log/.keep +0 -0
  90. data/spec/dummy/public/404.html +58 -0
  91. data/spec/dummy/public/422.html +58 -0
  92. data/spec/dummy/public/500.html +57 -0
  93. data/spec/dummy/public/favicon.ico +0 -0
  94. data/spec/helpers/documentation_helper_spec.rb +82 -0
  95. data/spec/lib/daylight/api_spec.rb +178 -0
  96. data/spec/lib/daylight/associations_spec.rb +325 -0
  97. data/spec/lib/daylight/collection_spec.rb +235 -0
  98. data/spec/lib/daylight/errors_spec.rb +111 -0
  99. data/spec/lib/daylight/mock_spec.rb +144 -0
  100. data/spec/lib/daylight/read_only_spec.rb +118 -0
  101. data/spec/lib/daylight/refinements_spec.rb +80 -0
  102. data/spec/lib/daylight/reflection_ext_spec.rb +50 -0
  103. data/spec/lib/daylight/resource_proxy_spec.rb +325 -0
  104. data/spec/rails/daylight/api_controller_spec.rb +421 -0
  105. data/spec/rails/daylight/helpers_spec.rb +41 -0
  106. data/spec/rails/daylight/params_spec.rb +45 -0
  107. data/spec/rails/daylight/refiners_spec.rb +178 -0
  108. data/spec/rails/extensions/array_ext_spec.rb +51 -0
  109. data/spec/rails/extensions/has_one_serializer_ext_spec.rb +135 -0
  110. data/spec/rails/extensions/nested_attributes_ext_spec.rb +177 -0
  111. data/spec/rails/extensions/render_json_meta_spec.rb +140 -0
  112. data/spec/rails/extensions/route_options_spec.rb +309 -0
  113. data/spec/rails/extensions/versioned_url_for_spec.rb +46 -0
  114. data/spec/spec_helper.rb +43 -0
  115. data/spec/support/migration_helper.rb +40 -0
  116. metadata +422 -0
@@ -0,0 +1,421 @@
1
+ require 'spec_helper'
2
+
3
+ class Suite < ActiveRecord::Base
4
+ has_many :cases
5
+
6
+ def odd_cases
7
+ # computational results used by `remoted`
8
+ cases.select {|c| c.id.odd? }
9
+ end
10
+ end
11
+
12
+ class SuitesController < Daylight::APIController
13
+ handles :all
14
+
15
+ private
16
+ def suite_params
17
+ params.require(:suite).permit(:name, :switch)
18
+ end
19
+ end
20
+
21
+ class Case < ActiveRecord::Base
22
+ belongs_to :suite
23
+ end
24
+
25
+ # defined second to prove `handles :all` in SuitesController doesn't
26
+ # publicize every subclass.
27
+ class TestCasesController < Daylight::APIController
28
+ handles :create, :update, :destroy
29
+
30
+ self.model_name = :case
31
+ self.record_name = :result
32
+ self.collection_name = :results
33
+ end
34
+
35
+ class TestAppRecord < ActiveResource::Base
36
+ end
37
+
38
+ class TestErrorsController < Daylight::APIController
39
+ self.model_name = :test_app_record
40
+
41
+ def raise_argument_error
42
+ raise ArgumentError.new('this is my message')
43
+ end
44
+
45
+ def raise_record_invalid_error
46
+ record = TestAppRecord.new
47
+ record.errors.add 'foo', 'error one'
48
+ record.errors.add 'bar', 'error two'
49
+ raise ActiveRecord::RecordInvalid.new(record)
50
+ end
51
+ end
52
+
53
+ describe Daylight::APIController, type: :controller do
54
+ migrate do
55
+ create_table :suites do |t|
56
+ t.boolean :switch
57
+ t.string :name
58
+ end
59
+
60
+ create_table :cases, id: false do |t|
61
+ t.primary_key :test_id
62
+ t.integer :suite_id
63
+ t.string :name
64
+ end
65
+ end
66
+
67
+ before :all do
68
+ FactoryGirl.define do
69
+ factory :suite do
70
+ name { Faker::Name.name }
71
+ switch false
72
+
73
+ after(:create) do |suite|
74
+ create_list :case, 3, suite: suite
75
+ end
76
+ end
77
+
78
+ factory :case do
79
+ name { Faker::Name.name }
80
+ end
81
+ end
82
+ end
83
+
84
+ after :all do
85
+ Rails.application.reload_routes!
86
+ end
87
+
88
+ describe "rescues errors" do
89
+ # rspec-rails does not honor the tests(controller) function
90
+ def self.controller_class
91
+ TestErrorsController
92
+ end
93
+
94
+ before do
95
+ @routes.draw do
96
+ get '/test_errors/raise_argument_error'
97
+ get '/test_errors/raise_record_invalid_error'
98
+ end
99
+ end
100
+
101
+ describe "rescue from ArgumentError" do
102
+ it "has status of bad_request" do
103
+ get :raise_argument_error
104
+
105
+ assert_response :bad_request
106
+ end
107
+
108
+ it "returns the error message as JSON" do
109
+ get :raise_argument_error
110
+
111
+ body = JSON.parse(response.body)
112
+ body['errors'].should == 'this is my message'
113
+ end
114
+ end
115
+
116
+ describe "rescue from RecordInvalid" do
117
+ it "has status of unprocessable_entity" do
118
+ get :raise_record_invalid_error
119
+
120
+ assert_response :unprocessable_entity
121
+ end
122
+
123
+ it "returns the record's errors as JSON" do
124
+ get :raise_record_invalid_error
125
+
126
+ body = JSON.parse(response.body)
127
+ body['errors'].count.should == 2
128
+ body['errors']['foo'].should == ['error one']
129
+ body['errors']['bar'].should == ['error two']
130
+ end
131
+ end
132
+
133
+ describe "rescue from UnpermittedParameters error" do
134
+ def self.controller_class
135
+ SuitesController
136
+ end
137
+
138
+ before do
139
+ @routes.draw do
140
+ resources :suites
141
+ end
142
+ end
143
+
144
+ it "has status of unprocessable_entity" do
145
+ post :create, suite: {unpermitted: 'attr'}
146
+
147
+ assert_response :unprocessable_entity
148
+ end
149
+
150
+ it "reports errors for unpermitted attributes" do
151
+ post :create, suite: {unpermitted: 'attr'}
152
+
153
+ body = JSON.parse(response.body)
154
+ body['errors']['unpermitted'].should == ['unpermitted parameter']
155
+ end
156
+ end
157
+
158
+ describe "rescue from ForbiddenAttributesError" do
159
+ def self.controller_class
160
+ TestCasesController
161
+ end
162
+
163
+ before do
164
+ @routes.draw do
165
+ resources :test_cases, only: [:create]
166
+ end
167
+ end
168
+
169
+ it "has status of unprocessable_entity" do
170
+ post :create, case: {suite_id: 0}
171
+
172
+ assert_response :bad_request
173
+ end
174
+
175
+ it "returns the record's errors as JSON" do
176
+ post :create, case: {name: 'unpermitted'}
177
+
178
+ assert_response :bad_request
179
+
180
+ body = JSON.parse(response.body)
181
+ body['errors'].should == 'parameters have not been permitted on this action'
182
+ end
183
+ end
184
+ end
185
+
186
+ describe "default configuration" do
187
+ let(:controller) { SuitesController }
188
+
189
+ it "uses controller name for record name" do
190
+ controller.record_name.should == 'suite'
191
+ end
192
+
193
+ it "uses controller name for model name" do
194
+ controller.model_name.should == 'suite'
195
+ end
196
+
197
+ it "uses 'collection' for collection name" do
198
+ controller.collection_name.should == 'collection'
199
+ end
200
+
201
+ it "uses controller name for model key" do
202
+ controller.send(:model_key).should == :suite
203
+ end
204
+
205
+ it 'determines model class' do
206
+ controller.send(:model).should == Suite
207
+ end
208
+
209
+ it 'delegates primary key to model class' do
210
+ controller.send(:primary_key).should == 'id'
211
+ end
212
+
213
+ it 'access record ivar' do
214
+ c = controller.new
215
+
216
+ c.send(:record=, 'foo')
217
+ c.send(:record).should == 'foo'
218
+ c.instance_variable_get('@suite').should == c.send(:record)
219
+ end
220
+
221
+ it 'access collection ivar' do
222
+ c = controller.new
223
+
224
+ c.send(:collection=, %w[foo bar])
225
+ c.send(:collection).should == %w[foo bar]
226
+ c.instance_variable_get('@collection').should == c.send(:collection)
227
+ end
228
+ end
229
+
230
+ describe "custom configuration" do
231
+ let(:controller) { TestCasesController }
232
+
233
+ it "overrides record name" do
234
+ controller.record_name.should == :result
235
+ end
236
+
237
+ it "overrides collection name" do
238
+ controller.collection_name.should == :results
239
+ end
240
+
241
+ it "overrides model name" do
242
+ controller.model_name.should == :case
243
+ end
244
+
245
+ it 'determines model key' do
246
+ controller.send(:model_key).should == :case
247
+ end
248
+
249
+ it 'determines model class' do
250
+ controller.send(:model).should == Case
251
+ end
252
+
253
+ it 'delegates primary key to determined model class' do
254
+ controller.send(:primary_key).should == 'test_id'
255
+ end
256
+
257
+ it 'access record ivar' do
258
+ c = controller.new
259
+
260
+ c.send(:record=, 'foo')
261
+ c.send(:record).should == 'foo'
262
+ c.instance_variable_get('@result').should == c.send(:record)
263
+ end
264
+
265
+ it 'access collection ivar' do
266
+ c = controller.new
267
+
268
+ c.send(:collection=, %w[foo bar])
269
+ c.send(:collection).should == %w[foo bar]
270
+ c.instance_variable_get('@results').should == c.send(:collection)
271
+ end
272
+ end
273
+
274
+ describe "API handling" do
275
+ let(:suites_controller) { SuitesController.new } # handles :all
276
+ let(:cases_controller) { TestCasesController.new } # handles some
277
+ let(:errors_controller) { TestErrorsController.new } # handles none
278
+
279
+ it 'handles no API actions by default' do
280
+ Daylight::APIController::API_ACTIONS.each do |action|
281
+ errors_controller.should_not respond_to(action)
282
+ end
283
+ end
284
+
285
+ it 'handles some API actions but not others' do
286
+ allowed = [:create, :update, :destroy]
287
+ denied = Daylight::APIController::API_ACTIONS.dup - allowed
288
+
289
+ allowed.each do |action|
290
+ cases_controller.should respond_to(action)
291
+ end
292
+
293
+ denied.each do |action|
294
+ cases_controller.should_not respond_to(action)
295
+ end
296
+ end
297
+
298
+ it 'handles all API actions' do
299
+ Daylight::APIController::API_ACTIONS.each do |action|
300
+ suites_controller.should respond_to(action)
301
+ end
302
+ end
303
+ end
304
+
305
+ describe "common actions" do
306
+ # rspec-rails does not honor the tests(controller) function
307
+ def self.controller_class
308
+ SuitesController
309
+ end
310
+
311
+ before do
312
+ @routes.draw do
313
+ resources :suites, associated: [:cases], remoted: [:odd_cases], controller: :suites
314
+ end
315
+ end
316
+
317
+ let!(:suite1) { create(:suite, switch: true) }
318
+ let!(:suite2) { create(:suite, switch: false) }
319
+ let!(:suite3) { create(:suite, switch: true) }
320
+
321
+ def parse_collection body, root='anonymous'
322
+ JSON.parse(body).values.first.
323
+ map(&:values).
324
+ map(&:first).
325
+ map(&:with_indifferent_access)
326
+ end
327
+
328
+ def parse_record body
329
+ JSON.parse(body).with_indifferent_access
330
+ end
331
+
332
+ it 'responds to index' do
333
+ get :index
334
+
335
+ results = parse_collection(response.body)
336
+ results.size.should == 3
337
+
338
+ names = results.map {|suite| suite["name"] }
339
+ names.should be_include(suite1.name)
340
+ names.should be_include(suite2.name)
341
+ names.should be_include(suite3.name)
342
+ end
343
+
344
+ it 'responds to index with refine_by' do
345
+ get :index, filters: {switch: true}
346
+
347
+ results = parse_collection(response.body)
348
+
349
+ results.size.should == 2
350
+ names = results.map {|suite| suite[:name] }
351
+ names.should be_include(suite1.name)
352
+ names.should be_include(suite3.name)
353
+ end
354
+
355
+ it 'creates a record' do
356
+ post :create, suite: suite = FactoryGirl.attributes_for(:suite)
357
+
358
+ result = parse_record(response.body)
359
+ result[:name].should == suite[:name]
360
+ result[:switch].should == suite[:switch]
361
+
362
+ response.headers['Location'].should == "/suites/#{result['id']}"
363
+ end
364
+
365
+ it 'shows a record' do
366
+ post :show, id: suite2.id
367
+
368
+ result = parse_record(response.body)
369
+ result[:id].should == suite2[:id]
370
+ result[:name].should == suite2[:name]
371
+ result[:switch].should == suite2[:switch]
372
+ end
373
+
374
+ it 'updates a record' do
375
+ patch :update, id: suite3.id, suite: {name: 'Rik Mayall'}
376
+
377
+ Suite.find(suite3.id).name.should == 'Rik Mayall'
378
+ end
379
+
380
+ it 'deletes a record' do
381
+ delete :destroy, id: suite1.id
382
+
383
+ expect { Suite.find(suite1.id) }.to raise_error(ActiveRecord::RecordNotFound)
384
+ end
385
+
386
+ it 'retrieves associated records' do
387
+ get :associated, id: suite1.id, associated: 'cases'
388
+
389
+ results = parse_collection(response.body)
390
+ results.size.should == 3
391
+
392
+ test_ids = results.map {|tc| tc['test_id'] }
393
+ test_ids.should be_include(suite1.cases[0].id)
394
+ test_ids.should be_include(suite1.cases[1].id)
395
+ test_ids.should be_include(suite1.cases[2].id)
396
+ end
397
+
398
+ it 'retrieves associated records with refine_by' do
399
+ get :associated, id: suite1.id, associated: 'cases', limit: 1
400
+
401
+ results = parse_collection(response.body)
402
+ results.size.should == 1
403
+
404
+ results.first['test_id'].should == suite1.cases.first.id
405
+ end
406
+
407
+ it 'retrieves remoted records' do
408
+ get :remoted, id: suite1.id, remoted: 'odd_cases'
409
+
410
+ results = parse_collection(response.body)
411
+ results.size.should == 2
412
+
413
+ odd_case_ids = suite1.odd_cases.map(&:test_id)
414
+
415
+ ids = results.map {|suite| suite["test_id"] }
416
+ ids.should be_include(odd_case_ids.first)
417
+ ids.should be_include(odd_case_ids.last)
418
+ end
419
+ end
420
+
421
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daylight::Helpers do
4
+ class HelperTestClass
5
+ include Daylight::Helpers
6
+
7
+ attr_accessor :params
8
+
9
+ def initialize params={}
10
+ @params = params
11
+ end
12
+ end
13
+
14
+ let(:helper) do
15
+ HelperTestClass.new(scopes: %w[scope_a scope_b], filters: {attr_a: 'Foo', attr_b: 'Bar'}, order: 'name ASC', limit: '10', offset: '100')
16
+ end
17
+
18
+ it 'returns refiner params' do
19
+ helper.scoped_params.should == %w[scope_a scope_b]
20
+ end
21
+
22
+ it 'returns filter params' do
23
+ helper.filter_params.should == {attr_a: 'Foo', attr_b: 'Bar'}
24
+ end
25
+
26
+ it 'returns order params' do
27
+ helper.order_params.should == 'name ASC'
28
+ end
29
+
30
+ it 'returns limit params' do
31
+ helper.limit_params.should == '10'
32
+ end
33
+
34
+ it 'returns offset params' do
35
+ helper.offset_params.should == 100
36
+ end
37
+
38
+ it 'raises an error if offset cannot be parsed as Integer' do
39
+ expect { HelperTestClass.new(offset: 'a').offset_params }.to raise_error(ArgumentError, %q[invalid value for Integer(): "a"])
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daylight::Params do
4
+
5
+ module HelperTest
6
+ def helper_method
7
+ params[:foo]
8
+ end
9
+ end
10
+
11
+ class ParamsTestClass
12
+ include Daylight::Params
13
+ end
14
+
15
+ let(:mixin) do
16
+ ParamsTestClass.new
17
+ end
18
+
19
+ it 'has no params method' do
20
+ mixin.should_not respond_to(:params)
21
+ end
22
+
23
+ describe :with_params do
24
+ it 'can access params directly' do
25
+ mixin.with_helper({foo: 'bar'}) do |helper|
26
+ helper.params[:foo].should == 'bar'
27
+ end
28
+ end
29
+
30
+ it 'can access params through helper methods' do
31
+ Daylight::Params::HelperProxy.send(:include, HelperTest)
32
+ mixin.with_helper({foo: 'baz'}) do |helper|
33
+ helper.helper_method.should == 'baz'
34
+ end
35
+ end
36
+
37
+ it 'still no params after accessed in context' do
38
+ mixin.with_helper({foo: 'bar'}) do |helper|
39
+ helper.params
40
+ end
41
+
42
+ mixin.should_not respond_to(:params)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ class RefinerMockActiveRecordBase
4
+ include Daylight::Refiners::Extension
5
+
6
+ def self.scope(name, body, &block); end
7
+ def self.where(*args); end
8
+ def self.order(*args); end
9
+ def self.reflections(*args); {} end
10
+ end
11
+
12
+ class RefinersTestClass < RefinerMockActiveRecordBase
13
+ scope :scope_a, -> { 'a' }
14
+ scope :scope_b, -> { 'b' }
15
+
16
+ def self.find(id)
17
+ RefinersTestClass.new
18
+ end
19
+
20
+ def foo
21
+ 123
22
+ end
23
+ end
24
+
25
+ describe Daylight::Refiners::AttributeSeive do
26
+ let(:valid_attribute_names) { %w[foo bar baz] }
27
+
28
+ describe 'with invalid attributes' do
29
+ let(:seive) do
30
+ Daylight::Refiners::AttributeSeive.new(valid_attribute_names, %w[foo bar wibble])
31
+ end
32
+
33
+ it 'returns valid attributes' do
34
+ seive.valid_attributes.should == %w[foo bar]
35
+ end
36
+
37
+ it 'returns invalid attributes' do
38
+ seive.invalid_attributes.should == %w[wibble]
39
+ end
40
+
41
+ it 'returns false attributes_valid?' do
42
+ seive.should_not be_attributes_valid
43
+ end
44
+ end
45
+
46
+ it 'returns true when valid' do
47
+ seive = Daylight::Refiners::AttributeSeive.new(valid_attribute_names, %w[foo bar])
48
+ seive.should be_attributes_valid
49
+ end
50
+
51
+
52
+ it 'returns true when nil' do
53
+ seive = Daylight::Refiners::AttributeSeive.new(valid_attribute_names, nil)
54
+ seive.should be_attributes_valid
55
+ end
56
+ end
57
+
58
+ describe Daylight::Refiners do
59
+ it 'tracks registered scopes' do
60
+ RefinersTestClass.registered_scopes.should == %w[scope_a scope_b]
61
+ end
62
+
63
+ it 'returns true if scoped? finds a match' do
64
+ RefinersTestClass.should be_scoped(:scope_a)
65
+ RefinersTestClass.should_not be_scoped(:foo)
66
+ end
67
+
68
+ describe :scope_by do
69
+ let (:all) { double }
70
+
71
+ before do
72
+ RefinersTestClass.stub(all: all)
73
+ end
74
+
75
+ it 'raises an error an unknown scope is supplied' do
76
+ expect { RefinersTestClass.scoped_by(:foo) }.to raise_error(ArgumentError, 'Unknown scope: foo')
77
+ end
78
+
79
+ it 'applies supplied scope' do
80
+ all.should_receive(:scope_a)
81
+
82
+ RefinersTestClass.scoped_by(:scope_a)
83
+ end
84
+
85
+ it 'applies supplied scope' do
86
+ all.should_receive(:scope_a).and_return(all)
87
+ all.should_receive(:scope_b).and_return(all)
88
+
89
+ RefinersTestClass.scoped_by(%w[scope_a scope_b])
90
+ end
91
+
92
+ it 'applies no scope when nil is supplied' do
93
+ RefinersTestClass.scoped_by(nil).should == all
94
+ end
95
+ end
96
+
97
+ describe :filter_by do
98
+ before do
99
+ RefinersTestClass.stub(attribute_names: %w[foo bar])
100
+ end
101
+
102
+ it 'raises an error if an unknown attribute is supplied' do
103
+ expect { RefinersTestClass.filter_by(baz: 'wibble') }.to raise_error(ArgumentError, 'Unknown key: baz')
104
+ end
105
+
106
+ it 'applies where clause for all supplied attributes' do
107
+ RefinersTestClass.should_receive(:where).with({'foo' => 'baz', 'bar' => 'wibble'})
108
+
109
+ RefinersTestClass.filter_by({foo: 'baz', bar: 'wibble'})
110
+ end
111
+
112
+ it 'applies where clause with no attributes (and will be discarded by rails)' do
113
+ RefinersTestClass.should_receive(:where).with({})
114
+
115
+ RefinersTestClass.filter_by(nil)
116
+ end
117
+
118
+ it 'allows reflection keys' do
119
+ RefinersTestClass.stub(reflections: {boing: 'boing'})
120
+
121
+ expect { RefinersTestClass.filter_by(boing: 'boing') }.to_not raise_error
122
+ end
123
+ end
124
+
125
+ describe :order_by do
126
+ before do
127
+ RefinersTestClass.stub(attribute_names: %w[foo bar])
128
+ end
129
+
130
+ it 'raises an error if unknown attribute is supplied (as a String)' do
131
+ expect { RefinersTestClass.order_by('baz') }.to raise_error(ArgumentError, 'Unknown attribute: baz')
132
+ expect { RefinersTestClass.order_by('bar ASC, baz') }.to raise_error(ArgumentError, 'Unknown attribute: baz')
133
+ expect { RefinersTestClass.order_by('bar, baz ASC') }.to raise_error(ArgumentError, 'Unknown attribute: baz')
134
+ expect { RefinersTestClass.order_by('bar ASC, baz DESC') }.to raise_error(ArgumentError, 'Unknown attribute: baz')
135
+ end
136
+
137
+ it 'raises an error if unknown attribute is supplied (as an Array)' do
138
+ expect { RefinersTestClass.order_by([:baz, :foo]) }.to raise_error(ArgumentError, 'Unknown attribute: baz')
139
+ end
140
+
141
+ it 'raises an error if unknown attribute is supplied (as a Hash)' do
142
+ expect { RefinersTestClass.order_by({foo: 'asc', baz: 'asc', bar: 'desc'}) }.to raise_error(ArgumentError, 'Unknown attribute: baz')
143
+ end
144
+
145
+ it 'applies order clause for the supplied order' do
146
+ RefinersTestClass.should_receive(:order).with(nil)
147
+
148
+ RefinersTestClass.order_by(nil)
149
+ end
150
+ end
151
+
152
+ describe :remoted_methods do
153
+ before do
154
+ RefinersTestClass.add_remoted(:foo)
155
+ end
156
+
157
+ it "keeps track of remoted methods" do
158
+ RefinersTestClass.remoted?(:foo).should be_true
159
+ RefinersTestClass.remoted?(:not_a_remoted_method).should be_false
160
+
161
+ RefinersTestClass.remoted_methods.should == [:foo]
162
+ end
163
+
164
+ it "does not set up non-existent methods as remotes" do
165
+ RefinersTestClass.add_remoted(:unknown)
166
+ RefinersTestClass.remoted_methods.should_not include(:unknown)
167
+ end
168
+
169
+ it "raises an error if an unknown remoted is supplied" do
170
+ expect { RefinersTestClass.remoted(id:1, remoted:'not_a_remoted_method') }.to raise_error(ArgumentError)
171
+ end
172
+
173
+ it "returns the remoted call data" do
174
+ RefinersTestClass.remoted(id:1, remoted:'foo').should == 123
175
+ end
176
+ end
177
+ end
178
+