ohm-contrib 0.0.19 → 0.0.20

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.19
1
+ 0.0.20
@@ -1,6 +1,8 @@
1
1
  require 'bigdecimal'
2
2
  require 'time'
3
3
  require 'date'
4
+ require 'json'
5
+ require 'forwardable'
4
6
 
5
7
  module Ohm
6
8
  # Provides all the primitive types. The following are included:
@@ -11,6 +13,8 @@ module Ohm
11
13
  # * Float
12
14
  # * Date
13
15
  # * Time
16
+ # * Hash
17
+ # * Array
14
18
  module Types
15
19
  def self.defined?(type)
16
20
  @constants ||= constants.map(&:to_s)
@@ -20,8 +24,35 @@ module Ohm
20
24
  def self.[](type)
21
25
  const_get(type.to_s.split('::').last)
22
26
  end
27
+
28
+ class Base < BasicObject
29
+ class Exception < ::Exception; end
23
30
 
24
- class Primitive < BasicObject
31
+ extend ::Forwardable
32
+
33
+ @@delegation_blacklist = [
34
+ :==, :to_s, :initialize, :inspect, :object_id, :__send__, :__id__
35
+ ]
36
+
37
+ def self.[](value)
38
+ return self::EMPTY if value.to_s.empty?
39
+
40
+ new(value)
41
+ end
42
+
43
+ def self.delegate_to(klass, except = @@delegation_blacklist)
44
+ methods = klass.public_instance_methods.map(&:to_sym) - except
45
+ def_delegators :object, *methods
46
+ end
47
+
48
+ def inspect
49
+ object.inspect
50
+ end
51
+ end
52
+
53
+ class Primitive < Base
54
+ EMPTY = nil
55
+
25
56
  def initialize(value)
26
57
  @raw = value
27
58
  end
@@ -30,28 +61,23 @@ module Ohm
30
61
  @raw.to_s
31
62
  end
32
63
 
33
- def inspect
34
- object
35
- end
36
-
37
64
  def ==(other)
38
65
  to_s == other.to_s
39
66
  end
40
-
67
+
41
68
  protected
42
69
  def object
43
70
  @raw
44
71
  end
45
-
46
- def method_missing(meth, *args, &blk)
47
- object.send(meth, *args, &blk)
48
- end
49
72
  end
50
73
 
51
74
  class String < Primitive
75
+ delegate_to ::String
52
76
  end
53
77
 
54
78
  class Decimal < Primitive
79
+ delegate_to ::BigDecimal
80
+
55
81
  def inspect
56
82
  object.to_s('F')
57
83
  end
@@ -63,6 +89,8 @@ module Ohm
63
89
  end
64
90
 
65
91
  class Integer < Primitive
92
+ delegate_to ::Fixnum
93
+
66
94
  protected
67
95
  def object
68
96
  ::Kernel::Integer(@raw)
@@ -70,6 +98,8 @@ module Ohm
70
98
  end
71
99
 
72
100
  class Float < Primitive
101
+ delegate_to ::Float
102
+
73
103
  protected
74
104
  def object
75
105
  ::Kernel::Float(@raw)
@@ -77,6 +107,8 @@ module Ohm
77
107
  end
78
108
 
79
109
  class Time < Primitive
110
+ delegate_to ::Time
111
+
80
112
  protected
81
113
  def object
82
114
  ::Time.parse(@raw)
@@ -84,11 +116,62 @@ module Ohm
84
116
  end
85
117
 
86
118
  class Date < Primitive
119
+ delegate_to ::Date
120
+
87
121
  protected
88
122
  def object
89
123
  ::Date.parse(@raw)
90
124
  end
91
125
  end
126
+
127
+ class Serialized < Base
128
+ attr :object
129
+
130
+ def initialize(raw)
131
+ @object = case raw
132
+ when self.class::RAW then raw
133
+ when ::String then ::JSON.parse(raw)
134
+ when self.class then raw.object
135
+ else
136
+ ::Kernel.raise ::TypeError,
137
+ "%s does not accept %s" % [self.class, raw.inspect]
138
+ end
139
+ end
140
+
141
+ def ==(other)
142
+ object == other
143
+ end
144
+
145
+ def to_s
146
+ object.to_json
147
+ end
148
+ end
149
+
150
+ class Hash < Serialized
151
+ EMPTY = {}
152
+ RAW = ::Hash
153
+
154
+ delegate_to ::Hash
155
+
156
+ # @private since basic object doesn't include a #class we need
157
+ # to define this manually
158
+ def class
159
+ ::Ohm::Types::Hash
160
+ end
161
+ end
162
+
163
+ class Array < Serialized
164
+ EMPTY = []
165
+ RAW = ::Array
166
+
167
+ delegate_to ::Array
168
+
169
+ # @private since basic object doesn't include a #class we need
170
+ # to define this manually
171
+ def class
172
+ ::Ohm::Types::Array
173
+ end
174
+ end
92
175
  end
