fancy-open-struct 0.1

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