datoki 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/specs/datoki.rb ADDED
@@ -0,0 +1,556 @@
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
+
@@ -0,0 +1,25 @@
1
+
2
+ require 'Bacon_Colored'
3
+ require 'datoki'
4
+ require 'pry'
5
+ require 'sequel'
6
+
7
+ DB = Sequel.connect ENV['DATABASE_URL']
8
+ DB.cache_schema = false
9
+
10
+ Datoki.db DB
11
+ CACHE = {}
12
+
13
+ def reset_db sql = nil
14
+ DB << "DROP TABLE IF EXISTS \"datoki_test\";"
15
+ sql ||= <<-EOF
16
+ CREATE TABLE "datoki_test" (
17
+ id serial NOT NULL PRIMARY KEY,
18
+ title varchar(123) NOT NULL,
19
+ body text
20
+ );
21
+ EOF
22
+ DB << sql
23
+ end # === def reset_db
24
+
25
+ reset_db
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datoki
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - da99
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bacon
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: Bacon_Colored
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sequel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.12'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pg
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0.17'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0.17'
97
+ description: "\n My way of dealing with postgresql 9.3+ databases.\n "
98
+ email:
99
+ - i-hate-spam-1234567@mailinator.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.md
108
+ - VERSION
109
+ - datoki.gemspec
110
+ - lib/datoki.rb
111
+ - specs/datoki.rb
112
+ - specs/lib/helpers.rb
113
+ homepage: https://github.com/da99/datoki
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.4.5
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: A gem to manage PGsql records.
137
+ test_files: []