classy_struct 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.rdoc +35 -3
- data/VERSION +1 -1
- data/lib/classy_struct.rb +31 -1
- data/spec/classy_struct_spec.rb +76 -5
- metadata +2 -2
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -1,9 +1,41 @@
|
|
1
1
|
= classy_struct
|
2
2
|
|
3
|
-
|
3
|
+
A better-performing alternative to OpenStruct
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
First, create a class instance of ClassyStruct:
|
8
|
+
|
9
|
+
require 'classy_struct'
|
10
|
+
Widget = ClassyStruct.new
|
11
|
+
|
12
|
+
|
13
|
+
Next, create instances of that class the same as you would with any
|
14
|
+
other class:
|
15
|
+
|
16
|
+
w1 = Widget.new
|
17
|
+
|
18
|
+
|
19
|
+
These instances are open in much the same way that OpenStruct
|
20
|
+
instances are open:
|
21
|
+
|
22
|
+
w1.foo = :bar
|
23
|
+
w1.foo # => :bar
|
24
|
+
|
25
|
+
However, generated accessors are applied to the class instance (in
|
26
|
+
this case, Widget) instead of the object instance. This means that
|
27
|
+
accessors only need to be generated once for the class, and instances
|
28
|
+
of that class get the accessors automatically.
|
29
|
+
|
30
|
+
You can also pass a Hash to the constructor, to be recursively
|
31
|
+
converted to ClassyStruct objects:
|
32
|
+
|
33
|
+
w1 = Widget.new(:foo => :bar, :baz => {:xyzzy => :thud})
|
34
|
+
w1.foo # => :bar
|
35
|
+
w1.baz.xyzzy # => :thud
|
4
36
|
|
5
37
|
== Note on Patches/Pull Requests
|
6
|
-
|
38
|
+
|
7
39
|
* Fork the project.
|
8
40
|
* Make your feature addition or bug fix.
|
9
41
|
* Add tests for it. This is important so I don't break it in a
|
@@ -15,4 +47,4 @@ Description goes here.
|
|
15
47
|
|
16
48
|
== Copyright
|
17
49
|
|
18
|
-
Copyright (c) 2009
|
50
|
+
Copyright (c) 2009 Alf Mikula. See LICENSE for details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/classy_struct.rb
CHANGED
@@ -2,11 +2,12 @@ class ClassyStruct
|
|
2
2
|
def self.new(&block)
|
3
3
|
klass = Class.new(ClassyStructClass)
|
4
4
|
klass.method_mapper = block
|
5
|
+
klass.attr_names = []
|
5
6
|
klass
|
6
7
|
end
|
7
8
|
|
8
9
|
class ClassyStructClass
|
9
|
-
def initialize(hash=
|
10
|
+
def initialize(hash=nil)
|
10
11
|
if hash
|
11
12
|
hash.each_pair do |k,v|
|
12
13
|
k = self.class.method_mapper.call(k.to_s) if self.class.method_mapper
|
@@ -24,6 +25,7 @@ class ClassyStruct
|
|
24
25
|
|
25
26
|
class << self
|
26
27
|
attr_accessor :method_mapper
|
28
|
+
attr_accessor :attr_names
|
27
29
|
|
28
30
|
def node_class(name)
|
29
31
|
@__node_classes ||= {}
|
@@ -42,6 +44,32 @@ class ClassyStruct
|
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
47
|
+
def _attrs
|
48
|
+
self.class.attr_names.inject({}) do |retval, attr_name|
|
49
|
+
retval[attr_name] = send(attr_name)
|
50
|
+
retval
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def inspect
|
55
|
+
to_hash.inspect
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_hash
|
59
|
+
retval = _attrs
|
60
|
+
|
61
|
+
retval.each_pair do |key,val|
|
62
|
+
case val
|
63
|
+
when ClassyStruct::ClassyStructClass
|
64
|
+
retval[key] = val.to_hash
|
65
|
+
when Array
|
66
|
+
retval[key] = val.map{|e| e.is_a?(ClassyStruct::ClassyStructClass) ? e.to_hash : e}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
retval
|
71
|
+
end
|
72
|
+
|
45
73
|
def new_child(key)
|
46
74
|
self.send("#{key}=", self.class.node_class(key).new)
|
47
75
|
end
|
@@ -59,6 +87,8 @@ class ClassyStruct
|
|
59
87
|
end
|
60
88
|
EOF
|
61
89
|
|
90
|
+
self.class.attr_names << base.to_sym
|
91
|
+
|
62
92
|
send(name, *args)
|
63
93
|
end
|
64
94
|
end
|
data/spec/classy_struct_spec.rb
CHANGED
@@ -46,11 +46,11 @@ describe ClassyStruct do
|
|
46
46
|
|
47
47
|
o.bar = :baz
|
48
48
|
|
49
|
-
o.methods.should include('bar')
|
50
|
-
o.class.instance_methods.should include('bar')
|
49
|
+
o.methods.map { |m| m.to_s }.should include('bar')
|
50
|
+
o.class.instance_methods.map { |m| m.to_s }.should include('bar')
|
51
51
|
|
52
52
|
p = @foo_struct.new
|
53
|
-
p.methods.should include('bar')
|
53
|
+
p.methods.map { |m| m.to_s }.should include('bar')
|
54
54
|
|
55
55
|
p.should_not_receive(:method_missing)
|
56
56
|
p.bar
|
@@ -60,8 +60,15 @@ describe ClassyStruct do
|
|
60
60
|
o = @foo_struct.new
|
61
61
|
o.bar = :baz
|
62
62
|
|
63
|
-
@foo_struct.instance_methods.should include('bar')
|
64
|
-
@bar_struct.instance_methods.should_not include('bar')
|
63
|
+
@foo_struct.instance_methods.map { |m| m.to_s }.should include('bar')
|
64
|
+
@bar_struct.instance_methods.map { |m| m.to_s }.should_not include('bar')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'adds attribute names to attr_names on the base class' do
|
68
|
+
o = @foo_struct.new
|
69
|
+
o.bar = :baz
|
70
|
+
|
71
|
+
@foo_struct.attr_names.should include(:bar)
|
65
72
|
end
|
66
73
|
end
|
67
74
|
|
@@ -178,5 +185,69 @@ describe ClassyStruct do
|
|
178
185
|
o.new_child(:foo).should == o.foo
|
179
186
|
end
|
180
187
|
end
|
188
|
+
|
189
|
+
describe :_attrs do
|
190
|
+
it 'returns a hash with all the attribute names and values' do
|
191
|
+
instance = @foo_struct.new(:foo => :bar, :baz => :xyzzy)
|
192
|
+
|
193
|
+
instance._attrs.should == {:foo => :bar, :baz => :xyzzy}
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'returns values which have been overridden since initialization' do
|
197
|
+
instance = @foo_struct.new(:foo => :bar, :baz => :xyzzy)
|
198
|
+
instance.foo = :thud
|
199
|
+
|
200
|
+
instance._attrs.should == {:foo => :thud, :baz => :xyzzy}
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'returns nil for attribute values which have not been assigned for this instance' do
|
204
|
+
instance1 = @foo_struct.new(:foo => :bar, :baz => :xyzzy)
|
205
|
+
instance1.foo = :thud
|
206
|
+
|
207
|
+
instance2 = @foo_struct.new(:fizz => :buzz)
|
208
|
+
instance2._attrs[:fizz].should == :buzz
|
209
|
+
|
210
|
+
instance2._attrs.should have_key(:foo)
|
211
|
+
instance2._attrs[:foo].should be_nil
|
212
|
+
|
213
|
+
instance2._attrs.should have_key(:baz)
|
214
|
+
instance2._attrs[:baz].should be_nil
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'does not convert nested ClassyStruct objects' do
|
218
|
+
instance = @foo_struct.new(:foo => :bar, :baz => {:xyzzy => :thud})
|
219
|
+
|
220
|
+
instance._attrs[:baz].should be_a(ClassyStruct::ClassyStructClass)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe :to_hash do
|
225
|
+
it 'returns a hash with all the attribute names and values' do
|
226
|
+
instance = @foo_struct.new(:foo => :bar, :baz => :xyzzy)
|
227
|
+
|
228
|
+
instance.to_hash.should == {:foo => :bar, :baz => :xyzzy}
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'recursively converts classy_struct children to hashes' do
|
232
|
+
instance = @foo_struct.new(:foo => :bar, :baz => {:xyzzy => :thud})
|
233
|
+
|
234
|
+
instance.to_hash.should == {:foo => :bar, :baz => {:xyzzy => :thud}}
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'recursively converts classy_struct children in arrays to hashes' do
|
238
|
+
instance = @foo_struct.new(:foo => :bar, :baz => [{:xyzzy => :thud}])
|
239
|
+
|
240
|
+
instance.to_hash.should == {:foo => :bar, :baz => [{:xyzzy => :thud}]}
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe :inspect do
|
245
|
+
it 'returns to_hash.inspect' do
|
246
|
+
instance = @foo_struct.new
|
247
|
+
instance.should_receive(:to_hash).and_return(mock(:inspect => 'inspected!'))
|
248
|
+
|
249
|
+
instance.inspect.should == 'inspected!'
|
250
|
+
end
|
251
|
+
end
|
181
252
|
end
|
182
253
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: classy_struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- amikula
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-15 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|