hashery 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,209 @@
1
+ #--
2
+ # OpenStructable
3
+ #
4
+ # Copyright (c) 2005 Thomas Sawyer
5
+ #
6
+ # Ruby License
7
+ #
8
+ # This module is free software. You may use, modify, and/or redistribute this
9
+ # software under the same terms as Ruby.
10
+ #
11
+ # This program is distributed in the hope that it will be useful, but WITHOUT
12
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ # FOR A PARTICULAR PURPOSE.
14
+ #
15
+ # ==========================================================================
16
+ # Revision History ::
17
+ # --------------------------------------------------------------------------
18
+ # 05.04.28 Trans
19
+ # - Minor modifications to documentation.
20
+ # ==========================================================================
21
+ #
22
+ # TODO
23
+ # Keep this uptodate with ostruct.rb.
24
+ #
25
+ # TODO Ask Matz
26
+ # See if Matz will accept it into core so we don't have to anymore.
27
+ #
28
+ # TODO Marshalling
29
+ # As with OpenStruct, marshalling is problematic at the moment.
30
+ #
31
+ #++
32
+
33
+ # OpensStructable is a mixin module which can provide OpenStruct behavior to
34
+ # any class or object. OpenStructable allows extention of data objects
35
+ # with arbitrary attributes.
36
+ #
37
+ # == Usage
38
+ #
39
+ # require 'ostructable'
40
+ #
41
+ # class Record
42
+ # include OpenStructable
43
+ # end
44
+ #
45
+ # record = Record.new
46
+ # record.name = "John Smith"
47
+ # record.age = 70
48
+ # record.pension = 300
49
+ #
50
+ # puts record.name # -> "John Smith"
51
+ # puts record.address # -> nil
52
+ #
53
+ # == Author(s)
54
+ #
55
+ # * Thomas Sawyer
56
+ # * Yukihiro Matsumoto
57
+ # * Gavin Sinclair (Documentation)
58
+ #
59
+ module OpenStructable
60
+
61
+ def initialize(hash=nil)
62
+ @__table__ = {}
63
+ if hash
64
+ for k,v in hash
65
+ @__table__[k.to_sym] = v
66
+ new_ostruct_member(k)
67
+ end
68
+ end
69
+ end
70
+
71
+ # duplicate an OpenStruct object members.
72
+ def initialize_copy(orig)
73
+ super
74
+ @__table__ = @__table__.dup
75
+ end
76
+
77
+ def marshal_dump
78
+ @table
79
+ end
80
+ def marshal_load(x)
81
+ @table = x
82
+ @table.each_key{|key| new_ostruct_member(key)}
83
+ end
84
+
85
+ def new_ostruct_member(name)
86
+ unless self.respond_to?(name)
87
+ self.instance_eval %{
88
+ def #{name}; @__table__[:#{name}]; end
89
+ def #{name}=(x); @__table__[:#{name}] = x; end
90
+ }
91
+ end
92
+ end
93
+
94
+ #
95
+ # Generate additional attributes and values.
96
+ #
97
+ def update(hash)
98
+ @__table__ ||= {}
99
+ if hash
100
+ for k,v in hash
101
+ @__table__[k.to_sym] = v
102
+ new_ostruct_member(k)
103
+ end
104
+ end
105
+ end
106
+
107
+ def method_missing(mid, *args) # :nodoc:
108
+ mname = mid.to_s
109
+ len = args.length
110
+ if mname =~ /=$/
111
+ if len != 1
112
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
113
+ end
114
+ if self.frozen?
115
+ raise TypeError, "can't modify frozen #{self.class}", caller(1)
116
+ end
117
+ mname.chop!
118
+ @__table__ ||= {}
119
+ @__table__[mname.intern] = args[0]
120
+ self.new_ostruct_member(mname)
121
+ elsif len == 0
122
+ @__table__ ||= {}
123
+ @__table__[mid]
124
+ else
125
+ raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
126
+ end
127
+ end
128
+
129
+ #
130
+ # Remove the named field from the object.
131
+ #
132
+ def delete_field(name)
133
+ @__table__ ||= {}
134
+ @__table__.delete name.to_sym
135
+ end
136
+
137
+ #
138
+ # Returns a string containing a detailed summary of the keys and values.
139
+ #
140
+ def inspect
141
+ str = "<#{self.class}"
142
+ for k,v in (@__table__ ||= {})
143
+ str << " #{k}=#{v.inspect}"
144
+ end
145
+ str << ">"
146
+ end
147
+
148
+ def __table__ # :nodoc:
149
+ @__table__ ||= {}
150
+ end
151
+ protected :__table__
152
+
153
+ # Compare this object and +other+ for equality.
154
+ def ==(other)
155
+ return false unless(other.kind_of?(OpenStruct))
156
+ return @__table__ == other.table
157
+ end
158
+
159
+ end
160
+
161
+ =begin
162
+ #
163
+ # It is possibe to implement OpenStruct itself with
164
+ # this OpenStructable module as follows:
165
+ #
166
+ class OpenStruct
167
+ include OpenStructable
168
+ end
169
+ =end
170
+
171
+
172
+
173
+ # _____ _
174
+ # |_ _|__ ___| |_
175
+ # | |/ _ \/ __| __|
176
+ # | | __/\__ \ |_
177
+ # |_|\___||___/\__|
178
+ #
179
+
180
+ =begin testing
181
+
182
+ require 'test/unit'
183
+
184
+ # fixture
185
+
186
+ class Record
187
+ include OpenStructable
188
+ end
189
+
190
+ # test
191
+
192
+ class TC_OpenStructable < Test::Unit::TestCase
193
+
194
+ def test_record
195
+ record = nil
196
+ assert_nothing_raised {
197
+ record = Record.new
198
+ record.name = "John Smith"
199
+ record.age = 70
200
+ record.pension = 300
201
+ }
202
+ assert_equal( "John Smith", record.name )
203
+ assert_equal( 70, record.age )
204
+ assert_equal( nil, record.address )
205
+ end
206
+
207
+ end
208
+
209
+ =end
@@ -0,0 +1,35 @@
1
+ # = QueryHash
2
+ #
3
+ # QueryHash is a similar to OpenHash. Like OpenHash,
4
+ # entries can be assigned via setter methods, but
5
+ # entries can only be looked up via query methods
6
+ # (i.e. methods ending in a ?-mark), hence the name
7
+ # of this class.
8
+ #
9
+ # A QueryHash is not quite as elegant as an OpenHash
10
+ # in that reader methods must end in ?-marks, but
11
+ # it remains fully compatible with Hash regardless
12
+ # of it's settings.
13
+
14
+ class QueryHash < Hash
15
+
16
+ def to_h
17
+ dup
18
+ end
19
+
20
+ def to_hash
21
+ dup
22
+ end
23
+
24
+ def method_missing(s,*a,&b)
25
+ case s.to_s
26
+ when /\=$/
27
+ self[s.to_s.chomp('=').to_sym] = a[0]
28
+ when /\?$/
29
+ self[s.to_s.chomp('?').to_sym]
30
+ else
31
+ super(s,*a,&b)
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,181 @@
1
+ # Stash is just like Hash, except that all keys are
2
+ # converted to Strings.
3
+ #
4
+ # Note this doesn't yet handle default_proc.
5
+
6
+ class Stash < Hash
7
+
8
+ #
9
+ def self.[](*hash)
10
+ s = new
11
+ super(*hash).each{ |k,v| s[k] = v }
12
+ s
13
+ end
14
+
15
+ #
16
+ def [](key)
17
+ super(convert_key(key))
18
+ end
19
+
20
+ #
21
+ def []=(key,value)
22
+ super(convert_key(key), value)
23
+ end
24
+
25
+ #
26
+ def <<(other)
27
+ case other
28
+ when Hash
29
+ super(other.rekey{ |key| convert_key(key) })
30
+ when Array
31
+ self[other[0]] = other[1]
32
+ else
33
+ raise ArgumentError
34
+ end
35
+ end
36
+
37
+ #
38
+ def fetch(key)
39
+ super(convert_key(key))
40
+ end
41
+
42
+ #
43
+ def store(key, value)
44
+ super(convert_key(key), value)
45
+ end
46
+
47
+ #
48
+ def key?(key)
49
+ super(convert_key(key))
50
+ end
51
+
52
+ #
53
+ def has_key?(key)
54
+ super(convert_key(key))
55
+ end
56
+
57
+ #
58
+ def include?(key)
59
+ super(convert_key(key))
60
+ end
61
+
62
+ #
63
+ def member?(key)
64
+ super(convert_key(key))
65
+ end
66
+
67
+
68
+ # Synonym for Hash#rekey, but modifies the receiver in place (and returns it).
69
+ #
70
+ # foo = { :name=>'Gavin', :wife=>:Lisa }.to_stash
71
+ # foo.rekey!{ |k| k.upcase } #=> { "NAME"=>"Gavin", "WIFE"=>:Lisa }
72
+ # foo.inspect #=> { "NAME"=>"Gavin", "WIFE"=>:Lisa }
73
+ #
74
+ def rekey!(*args, &block)
75
+ # for backward comptability (TODO: DEPRECATE?).
76
+ block = args.pop.to_sym.to_proc if args.size == 1
77
+ if args.empty?
78
+ block = lambda{|k| k} unless block
79
+ keys.each do |k|
80
+ nk = block[k]
81
+ self[nk.to_s]=delete(k) #if nk
82
+ end
83
+ else
84
+ raise ArgumentError, "3 for 2" if block
85
+ to, from = *args
86
+ self[to] = delete(from) if has_key?(from)
87
+ end
88
+ self
89
+ end
90
+
91
+ #
92
+ def rekey(*args, &block)
93
+ dup.rekey!(*args, &block)
94
+ end
95
+
96
+ #
97
+ def delete(key)
98
+ super(convert_key(key))
99
+ end
100
+
101
+ #
102
+ def update(other)
103
+ super(other.rekey{ |key| convert_key(key) })
104
+ end
105
+
106
+ # Same as #update.
107
+ def merge!(other)
108
+ super(other.rekey{ |key| convert_key(key) })
109
+ end
110
+
111
+ #
112
+ def merge(other)
113
+ super(other.rekey{ |key| convert_key(key) })
114
+ end
115
+
116
+ #
117
+ def replace(other)
118
+ super(other.rekey{ |key| convert_key(key) })
119
+ end
120
+
121
+ #
122
+ def values_at(*keys)
123
+ super(*keys.map{ |key| convert_key(key) })
124
+ end
125
+
126
+ #
127
+ def to_hash
128
+ h = {}
129
+ each{ |k,v| h[k] = v }
130
+ h
131
+ end
132
+
133
+ alias_method :to_h, :to_hash
134
+
135
+ private
136
+
137
+ def convert_key(key)
138
+ key.to_s
139
+ end
140
+
141
+ end
142
+
143
+ class Hash
144
+
145
+ # Convert a Hash to a Stash object.
146
+ def to_stash
147
+ Stash[self]
148
+ end
149
+
150
+ # Synonym for Hash#rekey, but modifies the receiver in place (and returns it).
151
+ #
152
+ # foo = { :name=>'Gavin', :wife=>:Lisa }
153
+ # foo.rekey!{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa }
154
+ # foo.inspect #=> { "name"=>"Gavin", "wife"=>:Lisa }
155
+ #
156
+ # This method comes from Ruby Facets.
157
+
158
+ def rekey!(*args, &block)
159
+ # for backward comptability (TODO: DEPRECATE).
160
+ block = args.pop.to_sym.to_proc if args.size == 1
161
+ if args.empty?
162
+ block = lambda{|k| k.to_sym} unless block
163
+ keys.each do |k|
164
+ nk = block[k]
165
+ self[nk]=delete(k) if nk
166
+ end
167
+ else
168
+ raise ArgumentError, "3 for 2" if block
169
+ to, from = *args
170
+ self[to] = self.delete(from) if self.has_key?(from)
171
+ end
172
+ self
173
+ end
174
+
175
+ # Non-inplace #rekey! method.
176
+ def rekey(*args, &block)
177
+ dup.rekey!(*args, &block)
178
+ end
179
+
180
+ end
181
+
@@ -0,0 +1,48 @@
1
+ # = StaticHash
2
+ #
3
+ # A Hash object which raises an error if any
4
+ # previously-defined key attempts to be set again.
5
+ #
6
+ # == Synopsis
7
+ #
8
+ # foo = Hash::Static.new
9
+ # foo['name'] = 'Tom' #=> 'Tom'
10
+ # foo['age'] = 30 #=> 30
11
+ # foo['name'] = 'Bob'
12
+ #
13
+ # _produces_
14
+ #
15
+ # ArgumentError: Duplicate key for StaticHash -- 'name'
16
+ #
17
+ # == Credit
18
+ #
19
+ # StaticHash has it's orgins in Gavin Kistner's WriteOnceHash
20
+ # class found in his +basiclibrary.rb+ script.
21
+
22
+ class StaticHash < Hash
23
+
24
+ # Set a value for a key. Raises an error if that key already
25
+ # exists with a different value.
26
+
27
+ def []=(key, value)
28
+ if key?(key) && self[key] != value
29
+ raise ArgumentError, "Duplicate key for StaticHash -- #{key.inspect}"
30
+ end
31
+ super(key, value)
32
+ end
33
+
34
+ #
35
+ def update(hash)
36
+ dups = (keys | hash.keys)
37
+ if dups.empty?
38
+ super(hash)
39
+ else
40
+ raise ArgumentError, "Duplicate key for StaticHash -- #{dups.inspect}"
41
+ end
42
+ end
43
+
44
+ #
45
+ alias_method :merge!, :update
46
+
47
+ end
48
+