93
176
 
94
177
  # Provides unobtrusive, non-explosive typecasting.Instead of exploding on set
@@ -184,12 +267,20 @@ module Ohm
184
267
  # @return [nil] if the attribute is already defined.
185
268
  def attribute(name, type = Ohm::Types::String, klass = Ohm::Types[type])
186
269
  define_method(name) do
187
- value = read_local(name)
188
- value && klass.new(value)
270
+ # Primitive types maintain a reference to the original object
271
+ # 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
274
+ # (or similar) to @_attributes[att] for mutation to work.
275
+ if klass.superclass == Ohm::Types::Primitive
276
+ klass[read_local(name)]
277
+ else
278
+ write_local(name, klass[read_local(name)])
279
+ end
189
280
  end
190
281
 
191
282
  define_method(:"#{name}=") do |value|
192
- write_local(name, value && value.to_s)
283
+ write_local(name, klass[value].to_s)
193
284
  end
194
285
 
195
286
  attributes << name unless attributes.include?(name)
@@ -1,35 +1,51 @@
1
1
  module Ohm
2
2
  # All credit goes to gnrfan of github
3
3
  # Basically an extraction from http://github.com/gnrfan/ohm_extra_validations
4
+ #
5
+ # * 2010-05-29 Updated Email Regex, Extracted out regexs to constants
6
+ #
7
+ # This module provides the following:
8
+ # * assert_slug
9
+ # * assert_email
10
+ # * assert_url
11
+ # * assert_ipv4
4
12
  module WebValidations
13
+ # @see http://fightingforalostcause.net/misc/2006/compare-email-regex.php
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
+
16
+ SLUG_REGEX = /^[-\w]+$/
17
+
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
+
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}$/
5
21
 
6
22
  protected
7
23
  def assert_slug(att, error = [att, :not_slug])
8
24
  if assert_present(att, error) and assert_unique(att)
9
- assert_format(att, /^[-\w]+$/, error)
25
+ assert_format(att, SLUG_REGEX, error)
10
26
  end
11
27
  end
12
28
 
13
29
  def assert_email(att, error = [att, :not_email])
14
30
  if assert_present(att, error)
15
- assert_format(att, /^([^@\s*]+)@((?:[-a-z0-9]+\.)+[a-z]{2,6})$/i, error)
31
+ assert_format(att, EMAIL_REGEX, error)
16
32
  end
17
33
  end
18
34
 
19
35
  def assert_url(att, error = [att, :not_url])
20
36
  if assert_present(att, error)
21
- assert_format(att, /^(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, error)
37
+ assert_format(att, URL_REGEX, error)
22
38
  end
23
39
  end
24
40
 
25
41
  def assert_ipv4(att, error = [att, :not_ipv4])
26
42
  if assert_present(att, error)
27
- assert_format(att, /^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$/, error)
43
+ assert_format(att, IPV4_REGEX, error)
28
44
  end
29
45
  end
30
46
 
31
47
  def assert_ipaddr(att, error = [att, :not_ipaddr])
32
- assert_ipv4(att, error)
48
+ assert_ipv4(att, error)
33
49
  end
34
50
  end
35
- end
51
+ end
data/lib/ohm/contrib.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Ohm
2
2
  module Contrib
3
- VERSION = '0.0.19'
3
+ VERSION = '0.0.20'
4
4
  end
5
5
 
6
6
  autoload :Boundaries, "ohm/contrib/boundaries"
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.19"
8
+ s.version = "0.0.20"
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-28}
12
+ s.date = %q{2010-05-29}
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 = [
@@ -145,7 +145,7 @@ class TestOhmTypecast < Test::Unit::TestCase
145
145
 
146
146
  test "inspecting" do
147
147
  post = Post.new(:price => "50000")
148
- assert_equal 50000, post.price.inspect
148
+ assert_equal '50000', post.price.inspect
149
149
  end
150
150
  end
151
151
 
@@ -191,7 +191,7 @@ class TestOhmTypecast < Test::Unit::TestCase
191
191
 
192
192
  test "inspecting" do
193
193
  post = Post.new(:price => "12345.67890")
194
- assert_equal 12345.67890, post.price.inspect
194
+ assert_equal '12345.6789', post.price.inspect
195
195
  end
196
196
  end
197
197
 
@@ -318,4 +318,231 @@ class TestOhmTypecast < Test::Unit::TestCase
318
318
  assert_equal Date.today, Post.new.today
319
319
  end
320
320
  end
321
+
322
+ context "when using a Hash" do
323
+ class Post < Ohm::Model
324
+ include Ohm::Typecast
325
+
326
+ attribute :address, Hash
327
+
328
+ def hash
329
+ Hash.new
330
+ end
331
+
332
+ def top_level_hash
333
+ Hash
334
+ end
335
+ end
336
+
337
+ test "importing" do
338
+ assert_equal Hash.new, Ohm::Types::Hash[nil]
339
+ assert_equal Hash.new, Ohm::Types::Hash[""]
340
+ assert_equal Hash.new, Ohm::Types::Hash[{}]
341
+
342
+ assert_equal Hash[:a => "b", :c => "d"],
343
+ Ohm::Types::Hash[{ :a => "b", :c => "d" }]
344
+ end
345
+
346
+ test "exporting / dumping" do
347
+ assert_equal "{}", Ohm::Types::Hash[nil].to_s
348
+ assert_equal "{}", Ohm::Types::Hash[""].to_s
349
+ assert_equal "{}", Ohm::Types::Hash[{}].to_s
350
+
351
+ assert_equal %q{{"a":"b","c":"d"}},
352
+ Ohm::Types::Hash[{ :a => "b", :c => "d" }].to_s
353
+ end
354
+
355
+ test "still able to get top level methods" do
356
+ assert_equal({}, Post.new.hash)
357
+ assert_equal Hash, Post.new.top_level_hash
358
+ end
359
+
360
+ test "handles nil case correctly" do
361
+ post = Post.create(:address => nil)
362
+ assert_equal({}, post.address)
363
+
364
+ post = Post[post.id]
365
+ assert_equal({}, post.address)
366
+ end
367
+
368
+ test "handles empty string case correctly" do
369
+ post = Post.create(:address => "")
370
+ assert_equal({}, post.address)
371
+
372
+ post = Post[post.id]
373
+ assert_equal({}, post.address)
374
+ end
375
+
376
+ test "handles populated hashes" do
377
+ address = { "address1" => "#123", "city" => "Singapore", "country" => "SG"}
378
+ post = Post.create(:address => address)
379
+ assert_equal address, post.address
380
+
381
+ post = Post[post.id]
382
+ assert_equal address, post.address
383
+ end
384
+
385
+ test "allows for hash operations" do
386
+ address = { "address1" => "#123", "city" => "Singapore", "country" => "SG"}
387
+ post = Post.create(:address => address)
388
+
389
+ assert_equal ["address1", "city", "country"], post.address.keys
390
+ assert_equal ["#123", "Singapore", "SG"], post.address.values
391
+
392
+ post = Post[post.id]
393
+ assert_equal ["address1", "city", "country"], post.address.keys
394
+ assert_equal ["#123", "Singapore", "SG"], post.address.values
395
+ end
396
+
397
+ test "handles mutation" do
398
+ address = { "address1" => "#123", "city" => "Singapore", "country" => "SG"}
399
+ post = Post.create(:address => address)
400
+
401
+ post.address["address1"] = "#456"
402
+ post.save
403
+
404
+ assert_equal ["address1", "city", "country"], post.address.keys
405
+ assert_equal ["#456", "Singapore", "SG"], post.address.values
406
+
407
+ post = Post[post.id]
408
+ assert_equal ["address1", "city", "country"], post.address.keys
409
+ assert_equal ["#456", "Singapore", "SG"], post.address.values
410
+ end
411
+
412
+ Address = Class.new(Struct.new(:city, :country))
413
+
414
+ test "raises when trying to assign a non-hash" do
415
+ assert_raise TypeError do
416
+ Post.new(:address => [])
417
+ end
418
+
419
+ assert_raise TypeError do
420
+ Post.new(:address => Address.new)
421
+ end
422
+ end
423
+ end
424
+
425
+ context "when using an Array" do
426
+ class Post < Ohm::Model
427
+ include Ohm::Typecast
428
+
429
+ attribute :addresses, Array
430
+
431
+ def array
432
+ Array.new
433
+ end
434
+
435
+ def top_level_array
436
+ Array
437
+ end
438
+ end
439
+
440
+ test "importing" do
441
+ assert_equal [], Ohm::Types::Array[nil]
442
+ assert_equal [], Ohm::Types::Array[""]
443
+ assert_equal [], Ohm::Types::Array[[]]
444
+
445
+ assert_equal ['a', 'b', 'c', 'd'],
446
+ Ohm::Types::Array[['a', 'b', 'c', 'd']]
447
+ end
448
+
449
+ test "exporting / dumping" do
450
+ assert_equal "[]", Ohm::Types::Array[nil].to_s
451
+ assert_equal "[]", Ohm::Types::Array[""].to_s
452
+ assert_equal "[]", Ohm::Types::Array[[]].to_s
453
+
454
+ assert_equal %q{["a","b","c","d"]},
455
+ Ohm::Types::Array[['a', 'b', 'c', 'd']].to_s
456
+ end
457
+
458
+ test "still able to get top level methods" do
459
+ assert_equal([], Post.new.array)
460
+ assert_equal Array, Post.new.top_level_array
461
+ end
462
+
463
+ test "handles nil case correctly" do
464
+ post = Post.create(:addresses => nil)
465
+ assert_equal([], post.addresses)
466
+
467
+ post = Post[post.id]
468
+ assert_equal([], post.addresses)
469
+ end
470
+
471
+ test "handles empty string case correctly" do
472
+ post = Post.create(:addresses => "")
473
+ assert_equal([], post.addresses)
474
+
475
+ post = Post[post.id]
476
+ assert_equal([], post.addresses)
477
+ end
478
+
479
+ test "handles populated arrays" do
480
+ addresses = [{"city" => "Singapore", "country" => "SG"},
481
+ {"city" => "Manila", "country" => "PH"}]
482
+
483
+ post = Post.create(:addresses => addresses)
484
+ assert_equal addresses, post.addresses
485
+
486
+ post = Post[post.id]
487
+ assert_equal addresses, post.addresses
488
+ end
489
+
490
+ class Address < Struct.new(:city, :country)
491
+ def to_json
492
+ [city, country].to_json
493
+ end
494
+ end
495
+
496
+ test "handles an arbitrary class as an element of the array" do
497
+ addresses = [Address.new("Singapore", "SG"),
498
+ Address.new("Philippines", "PH")]
499
+
500
+ post = Post.create(:addresses => addresses)
501
+ assert_equal [['Singapore', 'SG'], ['Philippines', 'PH']], post.addresses
502
+
503
+ post = Post[post.id]
504
+ assert_equal [['Singapore', 'SG'], ['Philippines', 'PH']], post.addresses
505
+ end
506
+
507
+ test "allows for array operations" do
508
+ addresses = [{"city" => "Singapore", "country" => "SG"},
509
+ {"city" => "Manila", "country" => "PH"}]
510
+
511
+
512
+ post = Post.create(:addresses => addresses)
513
+ assert_equal 2, post.addresses.size
514
+ assert_equal addresses + [{"city" => "Hong Kong", "country" => "ZN"}],
515
+ post.addresses.push({"city" => "Hong Kong", "country" => "ZN"})
516
+
517
+ post = Post[post.id]
518
+ assert_equal 2, post.addresses.size
519
+ assert_equal addresses + [{"city" => "Hong Kong", "country" => "ZN"}],
520
+ post.addresses.push({"city" => "Hong Kong", "country" => "ZN"})
521
+ end
522
+
523
+ test "handles mutation" do
524
+ post = Post.create(:addresses => [1, 2, 3])
525
+
526
+ post.addresses.push(4, 5, 6)
527
+ post.save
528
+
529
+ assert_equal 6, post.addresses.size
530
+ assert_equal [1, 2, 3, 4, 5, 6], post.addresses
531
+
532
+ post = Post[post.id]
533
+ assert_equal 6, post.addresses.size
534
+ assert_equal [1, 2, 3, 4, 5, 6], post.addresses
535
+ end
536
+
537
+
538
+ test "raises when trying to assign a non-array" do
539
+ assert_raise TypeError do
540
+ Post.new(:addresses => {})
541
+ end
542
+
543
+ assert_raise TypeError do
544
+ Post.new(:addresses => Address.new)
545
+ end
546
+ end
547
+ end
321
548
  end
@@ -29,6 +29,7 @@ class TestOhmWebValidations < Test::Unit::TestCase
29
29
 
30
30
  context "The slug should be valid" do
31
31
  def setup
32
+ Ohm.flush
32
33
  @blog_post_01 = BlogPost.new
33
34
  @blog_post_02 = BlogPost.new
34
35
  end
@@ -41,11 +42,11 @@ class TestOhmWebValidations < Test::Unit::TestCase
41
42
  assert_equal [[:slug, :not_slug]], @blog_post_01.errors
42
43
  end
43
44
 
44
- #should "succeed if the slug is valid" do
45
- # @blog_post_02.slug = "this-is-a-valid-slug"
46
- # @blog_post_02.create
47
- # assert_not_nil @blog_post_02.id
48
- #end
45
+ should "succeed if the slug is valid" do
46
+ @blog_post_02.slug = "this-is-a-valid-slug"
47
+ @blog_post_02.create
48
+ assert_not_nil @blog_post_02.id
49
+ end
49
50
 
50
51
  should "fail if the slug is not unique" do
51
52
 
@@ -110,4 +111,4 @@ class TestOhmWebValidations < Test::Unit::TestCase
110
111
  end
111
112
 
112
113
  end
113
- end
114
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 19
9
- version: 0.0.19
8
+ - 20
9
+ version: 0.0.20
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-28 00:00:00 +08:00
17
+ date: 2010-05-29 00:00:00 +08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency