ohm-contrib 0.0.20 → 0.0.21

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -68,6 +68,76 @@ Example usage
68
68
 
69
69
  Typecasting explained
70
70
  ---------------------
71
+
72
+ I studied various typecasting behaviors implemented by a few ORMs in Ruby.
73
+
74
+ ### ActiveRecord
75
+
76
+ class Post < ActiveRecord::Base
77
+ # say we have an integer column in the DB named votes
78
+ end
79
+ Post.new(:votes => "FooBar").votes == 0
80
+ # => true
81
+
82
+ ### DataMapper
83
+ class Post
84
+ include DataMapper::Resource
85
+
86
+ property :id, Serial
87
+ property :votes, Integer
88
+ end
89
+
90
+ post = Post.new(:votes => "FooBar")
91
+ post.votes == "FooBar"
92
+ # => true
93
+
94
+ post.save
95
+ post.reload
96
+
97
+ # Get ready!!!!
98
+ post.votes == 0
99
+ # => true
100
+
101
+ ### Ohm::Typecast approach.
102
+
103
+ #### Mindset:
104
+
105
+ 1. Explosion everytime is too cumbersome.
106
+ 2. Mutation of data is less than ideal (Also similar to MySQL silently allowing you
107
+ to store more than 255 chars in a VARCHAR and then truncating that data. Yes I know
108
+ you can configure it to be noisy but the defaults kill).
109
+ 3. We just want to operate on it like it should!
110
+
111
+ #### Short Demo:
112
+ class Post < Ohm::Model
113
+ include Ohm::Typecast
114
+ attribute :votes
115
+ end
116
+
117
+ post = Post.new(:votes => "FooBar")
118
+ post.votes == "FooBar"
119
+ # => true
120
+
121
+ post.save
122
+ post = Post[post.id]
123
+ post.votes == "FooBar"
124
+ # => true
125
+
126
+ # Here comes the cool part...
127
+ post.votes * 1
128
+ # => ArgumentError: invalid value for Integer: "FooBar"
129
+
130
+ post.votes = 50
131
+ post.votes * 2 == 100
132
+ # => true
133
+
134
+ post.votes.class == Ohm::Types::Integer
135
+ # => true
136
+ post.votes.inspect == "50"
137
+ # => true
138
+
139
+ #### More examples just to show the normal case.
140
+
71
141
  require 'ohm'
72
142
  require 'ohm/contrib'
73
143
 
@@ -77,9 +147,14 @@ Typecasting explained
77
147
  attribute :price, Decimal
78
148
  attribute :available_at, Time
79
149
  attribute :stock, Integer
150
+ attribute :address, Hash
151
+ attribute :tags, Array
80
152
  end
81
153
 
82
- post = Post.create(:price => "10.20", :stock => "100")
154
+ post = Post.create(:price => "10.20", :stock => "100",
155
+ :address => { "city" => "Boston", "country" => "US" },
156
+ :tags => ["redis", "ohm", "typecast"])
157
+
83
158
  post.price.to_s == "10.20"
84
159
  # => true
85
160
 
@@ -89,6 +164,23 @@ Typecasting explained
89
164
  post.stock / 10 == 10
90
165
  # => true
91
166
 
167
+ post.address["city"] == "Boston"
168
+ post.tags.map { |tag| tag.upcase }
169
+
170
+ # of course mutation works for both cases
171
+ post.price += 5
172
+ post.stock -= 1
173
+ post.tags << "contrib"
174
+ post.address["state"] = "MA"
175
+ post.save
176
+ post = Post[post.id]
177
+
178
+ post.address["state"] == "MA"
179
+ # => true
180
+ post.tags.include?("contrib")
181
+ # => true
182
+
183
+
92
184
  Credits
93
185
  -------
94
186
  Thanks to github user gnrfan for the web validations.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.20
1
+ 0.0.21
data/lib/ohm/contrib.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Ohm
2
2
  module Contrib
3
- VERSION = '0.0.20'
3
+ VERSION = '0.0.21'
4
4
  end
5
5
 
6
6
  autoload :Boundaries, "ohm/contrib/boundaries"
@@ -170,4 +170,4 @@ module Ohm
170
170
  end
171
171
  end
172
172
  end
173
- end
173
+ end
@@ -24,7 +24,7 @@ module Ohm
24
24
  def self.[](type)
25
25
  const_get(type.to_s.split('::').last)
26
26
  end
27
-
27
+
28
28
  class Base < BasicObject
29
29
  class Exception < ::Exception; end
