ostruct2 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +2 -2
- data/{DEMO.rdoc → DEMO.md} +0 -0
- data/HISTORY.rdoc +18 -2
- data/lib/ostruct2/ostruct.rb +7 -0
- data/lib/ostruct2.rb +280 -23
- metadata +12 -11
data/.ruby
CHANGED
@@ -52,7 +52,7 @@ revision: 0
|
|
52
52
|
created: '2010-04-21'
|
53
53
|
summary: A Better OpenStruct
|
54
54
|
title: OStruct2
|
55
|
-
version: 0.
|
55
|
+
version: 0.2.0
|
56
56
|
name: ostruct2
|
57
57
|
description: ! 'OStruct2 is a reimplementation of Ruby''s standard ostruct.rb library.
|
58
58
|
|
@@ -60,4 +60,4 @@ description: ! 'OStruct2 is a reimplementation of Ruby''s standard ostruct.rb li
|
|
60
60
|
|
61
61
|
member names and cloning.'
|
62
62
|
organization: Rubyworks
|
63
|
-
date: '2012-05-
|
63
|
+
date: '2012-05-23'
|
data/{DEMO.rdoc → DEMO.md}
RENAMED
File without changes
|
data/HISTORY.rdoc
CHANGED
@@ -1,8 +1,24 @@
|
|
1
1
|
= RELEASE HISTORY
|
2
2
|
|
3
|
-
0.
|
3
|
+
== 0.2.0 | 2012-05-23
|
4
4
|
|
5
|
-
|
5
|
+
This release brings the new OpenStruct to a production ready state.
|
6
|
+
|
7
|
+
Changes:
|
8
|
+
|
9
|
+
* Constructors cascade and auto/renew are now slightly different.
|
10
|
+
* Added nest/nested constructor for nests OpenStructs. (Cool!)
|
11
|
+
* Boost performace via on-demand creation of singleton methods.
|
12
|
+
* Add missing equality methods, hash method and dup/clone methods.
|
13
|
+
* Rename main class to OpenStruct2 to avoid conflicts with original.
|
14
|
+
* Require `ostruct2/ostruct` to get drop-in replacement.
|
15
|
+
|
16
|
+
|
17
|
+
== 0.1.0 | 2011-05-20
|
18
|
+
|
19
|
+
This is the initial relase of OpenSturct2.
|
20
|
+
|
21
|
+
Changes:
|
6
22
|
|
7
23
|
* First release.
|
8
24
|
|
data/lib/ostruct2.rb
CHANGED
@@ -1,29 +1,122 @@
|
|
1
|
-
#
|
1
|
+
# OpenStruct2 is a better OpenStruct class.
|
2
2
|
#
|
3
|
-
|
3
|
+
# To demonstrate the weakness of the original OpenStruct, try this IRB session:
|
4
|
+
#
|
5
|
+
# irb(main):001:0> o = OpenStruct.new
|
6
|
+
# => #<OpenStruct>
|
7
|
+
# irb(main):002:0> o.display = "Hello, World!"
|
8
|
+
# => "Hello, World!"
|
9
|
+
# irb(main):003:0> o.display
|
10
|
+
# #<OpenStruct display="Hello, World!">=> nil
|
11
|
+
#
|
12
|
+
# This new OpenStruct class allows *almost* any member name to be used.
|
13
|
+
# The only exceptions are methods starting with double underscores,
|
14
|
+
# such as `__id__` and `__send__`, and a few neccessary public
|
15
|
+
# methods: `clone`, `dup`, `freeze`, `hash`, `to_enum`, `to_h`,
|
16
|
+
# `to_s` and `inspect`, as well as `instance_eval` and `instance_exec`.
|
17
|
+
#
|
18
|
+
# Also note that `empty`, `eql`, `equal`, `frozen` and `key` can be used as
|
19
|
+
# members but the key-check shorthand of using `?`-methods cannot be used since
|
20
|
+
# these have special definitions.
|
21
|
+
#
|
22
|
+
# To offset the loss of most methods, OpenStruct provides numerous
|
23
|
+
# bang-methods which can be used to manipulate the data, e.g. `#each!`.
|
24
|
+
# Currently most bang-methods route directly to the underlying hash table,
|
25
|
+
# so developers should keep that in mind when using this feature. A future
|
26
|
+
# version may add an intermediate interface to always ensure proper "CRUD",
|
27
|
+
# functonality but in the vast majority of cases it will make no difference,
|
28
|
+
# so it is left for later consideration.
|
29
|
+
#
|
30
|
+
# This improved version of OpenStruct also has no issues with being cloned
|
31
|
+
# since it does not depend on singleton methods to work. But singleton methods
|
32
|
+
# are used to help boost performance. But instead of always creating singleton
|
33
|
+
# methods, it only creates them on the first attempt to use them.
|
34
|
+
#
|
35
|
+
class OpenStruct2 < BasicObject
|
4
36
|
|
5
37
|
class << self
|
6
38
|
#
|
7
|
-
# Create
|
39
|
+
# Create autovivified OpenStruct.
|
8
40
|
#
|
9
|
-
|
10
|
-
|
41
|
+
# @example
|
42
|
+
# o = OpenStruct2.renew
|
43
|
+
# o.a #=> #<OpenStruct2: {}>
|
44
|
+
#
|
45
|
+
def auto(data=nil)
|
46
|
+
leet = lambda{ |h,k| new(&leet) }
|
11
47
|
new(&leet)
|
12
48
|
end
|
13
49
|
|
14
|
-
#
|
15
|
-
#
|
50
|
+
#
|
51
|
+
# Another name for #auto method.
|
52
|
+
#
|
53
|
+
# TODO: Still wondering waht the best name is for this.
|
54
|
+
#
|
55
|
+
alias :renew :auto
|
56
|
+
|
57
|
+
#
|
58
|
+
# Create a nested OpenStruct, such that all sub-hashes
|
59
|
+
# added to the table also become OpenStruct objects.
|
60
|
+
#
|
61
|
+
def nested(data=nil)
|
62
|
+
o = new
|
63
|
+
o.nested!(true)
|
64
|
+
o.update!(data) if data
|
65
|
+
o
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Shorter name for `nested`.
|
70
|
+
#
|
71
|
+
alias :nest :nested
|
72
|
+
|
73
|
+
#
|
74
|
+
# Constructor that is both autovivified and nested.
|
75
|
+
#
|
76
|
+
def cascade(data=nil)
|
77
|
+
o = renew
|
78
|
+
o.nested!(true)
|
79
|
+
o.update!(data) if data
|
80
|
+
o
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
16
84
|
|
17
|
-
|
18
|
-
|
85
|
+
def const_missing(name)
|
86
|
+
::Object.const_get(name)
|
87
|
+
end
|
19
88
|
end
|
20
89
|
|
90
|
+
#
|
91
|
+
# Initialize new instance of OpenStruct.
|
92
|
+
#
|
93
|
+
# @param [Hash] data
|
21
94
|
#
|
22
95
|
def initialize(data=nil, &block)
|
23
96
|
@table = ::Hash.new(&block)
|
24
97
|
update!(data || {})
|
25
98
|
end
|
26
99
|
|
100
|
+
#
|
101
|
+
# Because there is no means of getting the class via a BasicObject instance,
|
102
|
+
# we define such a method manually.
|
103
|
+
#
|
104
|
+
def __class__
|
105
|
+
OpenStruct2
|
106
|
+
end
|
107
|
+
|
108
|
+
#
|
109
|
+
# Duplicate underlying table when OpenStruct is duplicated or cloned.
|
110
|
+
#
|
111
|
+
# @param [OpenStruct] original
|
112
|
+
#
|
113
|
+
def initialize_copy(original)
|
114
|
+
super
|
115
|
+
@table = @table.dup
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Dispatch unrecognized member calls.
|
27
120
|
#
|
28
121
|
def method_missing(sym, *args, &blk)
|
29
122
|
str = sym.to_s
|
@@ -31,42 +124,84 @@ class OpenStruct < BasicObject
|
|
31
124
|
name = str.chomp('=').chomp('!').chomp('?')
|
32
125
|
|
33
126
|
case type
|
34
|
-
when '='
|
35
|
-
store!(name, args.first)
|
36
127
|
when '!'
|
128
|
+
# TODO: Probably should have an indirect interface to ensure proper
|
129
|
+
# functonality in all cases.
|
37
130
|
@table.public_send(name, *args, &blk)
|
131
|
+
when '='
|
132
|
+
new_ostruct_member(name)
|
133
|
+
store!(name, args.first)
|
38
134
|
when '?'
|
135
|
+
new_ostruct_member(name)
|
39
136
|
key?(name)
|
40
137
|
else
|
138
|
+
new_ostruct_member(name)
|
41
139
|
read!(name)
|
42
140
|
end
|
43
141
|
end
|
44
142
|
|
143
|
+
#
|
144
|
+
# Get/set nested flag.
|
145
|
+
#
|
146
|
+
def nested!(boolean=nil)
|
147
|
+
if boolean.nil?
|
148
|
+
@nested
|
149
|
+
else
|
150
|
+
@nested = !!boolean
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
45
155
|
# CRUD method for listing all keys.
|
156
|
+
#
|
46
157
|
def keys!
|
47
158
|
@table.keys
|
48
159
|
end
|
49
160
|
|
161
|
+
#
|
50
162
|
# Also a CRUD method like #read!, but for checking for the existence of a key.
|
163
|
+
#
|
51
164
|
def key?(key)
|
52
165
|
@table.key?(key.to_sym)
|
53
166
|
end
|
54
167
|
|
168
|
+
#
|
55
169
|
# The CRUD method for read.
|
170
|
+
#
|
56
171
|
def read!(key)
|
57
172
|
@table[key.to_sym]
|
58
173
|
end
|
59
174
|
|
175
|
+
#
|
60
176
|
# The CRUD method for create and update.
|
177
|
+
#
|
61
178
|
def store!(key, value)
|
179
|
+
if @nested && Hash === value # value.respond_to?(:to_hash)
|
180
|
+
value = OpenStruct2.new(value)
|
181
|
+
end
|
182
|
+
|
183
|
+
#new_ostruct_member(key) # this is here only for speed bump
|
184
|
+
|
62
185
|
@table[key.to_sym] = value
|
63
186
|
end
|
64
187
|
|
188
|
+
#
|
65
189
|
# The CRUD method for destroy.
|
190
|
+
#
|
66
191
|
def delete!(key)
|
67
192
|
@table.delete(key.to_sym)
|
68
193
|
end
|
69
194
|
|
195
|
+
#
|
196
|
+
# Same as `#delete!`. This method provides compatibility
|
197
|
+
# with the original OpenStruct class.
|
198
|
+
#
|
199
|
+
# @deprecated Use `#delete!` method instead.
|
200
|
+
#
|
201
|
+
def delete_field(key)
|
202
|
+
@table.delete(key.to_sym)
|
203
|
+
end
|
204
|
+
|
70
205
|
#
|
71
206
|
# Like #read but will raise a KeyError if key is not found.
|
72
207
|
#
|
@@ -75,37 +210,83 @@ class OpenStruct < BasicObject
|
|
75
210
|
read!(key)
|
76
211
|
end
|
77
212
|
|
213
|
+
#
|
214
|
+
# If key is not present raise a KeyError.
|
215
|
+
#
|
216
|
+
# @param [#to_sym] key
|
217
|
+
#
|
218
|
+
# @raise [KeyError] If key is not present.
|
219
|
+
#
|
220
|
+
# @return key
|
221
|
+
#
|
222
|
+
def key!(key)
|
223
|
+
return key if key?(key)
|
224
|
+
::Kernel.raise ::KeyError, ("key not found: %s" % [key.inspect])
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# Alias for `#read!`.
|
229
|
+
#
|
230
|
+
# @param [#to_sym] key
|
231
|
+
#
|
232
|
+
# @return [Object]
|
78
233
|
#
|
79
234
|
def [](key)
|
80
235
|
read!(key)
|
81
236
|
end
|
82
237
|
|
238
|
+
#
|
239
|
+
# Alias for `#store!`.
|
240
|
+
#
|
241
|
+
# @param [#to_sym] key
|
242
|
+
#
|
243
|
+
# @param [Object] value
|
244
|
+
#
|
245
|
+
# @return value.
|
83
246
|
#
|
84
247
|
def []=(key, value)
|
85
248
|
store!(key, value)
|
86
249
|
end
|
87
250
|
|
251
|
+
#
|
88
252
|
# CRUDified each.
|
253
|
+
#
|
254
|
+
# @return nothing
|
255
|
+
#
|
89
256
|
def each!
|
90
257
|
@table.each_key do |key|
|
91
258
|
yield(key, read!(key))
|
92
259
|
end
|
93
260
|
end
|
94
261
|
|
262
|
+
#
|
263
|
+
def map!(&block)
|
264
|
+
to_enum.map(&block)
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
95
268
|
# CRUDified update method.
|
269
|
+
#
|
270
|
+
# @return [self]
|
271
|
+
#
|
96
272
|
def update!(other)
|
97
273
|
other.each do |k,v|
|
98
274
|
store!(k,v)
|
99
275
|
end
|
276
|
+
self
|
100
277
|
end
|
101
278
|
|
279
|
+
#
|
280
|
+
# Merge this OpenStruct with another OpenStruct or Hash object
|
281
|
+
# returning a new OpenStruct instance.
|
102
282
|
#
|
103
283
|
# IMPORTANT! This method does not act in-place like `Hash#merge!`,
|
104
284
|
# rather it works like `Hash#merge`.
|
105
285
|
#
|
286
|
+
# @return [OpenStruct]
|
287
|
+
#
|
106
288
|
def merge!(other)
|
107
|
-
|
108
|
-
o = ::OpenStruct.new(@table, &@table.default_proc)
|
289
|
+
o = dup
|
109
290
|
other.each do |k,v|
|
110
291
|
o.store!(k,v)
|
111
292
|
end
|
@@ -113,38 +294,114 @@ class OpenStruct < BasicObject
|
|
113
294
|
end
|
114
295
|
|
115
296
|
#
|
116
|
-
#
|
297
|
+
# Inspect OpenStruct object.
|
117
298
|
#
|
118
|
-
|
119
|
-
|
299
|
+
# @return [String]
|
300
|
+
#
|
301
|
+
def inspect
|
302
|
+
"#<#{__class__}: #{@table.inspect}>"
|
120
303
|
end
|
121
304
|
|
305
|
+
alias :to_s :inspect
|
306
|
+
|
122
307
|
#
|
123
308
|
# Get a duplicate of the underlying table.
|
124
309
|
#
|
310
|
+
# @return [Hash]
|
311
|
+
#
|
125
312
|
def to_h
|
126
313
|
@table.dup
|
127
314
|
end
|
128
315
|
|
316
|
+
# TODO: Should OpenStruct2 support #to_hash ?
|
317
|
+
#alias :to_hash :to_h
|
318
|
+
|
319
|
+
#
|
320
|
+
# Create an enumerator based on `#each!`.
|
321
|
+
#
|
322
|
+
# @return [Enumerator]
|
129
323
|
#
|
130
324
|
def to_enum
|
131
325
|
::Enumerator.new(self, :each!)
|
132
326
|
end
|
133
327
|
|
134
328
|
#
|
135
|
-
#
|
329
|
+
# Duplicate OpenStruct object.
|
136
330
|
#
|
137
|
-
|
138
|
-
|
139
|
-
|
331
|
+
# @return [OpenStruct] Duplicate instance.
|
332
|
+
#
|
333
|
+
def dup
|
334
|
+
__class__.new(@table, &@table.default_proc)
|
140
335
|
end
|
141
336
|
|
337
|
+
alias :clone :dup
|
338
|
+
|
142
339
|
#
|
340
|
+
# Hash number.
|
143
341
|
#
|
342
|
+
def hash
|
343
|
+
@table.hash
|
344
|
+
end
|
345
|
+
|
144
346
|
#
|
145
|
-
|
146
|
-
|
347
|
+
# Freeze OpenStruct instance.
|
348
|
+
#
|
349
|
+
def freeze
|
350
|
+
@table.freeze
|
147
351
|
end
|
148
352
|
|
149
|
-
|
353
|
+
#
|
354
|
+
# Is the OpenStruct instance frozen?
|
355
|
+
#
|
356
|
+
# @return [Boolean]
|
357
|
+
#
|
358
|
+
def frozen?
|
359
|
+
@table.frozen?
|
360
|
+
end
|
150
361
|
|
362
|
+
#
|
363
|
+
# Is the OpenStruct void of entries?
|
364
|
+
#
|
365
|
+
def empty?
|
366
|
+
@table.empty?
|
367
|
+
end
|
368
|
+
|
369
|
+
#
|
370
|
+
# Two OpenStructs are equal if they are the same class and their
|
371
|
+
# underlying tables are equal.
|
372
|
+
#
|
373
|
+
def eql?(other)
|
374
|
+
return false unless(other.kind_of?(__class__))
|
375
|
+
return @table == other.table #to_h
|
376
|
+
end
|
377
|
+
|
378
|
+
#
|
379
|
+
# Two OpenStructs are equal if they are the same class and their
|
380
|
+
# underlying tables are equal.
|
381
|
+
#
|
382
|
+
# TODO: Why not equal for other hash types, e.g. via #to_h?
|
383
|
+
#
|
384
|
+
def ==(other)
|
385
|
+
return false unless(other.kind_of?(__class__))
|
386
|
+
return @table == other.table #to_h
|
387
|
+
end
|
388
|
+
|
389
|
+
protected
|
390
|
+
|
391
|
+
def table
|
392
|
+
@table
|
393
|
+
end
|
394
|
+
|
395
|
+
def new_ostruct_member(name)
|
396
|
+
name = name.to_sym
|
397
|
+
# TODO: Check `#respond_to?` is needed? And if so how to do this in BasicObject?
|
398
|
+
#return name if self.respond_to?(name)
|
399
|
+
(class << self; self; end).class_eval do
|
400
|
+
define_method(name) { read!(name) }
|
401
|
+
define_method("#{name}?") { key?(name) }
|
402
|
+
define_method("#{name}=") { |value| store!(name, value) }
|
403
|
+
end
|
404
|
+
name
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ostruct2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: detroit
|
16
|
-
requirement: &
|
16
|
+
requirement: &22522480 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *22522480
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: qed
|
27
|
-
requirement: &
|
27
|
+
requirement: &22521760 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *22521760
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: ae
|
38
|
-
requirement: &
|
38
|
+
requirement: &22521140 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *22521140
|
47
47
|
description: ! 'OStruct2 is a reimplementation of Ruby''s standard ostruct.rb library.
|
48
48
|
|
49
49
|
This new OpenStruct class addresses issues the original has with conflicting
|
@@ -55,16 +55,17 @@ executables: []
|
|
55
55
|
extensions: []
|
56
56
|
extra_rdoc_files:
|
57
57
|
- HISTORY.rdoc
|
58
|
-
- DEMO.rdoc
|
59
58
|
- README.rdoc
|
59
|
+
- DEMO.md
|
60
60
|
files:
|
61
61
|
- .ruby
|
62
|
+
- lib/ostruct2/ostruct.rb
|
62
63
|
- lib/ostruct2.rb
|
63
64
|
- HISTORY.rdoc
|
64
|
-
- DEMO.rdoc
|
65
65
|
- README.rdoc
|
66
|
+
- DEMO.md
|
66
67
|
- Config.rb
|
67
|
-
homepage:
|
68
|
+
homepage: http://rubyworks.github.com/ostruct2
|
68
69
|
licenses:
|
69
70
|
- BSD-2-Clause
|
70
71
|
post_install_message:
|