datoki 1.0.1 → 2.0.0
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 +4 -4
- data/README.md +29 -1
- data/VERSION +1 -1
- data/lib/datoki.rb +322 -247
- data/specs/0010-Datoki.db.new.rb +31 -0
- data/specs/0010-Datoki.db.rb +67 -0
- data/specs/0010-Numeric.rb +66 -0
- data/specs/0010-Numeric_db.rb +28 -0
- data/specs/0010-href.rb +75 -0
- data/specs/0010-html_escape.rb +44 -0
- data/specs/0010-no_type.rb +14 -0
- data/specs/0010-schema_conflict.rb +38 -0
- data/specs/0010-varchar.rb +98 -0
- data/specs/0010-varchar_db.rb +33 -0
- data/specs/0020-clean.rb +58 -0
- data/specs/0020-on.rb +64 -0
- metadata +14 -3
- data/specs/datoki.rb +0 -556
data/specs/datoki.rb
DELETED
@@ -1,556 +0,0 @@
|
|
1
|
-
|
2
|
-
describe 'record_errors' do
|
3
|
-
|
4
|
-
it "prevents failing with an exception" do
|
5
|
-
r = Class.new {
|
6
|
-
include Datoki
|
7
|
-
record_errors
|
8
|
-
field(:title) { varchar }
|
9
|
-
}.create
|
10
|
-
|
11
|
-
r.errors.should == {:title=>{:msg=>'Title is required.', :value=>nil}}
|
12
|
-
end
|
13
|
-
|
14
|
-
end # === describe record_errors ====================================
|
15
|
-
|
16
|
-
describe 'No type' do
|
17
|
-
|
18
|
-
it "requires type to be specified" do
|
19
|
-
should.raise(RuntimeError) {
|
20
|
-
Class.new {
|
21
|
-
include Datoki
|
22
|
-
field(:title) { }
|
23
|
-
}
|
24
|
-
}.message.should.match /Type not specified/
|
25
|
-
end
|
26
|
-
|
27
|
-
end # === describe 'No type' ========================================
|
28
|
-
|
29
|
-
describe :varchar do # ================================================
|
30
|
-
|
31
|
-
it "requires field by default" do
|
32
|
-
should.raise(Datoki::Invalid) {
|
33
|
-
Class.new {
|
34
|
-
include Datoki
|
35
|
-
field(:title) { varchar }
|
36
|
-
}.create
|
37
|
-
}.message.should.match /Title is required/i
|
38
|
-
end
|
39
|
-
|
40
|
-
it "raises RuntimeError if allow :null and :min = 0" do
|
41
|
-
should.raise(RuntimeError) {
|
42
|
-
Class.new {
|
43
|
-
include Datoki
|
44
|
-
field(:name) { varchar nil, 0, 50 }
|
45
|
-
}
|
46
|
-
}.message.should.match /varchar can't be both: allow :null && :min = 0/
|
47
|
-
end
|
48
|
-
|
49
|
-
it "fails when varchar is less than min: varchar x, y" do
|
50
|
-
should.raise(Datoki::Invalid) {
|
51
|
-
Class.new {
|
52
|
-
include Datoki
|
53
|
-
field(:title) { varchar 3, 255 }
|
54
|
-
}.create :title => '1'
|
55
|
-
}.message.should.match /Title must be between 3 and 255 characters/i
|
56
|
-
end
|
57
|
-
|
58
|
-
it "fails when varchar is longer than max" do
|
59
|
-
should.raise(Datoki::Invalid) {
|
60
|
-
Class.new {
|
61
|
-
include Datoki
|
62
|
-
field(:title) { varchar 0, 5 }
|
63
|
-
}.create :title => '123456'
|
64
|
-
}.message.should.match /Title must be between 0 and 5 characters/
|
65
|
-
end
|
66
|
-
|
67
|
-
it "fails when varchar does not match pattern: match /../" do
|
68
|
-
should.raise(Datoki::Invalid) {
|
69
|
-
Class.new {
|
70
|
-
include Datoki
|
71
|
-
field :title do
|
72
|
-
varchar
|
73
|
-
match /\A[a-zA-Z0-9]+\z/i, "Title must be only: alphanumeric"
|
74
|
-
end
|
75
|
-
}.create :title => '$! title'
|
76
|
-
}.message.should.match /Title must be only: alphanumeric/
|
77
|
-
end
|
78
|
-
|
79
|
-
it "allows varchar to be nil" do
|
80
|
-
r = Class.new {
|
81
|
-
include Datoki
|
82
|
-
field(:title) {
|
83
|
-
varchar nil, 1, 123
|
84
|
-
}
|
85
|
-
}.create()
|
86
|
-
r.clean_data[:title].should == nil
|
87
|
-
end
|
88
|
-
|
89
|
-
it "sets field to return value of :set_to" do
|
90
|
-
Class.new {
|
91
|
-
include Datoki
|
92
|
-
field(:title) {
|
93
|
-
varchar
|
94
|
-
set_to :custom_error
|
95
|
-
def custom_error
|
96
|
-
'Custom title'
|
97
|
-
end
|
98
|
-
}
|
99
|
-
}.
|
100
|
-
create(:title => 'My Title').
|
101
|
-
clean_data[:title].should.match /Custom title/
|
102
|
-
end
|
103
|
-
|
104
|
-
it "strips varchars by default" do
|
105
|
-
Class.new {
|
106
|
-
include Datoki
|
107
|
-
field(:title) { varchar }
|
108
|
-
}.
|
109
|
-
create(:title => ' my title ').
|
110
|
-
clean_data[:title].should == 'my title'
|
111
|
-
end
|
112
|
-
|
113
|
-
it "can prevent varchar from being stripped" do
|
114
|
-
Class.new {
|
115
|
-
include Datoki
|
116
|
-
field(:title) {
|
117
|
-
varchar
|
118
|
-
disable :strip
|
119
|
-
}
|
120
|
-
}.
|
121
|
-
create(:title => ' my title ').
|
122
|
-
clean_data[:title].should == ' my title '
|
123
|
-
end
|
124
|
-
|
125
|
-
end # === describe Datoki ===
|
126
|
-
|
127
|
-
describe Numeric do
|
128
|
-
|
129
|
-
it "fails if number is outside the range" do
|
130
|
-
should.raise(Datoki::Invalid) {
|
131
|
-
Class.new {
|
132
|
-
include Datoki
|
133
|
-
field(:age) { smallint 1, 150 }
|
134
|
-
}.create :age=>0
|
135
|
-
}.message.should.match /age must be between 1 and 150/i
|
136
|
-
end
|
137
|
-
|
138
|
-
it "raises an exception if value is a non-numeric varchar." do
|
139
|
-
should.raise(Datoki::Invalid) {
|
140
|
-
Class.new {
|
141
|
-
include Datoki
|
142
|
-
field(:age) { smallint 1, 150 }
|
143
|
-
}.create :age=>'twenty-two'
|
144
|
-
}.message.should.match /age must be numeric/i
|
145
|
-
end
|
146
|
-
|
147
|
-
it "allows nil" do
|
148
|
-
Class.new {
|
149
|
-
include Datoki
|
150
|
-
field(:age) { smallint nil, 1, 99 }
|
151
|
-
}.create(:age=>nil).
|
152
|
-
clean_data[:age].should == nil
|
153
|
-
end
|
154
|
-
|
155
|
-
it "allows nil in an array" do
|
156
|
-
Class.new {
|
157
|
-
include Datoki
|
158
|
-
field(:age) { smallint [nil, 1,2,3,4] }
|
159
|
-
}.create(:age=>nil).
|
160
|
-
clean_data[:age].should == nil
|
161
|
-
end
|
162
|
-
|
163
|
-
it "allows to specify an Array of possible values" do
|
164
|
-
Class.new {
|
165
|
-
include Datoki
|
166
|
-
field(:age) { smallint [1,2,3,4] }
|
167
|
-
}.create(:age=>2).
|
168
|
-
clean_data[:age].should == 2
|
169
|
-
end
|
170
|
-
|
171
|
-
it "fails if value is not in Array of possible values" do
|
172
|
-
should.raise(Datoki::Invalid) {
|
173
|
-
Class.new {
|
174
|
-
include Datoki
|
175
|
-
field(:num) { smallint [1,2,3,4] }
|
176
|
-
}.create :num=>0
|
177
|
-
}.message.should.match /Num can only be: 1, 2, 3, 4/
|
178
|
-
end
|
179
|
-
|
180
|
-
end # === describe Numeric
|
181
|
-
|
182
|
-
describe "on :create" do
|
183
|
-
|
184
|
-
it "after all fields have been cleaned" do
|
185
|
-
Class.new {
|
186
|
-
|
187
|
-
include Datoki
|
188
|
-
|
189
|
-
on :create, def collect_values
|
190
|
-
clean_data[:values] = clean_data.values.join ', '
|
191
|
-
end
|
192
|
-
|
193
|
-
field(:title) { varchar }
|
194
|
-
|
195
|
-
field(:body) { varchar }
|
196
|
-
|
197
|
-
}.
|
198
|
-
create(:title=>'my title', :body=>'my body').
|
199
|
-
clean_data[:values].should == 'my title, my body'
|
200
|
-
end
|
201
|
-
|
202
|
-
it "runs after validation for a field" do
|
203
|
-
Class.new {
|
204
|
-
include Datoki
|
205
|
-
field(:body) {
|
206
|
-
on :create, def add_stuff
|
207
|
-
clean_data[:body] << ' with new stuff'
|
208
|
-
end
|
209
|
-
|
210
|
-
varchar
|
211
|
-
}
|
212
|
-
}.
|
213
|
-
create(:body=>'the body').
|
214
|
-
clean_data[:body].should == 'the body with new stuff'
|
215
|
-
end
|
216
|
-
|
217
|
-
end # === describe on :create
|
218
|
-
|
219
|
-
describe "on :update" do
|
220
|
-
|
221
|
-
it "runs after data has been cleaned" do
|
222
|
-
r = Class.new {
|
223
|
-
include Datoki
|
224
|
-
on :update, def do_something
|
225
|
-
clean_data[:vals] = clean_data.values.join ' -- '
|
226
|
-
end
|
227
|
-
|
228
|
-
field(:title) { varchar }
|
229
|
-
field(:body) { varchar }
|
230
|
-
}.new(:title=>'old title')
|
231
|
-
r.update title: ' new title ', :body=>' new body '
|
232
|
-
r.clean_data[:vals].should == 'new title -- new body'
|
233
|
-
end
|
234
|
-
|
235
|
-
end # === describe on :update
|
236
|
-
|
237
|
-
describe "Datoki.db" do
|
238
|
-
|
239
|
-
before {
|
240
|
-
|
241
|
-
CACHE[:datoki_db_test] ||= reset_db <<-EOF
|
242
|
-
CREATE TABLE "datoki_test" (
|
243
|
-
id serial NOT NULL PRIMARY KEY,
|
244
|
-
title varchar(123) NOT NULL,
|
245
|
-
body text DEFAULT 'hello'
|
246
|
-
);
|
247
|
-
EOF
|
248
|
-
|
249
|
-
@klass = Class.new {
|
250
|
-
include Datoki
|
251
|
-
record_errors
|
252
|
-
table "datoki_test"
|
253
|
-
field(:id) { integer; primary_key }
|
254
|
-
field(:title) { varchar 1, 123 }
|
255
|
-
field(:body) { text nil, 1, 123 }
|
256
|
-
}
|
257
|
-
}
|
258
|
-
|
259
|
-
it 'raises Schema_Conflict if a field is found that allows null, but not specifed to do so' do
|
260
|
-
should.raise(Datoki::Schema_Conflict) {
|
261
|
-
Class.new {
|
262
|
-
include Datoki
|
263
|
-
table :datoki_test
|
264
|
-
field(:id) { integer; primary_key }
|
265
|
-
field(:title) { varchar 1, 123 }
|
266
|
-
field(:body) { text 1, 123 }
|
267
|
-
}
|
268
|
-
}.message.should.match /:allow_null: true != false/
|
269
|
-
end
|
270
|
-
|
271
|
-
it "requires field if value = null and default = null and :allow_null = false" do
|
272
|
-
r = @klass.create :title=>nil, :body=>"hiya"
|
273
|
-
r.errors.should == {:title=>{:msg=>'Title is required.', :value=>nil}}
|
274
|
-
end
|
275
|
-
|
276
|
-
it "requires a value if: :text field, value = (empty string), min = 1, allow null" do
|
277
|
-
r = @klass.create :title=>"The title", :body=>' '
|
278
|
-
r.errors.should == {:body=>{:msg=>'Body is required.', :value=>""}}
|
279
|
-
end
|
280
|
-
|
281
|
-
it "does not turn strip.empty? strings into nulls" do
|
282
|
-
r = @klass.create :title=>"The title", :body=>' '
|
283
|
-
r.clean_data[:body].should == ''
|
284
|
-
end
|
285
|
-
|
286
|
-
it "imports field names into class" do
|
287
|
-
@klass.fields.keys.should == [:id, :title, :body]
|
288
|
-
end
|
289
|
-
|
290
|
-
it "imports field types into class" do
|
291
|
-
@klass.fields.values.map { |meta| meta[:type] }.should == [:integer, :varchar, :text]
|
292
|
-
end
|
293
|
-
|
294
|
-
it "removes field from :clean_data if set to nil and database has a default value" do
|
295
|
-
r = @klass.create :title=>'hello', :body=>nil
|
296
|
-
r.clean_data.keys.should == [:title]
|
297
|
-
end
|
298
|
-
|
299
|
-
end # === describe Datoki.db
|
300
|
-
|
301
|
-
describe "Datoki.db Schema_Conflict" do
|
302
|
-
|
303
|
-
before {
|
304
|
-
CACHE[:schema_conflict] ||= begin
|
305
|
-
reset_db <<-EOF
|
306
|
-
CREATE TABLE "datoki_test" (
|
307
|
-
id serial NOT NULL PRIMARY KEY,
|
308
|
-
title varchar(123),
|
309
|
-
body varchar(255) NOT NULL,
|
310
|
-
created_at timestamp with time zone NOT NULL DEFAULT timezone('UTC'::text, now())
|
311
|
-
);
|
312
|
-
EOF
|
313
|
-
end
|
314
|
-
}
|
315
|
-
|
316
|
-
it "raises Schema_Conflict when specified to allow nil, but db doesn not" do
|
317
|
-
should.raise(Datoki::Schema_Conflict) {
|
318
|
-
Class.new {
|
319
|
-
include Datoki
|
320
|
-
table :datoki_test
|
321
|
-
field(:body) { varchar nil, 1, 255 }
|
322
|
-
}
|
323
|
-
}.message.should.match /:allow_null: false != true/i
|
324
|
-
end
|
325
|
-
|
326
|
-
it "raises Schema_Conflict when there is a :max_length conflict" do
|
327
|
-
should.raise(Datoki::Schema_Conflict) {
|
328
|
-
Class.new {
|
329
|
-
include Datoki
|
330
|
-
table :datoki_test
|
331
|
-
field(:title) { varchar nil, 1, 200 }
|
332
|
-
}
|
333
|
-
}.message.should.match /:max: 123 != 200/i
|
334
|
-
end
|
335
|
-
|
336
|
-
end # === describe Datoki.db
|
337
|
-
|
338
|
-
describe "Datoki.db :varchar" do
|
339
|
-
|
340
|
-
before {
|
341
|
-
CACHE[:datoki_db_varchar] ||= reset_db <<-EOF
|
342
|
-
CREATE TABLE "datoki_test" (
|
343
|
-
id serial NOT NULL PRIMARY KEY,
|
344
|
-
title varchar(123) NOT NULL,
|
345
|
-
body text
|
346
|
-
);
|
347
|
-
EOF
|
348
|
-
@klass = Class.new {
|
349
|
-
include Datoki
|
350
|
-
table "datoki_test"
|
351
|
-
field(:id) { primary_key }
|
352
|
-
field(:title) { varchar 1, 123 }
|
353
|
-
field(:body) { text nil, 1, 3000 }
|
354
|
-
}
|
355
|
-
}
|
356
|
-
|
357
|
-
it "imports max length" do
|
358
|
-
@klass.fields[:title][:max].should == 123
|
359
|
-
end
|
360
|
-
|
361
|
-
it "sets :min = 1 (by default, during import, if NOT NULL)" do
|
362
|
-
@klass.fields[:title][:min].should == 1
|
363
|
-
end
|
364
|
-
|
365
|
-
it "sets :min = 1 (by default, during import, if :allow_null = true)" do
|
366
|
-
@klass.fields[:body][:min].should == 1
|
367
|
-
end
|
368
|
-
|
369
|
-
end # === describe Datoki.db :varchar
|
370
|
-
|
371
|
-
describe 'Datoki.db number' do
|
372
|
-
|
373
|
-
before {
|
374
|
-
CACHE[:datoki_db_number] ||= begin
|
375
|
-
reset_db <<-EOF
|
376
|
-
CREATE TABLE "datoki_test" (
|
377
|
-
id serial NOT NULL PRIMARY KEY,
|
378
|
-
parent_id smallint NOT NULL,
|
379
|
-
title varchar(123) NOT NULL,
|
380
|
-
body text
|
381
|
-
);
|
382
|
-
EOF
|
383
|
-
end
|
384
|
-
}
|
385
|
-
|
386
|
-
it "does not set :min = 1" do
|
387
|
-
Class.new {
|
388
|
-
include Datoki
|
389
|
-
table "datoki_test"
|
390
|
-
field(:parent_id) { smallint }
|
391
|
-
}.
|
392
|
-
fields[:parent_id][:min].should == nil
|
393
|
-
end
|
394
|
-
|
395
|
-
end # === Datoki.db number
|
396
|
-
|
397
|
-
describe 'Datoki.db :new' do
|
398
|
-
|
399
|
-
before {
|
400
|
-
CACHE[:datoki_db_new] ||= begin
|
401
|
-
reset_db <<-EOF
|
402
|
-
CREATE TABLE "datoki_test" (
|
403
|
-
id serial NOT NULL PRIMARY KEY,
|
404
|
-
parent_id smallint NOT NULL,
|
405
|
-
title varchar(123) NOT NULL,
|
406
|
-
body text
|
407
|
-
);
|
408
|
-
EOF
|
409
|
-
end
|
410
|
-
}
|
411
|
-
|
412
|
-
it "raises Schema_Conflict if field has not been defined, but exists in the db schema" do
|
413
|
-
should.raise(Datoki::Schema_Conflict) {
|
414
|
-
Class.new {
|
415
|
-
include Datoki
|
416
|
-
table :datoki_test
|
417
|
-
field(:id) { primary_key }
|
418
|
-
field(:parent_id) { smallint }
|
419
|
-
field(:body) { text nil, 1, 222 }
|
420
|
-
}.new
|
421
|
-
}.message.should.match /:title has not been defined/
|
422
|
-
end
|
423
|
-
|
424
|
-
end # === describe Datoki.db :new
|
425
|
-
|
426
|
-
|
427
|
-
describe :href do
|
428
|
-
|
429
|
-
before {
|
430
|
-
CACHE[:datoki_db_href] ||= begin
|
431
|
-
reset_db <<-EOF
|
432
|
-
CREATE TABLE "datoki_test" (
|
433
|
-
id serial NOT NULL PRIMARY KEY,
|
434
|
-
homepage varchar(255) NOT NULL
|
435
|
-
);
|
436
|
-
EOF
|
437
|
-
end
|
438
|
-
|
439
|
-
@klass = Class.new {
|
440
|
-
include Datoki
|
441
|
-
table :datoki_test
|
442
|
-
field(:id) { primary_key }
|
443
|
-
field(:homepage) { href }
|
444
|
-
}
|
445
|
-
}
|
446
|
-
|
447
|
-
it "sets :type to :varchar" do
|
448
|
-
@klass.fields[:homepage][:type].should == :varchar
|
449
|
-
end
|
450
|
-
|
451
|
-
it "sets :max to 255" do
|
452
|
-
@klass.fields[:homepage][:max].should == 255
|
453
|
-
end
|
454
|
-
|
455
|
-
it "sets :min to 0" do
|
456
|
-
@klass.fields[:homepage][:min].should == 0
|
457
|
-
end
|
458
|
-
|
459
|
-
it "sets allow :null to false" do
|
460
|
-
@klass.fields[:homepage][:allow][:null].should == false
|
461
|
-
end
|
462
|
-
|
463
|
-
it "sets :html_escape to :href" do
|
464
|
-
@klass.fields[:homepage][:html_escape].should == :href
|
465
|
-
end
|
466
|
-
|
467
|
-
it "accepts a :min and :max" do
|
468
|
-
CACHE[:datoki_db_href] = nil
|
469
|
-
reset_db <<-EOF
|
470
|
-
CREATE TABLE "datoki_test" (
|
471
|
-
id serial NOT NULL PRIMARY KEY,
|
472
|
-
homepage varchar(123) NOT NULL
|
473
|
-
);
|
474
|
-
EOF
|
475
|
-
k = Class.new {
|
476
|
-
include Datoki
|
477
|
-
table :datoki_test
|
478
|
-
field(:id) { primary_key }
|
479
|
-
field(:homepage) { href 5, 123 }
|
480
|
-
}
|
481
|
-
k.fields[:homepage][:min].should == 5
|
482
|
-
k.fields[:homepage][:max].should == 123
|
483
|
-
end
|
484
|
-
|
485
|
-
it "sets :min = 1 when null is allowed." do
|
486
|
-
CACHE[:datoki_db_href] = nil
|
487
|
-
reset_db <<-EOF
|
488
|
-
CREATE TABLE "datoki_test" (
|
489
|
-
id serial NOT NULL PRIMARY KEY,
|
490
|
-
homepage varchar(222)
|
491
|
-
);
|
492
|
-
EOF
|
493
|
-
k = Class.new {
|
494
|
-
include Datoki
|
495
|
-
table :datoki_test
|
496
|
-
field(:id) { primary_key }
|
497
|
-
field(:homepage) { href nil }
|
498
|
-
}
|
499
|
-
k.fields[:homepage][:min].should == 1
|
500
|
-
k.fields[:homepage][:max].should == 222
|
501
|
-
end
|
502
|
-
|
503
|
-
end # === describe :href
|
504
|
-
|
505
|
-
|
506
|
-
describe :html_escape do
|
507
|
-
|
508
|
-
before {
|
509
|
-
CACHE[:datoki_db_escape] ||= begin
|
510
|
-
reset_db <<-EOF
|
511
|
-
CREATE TABLE "datoki_test" (
|
512
|
-
id serial NOT NULL PRIMARY KEY,
|
513
|
-
parent_id smallint NOT NULL,
|
514
|
-
title varchar(123) NOT NULL,
|
515
|
-
url varchar(255) NOT NULL,
|
516
|
-
body text NOT NULL
|
517
|
-
);
|
518
|
-
EOF
|
519
|
-
end
|
520
|
-
|
521
|
-
@klass = Class.new {
|
522
|
-
include Datoki
|
523
|
-
table :datoki_test
|
524
|
-
field(:id) { primary_key }
|
525
|
-
field(:parent_id) { smallint }
|
526
|
-
field(:title) { varchar 1, 123 }
|
527
|
-
field(:url) { href }
|
528
|
-
field(:body) { text 1, 244 }
|
529
|
-
}
|
530
|
-
}
|
531
|
-
|
532
|
-
it "returns a hash of all defined fields" do
|
533
|
-
@klass.html_escape.should == {
|
534
|
-
:id => :number,
|
535
|
-
:parent_id => :number,
|
536
|
-
:title => :string,
|
537
|
-
:url => :href,
|
538
|
-
:body => :string
|
539
|
-
}
|
540
|
-
end
|
541
|
-
|
542
|
-
it "sets :href for urls" do
|
543
|
-
@klass.html_escape[:url].should == :href
|
544
|
-
end
|
545
|
-
|
546
|
-
end # === describe :html_escape
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|