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.
- checksums.yaml +7 -0
- data/README.md +113 -0
- data/app/controllers/daylight_documentation/documentation_controller.rb +27 -0
- data/app/helpers/daylight_documentation/documentation_helper.rb +57 -0
- data/app/views/daylight_documentation/documentation/_header.haml +4 -0
- data/app/views/daylight_documentation/documentation/index.haml +12 -0
- data/app/views/daylight_documentation/documentation/model.haml +114 -0
- data/app/views/layouts/documentation.haml +22 -0
- data/config/routes.rb +8 -0
- data/doc/actions.md +70 -0
- data/doc/benchmarks.md +17 -0
- data/doc/contribute.md +80 -0
- data/doc/develop.md +1205 -0
- data/doc/environment.md +109 -0
- data/doc/example.md +3 -0
- data/doc/framework.md +31 -0
- data/doc/install.md +128 -0
- data/doc/principles.md +42 -0
- data/doc/testing.md +107 -0
- data/doc/usage.md +970 -0
- data/lib/daylight/api.rb +293 -0
- data/lib/daylight/associations.rb +247 -0
- data/lib/daylight/client_reloader.rb +45 -0
- data/lib/daylight/collection.rb +161 -0
- data/lib/daylight/errors.rb +94 -0
- data/lib/daylight/inflections.rb +7 -0
- data/lib/daylight/mock.rb +282 -0
- data/lib/daylight/read_only.rb +88 -0
- data/lib/daylight/refinements.rb +63 -0
- data/lib/daylight/reflection_ext.rb +67 -0
- data/lib/daylight/resource_proxy.rb +226 -0
- data/lib/daylight/version.rb +10 -0
- data/lib/daylight.rb +27 -0
- data/rails/daylight/api_controller.rb +354 -0
- data/rails/daylight/documentation.rb +13 -0
- data/rails/daylight/helpers.rb +32 -0
- data/rails/daylight/params.rb +23 -0
- data/rails/daylight/refiners.rb +186 -0
- data/rails/daylight/server.rb +29 -0
- data/rails/daylight/tasks.rb +37 -0
- data/rails/extensions/array_ext.rb +9 -0
- data/rails/extensions/autosave_association_fix.rb +49 -0
- data/rails/extensions/has_one_serializer_ext.rb +111 -0
- data/rails/extensions/inflections.rb +6 -0
- data/rails/extensions/nested_attributes_ext.rb +94 -0
- data/rails/extensions/read_only_attributes.rb +35 -0
- data/rails/extensions/render_json_meta.rb +99 -0
- data/rails/extensions/route_options.rb +47 -0
- data/rails/extensions/versioned_url_for.rb +22 -0
- data/spec/config/dependencies.rb +2 -0
- data/spec/config/factory_girl.rb +4 -0
- data/spec/config/simplecov_rcov.rb +26 -0
- data/spec/config/test_api.rb +1 -0
- data/spec/controllers/documentation_controller_spec.rb +24 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +24 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/daylight.rb +1 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +59 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/helpers/documentation_helper_spec.rb +82 -0
- data/spec/lib/daylight/api_spec.rb +178 -0
- data/spec/lib/daylight/associations_spec.rb +325 -0
- data/spec/lib/daylight/collection_spec.rb +235 -0
- data/spec/lib/daylight/errors_spec.rb +111 -0
- data/spec/lib/daylight/mock_spec.rb +144 -0
- data/spec/lib/daylight/read_only_spec.rb +118 -0
- data/spec/lib/daylight/refinements_spec.rb +80 -0
- data/spec/lib/daylight/reflection_ext_spec.rb +50 -0
- data/spec/lib/daylight/resource_proxy_spec.rb +325 -0
- data/spec/rails/daylight/api_controller_spec.rb +421 -0
- data/spec/rails/daylight/helpers_spec.rb +41 -0
- data/spec/rails/daylight/params_spec.rb +45 -0
- data/spec/rails/daylight/refiners_spec.rb +178 -0
- data/spec/rails/extensions/array_ext_spec.rb +51 -0
- data/spec/rails/extensions/has_one_serializer_ext_spec.rb +135 -0
- data/spec/rails/extensions/nested_attributes_ext_spec.rb +177 -0
- data/spec/rails/extensions/render_json_meta_spec.rb +140 -0
- data/spec/rails/extensions/route_options_spec.rb +309 -0
- data/spec/rails/extensions/versioned_url_for_spec.rb +46 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/migration_helper.rb +40 -0
- 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
|
+
|