fancy-open-struct 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
@@ -0,0 +1,52 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ .DS_Store
31
+
32
+ # For TextMate
33
+ *.tmproj
34
+ tmtags
35
+
36
+ # For emacs:
37
+ *~
38
+ \#*
39
+ .\#*
40
+
41
+ # For vim:
42
+ *.swp
43
+
44
+ # For redcar:
45
+ #.redcar
46
+
47
+ # For rubinius:
48
+ #*.rbc
49
+
50
+ Gemfile.lock
51
+
52
+ *.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
3
+
4
+ group :development do
5
+ if RUBY_VERSION =~ /^1\.8/
6
+ gem 'rcov'
7
+ else
8
+ gem 'simplecov'
9
+ end
10
+ gem 'pry'
11
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Thomas H. Chapin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ = fancy-open-struct
2
+
3
+ OpenStruct subclass that returns nested (recursive) hash attributes as FancyOpenStructs,
4
+ and also allows usage of Hash methods for getting and setting values.
5
+
6
+ It allows for hashes within hashes to be called in a chain of methods:
7
+
8
+ fos = FancyOpenStruct.new( { :fooa => { :foob => 'fooc' } } )
9
+
10
+ fos.fooa.foob # => 'fooc'
11
+
12
+ Also, if needed, nested hashes can still be accessed as hashes:
13
+
14
+ fos.fooa_as_a_hash # { :foob => 'fooc' }
15
+
16
+ Get and set values either via dot syntax, or hash syntax (Hash keys are handled as Symbols):
17
+
18
+ fos.foo = 'bar'
19
+ fos[:foo] # 'bar'
20
+
21
+ fos[:baz] = 'qux'
22
+ fos.baz # 'qux'
23
+
24
+ fos.length # 2
25
+
26
+ FancyOpenStruct can also optionally recurse across arrays, although you
27
+ have to explicitly enable it:
28
+
29
+ h = { :somearr => [ { :name => 'a'}, { :name => 'b' } ] }
30
+
31
+ fos = FancyOpenStruct.new(h, :recurse_over_arrays => true )
32
+
33
+ fos.somarr[0].name # => 'a'
34
+ fos.somarr[1].name # => 'b'
35
+
36
+ == Installation
37
+
38
+ Available as a gem in rubygems, the default gem repository.
39
+
40
+ If you use bundler, just throw that in your gemfile :
41
+
42
+ gem 'fancy-open-struct'
43
+
44
+ You may also install the gem manually :
45
+
46
+ gem install fancy-open-struct
47
+
48
+ == Note on Patches/Pull Requests
49
+
50
+ * Fork the project.
51
+ * Make your feature addition or bug fix.
52
+ * Add tests for it. This is important so I don't break it in a
53
+ future version unintentionally.
54
+ * Commit, do not mess with rakefile, version, or history.
55
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
56
+ * Send me a pull request. Bonus points for topic branches.
57
+
58
+ == Copyright
59
+
60
+ Copyright (c) 2014 Thomas H. Chapin. See LICENSE for details.
61
+
62
+ This gem is based on the recursive-open-struct gem by William (B.J.) Snow Orvis, which can be found here:
63
+ https://github.com/aetherknight/recursive-open-struct
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler/gem_tasks'
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ spec.pattern = FileList['spec/**/*_spec.rb']
9
+ end
10
+ namespace :spec do
11
+ if RUBY_VERSION =~ /^1\.8/
12
+ desc "Rspec code coverage (1.8.7)"
13
+ RSpec::Core::RakeTask.new(:coverage) do |spec|
14
+ spec.pattern = 'spec/**/*_spec.rb'
15
+ spec.rcov = true
16
+ end
17
+ else
18
+ desc "Rspec code coverage (1.9+)"
19
+ task :coverage do
20
+ ENV['COVERAGE'] = 'true'
21
+ Rake::Task["spec"].execute
22
+ end
23
+ end
24
+ end
25
+
26
+ require 'rdoc/task'
27
+ Rake::RDocTask.new do |rdoc|
28
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
29
+
30
+ rdoc.rdoc_dir = 'rdoc'
31
+ rdoc.title = "fancy-open-struct #{version}"
32
+ rdoc.rdoc_files.include('README*')
33
+ rdoc.rdoc_files.include('lib/**/*.rb')
34
+ end
35
+
36
+ task :default => :spec
37
+
38
+ task :fix_permissions do
39
+ File.umask 0022
40
+ filelist = `git ls-files`.split("\n")
41
+ FileUtils.chmod 0644, filelist, :verbose => true
42
+ FileUtils.chmod 0755, ['lib','spec'], :verbose => true
43
+ end
44
+
45
+ task :build => :fix_permissions
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require './lib/fancy_open_struct'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "fancy-open-struct"
7
+ s.version = FancyOpenStruct::VERSION
8
+ s.authors = ["Thomas H. Chapin"]
9
+ s.email = "tchapin@gmail.com"
10
+ s.date = Time.now.utc.strftime("%Y-%m-%d")
11
+ s.homepage = "http://github.com/tomchapin/fancy-open-struct"
12
+ s.licenses = ["MIT"]
13
+
14
+ s.summary = "OpenStruct subclass that returns nested hash attributes as FancyOpenStructs"
15
+ s.description = <<-QUOTE .gsub(/^ /,'')
16
+ FancyOpenStruct is a subclass of OpenStruct, and is a variant of RecursiveOpenStruct.
17
+ It differs from OpenStruct in that it allows nested hashes to be treated in a recursive
18
+ fashion, and it also provides Hash methods for getting and setting values.
19
+
20
+ For example:
21
+
22
+ fos = FancyOpenStruct.new({ :a => { :b => 'c' } })
23
+ fos.a.b # 'c'
24
+
25
+ fos.foo = 'bar'
26
+ fos[:foo] # 'bar'
27
+
28
+ fos.length # 2
29
+
30
+ Also, nested hashes can still be accessed as hashes:
31
+
32
+ fos.a_as_a_hash # { :b => 'c' }
33
+
34
+ QUOTE
35
+
36
+ s.files = `git ls-files`.split("\n")
37
+ s.test_files = `git ls-files spec`.split("\n")
38
+ s.require_paths = ["lib"]
39
+ s.extra_rdoc_files = [
40
+ "LICENSE.txt",
41
+ "README.rdoc"
42
+ ]
43
+
44
+ s.add_development_dependency(%q<rspec>, [">= 0"])
45
+ s.add_development_dependency(%q<bundler>, [">= 0"])
46
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
47
+ end
48
+
@@ -0,0 +1 @@
1
+ require 'fancy_open_struct'
@@ -0,0 +1,126 @@
1
+ require 'pry'
2
+ require 'ostruct'
3
+ require 'delegate'
4
+
5
+ class FancyOpenStruct < OpenStruct
6
+ VERSION = "0.1"
7
+
8
+ extend Forwardable
9
+
10
+ hash_methods = Hash.instance_methods(false) - (Hash.instance_methods(false) & OpenStruct.instance_methods(false))
11
+ def_delegators :@table, *hash_methods
12
+
13
+ def initialize(hash=nil, args={})
14
+ @recurse_over_arrays = args.fetch(:recurse_over_arrays, false)
15
+ @table = {}
16
+ if hash
17
+ for k, v in hash
18
+ @table[k.to_sym] = v
19
+ new_ostruct_member(k)
20
+ end
21
+ end
22
+ @sub_elements = {}
23
+ end
24
+
25
+ def to_h
26
+ @table.dup.update(@sub_elements) do |k, oldval, newval|
27
+ if newval.kind_of?(self.class)
28
+ newval.to_h
29
+ elsif newval.kind_of?(Array)
30
+ newval.map { |a| a.kind_of?(self.class) ? a.to_h : a }
31
+ else
32
+ raise "Cached value of unsupported type: #{newval.inspect}"
33
+ end
34
+ end
35
+ end
36
+
37
+ def new_ostruct_member(name)
38
+ name = name.to_sym
39
+ unless self.respond_to?(name)
40
+ define_singleton_method name do
41
+ v = @table[name]
42
+ if v.is_a?(Hash)
43
+ @sub_elements[name] ||= self.class.new(v, :recurse_over_arrays => @recurse_over_arrays)
44
+ elsif v.is_a?(Array) and @recurse_over_arrays
45
+ @sub_elements[name] ||= recurse_over_array v
46
+ else
47
+ v
48
+ end
49
+ end
50
+ define_singleton_method("#{name}=") { |x| modifiable[name] = x }
51
+ define_singleton_method("#{name}_as_a_hash") { @table[name] }
52
+ end
53
+ name
54
+ end
55
+
56
+ def recurse_over_array array
57
+ array.map do |a|
58
+ if a.is_a? Hash
59
+ self.class.new(a, :recurse_over_arrays => true)
60
+ elsif a.is_a? Array
61
+ recurse_over_array a
62
+ else
63
+ a
64
+ end
65
+ end
66
+ end
67
+
68
+ def debug_inspect(io = STDOUT, indent_level = 0, recursion_limit = 12)
69
+ display_recursive_open_hash(io, @table, indent_level, recursion_limit)
70
+ end
71
+
72
+ def display_recursive_open_hash(io, ostrct_or_hash, indent_level, recursion_limit)
73
+
74
+ if recursion_limit <= 0
75
+ # protection against recursive structure (like in the tests)
76
+ io.puts ' '*indent_level + '(recursion limit reached)'
77
+ else
78
+ #puts ostrct_or_hash.inspect
79
+ if ostrct_or_hash.is_a?(self.class)
80
+ ostrct_or_hash = ostrct_or_hash.marshal_dump
81
+ end
82
+
83
+ # We'll display the key values like this : key = value
84
+ # to align display, we look for the maximum key length of the data that will be displayed
85
+ # (everything except hashes)
86
+ data_indent = ostrct_or_hash \
87
+ .reject { |k, v| v.is_a?(self.class) || v.is_a?(Hash) } \
88
+ .max { |a, b| a[0].to_s.length <=> b[0].to_s.length }[0].to_s.length
89
+ # puts "max length = #{data_indent}"
90
+
91
+ ostrct_or_hash.each do |key, value|
92
+ if value.is_a?(self.class) || value.is_a?(Hash)
93
+ io.puts ' '*indent_level + key.to_s + '.'
94
+ display_recursive_open_hash(io, value, indent_level + 1, recursion_limit - 1)
95
+ else
96
+ io.puts ' '*indent_level + key.to_s + ' '*(data_indent - key.to_s.length) + ' = ' + value.inspect
97
+ end
98
+ end
99
+ end
100
+
101
+ true
102
+ end
103
+
104
+ def []=(*args)
105
+ len = args.length
106
+ raise ArgumentError, "wrong number of arguments (#{len} for 2)", caller(1) if len != 2
107
+ modifiable[new_ostruct_member(args[0].to_sym)] = args[1]
108
+ end
109
+
110
+ def method_missing(mid, *args) # :nodoc:
111
+ mname = mid.id2name
112
+ len = args.length
113
+ if mname.chomp!('=')
114
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) if len != 1
115
+ # Set up an instance method to point to the key/value in the table and set the value
116
+ modifiable[new_ostruct_member(mname.to_sym)] = args[0]
117
+ elsif @table.has_key?(mid)
118
+ # The table has apparently been modified externally, so we need to set up
119
+ # an instance method to point to the key/value in the table.
120
+ new_ostruct_member(mname.to_sym)
121
+ self.send(mid)
122
+ else
123
+ nil
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,308 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'fancy_open_struct'
3
+
4
+ describe FancyOpenStruct do
5
+
6
+ describe "behavior it inherits from OpenStruct" do
7
+
8
+ it "can represent arbitrary data objects" do
9
+ fos = FancyOpenStruct.new
10
+ fos.blah = "John Smith"
11
+ fos.blah.should == "John Smith"
12
+ end
13
+
14
+ it "can be created from a hash" do
15
+ h = {:asdf => 'John Smith'}
16
+ fos = FancyOpenStruct.new(h)
17
+ fos.asdf.should == "John Smith"
18
+ end
19
+
20
+ it "can modify an existing key" do
21
+ h = {:blah => 'John Smith'}
22
+ fos = FancyOpenStruct.new(h)
23
+ fos.blah = "George Washington"
24
+ fos.blah.should == "George Washington"
25
+ end
26
+
27
+ describe "handling of arbitrary attributes" do
28
+ subject { FancyOpenStruct.new }
29
+ before(:each) do
30
+ subject.blah = "John Smith"
31
+ end
32
+
33
+ describe "#respond?" do
34
+ it { subject.should respond_to :blah }
35
+ it { subject.should respond_to :blah= }
36
+ it { subject.should_not respond_to :asdf }
37
+ it { subject.should_not respond_to :asdf= }
38
+ end # describe #respond?
39
+
40
+ describe "#methods" do
41
+ it { subject.methods.map(&:to_sym).should include :blah }
42
+ it { subject.methods.map(&:to_sym).should include :blah= }
43
+ it { subject.methods.map(&:to_sym).should_not include :asdf }
44
+ it { subject.methods.map(&:to_sym).should_not include :asdf= }
45
+ end # describe #methods
46
+ end # describe handling of arbitrary attributes
47
+ end # describe behavior it inherits from OpenStruct
48
+
49
+ describe "improvements on OpenStruct" do
50
+ it "can be converted back to a hash" do
51
+ h = {:asdf => 'John Smith'}
52
+ fos = FancyOpenStruct.new(h)
53
+ fos.to_h.should == h
54
+ end
55
+
56
+ describe 'hash methods' do
57
+ it "handles hash methods for setting values" do
58
+ fos = FancyOpenStruct.new
59
+ fos['blah'] = "John Smith"
60
+ fos[:foo] = "George Washington"
61
+ fos.blah.should == "John Smith"
62
+ fos.foo.should == "George Washington"
63
+ end
64
+
65
+ it 'converts string hash keys to symbols' do
66
+ fos = FancyOpenStruct.new
67
+ fos['blah'] = "John Smith"
68
+ fos['blah'].should == nil
69
+ fos[:blah].should == "John Smith"
70
+ fos.blah.should == "John Smith"
71
+ end
72
+
73
+ it 'forwards all of the basic Hash methods directly to the @table instance variable' do
74
+ fos = FancyOpenStruct.new
75
+ Hash.instance_methods(false).each do |method_name|
76
+ fos.respond_to?(method_name).should be_true
77
+ end
78
+ end
79
+
80
+ it 'recovers gracefully even when the internal hash @table is directly modified via hash methods' do
81
+ fos = FancyOpenStruct.new
82
+ fos.foo = 'bar'
83
+ fos.to_h.should == {:foo => "bar"}
84
+ other_hash = {:baz => :qux}
85
+ fos.merge! other_hash
86
+ fos.to_h.should == {:foo => "bar", :baz => :qux}
87
+ fos.foo.should == 'bar'
88
+ fos.baz.should == :qux
89
+ fos.instance_variable_set :@table, {}
90
+ fos.foo.should == nil
91
+ fos.baz.should == nil
92
+ end
93
+ end
94
+
95
+ context 'using strings instead of symbols as hash keys' do
96
+ it "can be created from a hash" do
97
+ h = {'asdf' => 'John Smith'}
98
+ fos = FancyOpenStruct.new(h)
99
+ fos.asdf.should == "John Smith"
100
+ end
101
+
102
+ it "can modify an existing key" do
103
+ h = {'blah' => 'John Smith'}
104
+ fos = FancyOpenStruct.new(h)
105
+ fos.blah = "George Washington"
106
+ fos.blah.should == "George Washington"
107
+ end
108
+
109
+ it 'saves string hash keys as symbols' do
110
+ h = {'blah' => 'John Smith'}
111
+ fos = FancyOpenStruct.new(h)
112
+ fos.to_h.should == {:blah => "John Smith"}
113
+ fos[:blah].should == "John Smith"
114
+ end
115
+ end
116
+ end
117
+
118
+ describe "recursive behavior" do
119
+ let(:h) { {:blah => {:another => 'value'}} }
120
+ subject { FancyOpenStruct.new(h) }
121
+
122
+ it "returns accessed hashes as FancyOpenStructs instead of hashes" do
123
+ subject.blah.another.should == 'value'
124
+ end
125
+
126
+ it "uses #key_as_a_hash to return key as a Hash" do
127
+ subject.blah_as_a_hash.should == {:another => 'value'}
128
+ end
129
+
130
+ describe "handling loops in the origin Hashes" do
131
+ let(:h1) { {:a => 'a'} }
132
+ let(:h2) { {:a => 'b', :h1 => h1} }
133
+ before(:each) { h1[:h2] = h2 }
134
+
135
+ subject { FancyOpenStruct.new(h2) }
136
+
137
+ it { subject.h1.a.should == 'a' }
138
+ it { subject.h1.h2.a.should == 'b' }
139
+ it { subject.h1.h2.h1.a.should == 'a' }
140
+ it { subject.h1.h2.h1.h2.a.should == 'b' }
141
+ it { subject.h1.should == subject.h1.h2.h1 }
142
+ it { subject.h1.should_not == subject.h1.h2 }
143
+ end # describe handling loops in the origin Hashes
144
+
145
+ it "can modify a key of a sub-element" do
146
+ h = {
147
+ :blah => {
148
+ :blargh => 'Brad'
149
+ }
150
+ }
151
+ fos = FancyOpenStruct.new(h)
152
+ fos.blah.blargh = "Janet"
153
+ fos.blah.blargh.should == "Janet"
154
+ end
155
+
156
+ context "after a sub-element has been modified" do
157
+ let(:hash) do
158
+ {
159
+ :blah => {
160
+ :blargh => 'Brad'
161
+ }
162
+ }
163
+ end
164
+ subject { FancyOpenStruct.new(hash) }
165
+ before(:each) { subject.blah.blargh = "Janet" }
166
+ it "returns a hash that contains those modifications" do
167
+ subject.to_h.should == {:blah => {:blargh => "Janet"}}
168
+ end
169
+ end
170
+
171
+
172
+ describe 'recursing over arrays' do
173
+ let(:blah_list) { [{:foo => '1'}, {:foo => '2'}, 'baz'] }
174
+ let(:h) { {:blah => blah_list} }
175
+
176
+ context "when recursing over arrays is enabled" do
177
+ subject { FancyOpenStruct.new(h, :recurse_over_arrays => true) }
178
+
179
+ it { subject.blah.length.should == 3 }
180
+ it { subject.blah[0].foo.should == '1' }
181
+ it { subject.blah[1].foo.should == '2' }
182
+ it { subject.blah_as_a_hash.should == blah_list }
183
+ it { subject.blah[2].should == 'baz' }
184
+ it "Retains changes acfoss Array lookups" do
185
+ subject.blah[1].foo = "Dr Scott"
186
+ subject.blah[1].foo.should == "Dr Scott"
187
+ end
188
+ it "propagates the changes through to .to_h acfoss Array lookups" do
189
+ subject.blah[1].foo = "Dr Scott"
190
+ subject.to_h.should == {
191
+ :blah => [{:foo => '1'}, {:foo => "Dr Scott"}, 'baz']
192
+ }
193
+ end
194
+
195
+ context "when array is nested deeper" do
196
+ let(:deep_hash) { {:foo => {:blah => blah_list}} }
197
+ subject { FancyOpenStruct.new(deep_hash, :recurse_over_arrays => true) }
198
+
199
+ it { subject.foo.blah.length.should == 3 }
200
+ it "Retains changes acfoss Array lookups" do
201
+ subject.foo.blah[1].foo = "Dr Scott"
202
+ subject.foo.blah[1].foo.should == "Dr Scott"
203
+ end
204
+
205
+ end
206
+
207
+ context "when array is in an array" do
208
+ let(:haah) { {:blah => [blah_list]} }
209
+ subject { FancyOpenStruct.new(haah, :recurse_over_arrays => true) }
210
+
211
+ it { subject.blah.length.should == 1 }
212
+ it { subject.blah[0].length.should == 3 }
213
+ it "Retains changes acfoss Array lookups" do
214
+ subject.blah[0][1].foo = "Dr Scott"
215
+ subject.blah[0][1].foo.should == "Dr Scott"
216
+ end
217
+
218
+ end
219
+
220
+ end # when recursing over arrays is enabled
221
+
222
+ context "when recursing over arrays is disabled" do
223
+ subject { FancyOpenStruct.new(h) }
224
+
225
+ it { subject.blah.length.should == 3 }
226
+ it { subject.blah[0].should == {:foo => '1'} }
227
+ it { subject.blah[0][:foo].should == '1' }
228
+ end # when recursing over arrays is disabled
229
+
230
+ end # recursing over arrays
231
+ end # recursive behavior
232
+
233
+ describe "additional features" do
234
+
235
+ before(:each) do
236
+ h1 = {:a => 'a'}
237
+ h2 = {:a => 'b', :h1 => h1}
238
+ h1[:h2] = h2
239
+ @fos = FancyOpenStruct.new(h2)
240
+ end
241
+
242
+ it "should have a simple way of display" do
243
+ @output = <<-QUOTE
244
+ a = "b"
245
+ h1.
246
+ a = "a"
247
+ h2.
248
+ a = "b"
249
+ h1.
250
+ a = "a"
251
+ h2.
252
+ a = "b"
253
+ h1.
254
+ a = "a"
255
+ h2.
256
+ a = "b"
257
+ h1.
258
+ a = "a"
259
+ h2.
260
+ a = "b"
261
+ h1.
262
+ a = "a"
263
+ h2.
264
+ a = "b"
265
+ h1.
266
+ a = "a"
267
+ h2.
268
+ (recursion limit reached)
269
+ QUOTE
270
+ @io = StringIO.new
271
+ @fos.debug_inspect(@io)
272
+ @io.string.should match /^a = "b"$/
273
+ @io.string.should match /^h1\.$/
274
+ @io.string.should match /^ a = "a"$/
275
+ @io.string.should match /^ h2\.$/
276
+ @io.string.should match /^ a = "b"$/
277
+ @io.string.should match /^ h1\.$/
278
+ @io.string.should match /^ a = "a"$/
279
+ @io.string.should match /^ h2\.$/
280
+ @io.string.should match /^ a = "b"$/
281
+ @io.string.should match /^ h1\.$/
282
+ @io.string.should match /^ a = "a"$/
283
+ @io.string.should match /^ h2\.$/
284
+ @io.string.should match /^ a = "b"$/
285
+ @io.string.should match /^ h1\.$/
286
+ @io.string.should match /^ a = "a"$/
287
+ @io.string.should match /^ h2\.$/
288
+ @io.string.should match /^ a = "b"$/
289
+ @io.string.should match /^ h1\.$/
290
+ @io.string.should match /^ a = "a"$/
291
+ @io.string.should match /^ h2\.$/
292
+ @io.string.should match /^ a = "b"$/
293
+ @io.string.should match /^ h1\.$/
294
+ @io.string.should match /^ a = "a"$/
295
+ @io.string.should match /^ h2\.$/
296
+ @io.string.should match /^ \(recursion limit reached\)$/
297
+ end
298
+
299
+ it "creates nested objects via subclass" do
300
+ FancyOpenStructSubClass = Class.new(FancyOpenStruct)
301
+
302
+ fossc = FancyOpenStructSubClass.new({:one => [{:two => :three}]}, recurse_over_arrays: true)
303
+
304
+ fossc.one.first.class.should == FancyOpenStructSubClass
305
+ end
306
+ end # additionnel features
307
+
308
+ end # describe FancyOpenStruct
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+
5
+ if ENV['COVERAGE'] == 'true'
6
+ require 'simplecov'
7
+ SimpleCov.start
8
+ end
9
+
10
+ # Requires supporting files with custom matchers and macfos, etc,
11
+ # in ./support/ and its subdirectories.
12
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
+
14
+ RSpec.configure do |config|
15
+ config.filter_run focus: true
16
+ config.run_all_when_everything_filtered = true
17
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fancy-open-struct
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Thomas H. Chapin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rdoc
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: ! "FancyOpenStruct is a subclass of OpenStruct, and is a variant of RecursiveOpenStruct.\nIt
63
+ differs from OpenStruct in that it allows nested hashes to be treated in a recursive\nfashion,
64
+ and it also provides Hash methods for getting and setting values.\n\nFor example:\n\n
65
+ \ fos = FancyOpenStruct.new({ :a => { :b => 'c' } })\n fos.a.b # 'c'\n\n fos.foo
66
+ = 'bar'\n fos[:foo] # 'bar'\n\n fos.length # 2\n\nAlso, nested hashes can
67
+ still be accessed as hashes:\n\n fos.a_as_a_hash # { :b => 'c' }\n\n"
68
+ email: tchapin@gmail.com
69
+ executables: []
70
+ extensions: []
71
+ extra_rdoc_files:
72
+ - LICENSE.txt
73
+ - README.rdoc
74
+ files:
75
+ - .document
76
+ - .gitignore
77
+ - .rspec
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.rdoc
81
+ - Rakefile
82
+ - fancy-open-struct.gemspec
83
+ - lib/fancy-open-struct.rb
84
+ - lib/fancy_open_struct.rb
85
+ - spec/fancy_open_struct_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: http://github.com/tomchapin/fancy-open-struct
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23.2
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: OpenStruct subclass that returns nested hash attributes as FancyOpenStructs
112
+ test_files:
113
+ - spec/fancy_open_struct_spec.rb
114
+ - spec/spec_helper.rb