dao 4.2.1 → 4.4.2
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.
- 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
|
}
|