hashery 1.0.0

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.
@@ -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
+