ohm-contrib 0.0.5 → 0.0.6

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
@@ -1,6 +1,6 @@
1
1
  module Ohm
2
2
  module Contrib
3
- VERSION = '0.0.5'
3
+ VERSION = '0.0.6'
4
4
  end
5
5
 
6
6
  autoload :Boundaries, "ohm/contrib/boundaries"
@@ -6,7 +6,7 @@ module Ohm
6
6
  module Types
7
7
  class String < ::String
8
8
  def self.[](value)
9
- value
9
+ new(value)
10
10
  end
11
11
  end
12
12
 
@@ -29,14 +29,14 @@ module Ohm
29
29
  end
30
30
  end
31
31
 
32
- class Decimal
32
+ class Decimal < BigDecimal
33
33
  CANONICAL = /^(\d+)?(\.\d+)?(E[+\-]\d+)?$/
34
34
 
35
35
  def self.[](value)
36
36
  return value if value.to_s.empty?
37
37
 
38
38
  if value.to_s =~ CANONICAL
39
- BigDecimal(value)
39
+ new(value)
40
40
  else
41
41
  value
42
42
  end
@@ -63,26 +63,74 @@ module Ohm
63
63
  end
64
64
  end
65
65
  end
66
+
67
+ module TypeAssertions
68
+
69
+ protected
70
+ def assert_type_decimal(att, error = [att, :not_decimal])
71
+ assert send(att).is_a?(Ohm::Types::Decimal), error
72
+ end
73
+
74
+ def assert_type_time(att, error = [att, :not_time])
75
+ assert send(att).is_a?(Ohm::Types::Time), error
76
+ end
77
+
78
+ def assert_type_date(att, error = [att, :not_date])
79
+ assert send(att).is_a?(Ohm::Types::Date), error
80
+ end
81
+
82
+ def assert_type_integer(att, error = [att, :not_integer])
83
+ assert send(att).is_a?(Ohm::Types::Integer), error
84
+ end
85
+
86
+ def assert_type_float(att, error = [att, :not_float])
87
+ assert send(att).is_a?(Ohm::Types::Float), error
88
+ end
89
+ end
66
90
 
67
91
  module Typecast
68
- include Types
92
+ MissingValidation = Class.new(StandardError)
69
93
 
94
+ include Types
95
+ include TypeAssertions
96
+
70
97
  def self.included(base)
71
- base.extend Macros
98
+ base.extend ClassMethods
72
99
  end
73
100
 
74
- module Macros
75
- def attribute(name, type = String)
101
+ module ClassMethods
102
+ def attribute(name, type = Ohm::Types::String)
76
103
  define_method(name) do
77
104
  type[read_local(name)]
78
105
  end
79
106
 
80
107
  define_method(:"#{name}=") do |value|
81
- write_local(name, value && value.to_s)
108
+ if type.respond_to?(:dump)
109
+ write_local(name, type.dump(value))
110
+ else
111
+ write_local(name, value && value.to_s)
112
+ end
82
113
  end
83
114
 
84
115
  attributes << name unless attributes.include?(name)
116
+ types[name] = type unless types.has_key?(name)
117
+ end
118
+
119
+ def types
120
+ @types ||= {}
121
+ end
122
+ end
123
+
124
+ def valid?
125
+ return unless super
126
+
127
+ self.class.types.each do |field, type|
128
+ value = send(field)
129
+
130
+ unless value.kind_of?(type)
131
+ raise MissingValidation, "#{field} expected to be #{type}, but was #{value.class}"
132
+ end
85
133
  end
86
134
  end
87
135
  end
88
- end
136
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ohm-contrib}
8
- s.version = "0.0.5"
8
+ s.version = "0.0.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Cyril David"]
12
- s.date = %q{2010-05-15}
12
+ s.date = %q{2010-05-18}
13
13
  s.description = %q{Highly decoupled drop-in functionality for Ohm models}
