ostruct2 0.1.0 → 0.2.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.
- 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:
|