classy_struct 0.2.0 → 0.3.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/.gitignore CHANGED
@@ -20,3 +20,4 @@ pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
22
  tmp/
23
+ /classy_struct.gemspec
data/README.rdoc CHANGED
@@ -1,9 +1,41 @@
1
1
  = classy_struct
2
2
 
3
- Description goes here.
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 amikula. See LICENSE for details.
50
+ Copyright (c) 2009 Alf Mikula. See LICENSE for details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.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=hil)
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
@@ -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.2.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-11-18 00:00:00 -08:00
12
+ date: 2009-12-15 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency