daylight 0.9.0.rc1

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 (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
+