30
30
 
@@ -36,23 +36,23 @@ module Ohm
36
36
 
37
37
  def self.[](value)
38
38
  return self::EMPTY if value.to_s.empty?
39
-
39
+
40
40
  new(value)
41
41
  end
42
42
 
43
43
  def self.delegate_to(klass, except = @@delegation_blacklist)
44
44
  methods = klass.public_instance_methods.map(&:to_sym) - except
45
- def_delegators :object, *methods
45
+ def_delegators :object, *methods
46
46
  end
47
47
 
48
48
  def inspect
49
- object.inspect
49
+ @raw.inspect
50
50
  end
51
51
  end
52
52
 
53
53
  class Primitive < Base
54
54
  EMPTY = nil
55
-
55
+
56
56
  def initialize(value)
57
57
  @raw = value
58
58
  end
@@ -64,7 +64,7 @@ module Ohm
64
64
  def ==(other)
65
65
  to_s == other.to_s
66
66
  end
67
-
67
+
68
68
  protected
69
69
  def object
70
70
  @raw
@@ -78,10 +78,6 @@ module Ohm
78
78
  class Decimal < Primitive
79
79
  delegate_to ::BigDecimal
80
80
 
81
- def inspect
82
- object.to_s('F')
83
- end
84
-
85
81
  protected
86
82
  def object
87
83
  ::Kernel::BigDecimal(@raw)
@@ -123,28 +119,36 @@ module Ohm
123
119
  ::Date.parse(@raw)
124
120
  end
125
121
  end
126
-
122
+
127
123
  class Serialized < Base
128
124
  attr :object
129
125
 
130
126
  def initialize(raw)
131
127
  @object = case raw
132
- when self.class::RAW then raw
133
- when ::String then ::JSON.parse(raw)
134
- when self.class then raw.object
128
+ when self.class::RAW
129
+ raw
130
+ when ::String
131
+ begin
132
+ ::JSON.parse(raw)
133
+ rescue ::JSON::ParserError
134
+ raw
135
+ end
136
+ when self.class
137
+ raw.object
135
138
  else
136
- ::Kernel.raise ::TypeError,
139
+ ::Kernel.raise ::TypeError,
137
140
  "%s does not accept %s" % [self.class, raw.inspect]
138
141
  end
139
142
  end
140
143
 
141
144
  def ==(other)
142
- object == other
145
+ object == other
143
146
  end
144
-
147
+
145
148
  def to_s
146
149
  object.to_json
147
150
  end
151
+ alias :inspect :to_s
148
152
  end
149
153
 
150
154
  class Hash < Serialized
@@ -163,9 +167,9 @@ module Ohm
163
167
  class Array < Serialized
164
168
  EMPTY = []
165
169
  RAW = ::Array
166
-
170
+
167
171
  delegate_to ::Array
168
-
172
+
169
173
  # @private since basic object doesn't include a #class we need
170
174
  # to define this manually
171
175
  def class
@@ -234,7 +238,7 @@ module Ohm
234
238
  # Defines a typecasted attribute.
235
239
  #
236
240
  # @example
237
- #
241
+ #
238
242
  # class User < Ohm::Model
239
243
  # include Ohm::Typecast
240
244
  #
@@ -258,9 +262,9 @@ module Ohm
258
262
  # user = User.new(:age => 20)
259
263
  # user.age - 1 == 19
260
264
  # => true
261
- #
265
+ #
262
266
  # @param [Symbol] name the name of the attribute to define.
263
- # @param [Class] type (defaults to Ohm::Types::String) a class defined in
267
+ # @param [Class] type (defaults to Ohm::Types::String) a class defined in
264
268
  # Ohm::Types. You may define custom types in Ohm::Types if
265
269
  # you need to.
266
270
  # @return [Array] the array of attributes already defined.
@@ -269,8 +273,8 @@ module Ohm
269
273
  define_method(name) do
270
274
  # Primitive types maintain a reference to the original object
271
275
  # stored in @_attributes[att]. Hence mutation works for the
272
- # Primitive case. For cases like Hash, Array where the value
273
- # is `JSON.parse`d, we need to set the actual Ohm::Types::Hash
276
+ # Primitive case. For cases like Hash, Array where the value
277
+ # is `JSON.parse`d, we need to set the actual Ohm::Types::Hash
274
278
  # (or similar) to @_attributes[att] for mutation to work.
275
279
  if klass.superclass == Ohm::Types::Primitive
276
280
  klass[read_local(name)]
