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 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