14
14
  s.email = %q{cyx.ucron@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -16,6 +16,42 @@ class TestOhmTypecast < Test::Unit::TestCase
16
16
 
17
17
  assert_equal 0.1, sum
18
18
  end
19
+
20
+ context "given a non-decimal value FooBar" do
21
+ should "preseve the value FooBar" do
22
+ product = Product.new(:price => "FooBar")
23
+ assert_equal "FooBar", product.price
24
+ end
25
+
26
+ should "raise a MissingValidation error during validation" do
27
+ product = Product.new(:price => "FooBar")
28
+
29
+ assert_raise Ohm::Typecast::MissingValidation do
30
+ product.valid?
31
+ end
32
+ end
33
+ end
34
+
35
+ context "given a non-decimal value FooBar but with assert_type_decimal" do
36
+ class Post < Ohm::Model
37
+ include Ohm::Typecast
38
+
39
+ attribute :price, Decimal
40
+
41
+ def validate
42
+ super
43
+
44
+ assert_type_decimal :price
45
+ end
46
+ end
47
+
48
+ should "be invalid and have an error on the field" do
49
+ post = Post.new(:price => "FooBar")
50
+
51
+ assert ! post.valid?
52
+ assert_equal [[:price, :not_decimal]], post.errors
53
+ end
54
+ end
19
55
  end
20
56
 
21
57
  context "time typecasting" do
@@ -30,7 +66,7 @@ class TestOhmTypecast < Test::Unit::TestCase
30
66
  product = Product.create(:start_of_sale => @time)
31
67
  product = Product[product.id]
32
68
 
33
- assert_equal @time, product.start_of_sale
69
+ assert_equal @time.to_s, product.start_of_sale.to_s
34
70
  end
35
71
 
36
72
  test "the retrieved value is a Time object" do
@@ -42,10 +78,9 @@ class TestOhmTypecast < Test::Unit::TestCase
42
78
  end
43
79
 
44
80
  test "an invalid string" do
45
- product = Product.create(:start_of_sale => "invalid time")
46
- product = Product[product.id]
47
-
48
- assert_equal "invalid time", product.start_of_sale
81
+ assert_raise Ohm::Typecast::MissingValidation do
82
+ product = Product.create(:start_of_sale => "invalid time")
83
+ end
49
84
  end
50
85
 
51
86
  test "when nil" do
@@ -59,6 +94,26 @@ class TestOhmTypecast < Test::Unit::TestCase
59
94
  end
60
95
  end
61
96
 
97
+ context "time typecasting with assert_type_time" do
98
+ class Post < Ohm::Model
99
+ include Ohm::Typecast
100
+
101
+ attribute :start_of_sale, Time
102
+
103
+ def validate
104
+ super
105
+
106
+ assert_type_time :start_of_sale
107
+ end
108
+ end
109
+
110
+ should "be valid given invalid time value" do
111
+ post = Post.new(:start_of_sale => "FooBar")
112
+ assert ! post.valid?
113
+ assert_equal [[:start_of_sale, :not_time]], post.errors
114
+ end
115
+ end
116
+
62
117
  context "date typecasting" do
63
118
  class Product < Ohm::Model
64
119
  include Ohm::Typecast
@@ -82,10 +137,10 @@ class TestOhmTypecast < Test::Unit::TestCase
82
137
  assert_kind_of Date, product.date_bought
83
138
  end
84
139
 
85
- test "assigning a string which is not a valid date before persisting" do
86
- product = Product.create(:date_bought => "Bla Bla")
87
-
88
- assert_equal "Bla Bla", product.date_bought
140
+ test "assigning a string which is not a valid date" do
141
+ assert_raise Ohm::Typecast::MissingValidation do
142
+ product = Product.create(:date_bought => "Bla Bla")
143
+ end
89
144
  end
90
145
 
91
146
  test "when nil" do
@@ -97,6 +152,25 @@ class TestOhmTypecast < Test::Unit::TestCase
97
152
  end
98
153
  end
99
154
 
155
+ context "date typecasting with assert_type_date" do
156
+ class Post < Ohm::Model
157
+ include Ohm::Typecast
158
+
159
+ attribute :created, Date
160
+
161
+ def validate
162
+ assert_type_date :created
163
+ end
164
+ end
165
+
166
+ should "have an invalid created field on save" do
167
+ post = Post.new(:created => "FooBar")
168
+ assert ! post.valid?
169
+ assert_equal [[:created, :not_date]], post.errors
170
+ assert_equal "FooBar", post.created
171
+ end
172
+ end
173
+
100
174
  context "integer typecasting" do
101
175
  class Product < Ohm::Model
102
176
  include Ohm::Typecast
@@ -125,6 +199,25 @@ class TestOhmTypecast < Test::Unit::TestCase
125
199
  end
126
200
  end
127
201
 
202
+ context "integer typecasting with assert_type_integer validation" do
203
+ class Post < Ohm::Model
204
+ include Ohm::Typecast
205
+
206
+ attribute :counter, Integer
207
+
208
+ def validate
209
+ assert_type_integer :counter
210
+ end
211
+ end
212
+
213
+ should "have an error on counter given a non-integer value" do
214
+ post = Post.new(:counter => "FooBar")
215
+ assert ! post.valid?
216
+ assert_equal [[:counter, :not_integer]], post.errors
217
+ assert_equal "FooBar", post.counter
218
+ end
219
+ end
220
+
128
221
  context "float typecasting" do
129
222
  class Product < Ohm::Model
130
223
  include Ohm::Typecast
@@ -157,6 +250,26 @@ class TestOhmTypecast < Test::Unit::TestCase
157
250
  end
158
251
  end
159
252
 
253
+ context "float typecasting with assert_type_float validation" do
254
+ class Post < Ohm::Model
255
+ include Ohm::Typecast
256
+
257
+ attribute :quotient, Float
258
+
259
+ def validate
260
+ assert_type_float :quotient
261
+ end
262
+ end
263
+
264
+ should "have an error on quotient given a non-float value" do
265
+ post = Post.new(:quotient => "FooBar")
266
+ assert ! post.valid?
267
+ assert_equal [[:quotient, :not_float]], post.errors
268
+ assert_equal "FooBar", post.quotient
269
+ end
270
+ end
271
+
272
+
160
273
  context "trying to validate numericality of a decimal" do
161
274
  class Item < Ohm::Model
162
275
  include Ohm::Typecast
@@ -177,4 +290,82 @@ class TestOhmTypecast < Test::Unit::TestCase
177
290
  assert_equal [[:price, :not_numeric]], @item.errors
178
291
  end
179
292
  end
180
- end
293
+
294
+ context "specifying a custom DataType without a #dump method" do
295
+ class ::FooBar < Struct.new(:value)
296
+ def self.[](str)
297
+ new(str)
298
+ end
299
+ end
300
+
301
+ class Item < Ohm::Model
302
+ include Ohm::Typecast
303
+
304
+ attribute :foo, FooBar
305
+ end
306
+
307
+ should "use it as expected" do
308
+ item = Item.new(:foo => "bar")
309
+ assert_equal FooBar.new("bar"), item.foo
310
+ end
311
+ end
312
+
313
+ context "specifying a customer DataType but with a #dump method" do
314
+ class ::CSV < Array
315
+ def self.dump(arr)
316
+ arr.join(',')
317
+ end
318
+
319
+ def self.[](str)
320
+ new(str.split(','))
321
+ end
322
+
323
+ def to_s
324
+ join(',')
325
+ end
326
+ end
327
+
328
+ class Item < Ohm::Model
329
+ include Ohm::Typecast
330
+
331
+ attribute :csv, CSV
332
+ end
333
+
334
+
335
+ should "dump the appropriate value" do
336
+ item = Item.new(:csv => [ 'first', 'second', 'third'])
337
+
338
+ assert_equal 'first,second,third', item.send(:read_local, :csv)
339
+ end
340
+
341
+ should "be able to retrieve it back properly" do
342
+ item = Item.create(:csv => [ 'first', 'second', 'third'])
343
+ item = Item[item.id]
344
+
345
+ assert_equal ['first', 'second', 'third'], item.csv
346
+ end
347
+
348
+ should "also be able to read/write with raw CSV" do
349
+ item = Item.create(:csv => CSV.new(['first', 'second', 'third']))
350
+ item = Item[item.id]
351
+
352
+ assert_equal ['first', 'second', 'third'], item.csv
353
+ end
354
+ end
355
+
356
+ context "defaut Strings" do
357
+ class Post < Ohm::Model
358
+ include Ohm::Typecast
359
+
360
+ attribute :field1
361
+ attribute :field2
362
+ attribute :field3
363
+ end
364
+
365
+ should "not throw a validation missing exception during save" do
366
+ assert_nothing_raised Ohm::Typecast::MissingValidation do
367
+ Post.create :field1 => "field1", :field2 => "field2", :field3 => "fld3"
368
+ end
369
+ end
370
+ end
371
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 5
9
- version: 0.0.5
8
+ - 6
9
+ version: 0.0.6
10
10
  platform: ruby
11
11
  authors:
12
12
  - Cyril David
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-15 00:00:00 +08:00
17
+ date: 2010-05-18 00:00:00 +08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency