ohm-contrib 0.0.19 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
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