@@ -296,4 +300,4 @@ module Ohm
296
300
  end
297
301
  end
298
302
  end
299
- end
303
+ end
@@ -12,9 +12,9 @@ module Ohm
12
12
  module WebValidations
13
13
  # @see http://fightingforalostcause.net/misc/2006/compare-email-regex.php
14
14
  EMAIL_REGEX = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i
15
-
15
+
16
16
  SLUG_REGEX = /^[-\w]+$/
17
-
17
+
18
18
  URL_REGEX = /^(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}|localhost)(:[0-9]{1,5})?(\/.*)?$/ix
19
19
 
20
20
  IPV4_REGEX = /^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$/
@@ -48,4 +48,4 @@ module Ohm
48
48
  assert_ipv4(att, error)
49
49
  end
50
50
  end
51
- end
51
+ end
data/ohm-contrib.gemspec CHANGED
@@ -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.20"
8
+ s.version = "0.0.21"
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-29}
12
+ s.date = %q{2010-05-30}
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 = [
@@ -14,7 +14,7 @@ class OhmContribCallbacksTest < Test::Unit::TestCase
14
14
 
15
15
  before :save, :do_before_save
16
16
  after :save, :do_after_save
17
-
17
+
18
18
  before :delete, :do_before_delete
19
19
  after :delete, :do_after_delete
20
20
 
@@ -151,8 +151,8 @@ class OhmContribCallbacksTest < Test::Unit::TestCase
151
151
  @post = Post[@post.id]
152
152
  @post.delete
153
153
  end
154
-
155
-
154
+
155
+
156
156
  should "call delete related callbacks once" do
157
157
  assert_equal 1, @post.count(:do_before_delete)
158
158
  assert_equal 1, @post.count(:do_after_delete)
@@ -167,4 +167,4 @@ class OhmContribCallbacksTest < Test::Unit::TestCase
167
167
  assert ! @post.did?(:do_after_save)
168
168
  end
169
169
  end
170
- end
170
+ end
@@ -92,14 +92,23 @@ class TestOhmTypecast < Test::Unit::TestCase
92
92
  assert_kind_of String, post.price.to_s
93
93
  end
94
94
 
95
- test "equality matching" do
95
+ test "equality and comparable matching" do
96
96
  post = Post.create(:price => "399.50")
97
97
  assert (post.price == "399.50")
98
+ assert (post.price < 399.51)
99
+ assert (post.price > 399.49)
100
+ assert (post.price <= 399.50)
101
+ assert (post.price <= 399.51)
102
+ assert (post.price >= 399.50)
103
+ assert (post.price >= 399.49)
98
104
  end
99
105
 
100
106
  test "inspecting a Decimal" do
101
107
  post = Post.new(:price => 399.50)
102
- assert_equal '399.5', post.price.inspect
108
+ assert_equal '"399.5"', post.price.inspect
109
+
110
+ post.price = 'FooBar'
111
+ assert_equal '"FooBar"', post.price.inspect
103
112
  end
104
113
  end
105
114
 
@@ -145,7 +154,10 @@ class TestOhmTypecast < Test::Unit::TestCase
145
154
 
146
155
  test "inspecting" do
147
156
  post = Post.new(:price => "50000")
148
- assert_equal '50000', post.price.inspect
157
+ assert_equal '"50000"', post.price.inspect
158
+
159
+ post.price = 'FooBar'
160
+ assert_equal '"FooBar"', post.price.inspect
149
161
  end
150
162
  end
151
163
 
@@ -191,7 +203,10 @@ class TestOhmTypecast < Test::Unit::TestCase
191
203
 
192
204
  test "inspecting" do
193
205
  post = Post.new(:price => "12345.67890")
194
- assert_equal '12345.6789', post.price.inspect
206
+ assert_equal '"12345.67890"', post.price.inspect
207
+
208
+ post.price = 'FooBar'
209
+ assert_equal '"FooBar"', post.price.inspect
195
210
  end
196
211
  end
197
212
 
@@ -250,6 +265,14 @@ class TestOhmTypecast < Test::Unit::TestCase
250
265
  post.created_at.slice
251
266
  end
252
267
  end
268
+
269
+ test "inspecting" do
270
+ post = Post.create(:created_at => Time.utc(2010, 05, 05))
271
+ assert_equal '"2010-05-05 00:00:00 UTC"', post.created_at.inspect
272
+
273
+ post.created_at = 'FooBar'
274
+ assert_equal '"FooBar"', post.created_at.inspect
275
+ end
253
276
  end
254
277
 
255
278
  context "when using a date" do
@@ -317,6 +340,14 @@ class TestOhmTypecast < Test::Unit::TestCase
317
340
  test "still able to access Date" do
318
341
  assert_equal Date.today, Post.new.today
319
342
  end
343
+
344
+ test "inspecting" do
345
+ post = Post.create(:created_on => Date.new(2010, 5, 5))
346
+ assert_equal '"2010-05-05"', post.created_on.inspect
347
+
348
+ post.created_on = 'FooBar'
349
+ assert_equal '"FooBar"', post.created_on.inspect
350
+ end
320
351
  end
321
352
 
322
353
  context "when using a Hash" do
@@ -333,13 +364,13 @@ class TestOhmTypecast < Test::Unit::TestCase
333
364
  Hash
334
365
  end
335
366
  end
336
-
367
+
337
368
  test "importing" do
338
369
  assert_equal Hash.new, Ohm::Types::Hash[nil]
339
370
  assert_equal Hash.new, Ohm::Types::Hash[""]
340
371
  assert_equal Hash.new, Ohm::Types::Hash[{}]
341
372
 
342
- assert_equal Hash[:a => "b", :c => "d"],
373
+ assert_equal Hash[:a => "b", :c => "d"],
343
374
  Ohm::Types::Hash[{ :a => "b", :c => "d" }]
344
375
  end
345
376
 
@@ -360,7 +391,7 @@ class TestOhmTypecast < Test::Unit::TestCase
360
391
  test "handles nil case correctly" do
361
392
  post = Post.create(:address => nil)
362
393
  assert_equal({}, post.address)
363
-
394
+
364
395
  post = Post[post.id]
365
396
  assert_equal({}, post.address)
366
397
  end
@@ -368,7 +399,7 @@ class TestOhmTypecast < Test::Unit::TestCase
368
399
  test "handles empty string case correctly" do
369
400
  post = Post.create(:address => "")
370
401
  assert_equal({}, post.address)
371
-
402
+
372
403
  post = Post[post.id]
373
404
  assert_equal({}, post.address)
374
405
  end
@@ -377,7 +408,7 @@ class TestOhmTypecast < Test::Unit::TestCase
377
408
  address = { "address1" => "#123", "city" => "Singapore", "country" => "SG"}
378
409
  post = Post.create(:address => address)
379
410
  assert_equal address, post.address
380
-
411
+
381
412
  post = Post[post.id]
382
413
  assert_equal address, post.address
383
414
  end
@@ -385,7 +416,7 @@ class TestOhmTypecast < Test::Unit::TestCase
385
416
  test "allows for hash operations" do
386
417
  address = { "address1" => "#123", "city" => "Singapore", "country" => "SG"}
387
418
  post = Post.create(:address => address)
388
-
419
+
389
420
  assert_equal ["address1", "city", "country"], post.address.keys
390
421
  assert_equal ["#123", "Singapore", "SG"], post.address.values
391
422
 
@@ -397,7 +428,7 @@ class TestOhmTypecast < Test::Unit::TestCase
397
428
  test "handles mutation" do
398
429
  address = { "address1" => "#123", "city" => "Singapore", "country" => "SG"}
399
430
  post = Post.create(:address => address)
400
-
431
+
401
432
  post.address["address1"] = "#456"
402
433
  post.save
403
434
 
@@ -408,7 +439,7 @@ class TestOhmTypecast < Test::Unit::TestCase
408
439
  assert_equal ["address1", "city", "country"], post.address.keys
409
440
  assert_equal ["#456", "Singapore", "SG"], post.address.values
410
441
  end
411
-
442
+
412
443
  Address = Class.new(Struct.new(:city, :country))
413
444
 
414
445
  test "raises when trying to assign a non-hash" do
@@ -420,6 +451,18 @@ class TestOhmTypecast < Test::Unit::TestCase
420
451
  Post.new(:address => Address.new)
421
452
  end
422
453
  end
454
+
455
+ test "inspecting" do
456
+ post = Post.create(:address => { "address1" => "#456",
457
+ "city" => "Singapore",
458
+ "country" => "SG" })
459
+
460
+ assert_equal %q{{"address1":"#456","city":"Singapore","country":"SG"}},
461
+ post.address.inspect
462
+
463
+ post.address = 'FooBar'
464
+ assert_equal %{"\\\"FooBar\\\""}, post.address.inspect
465
+ end
423
466
  end
424
467
 
425
468
  context "when using an Array" do
@@ -436,7 +479,7 @@ class TestOhmTypecast < Test::Unit::TestCase
436
479
  Array
437
480
  end
438
481
  end
439
-
482
+
440
483
  test "importing" do
441
484
  assert_equal [], Ohm::Types::Array[nil]
442
485
  assert_equal [], Ohm::Types::Array[""]
@@ -463,7 +506,7 @@ class TestOhmTypecast < Test::Unit::TestCase
463
506
  test "handles nil case correctly" do
464
507
  post = Post.create(:addresses => nil)
465
508
  assert_equal([], post.addresses)
466
-
509
+
467
510
  post = Post[post.id]
468
511
  assert_equal([], post.addresses)
469
512
  end
@@ -471,7 +514,7 @@ class TestOhmTypecast < Test::Unit::TestCase
471
514
  test "handles empty string case correctly" do
472
515
  post = Post.create(:addresses => "")
473
516
  assert_equal([], post.addresses)
474
-
517
+
475
518
  post = Post[post.id]
476
519
  assert_equal([], post.addresses)
477
520
  end
@@ -482,11 +525,11 @@ class TestOhmTypecast < Test::Unit::TestCase
482
525
 
483
526
  post = Post.create(:addresses => addresses)
484
527
  assert_equal addresses, post.addresses
485
-
528
+
486
529
  post = Post[post.id]
487
530
  assert_equal addresses, post.addresses
488
531
  end
489
-
532
+
490
533
  class Address < Struct.new(:city, :country)
491
534
  def to_json
492
535
  [city, country].to_json
@@ -496,10 +539,10 @@ class TestOhmTypecast < Test::Unit::TestCase
496
539
  test "handles an arbitrary class as an element of the array" do
497
540
  addresses = [Address.new("Singapore", "SG"),
498
541
  Address.new("Philippines", "PH")]
499
-
542
+
500
543
  post = Post.create(:addresses => addresses)
501
544
  assert_equal [['Singapore', 'SG'], ['Philippines', 'PH']], post.addresses
502
-
545
+
503
546
  post = Post[post.id]
504
547
  assert_equal [['Singapore', 'SG'], ['Philippines', 'PH']], post.addresses
505
548
  end
@@ -520,9 +563,23 @@ class TestOhmTypecast < Test::Unit::TestCase
520
563
  post.addresses.push({"city" => "Hong Kong", "country" => "ZN"})
521
564
  end
522
565
 
566
+ test "looping! and other enumerablems" do
567
+ array = [1, 2, 3]
568
+ post = Post.create(:addresses => array)
569
+
570
+ total = 0
571
+ post.addresses.each { |e| total += e }
572
+ assert_equal 6, total
573
+
574
+ post = Post[post.id]
575
+ total = 0
576
+ post.addresses.each { |e| total += e }
577
+ assert_equal 6, total
578
+ end
579
+
523
580
  test "handles mutation" do
524
581
  post = Post.create(:addresses => [1, 2, 3])
525
-
582
+
526
583
  post.addresses.push(4, 5, 6)
527
584
  post.save
528
585
 
@@ -533,7 +590,7 @@ class TestOhmTypecast < Test::Unit::TestCase
533
590
  assert_equal 6, post.addresses.size
534
591
  assert_equal [1, 2, 3, 4, 5, 6], post.addresses
535
592
  end
536
-
593
+
537
594
 
538
595
  test "raises when trying to assign a non-array" do
539
596
  assert_raise TypeError do
@@ -544,5 +601,17 @@ class TestOhmTypecast < Test::Unit::TestCase
544
601
  Post.new(:addresses => Address.new)
545
602
  end
546
603
  end
604
+
605
+ test "inspecting" do
606
+ post = Post.create(:addresses => [{ "address1" => "#456",
607
+ "city" => "Singapore",
608
+ "country" => "SG" }])
609
+
610
+ assert_equal %q{[{"address1":"#456","city":"Singapore","country":"SG"}]},
611
+ post.addresses.inspect
612
+
613
+ post.addresses = 'FooBar'
614
+ assert_equal %{"\\\"FooBar\\\""}, post.addresses.inspect
615
+ end
547
616
  end
548
- end
617
+ end
@@ -111,4 +111,4 @@ class TestOhmWebValidations < Test::Unit::TestCase
111
111
  end
112
112
 
113
113
  end
114
- end
114
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 20
9
- version: 0.0.20
8
+ - 21
9
+ version: 0.0.21
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-29 00:00:00 +08:00
17
+ date: 2010-05-30 00:00:00 +08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency