hashery 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- require 'hashery/open_object'
1
+ raise 'Use BasicStruct in place of OpenObject for future versions.'
@@ -1,279 +1 @@
1
- require 'facets/basicobject'
2
- #require 'facets/hash/to_h'
3
- #require 'facets/kernel/object_class'
4
- #require 'facets/kernel/object_hexid'
5
-
6
- # = OpenObject
7
- #
8
- # OpenObject is very similar to Ruby's own OpenStruct, but it offers some
9
- # advantages. With OpenStruct, slots with the same name as predefined
10
- # Object methods cannot be used. With OpenObject, almost any slot can be
11
- # defined. OpenObject is a subclass of BasicObject to ensure all method
12
- # slots, except those that are absolutely essential, are open for use.
13
- #
14
- #--
15
- # If you wish to pass an OpenObject to a routine that normal takes a Hash,
16
- # but are uncertain it can handle the distictions properly you can convert
17
- # easily to a Hash using #as_hash! and the result will automatically be
18
- # converted back to an OpenObject on return.
19
- #
20
- # o = OpenObject.new(:a=>1,:b=>2)
21
- # o.as_hash!{ |h| h.update(:a=>6) }
22
- # o #=> #<OpenObject {:a=>6,:b=>2}>
23
- #++
24
- #
25
- # Unlike a Hash, all OpenObject's keys are symbols and all keys are converted
26
- # to such using #to_sym on the fly.
27
-
28
- class OpenObject < BasicObject
29
-
30
- #PUBLIC_METHODS = /(^__|^instance_|^object_|^\W|^as$|^send$|^class$|\?$)/
31
- #protected(*public_instance_methods.select{ |m| m !~ PUBLIC_METHODS })
32
-
33
- def self.[](hash=nil)
34
- new(hash)
35
- end
36
-
37
- # Inititalizer for OpenObject is slightly different than that of Hash.
38
- # It does not take a default parameter, but an initial priming Hash,
39
- # like OpenStruct. The initializer can still take a default block
40
- # however. To set the default value use <code>#default!(value)</code>.
41
- #
42
- # OpenObject.new(:a=>1).default!(0)
43
- #
44
- def initialize(hash=nil, &yld)
45
- @hash = Hash.new(&yld)
46
- if hash
47
- hash.each{ |k,v| store(k,v) }
48
- end
49
- end
50
-
51
- #
52
- def initialize_copy( orig )
53
- orig.each{ |k,v| store(k,v) }
54
- end
55
-
56
- # Object inspection.
57
- # TODO: Need to get __class__ and __id__ in hex form.
58
- def inspect
59
- #@hash.inspect
60
- hexid = __id__
61
- klass = "OpenObject" # __class__
62
- "#<#{klass}:#{hexid} #{@hash.inspect}>"
63
- end
64
-
65
- # Convert to an associative array.
66
- def to_a
67
- @hash.to_a
68
- end
69
-
70
- #
71
- def to_h
72
- @hash.dup
73
- end
74
-
75
- #
76
- def to_hash
77
- @hash.dup
78
- end
79
-
80
- #
81
- def to_openobject
82
- self
83
- end
84
-
85
- # Convert to an assignment procedure.
86
- def to_proc(response=false)
87
- hash = @hash
88
- if response
89
- lambda do |o|
90
- hash.each do |k,v|
91
- o.__send__("#{k}=", v) rescue nil
92
- end
93
- end
94
- else
95
- lambda do |o|
96
- hash.each{ |k,v| o.__send__("#{k}=", v) }
97
- end
98
- end
99
- end
100
-
101
- # NOT SURE ABOUT THIS
102
- def as_hash
103
- @hash
104
- end
105
-
106
- # Is a given +key+ defined?
107
- def key?(key)
108
- @hash.key?(key.to_sym)
109
- end
110
-
111
- #
112
- def is_a?(klass)
113
- return true if klass == Hash # TODO: Is this wise? How to fake a subclass?
114
- return true if klass == OpenObject
115
- false
116
- end
117
-
118
- # Iterate over each key-value pair.
119
- def each(&yld)
120
- @hash.each(&yld)
121
- end
122
-
123
- # Set the default value.
124
- def default!(default)
125
- @hash.default = default
126
- end
127
-
128
- # Check equality.
129
- def ==( other )
130
- case other
131
- when OpenObject
132
- @hash == other.as_hash
133
- when Hash
134
- @hash == other
135
- else
136
- if other.respond_to?(:to_hash)
137
- @hash == other.to_hash
138
- else
139
- false
140
- end
141
- end
142
- end
143
-
144
- #
145
- def eql?( other )
146
- case other
147
- when OpenObject
148
- @hash.eql?(other.as_hash)
149
- else
150
- false
151
- end
152
- end
153
-
154
- #
155
- def <<(x)
156
- case x
157
- when Hash
158
- @hash.update(x)
159
- when Array
160
- x.each_slice(2) do |(k,v)|
161
- @hash[k] = v
162
- end
163
- end
164
- end
165
-
166
- #
167
- def []=(key, value)
168
- @hash[key.to_sym] = value
169
- end
170
-
171
- #
172
- def [](key)
173
- @hash[key.to_sym]
174
- end
175
-
176
- #
177
- def merge!(other)
178
- OpenObject.new(@hash.merge!(other))
179
- end
180
-
181
- #
182
- def update!(other)
183
- @hash.update(other)
184
- self
185
- end
186
-
187
- protected
188
-
189
- def store(k, v)
190
- @hash.store(k.to_sym, v)
191
- end
192
-
193
- def fetch(k, *d, &b)
194
- @hash.fetch(k.to_sym, *d, &b)
195
- end
196
-
197
- #def as_hash!
198
- # Functor.new do |op,*a,&b|
199
- # result = @hash.__send__(op,*a,&b)
200
- # case result
201
- # when Hash
202
- # OpenObject.new(result)
203
- # else
204
- # result
205
- # end
206
- # end
207
- #end
208
-
209
- #def define_slot(key, value=nil)
210
- # @hash[key.to_sym] = value
211
- #end
212
-
213
- #def protect_slot( key )
214
- # (class << self; self; end).class_eval {
215
- # protected key rescue nil
216
- # }
217
- #end
218
-
219
- def method_missing(sym, *args, &blk)
220
- type = sym.to_s[-1,1]
221
- key = sym.to_s.sub(/[=?!]$/,'').to_sym
222
- case type
223
- when '='
224
- store(key, args[0])
225
- when '!'
226
- @hash.__send__(key, *args, &blk)
227
- # if key?(key)
228
- # fetch(key)
229
- # else
230
- # store(key, OpenObject.new)
231
- # end
232
- when '?'
233
- fetch(key)
234
- else
235
- fetch(key)
236
- end
237
- end
238
-
239
- end
240
-
241
- # Core Extensions
242
-
243
- class Hash
244
- # Convert a Hash into an OpenObject.
245
- def to_openobject
246
- OpenObject[self]
247
- end
248
- end
249
-
250
- =begin
251
- class NilClass
252
- # Nil converts to an empty OpenObject.
253
- def to_openobject
254
- OpenObject.new
255
- end
256
- end
257
-
258
- class Proc
259
- # Translates a Proc into an OpenObject. By droping an OpenObject into
260
- # the Proc, the resulting assignments incured as the procedure is
261
- # evaluated produce the OpenObject. This technique is simlar to that
262
- # of MethodProbe.
263
- #
264
- # p = lambda { |x|
265
- # x.word = "Hello"
266
- # }
267
- # o = p.to_openobject
268
- # o.word #=> "Hello"
269
- #
270
- # NOTE The Proc must have an arity of one --no more and no less.
271
- def to_openobject
272
- raise ArgumentError, 'bad arity for converting Proc to openobject' if arity != 1
273
- o = OpenObject.new
274
- self.call( o )
275
- o
276
- end
277
- end
278
- =end
279
-
1
+ raise 'Use BasicStruct in place of OpenObject for future versions.'
@@ -3,15 +3,6 @@
3
3
  #
4
4
  # Copyright (c) 2005 Thomas Sawyer
5
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
6
  # ==========================================================================
16
7
  # Revision History ::
17
8
  # --------------------------------------------------------------------------
@@ -58,35 +49,53 @@
58
49
  #
59
50
  module OpenStructable
60
51
 
52
+ def self.include(base)
53
+ if Hash > base
54
+ base.module_eval do
55
+ define_method(:__table__) do
56
+ self
57
+ end
58
+ end
59
+ protected :__table__
60
+ end
61
+ end
62
+
61
63
  def initialize(hash=nil)
62
64
  @__table__ = {}
63
65
  if hash
64
66
  for k,v in hash
65
- @__table__[k.to_sym] = v
67
+ __table__[k.to_sym] = v
66
68
  new_ostruct_member(k)
67
69
  end
68
70
  end
69
71
  end
70
72
 
73
+ #
74
+ def __table__
75
+ @__table__ ||= {}
76
+ end
77
+ protected :__table__
78
+
71
79
  # duplicate an OpenStruct object members.
72
80
  def initialize_copy(orig)
73
81
  super
74
- @__table__ = @__table__.dup
82
+ __table__.replace(__table__.dup)
75
83
  end
76
84
 
77
85
  def marshal_dump
78
- @table
86
+ __table__
79
87
  end
80
- def marshal_load(x)
81
- @table = x
82
- @table.each_key{|key| new_ostruct_member(key)}
88
+
89
+ def marshal_load(hash)
90
+ __table__.replace(hash)
91
+ __table__.each_key{|key| new_ostruct_member(key)}
83
92
  end
84
93
 
85
94
  def new_ostruct_member(name)
86
95
  unless self.respond_to?(name)
87
96
  self.instance_eval %{
88
- def #{name}; @__table__[:#{name}]; end
89
- def #{name}=(x); @__table__[:#{name}] = x; end
97
+ def #{name}; __table__[:#{name}]; end
98
+ def #{name}=(x); __table__[:#{name}] = x; end
90
99
  }
91
100
  end
92
101
  end
@@ -95,15 +104,16 @@ module OpenStructable
95
104
  # Generate additional attributes and values.
96
105
  #
97
106
  def update(hash)
98
- @__table__ ||= {}
107
+ #__table__ ||= {}
99
108
  if hash
100
109
  for k,v in hash
101
- @__table__[k.to_sym] = v
110
+ __table__[k.to_sym] = v
102
111
  new_ostruct_member(k)
103
112
  end
104
113
  end
105
114
  end
106
115
 
116
+ #
107
117
  def method_missing(mid, *args) # :nodoc:
108
118
  mname = mid.to_s
109
119
  len = args.length
@@ -115,12 +125,12 @@ module OpenStructable
115
125
  raise TypeError, "can't modify frozen #{self.class}", caller(1)
116
126
  end
117
127
  mname.chop!
118
- @__table__ ||= {}
119
- @__table__[mname.intern] = args[0]
128
+ #@__table__ ||= {}
129
+ __table__[mname.intern] = args[0]
120
130
  self.new_ostruct_member(mname)
121
131
  elsif len == 0
122
- @__table__ ||= {}
123
- @__table__[mid]
132
+ #@__table__ ||= {}
133
+ __table__[mid]
124
134
  else
125
135
  raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
126
136
  end
@@ -130,8 +140,8 @@ module OpenStructable
130
140
  # Remove the named field from the object.
131
141
  #
132
142
  def delete_field(name)
133
- @__table__ ||= {}
134
- @__table__.delete name.to_sym
143
+ #@__table__ ||= {}
144
+ __table__.delete(name.to_sym)
135
145
  end
136
146
 
137
147
  #
@@ -145,15 +155,21 @@ module OpenStructable
145
155
  str << ">"
146
156
  end
147
157
 
148
- def __table__ # :nodoc:
149
- @__table__ ||= {}
150
- end
151
- protected :__table__
152
-
153
158
  # Compare this object and +other+ for equality.
159
+ #--
160
+ # TODO: OpenStruct could be compared too, but only if it is loaded. How?
161
+ #++
154
162
  def ==(other)
155
- return false unless(other.kind_of?(OpenStruct))
156
- return @__table__ == other.table
163
+ case other
164
+ when OpenStructable
165
+ __table__ == other.__table__
166
+ #when OpenStruct
167
+ # __table__ == other.__table__
168
+ when Hash
169
+ __table__ == other
170
+ else
171
+ false
172
+ end
157
173
  end
158
174
 
159
175
  end
@@ -168,42 +184,3 @@ class OpenStruct
168
184
  end
169
185
  =end
170
186
 
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