dao 4.2.1 → 4.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +103 -63
- data/Rakefile +3 -3
- data/dao.gemspec +27 -16
- data/lib/dao.rb +17 -17
- data/lib/dao/active_record.rb +1 -0
- data/lib/dao/api.rb +2 -1
- data/lib/dao/api/{endpoints.rb → call.rb} +1 -0
- data/lib/dao/api/context.rb +2 -0
- data/lib/dao/api/dsl.rb +1 -0
- data/lib/dao/api/initializers.rb +1 -0
- data/lib/dao/api/modes.rb +1 -0
- data/lib/dao/api/routes.rb +1 -0
- data/lib/dao/blankslate.rb +1 -0
- data/lib/dao/conducer.rb +315 -274
- data/lib/dao/conducer/active_model.rb +98 -0
- data/lib/dao/conducer/attributes.rb +1 -0
- data/lib/dao/conducer/autocrud.rb +58 -0
- data/lib/dao/conducer/callback_support.rb +20 -0
- data/lib/dao/conducer/collection.rb +45 -0
- data/lib/dao/conducer/controller_support.rb +104 -0
- data/lib/dao/conducer/nav_support.rb +9 -0
- data/lib/dao/conducer/view_support.rb +16 -0
- data/lib/dao/data.rb +2 -1
- data/lib/dao/db.rb +2 -0
- data/lib/dao/endpoint.rb +1 -0
- data/lib/dao/engine.rb +1 -0
- data/lib/dao/errors.rb +109 -99
- data/lib/dao/exceptions.rb +1 -0
- data/lib/dao/extractor.rb +1 -0
- data/lib/dao/form.rb +175 -20
- data/lib/dao/instance_exec.rb +1 -0
- data/lib/dao/mode.rb +1 -0
- data/lib/dao/mongo_mapper.rb +1 -0
- data/lib/dao/name.rb +1 -0
- data/lib/dao/params.rb +2 -1
- data/lib/dao/path.rb +1 -0
- data/lib/dao/path_map.rb +24 -0
- data/lib/dao/rack.rb +1 -0
- data/lib/dao/rack/middleware.rb +1 -0
- data/lib/dao/rack/middleware/params_parser.rb +1 -0
- data/lib/dao/rails.rb +12 -32
- data/lib/dao/rails/lib/generators/dao/USAGE +2 -2
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +8 -27
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +2 -1
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +22 -20
- data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +49 -43
- data/lib/dao/rails/lib/generators/dao/templates/dao.css +26 -25
- data/lib/dao/rails/lib/generators/dao/templates/dao.js +3 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +58 -45
- data/lib/dao/result.rb +50 -1
- data/lib/dao/route.rb +1 -0
- data/lib/dao/slug.rb +12 -36
- data/lib/dao/status.rb +91 -7
- data/lib/dao/stdext.rb +1 -0
- data/lib/dao/support.rb +90 -80
- data/lib/dao/upload.rb +396 -0
- data/lib/dao/validations.rb +23 -5
- data/lib/dao/validations/callback.rb +5 -0
- data/lib/dao/validations/common.rb +100 -3
- data/lib/dao/validations/instance.rb +17 -0
- data/lib/dao/validations/validator.rb +192 -91
- data/test/active_model_conducer_lint_test.rb +1 -0
- data/test/api_test.rb +15 -0
- data/test/conducer_test.rb +608 -90
- data/test/data/han-solo.jpg +0 -0
- data/test/form_test.rb +1 -0
- data/test/helper.rb +1 -0
- data/test/leak.rb +1 -0
- data/test/support_test.rb +4 -1
- data/test/testing.rb +1 -0
- data/test/validations_test.rb +176 -30
- metadata +120 -131
- data/b.rb +0 -38
- data/lib/dao/conducer/crud.rb +0 -70
- data/lib/dao/current.rb +0 -66
- data/lib/dao/image_cache.rb +0 -193
- data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +0 -79
- data/test/db.yml +0 -9
data/test/api_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
|
2
3
|
|
3
4
|
Testing Dao do
|
@@ -132,6 +133,20 @@ Testing Dao do
|
|
132
133
|
end
|
133
134
|
end
|
134
135
|
|
136
|
+
## context
|
137
|
+
#
|
138
|
+
testing 'that calls have a shortcut to status' do
|
139
|
+
api_class =
|
140
|
+
assert{
|
141
|
+
Dao.api do
|
142
|
+
call(:foo){ status! 420 }
|
143
|
+
end
|
144
|
+
}
|
145
|
+
api = assert{ api_class.new }
|
146
|
+
result = assert{ api.call(:foo) }
|
147
|
+
assert{ result.status =~ 420 }
|
148
|
+
end
|
149
|
+
|
135
150
|
## results
|
136
151
|
#
|
137
152
|
testing 'that results can be created' do
|
data/test/conducer_test.rb
CHANGED
@@ -1,52 +1,329 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
|
1
4
|
Testing Dao::Conducer do
|
2
5
|
##
|
3
6
|
#
|
4
|
-
|
5
|
-
|
7
|
+
context :teh_ctor do
|
8
|
+
#
|
9
|
+
testing 'conducers have a POLS .new method' do
|
10
|
+
[
|
11
|
+
{:key => :val, :array => [0,1,2]},
|
12
|
+
{}
|
13
|
+
].each do |attributes|
|
14
|
+
c = new_conducer(attributes)
|
15
|
+
assert{ c.attributes =~ attributes }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
testing 'models passed to .new are automatically tracked' do
|
21
|
+
user = User.new
|
22
|
+
post = Post.new
|
23
|
+
comment = Comment.new
|
24
|
+
params = {}
|
25
|
+
|
26
|
+
args = [comment, post, user, params]
|
27
|
+
|
28
|
+
c = new_conducer(*args)
|
29
|
+
|
30
|
+
assert{ c.models == [comment, post, user] }
|
31
|
+
assert{ c.model == comment }
|
32
|
+
assert{ c.model == c.conduces }
|
33
|
+
|
34
|
+
assert{ c.conduces(post) }
|
35
|
+
assert{ c.models == [post, comment, user] }
|
36
|
+
assert{ c.model == post }
|
37
|
+
assert{ c.model == c.conduces }
|
38
|
+
end
|
6
39
|
end
|
7
40
|
|
8
41
|
##
|
9
42
|
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
43
|
+
context :teh_default_initialize do
|
44
|
+
#
|
45
|
+
testing 'that the last mode determines the lifecycle state when a models are passed in' do
|
46
|
+
user = User.new
|
47
|
+
post = Post.new
|
48
|
+
comment = Comment.new
|
49
|
+
params = {}
|
50
|
+
|
51
|
+
args = [comment, post, user, params]
|
52
|
+
|
53
|
+
c = new_conducer(*args)
|
54
|
+
|
55
|
+
%w( new_record? persisted? destroyed? ).each do |state|
|
56
|
+
assert{ comment.send(state) == c.send(state) }
|
57
|
+
end
|
58
|
+
|
59
|
+
comment.new_record = false
|
60
|
+
comment.persisted = true
|
61
|
+
assert{ c.new_record? == false }
|
62
|
+
assert{ c.persisted == true }
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
testing 'that passed in models/params are sanely ker-sploded onto the attributes' do
|
67
|
+
user = User.new :k => 1
|
68
|
+
post = Post.new :k => 2
|
69
|
+
comment = Comment.new :k => 3, :x => 4
|
70
|
+
params = {:x => 5}
|
71
|
+
|
72
|
+
args = [comment, post, user, params]
|
73
|
+
|
74
|
+
c = new_conducer(*args)
|
75
|
+
|
76
|
+
assert{ c.attributes[:user] =~ {:k => 1} }
|
77
|
+
assert{ c.instance_variable_get('@user') == user }
|
78
|
+
|
79
|
+
assert{ c.attributes[:post] =~ {:k => 2} }
|
80
|
+
assert{ c.instance_variable_get('@post') == post }
|
81
|
+
|
82
|
+
expected = Map.new
|
83
|
+
expected.update :user => user.attributes
|
84
|
+
expected.update :post => post.attributes
|
85
|
+
expected.update comment.attributes
|
86
|
+
expected.update params
|
87
|
+
|
88
|
+
assert{ c.attributes =~ expected }
|
89
|
+
assert{ c.instance_variable_get('@comment') == comment }
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
testing 'that .new specialises based on current action' do
|
94
|
+
conducer_class =
|
95
|
+
new_conducer_class do
|
96
|
+
def initialize_for_new
|
97
|
+
attributes.update(:new => Time.now)
|
98
|
+
end
|
99
|
+
def initialize_for_create
|
100
|
+
attributes.update(:create => Time.now)
|
101
|
+
end
|
102
|
+
def initialize_for_edit
|
103
|
+
attributes.update(:edit => Time.now)
|
104
|
+
end
|
16
105
|
end
|
17
|
-
|
106
|
+
|
107
|
+
post = Post.new
|
18
108
|
|
19
|
-
|
20
|
-
|
109
|
+
%w( new create edit ).each do |action|
|
110
|
+
c = conducer_class.for(action, post)
|
111
|
+
assert{ c.action == action }
|
112
|
+
assert{ c.respond_to?("initialize_for_#{ action }") }
|
113
|
+
assert{ c.attributes[action].is_a?(Time) }
|
114
|
+
assert{ c.attributes.size == 1 }
|
115
|
+
end
|
116
|
+
end
|
21
117
|
|
22
|
-
|
23
|
-
|
118
|
+
#
|
119
|
+
testing 'that conducers can build a highly specialized .new method based on action' do
|
120
|
+
c =
|
121
|
+
new_conducer_class do
|
122
|
+
def initialize(a, b, c, params)
|
123
|
+
@a, @b, @c = a, b, c
|
24
124
|
|
25
|
-
|
26
|
-
|
125
|
+
update_attributes(
|
126
|
+
:foo => :bar
|
127
|
+
)
|
27
128
|
|
28
|
-
|
129
|
+
case action
|
130
|
+
when 'new'
|
131
|
+
update_attributes(:action => :new)
|
132
|
+
when 'edit'
|
133
|
+
update_attributes(:action => :edit)
|
134
|
+
else
|
135
|
+
update_attributes(:action => nil)
|
136
|
+
end
|
137
|
+
|
138
|
+
update_attributes(params)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
params = {:key => :val}
|
143
|
+
|
144
|
+
%w( new edit ).each do |action|
|
145
|
+
o = assert{ c.for(action, :a, :b, :c, params) }
|
146
|
+
assert{ o.action == action }
|
147
|
+
assert{ o.key == :val }
|
148
|
+
assert{ o.foo == :bar }
|
149
|
+
%w( a b c ).each{|var| assert{ o.instance_variable_get("@#{ var }") == var.to_sym } }
|
150
|
+
end
|
151
|
+
end
|
29
152
|
end
|
30
153
|
|
31
154
|
##
|
32
155
|
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
156
|
+
context :teh_default_save do
|
157
|
+
#
|
158
|
+
testing 'is sane and based solely on the last model' do
|
159
|
+
user = User.new
|
160
|
+
post = Post.new
|
161
|
+
comment = Comment.new
|
162
|
+
params = {:text => 'hai!', :user => {:params => 'are ignored'}, :post => {:params => 'are ignored'}}
|
163
|
+
|
164
|
+
args = [comment, post, user, params]
|
165
|
+
|
166
|
+
c = new_conducer(*args)
|
167
|
+
|
168
|
+
assert{ comment[:text].nil? }
|
169
|
+
assert{ c.save }
|
170
|
+
assert{ comment.text == 'hai!' }
|
171
|
+
assert{ comment[:user].nil? }
|
172
|
+
assert{ comment[:post].nil? }
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
testing 'halts when the conducer is invalid with errors' do
|
177
|
+
conducer_class =
|
178
|
+
new_conducer_class do
|
179
|
+
validates_presence_of(:foo)
|
38
180
|
end
|
39
|
-
}
|
40
181
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
182
|
+
c = conducer_class.new
|
183
|
+
|
184
|
+
assert{ !c.save }
|
185
|
+
assert{ !c.valid? }
|
186
|
+
assert{ !c.errors.empty? }
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
testing 'halts when the model is invalid and relays errors' do
|
191
|
+
post = Post.new
|
192
|
+
post.errors[:foo] = 'is fucked'
|
193
|
+
c = new_conducer(post)
|
194
|
+
assert{ !c.save }
|
195
|
+
assert{ c.errors[:foo] == Array(post.errors[:foo]) }
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
testing 'raises a validation error on #save!' do
|
200
|
+
post = Post.new
|
201
|
+
post.errors[:foo] = 'is fucked'
|
202
|
+
c = new_conducer(post)
|
203
|
+
|
204
|
+
error = assert{ begin; c.save!; rescue Object => e; e; end; }
|
205
|
+
|
206
|
+
assert{ error.errors == c.errors }
|
207
|
+
assert{ error.errors[:foo] = Array(post.errors[:foo]) }
|
208
|
+
assert{ c.errors[:foo] = Array(post.errors[:foo]) }
|
209
|
+
end
|
45
210
|
end
|
46
211
|
|
47
212
|
##
|
48
213
|
#
|
49
|
-
context
|
214
|
+
context :validations do
|
215
|
+
#
|
216
|
+
testing 'that simple validations/errors work' do
|
217
|
+
c =
|
218
|
+
assert{
|
219
|
+
new_foo_conducer_class do
|
220
|
+
validates_presence_of :bar
|
221
|
+
validates_presence_of :foo, :bar
|
222
|
+
end
|
223
|
+
}
|
224
|
+
|
225
|
+
o = assert{ c.new }
|
226
|
+
assert{ !o.valid? }
|
227
|
+
|
228
|
+
assert{ Array(o.errors.get(:bar)).size == 1 }
|
229
|
+
assert{ Array(o.errors.get(:foo, :bar)).size == 1 }
|
230
|
+
|
231
|
+
o.attributes.set :foo, :bar, 42
|
232
|
+
assert{ !o.valid? }
|
233
|
+
|
234
|
+
assert{ Array(o.errors.get(:bar)).size == 1 }
|
235
|
+
assert{ Array(o.errors.get(:foo, :bar)).size == 0 }
|
236
|
+
|
237
|
+
assert{ Array(o.errors.get(:foo, :bar)).empty? }
|
238
|
+
end
|
239
|
+
|
240
|
+
#
|
241
|
+
testing 'that validations are evaluated in the context of the object' do
|
242
|
+
c =
|
243
|
+
assert{
|
244
|
+
new_foo_conducer_class do
|
245
|
+
klass = self
|
246
|
+
validates(:a){ klass == self.class }
|
247
|
+
|
248
|
+
validates(:b){ value != 42.0 }
|
249
|
+
|
250
|
+
def value() 42 end
|
251
|
+
end
|
252
|
+
}
|
253
|
+
|
254
|
+
o = assert{ c.new }
|
255
|
+
assert{ !o.valid? }
|
256
|
+
assert{ o.errors.get(:a).empty? }
|
257
|
+
assert{ !o.errors.get(:b).empty? }
|
258
|
+
end
|
259
|
+
|
260
|
+
#
|
261
|
+
testing 'that validates_each werks at the class and instance level' do
|
262
|
+
conducer_class =
|
263
|
+
new_conducer_class do
|
264
|
+
validates_each :a do |item|
|
265
|
+
validated.push(item)
|
266
|
+
true
|
267
|
+
end
|
268
|
+
|
269
|
+
def save
|
270
|
+
validates_each :b do |item|
|
271
|
+
validated.push(item)
|
272
|
+
true
|
273
|
+
end
|
274
|
+
return valid?
|
275
|
+
end
|
276
|
+
|
277
|
+
def validated
|
278
|
+
@validated ||= []
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
a = %w( a b c )
|
283
|
+
b = %w( 1 2 3 )
|
284
|
+
|
285
|
+
c = assert{ conducer_class.new(:a => a, :b => b) }
|
286
|
+
assert{ c.run_validations }
|
287
|
+
assert{ c.validated == %w( a b c ) }
|
288
|
+
|
289
|
+
|
290
|
+
c = conducer_class.new(:a => a, :b => b)
|
291
|
+
assert{ c.save }
|
292
|
+
assert{ c.validated == %w( a b c 1 2 3 ) }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
##
|
297
|
+
#
|
298
|
+
context :forms do
|
299
|
+
#
|
300
|
+
testing 'that basic form helpers work' do
|
301
|
+
c =
|
302
|
+
assert{
|
303
|
+
new_foo_conducer_class do
|
304
|
+
validates_presence_of :bar
|
305
|
+
end
|
306
|
+
}
|
307
|
+
|
308
|
+
o = assert{ c.new }
|
309
|
+
assert{ !o.valid? } # make validations run...
|
310
|
+
assert{ o.form }
|
311
|
+
assert{ o.form.input(:foo) =~ /\<input/ }
|
312
|
+
assert{ o.form.input(:foo) !~ /errors/ }
|
313
|
+
assert{ o.form.textarea(:bar) =~ /\<textarea/ }
|
314
|
+
assert{ o.form.textarea(:bar) =~ /errors/ }
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
#
|
320
|
+
context :class_methods do
|
321
|
+
#
|
322
|
+
testing 'that base classes can be constructed and named' do
|
323
|
+
new_foo_conducer_class()
|
324
|
+
end
|
325
|
+
|
326
|
+
#
|
50
327
|
testing '.new' do
|
51
328
|
c = assert{ new_foo_conducer_class }
|
52
329
|
controller = assert{ Dao.mock_controller }
|
@@ -70,19 +347,7 @@ Testing Dao::Conducer do
|
|
70
347
|
end
|
71
348
|
end
|
72
349
|
|
73
|
-
|
74
|
-
c = assert{ new_foo_conducer_class }
|
75
|
-
assert{ c.all().is_a?(Array) }
|
76
|
-
assert{ c.all(nil).is_a?(Array) }
|
77
|
-
assert{ c.all({}).is_a?(Array) }
|
78
|
-
end
|
79
|
-
|
80
|
-
testing '.find' do
|
81
|
-
c = assert{ new_foo_conducer_class }
|
82
|
-
o = assert{ c.new }
|
83
|
-
assert{ c.find(o.id).is_a?(Dao::Conducer) }
|
84
|
-
end
|
85
|
-
|
350
|
+
#
|
86
351
|
testing '.model_name' do
|
87
352
|
c = assert{ new_foo_conducer_class }
|
88
353
|
assert{ c.model_name }
|
@@ -91,54 +356,21 @@ Testing Dao::Conducer do
|
|
91
356
|
end
|
92
357
|
end
|
93
358
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
assert{ o.
|
106
|
-
assert{
|
359
|
+
##
|
360
|
+
#
|
361
|
+
context :instance_methods do
|
362
|
+
testing '#id' do
|
363
|
+
[:_id, :id].each do |id_key|
|
364
|
+
o = assert{ new_foo_conducer() }
|
365
|
+
assert{ o.id.nil? }
|
366
|
+
o.attributes.update(id_key => 42)
|
367
|
+
assert{ o.id==42 }
|
368
|
+
assert{ o.id = nil; true }
|
369
|
+
assert{ !o.id }
|
370
|
+
assert{ o.attributes[:id].nil? }
|
371
|
+
assert{ o.attributes[:_id].nil? }
|
107
372
|
end
|
108
373
|
end
|
109
|
-
end
|
110
|
-
|
111
|
-
context 'instance endpoint' do
|
112
|
-
testing '#save' do
|
113
|
-
params = {:k => :v}
|
114
|
-
o = assert{ new_foo_conducer(params) }
|
115
|
-
assert{ o.save }
|
116
|
-
id = assert{ o.id }
|
117
|
-
assert{ db.foos.find(id)[:k] == o.attributes[:k] }
|
118
|
-
assert{ id == o.id }
|
119
|
-
assert{ o.attributes =~ params.merge(:id => id) }
|
120
|
-
end
|
121
|
-
|
122
|
-
testing '#update_attributes' do
|
123
|
-
params = {:k => :v}
|
124
|
-
o = assert{ new_foo_conducer(params) }
|
125
|
-
t = Time.now
|
126
|
-
assert{ o.update_attributes :t => t }
|
127
|
-
assert{ o.save }
|
128
|
-
id = assert{ o.id }
|
129
|
-
assert{ db.foos.find(id).id == o.id }
|
130
|
-
assert{ db.foos.find(id) =~ params.merge(:id => id, :t => t) }
|
131
|
-
end
|
132
|
-
|
133
|
-
testing '#destroy' do
|
134
|
-
params = {:k => :v}
|
135
|
-
o = assert{ new_foo_conducer(params) }
|
136
|
-
assert{ o.save }
|
137
|
-
id = assert{ o.id }
|
138
|
-
assert{ db.foos.find(id).id == o.id }
|
139
|
-
assert{ o.destroy }
|
140
|
-
assert{ db.foos.find(id).nil? }
|
141
|
-
end
|
142
374
|
|
143
375
|
testing '#to_param' do
|
144
376
|
o = assert{ new_foo_conducer() }
|
@@ -151,24 +383,181 @@ Testing Dao::Conducer do
|
|
151
383
|
o = assert{ new_foo_conducer() }
|
152
384
|
assert{ o.errors.respond_to?(:[]) }
|
153
385
|
end
|
386
|
+
|
387
|
+
=begin
|
388
|
+
testing 'that conducers can register handlers for setting deeply nested attributes' do
|
389
|
+
c =
|
390
|
+
new_conducer_class do
|
391
|
+
def _update_attributes(attributes = {})
|
392
|
+
attributes.each do |key, value|
|
393
|
+
case Array(key).join('.')
|
394
|
+
when 'user.first_name'
|
395
|
+
set(key, value.to_s.upcase)
|
396
|
+
return true
|
397
|
+
else
|
398
|
+
return false
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
o = assert{ c.new :user => {:first_name => 'ara', :last_name => 'howard'} }
|
405
|
+
assert{ o.user.first_name == 'ARA' }
|
406
|
+
assert{ o.user.last_name == 'howard' }
|
407
|
+
|
408
|
+
o = assert{ c.new :name => 'ara howard' }
|
409
|
+
assert{ o.attributes.get(:name) == 'ara howard' }
|
410
|
+
end
|
411
|
+
=end
|
154
412
|
end
|
155
413
|
|
156
|
-
|
414
|
+
|
415
|
+
##
|
416
|
+
#
|
417
|
+
context :teh_mount do
|
418
|
+
#
|
419
|
+
testing 'that mounted objects can be declared at the class level' do
|
420
|
+
conducer_class =
|
421
|
+
new_conducer_class do
|
422
|
+
mount Dao::Upload, :a, :b, :placeholder => '/images/foo.jpg'
|
423
|
+
end
|
424
|
+
|
425
|
+
assert{ !conducer_class.mounted.empty? }
|
426
|
+
|
427
|
+
c = conducer_class.new
|
428
|
+
|
429
|
+
assert{ c.mounted.first.is_a?(Dao::Upload) }
|
430
|
+
assert{ c.mounted.first._key.join('.') == 'a.b' }
|
431
|
+
assert{ c.mounted.first._value.nil? }
|
432
|
+
end
|
433
|
+
|
434
|
+
#
|
435
|
+
testing 'that mounted objects replace their location in attributes' do
|
436
|
+
conducer_class =
|
437
|
+
new_conducer_class do
|
438
|
+
mount Dao::Upload, :a, :b, :placeholder => '/images/foo.jpg'
|
439
|
+
end
|
440
|
+
|
441
|
+
path = __FILE__
|
442
|
+
up = Upload.new(path)
|
443
|
+
|
444
|
+
c = conducer_class.new( :a => {:b => {:file => up}} )
|
445
|
+
|
446
|
+
upload = assert{ c.get(:a, :b) }
|
447
|
+
|
448
|
+
assert{ upload.is_a?(Dao::Upload) }
|
449
|
+
assert{ test(?f, upload._value) }
|
450
|
+
end
|
451
|
+
|
452
|
+
#
|
453
|
+
testing 'that the default save uses the mounted _value and _clears it' do
|
454
|
+
conducer_class =
|
455
|
+
new_conducer_class do
|
456
|
+
mount Dao::Upload, :up, :placeholder => '/images/foo.jpg'
|
457
|
+
end
|
458
|
+
|
459
|
+
path = File.join(File.dirname(__FILE__), 'data/han-solo.jpg')
|
460
|
+
assert{ test(?s, path) }
|
461
|
+
up = Upload.new(path)
|
462
|
+
comment = Comment.new
|
463
|
+
|
464
|
+
c = conducer_class.new( comment, :up => {:file => up} )
|
465
|
+
|
466
|
+
upload = assert{ c.get(:up) }
|
467
|
+
assert{ upload.is_a?(Dao::Upload) }
|
468
|
+
|
469
|
+
assert{ test(?f, upload.path) }
|
470
|
+
assert{ File.basename(upload.path) == File.basename(path) }
|
471
|
+
assert{ IO.read(upload.path) == IO.read(path) }
|
472
|
+
|
473
|
+
assert{ c.save }
|
474
|
+
|
475
|
+
value_was_relayed = assert{ comment.attributes[:up] == upload._value }
|
476
|
+
value_was_cleared = assert{ !test(?f, upload.path) }
|
477
|
+
|
478
|
+
assert{ test(?s, path) }
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
##
|
483
|
+
#
|
484
|
+
context :collections do
|
485
|
+
testing 'can be created from page-y blessed arrays' do
|
486
|
+
paginated = Paginated[Post.new, Post.new, Post.new]
|
487
|
+
paginated.limit = 42
|
488
|
+
paginated.offset = 42.0
|
489
|
+
paginated.total_count = 420
|
490
|
+
|
491
|
+
conducer_class = new_conducer_class
|
492
|
+
|
493
|
+
conducer_class.collection_for(paginated)
|
494
|
+
collection = assert{ conducer_class.collection_for(paginated) }
|
495
|
+
assert{ collection.models == paginated }
|
496
|
+
assert{ collection.limit == 42 }
|
497
|
+
assert{ collection.offset == 42.0 }
|
498
|
+
assert{ collection.total_count == 420 }
|
499
|
+
|
500
|
+
user = User.new
|
501
|
+
collection = assert{ conducer_class.collection_for(paginated){|model| conducer_class.for(:show, user, model)} }
|
502
|
+
assert{ collection.all?{|conducer| conducer.action == :show} }
|
503
|
+
assert{ collection.all?{|conducer| conducer.models.first==user} }
|
504
|
+
assert{ collection.all?{|conducer| conducer.models.last.is_a?(Post)} }
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
##
|
509
|
+
#
|
510
|
+
context :callbacks do
|
511
|
+
testing 'can be added lazily in an ad-hoc fashion' do
|
512
|
+
callbacks = []
|
513
|
+
|
514
|
+
conducer_class =
|
515
|
+
new_conducer_class do
|
516
|
+
before_initialize do
|
517
|
+
callbacks.push(:before_initialize)
|
518
|
+
end
|
519
|
+
|
520
|
+
after_initialize do
|
521
|
+
callbacks.push(:after_initialize)
|
522
|
+
end
|
523
|
+
|
524
|
+
define_method(:foobar){ 42 }
|
525
|
+
|
526
|
+
before :foobar do
|
527
|
+
callbacks.push(:before_foobar)
|
528
|
+
end
|
529
|
+
|
530
|
+
after :foobar do
|
531
|
+
callbacks.push(:after_foobar)
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
c = assert{ conducer_class.new }
|
536
|
+
assert{ callbacks == [:before_initialize, :after_initialize] }
|
537
|
+
assert{ c.foobar; true }
|
538
|
+
assert{ callbacks == [:before_initialize, :after_initialize, :before_foobar, :after_foobar] }
|
539
|
+
end
|
540
|
+
end
|
157
541
|
|
158
542
|
protected
|
159
543
|
def new_foo_conducer_class(&block)
|
160
|
-
|
161
|
-
|
544
|
+
const = :FooConducer
|
545
|
+
Object.send(:remove_const, const) if Object.send(:const_defined?, const)
|
546
|
+
name = const.to_s
|
547
|
+
c = assert{ Class.new(Dao::Conducer){ self.name = name } }
|
548
|
+
Object.send(:const_set, const, c)
|
162
549
|
assert{ c.name == 'FooConducer' }
|
163
550
|
assert{ c.model_name == 'Foo' }
|
164
551
|
assert{ c.table_name == 'foos' && c.collection_name == 'foos' }
|
165
|
-
assert{ c.
|
552
|
+
assert{ c.class_eval(&block); true } if block
|
166
553
|
c
|
167
554
|
end
|
555
|
+
alias_method :new_conducer_class, :new_foo_conducer_class
|
168
556
|
|
169
557
|
def new_foo_conducer(*args, &block)
|
170
558
|
assert{ new_foo_conducer_class(&block).new(*args) }
|
171
559
|
end
|
560
|
+
alias_method :new_conducer, :new_foo_conducer
|
172
561
|
|
173
562
|
prepare do
|
174
563
|
$db = Dao::Db.new(:path => 'test/db.yml')
|
@@ -193,6 +582,134 @@ protected
|
|
193
582
|
def collection
|
194
583
|
$db[:foos]
|
195
584
|
end
|
585
|
+
|
586
|
+
class Mounted
|
587
|
+
def Mounted.mount(*args, &block)
|
588
|
+
new(*args, &block)
|
589
|
+
end
|
590
|
+
|
591
|
+
def _set
|
592
|
+
end
|
593
|
+
|
594
|
+
def _key
|
595
|
+
end
|
596
|
+
|
597
|
+
def _value
|
598
|
+
end
|
599
|
+
|
600
|
+
def _clear
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
class Upload < StringIO
|
605
|
+
attr_accessor :path
|
606
|
+
|
607
|
+
def initialize(path)
|
608
|
+
super(IO.read(@path = path))
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
class Model
|
613
|
+
class << self
|
614
|
+
def model_name
|
615
|
+
name = self.name.split(/::/).last
|
616
|
+
ActiveModel::Name.new(Map[:name, name])
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
{
|
621
|
+
:new_record => true,
|
622
|
+
:persisted => false,
|
623
|
+
:destroyed => false,
|
624
|
+
}.each do |state, value|
|
625
|
+
class_eval <<-__
|
626
|
+
attr_writer :#{ state }
|
627
|
+
|
628
|
+
def #{ state }
|
629
|
+
@#{ state } = #{ value } unless defined?(@#{ state })
|
630
|
+
@#{ state }
|
631
|
+
end
|
632
|
+
|
633
|
+
def #{ state }?
|
634
|
+
#{ state }
|
635
|
+
end
|
636
|
+
__
|
637
|
+
end
|
638
|
+
|
639
|
+
def initialize(attributes = {})
|
640
|
+
self.attributes.update(attributes)
|
641
|
+
end
|
642
|
+
|
643
|
+
def attributes
|
644
|
+
@attributes ||= Map.new
|
645
|
+
end
|
646
|
+
|
647
|
+
def [](key)
|
648
|
+
attributes[key]
|
649
|
+
end
|
650
|
+
|
651
|
+
def []=(key, val)
|
652
|
+
attributes[key] = val
|
653
|
+
end
|
654
|
+
|
655
|
+
def update_attributes(hash = {})
|
656
|
+
hash.each{|k,v| attributes[k] = v }
|
657
|
+
end
|
658
|
+
|
659
|
+
def method_missing(method, *args, &block)
|
660
|
+
re = /^([^=!?]+)([=!?])?$/imox
|
661
|
+
matched, key, suffix = re.match(method.to_s).to_a
|
662
|
+
|
663
|
+
case suffix
|
664
|
+
when '=' then attributes.set(key, args.first)
|
665
|
+
when '!' then attributes.set(key, args.size > 0 ? args.first : true)
|
666
|
+
when '?' then attributes.has?(key)
|
667
|
+
else
|
668
|
+
attributes.has?(key) ? attributes.get(key) : super
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
def inspect(*args, &block)
|
673
|
+
"#{ self.class.name }( #{ attributes.inspect } )"
|
674
|
+
end
|
675
|
+
|
676
|
+
def errors
|
677
|
+
@errors ||= Map.new
|
678
|
+
end
|
679
|
+
|
680
|
+
def valid?
|
681
|
+
errors.empty?
|
682
|
+
end
|
683
|
+
|
684
|
+
def save
|
685
|
+
return false unless valid?
|
686
|
+
self.new_record = false
|
687
|
+
self.persisted = true
|
688
|
+
return true
|
689
|
+
end
|
690
|
+
|
691
|
+
def destroy
|
692
|
+
true
|
693
|
+
ensure
|
694
|
+
self.new_record = false
|
695
|
+
self.destroyed = true
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
class Paginated < ::Array
|
700
|
+
attr_accessor :limit
|
701
|
+
attr_accessor :offset
|
702
|
+
attr_accessor :total_count
|
703
|
+
end
|
704
|
+
|
705
|
+
class User < Model
|
706
|
+
end
|
707
|
+
|
708
|
+
class Post < Model
|
709
|
+
end
|
710
|
+
|
711
|
+
class Comment < Model
|
712
|
+
end
|
196
713
|
end
|
197
714
|
|
198
715
|
|
@@ -202,4 +719,5 @@ BEGIN {
|
|
202
719
|
libdir = File.join(rootdir, 'lib')
|
203
720
|
require File.join(libdir, 'dao')
|
204
721
|
require File.join(testdir, 'testing')
|
722
|
+
require 'stringio'
|
205
723
|
}
|