hashstruct 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.mdown +69 -0
  2. data/lib/hashstruct.rb +89 -0
  3. metadata +49 -0
data/README.mdown ADDED
@@ -0,0 +1,69 @@
1
+ HashStruct provides an object based on Hash, but acts like Struct (or OpenStruct), providing helpful
2
+ accessors for each key from the get-go. It also magically parses string values when it can (eg, dates,
3
+ URIs, numbers, and does so recursively.
4
+
5
+ You can use HashStruct as a first-class object like this:
6
+
7
+ h = HashStruct.new
8
+ h.foo = 1
9
+ h.bar = 'zot'
10
+
11
+ Or you can pass it a hash of existing values:
12
+
13
+ h = HashStruct.new(
14
+ :foo => 1,
15
+ :bar => 'z')
16
+
17
+ Or simply an existing hash:
18
+
19
+ thing = { :foo => 1, :bar => 'z' }
20
+ h = HashStruct.new(thing)
21
+
22
+ Or because it's a Hash underneath, you can use the '[]' construction method:
23
+
24
+ h = HashStruct[
25
+ [:foo, 1], [:bar, 'z']
26
+ ]
27
+
28
+ Or a combination:
29
+
30
+ h = HashStruct.new(
31
+ :foo => 1,
32
+ :bar => 'z')
33
+ h.zot = :x
34
+
35
+ HashStruct magically converts any values from strings (and sub-arrays/hashes) to native objects, when it can. Like so:
36
+
37
+ h = HashStruct.new(
38
+ :an_int => '1', # => 1
39
+ :a_float => '1.5', # => 1.5
40
+ :a_url => 'http://foo.com/bar', # => #<URI::HTTP:0x000001009c6160 URL:http://foo.com/bar>
41
+ :a_date => '2008-01-02', # => #<DateTime: 2008-01-02T00:00:00+00:00 (4908935/2,0/1,2299161)>
42
+ :a_date_time => '2011-06-17 09:36:49 -0700', # => #<DateTime: 2011-06-17T09:36:49-07:00 (212175088609/86400,-7/24,2299161)>
43
+ :a_bool_true => 'true', # => true
44
+ :a_bool_false => 'false', # => false
45
+ :a_percentage => '50%', # => 0.5
46
+ :an_int_array => %w{1 2 3}, # => [1, 2, 3]
47
+ )
48
+
49
+ You can't turn off the magical conversion. (Secret: you could monkey-patch `#convert_value` to
50
+ simply return the passed-in object.)
51
+
52
+ You can also easily subclass HashStruct. You can add helper methods if needed, or accessor methods,
53
+ which will be noticed and used by HashStruct. Note that any additional instance variables are not treated
54
+ as part of the structure.
55
+
56
+ class Thing < HashStruct
57
+
58
+ attr_accessor :mul
59
+
60
+ def foo_mul
61
+ self.foo * @mul
62
+ end
63
+
64
+ end
65
+
66
+ t = Thing.new
67
+ t.foo = 2
68
+ t.mul = 2
69
+ t.foo_mul # => 4
data/lib/hashstruct.rb ADDED
@@ -0,0 +1,89 @@
1
+ require 'date'
2
+ require 'uri'
3
+
4
+ class HashStruct < Hash
5
+
6
+ VERSION = '1.0.0'
7
+
8
+ def initialize(args={})
9
+ super()
10
+ args.each { |key, value| self[key] = value }
11
+ end
12
+
13
+ def [](key)
14
+ key = key.to_sym
15
+ if methods.include?(key)
16
+ method(key).call
17
+ elsif has_key?(key)
18
+ fetch(key)
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ def []=(key, value)
25
+ if methods.include?(:"#{key}=")
26
+ method(:"#{key}=").call(convert_object(value))
27
+ else
28
+ store(key.to_s.to_sym, convert_object(value))
29
+ end
30
+ end
31
+
32
+ def method_missing(method_id, *args)
33
+ method_name = method_id.to_s
34
+ if method_name =~ /=$/
35
+ raise ArgumentError, "wrong number of arguments for method #{method_name.inspect} (#{args.length} for 1)", caller(1) if args.length != 1
36
+ raise TypeError, "can't modify frozen #{self.class}", caller(1) if self.frozen?
37
+ self[method_name.chop.to_sym] = args.first
38
+ else
39
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 0)", caller(1) if args.length != 0
40
+ self[method_id]
41
+ end
42
+ end
43
+
44
+ def convert_object(obj)
45
+ case obj
46
+ when String
47
+ case obj
48
+ # URI
49
+ when %r{^(ftp|http|https|mailto):}
50
+ URI.parse(obj) rescue obj
51
+ # integer
52
+ when %r{^-?[1-9][\d,]*$}
53
+ obj.gsub(/,/, '').to_i
54
+ # hex integer
55
+ when %r{^0x[0-9a-f]+$}i
56
+ obj.hex
57
+ # float
58
+ when %r{^-?[\d,]+\.\d+$}
59
+ obj.gsub(/,/, '').to_f
60
+ # percent
61
+ when %r{^-?[\d,]+(\.\d+)?%$}
62
+ obj.to_f / 100
63
+ # date
64
+ when %r{^\d{4}-\d{2}-\d{2}}, # 2010-06-06
65
+ %r{^\d{1,2}/\d{1,2}/\d{4}}, # 06/06/2010
66
+ %r{^\d{4}/\d{1,2}/\d{1,2}}, # 2010/06/06
67
+ %r{^(Sun|Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s*\d*\s*\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b} # Sun, 06 Jun 2010 23:02:25 GMT
68
+ DateTime.parse(obj)
69
+ # boolean true
70
+ when 'true'
71
+ true
72
+ # boolean false
73
+ when 'false'
74
+ false
75
+ else
76
+ obj
77
+ end
78
+ when Array
79
+ obj.map { |o| convert_object(o) }
80
+ when Hash
81
+ HashStruct.new(obj)
82
+ when HashStruct
83
+ obj
84
+ else
85
+ obj
86
+ end
87
+ end
88
+
89
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashstruct
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John Labovitz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! "\n HashStruct provides an object based on Hash, but acts like Struct
15
+ (or OpenStruct), providing helpful\n accessors for each key from the get-go.
16
+ It also magically parses string values when it can (eg, dates,\n URIs, numbers,
17
+ and does so recursively.\n "
18
+ email: johnl@johnlabovitz.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - lib/hashstruct.rb
24
+ - README.mdown
25
+ homepage: http://github.com/jslabovitz/hashstruct
26
+ licenses: []
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.25
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: Overly helpful magic hash structure.
49
+ test_files: []