recursive-open-struct 0.5.0 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4442ce9b9b12e7e7b187a2df666cf8018c97a30d
4
- data.tar.gz: d869866022cbc4e3bc5f7ade8b2ed424010b0823
3
+ metadata.gz: 72ff48c134d3b90f56c0c2808e55f107c8bdd302
4
+ data.tar.gz: 3acf497ceb23b7b86de443dc3b4a83ddffa143eb
5
5
  SHA512:
6
- metadata.gz: 217c2af046ca66b67c2f35d513dcd05dd4e00255ec73a67f66b80acf8403c62919728cab7c255f0435cd1e7264c2e53cb57a1be1b79eacc4bb85fe0b91ca27d5
7
- data.tar.gz: f6a6aa2438f32e8f3d4021ddecd1547915702e00599475dff6e6ce6bfb23f9191f4f2400057531abfb455a0e6b1710cc184fc2884c21e9afcacca931bd13dc40
6
+ metadata.gz: 0279ca781f0607ae71c16d21589644a7b6151605f7f8cd74b47129ff2aad4c99536893375215aa9308342db54ba117ce271346bbc9c6a589c10e0f5bb105c9d1
7
+ data.tar.gz: be2d8df65e43686494858e8ab4cbec822336bcd5f68d0f87445fd968afd4ea08770e766192c489d900ae7d6b7233b9a17308da0089c7f6aa821b45e967f29a4c
data/.travis.yml CHANGED
@@ -4,4 +4,5 @@ rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
6
  - 2.1.0
7
+ - 2.2
7
8
  - jruby-19mode
data/CHANGELOG.md ADDED
@@ -0,0 +1,92 @@
1
+ 0.6.0 / 2015-03-28
2
+ ==================
3
+
4
+ * NEW: fervic: Make subscript notation be recursive like dot-method notation
5
+ * NEW: fervic: Added a new option, `:mutate_input_hash`, that allows the caller
6
+ to determine whether the original hash is mutated or not when a nested value
7
+ in the ROS tree is modified. If false (the default), the ROS will not modify
8
+ the original hash tree. If tree, changes within the ROS tree will also be
9
+ reflected in the hash tree.
10
+ * FIX: fervic: Setting/updating a value nested deep in an ROS tree is kept
11
+ when the top-level ROS object is duped.
12
+ * MAINT: Extracted `#deep_dup` added by fervic into its own class. This makes it
13
+ possibly easier to use/copy for others, and it cleans up the main class file.
14
+ * MAINT: Moved `#debug_inspect` out to its own module. This cleans up the main
15
+ class file a bit. It is also something I may remove if I ever have a major
16
+ version bump.
17
+ * MAINT: Adding MRI 2.2 to Travis-CI
18
+
19
+ 0.5.0 / 2014-06-14
20
+ ==================
21
+
22
+ * NEW: Tom Chapin: Added a `#to_hash` alias for `#to_h`
23
+ * MAINT: Added Travis-CI support. Testing against MRI 1.9.3, MRI 2.0, MRI 2.1,
24
+ and JRuby in 1.9 mode. Not aiming to support 1.8.7 since it has been nearly a
25
+ year since it has officially been retired.
26
+
27
+ 0.4.5 / 2013-10-23
28
+ ==================
29
+
30
+ * FIX: Matt Culpepper: Allow ROS subclasses to use their own type when creating
31
+ nested objects in the tree.
32
+
33
+ 0.4.4 / 2013-08-28
34
+ ==================
35
+
36
+ * FIX: Ensure proper file permissions when building the gem archive
37
+
38
+ 0.4.3 / 2013-05-30
39
+ ==================
40
+
41
+ * FIX: Sebastian Gaul: Make `recurse_over_arrays` option work on more
42
+ deeply-nested hashes.
43
+
44
+ 0.4.2 / 2013-05-29
45
+ ==================
46
+
47
+ * FIX: Setting a value on a nested element, then getting that value should show
48
+ the updated value
49
+ * FIX: Calling `#to_h` on the top-level ROS object also reflects changed nested
50
+ elements.
51
+
52
+ 0.4.1 / 2013-05-28
53
+ ==================
54
+
55
+ * FIX: Fixing the `spec:coverage` Rake task
56
+
57
+ 0.4.0 / 2013-05-26
58
+ ==================
59
+
60
+ * NEW: Added `#to_h`
61
+ * MAINT: Stopped using jeweler for gem development/packaging
62
+
63
+ 0.3.1 / 2012-10-23
64
+ ==================
65
+
66
+ * FIX: Cédric Felizard: Fix to make it work with MRI 1.8.7 again
67
+ * MAINT: More spec fixups to improve spec runs on MRI 1.9.3
68
+
69
+ 0.3.0 / 2013-10-23
70
+ ==================
71
+
72
+ * NEW: Matthew O'Riordan: Add support for recursion working over Arrays
73
+ * NEW: Made recursion over Arrays optional with `recurse_over_arrays` option.
74
+ * NEW: Improving `#debug_inspect` so that it can use any IO object, not just
75
+ STDOUT.
76
+ * MAINT: Much cleanup of development dependencies, README file, etc.
77
+
78
+ 0.2.1 / 2011-05-31
79
+ ==================
80
+
81
+ * FIX: Offirmo: Slight improvement for `#debug_inspect`
82
+
83
+ 0.2.0 / 2011-05-25
84
+ ==================
85
+
86
+ * NEW: Offirmo: Added `debug_inspect`
87
+ * MAINT: Offirmo: Worked the development files so that it can be built as a gem
88
+
89
+ 0.1.0 / 2010-01-12
90
+ ==================
91
+
92
+ * Initial release
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 William (B.J.) Snow Orvis
1
+ Copyright (c) 2009-2015 William (B.J.) Snow Orvis
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -1,4 +1,4 @@
1
- = recursive-open-struct
1
+ # recursive-open-struct
2
2
 
3
3
  OpenStruct subclass that returns nested hash attributes as
4
4
  RecursiveOpenStructs.
@@ -23,7 +23,7 @@ have to explicitly enable it:
23
23
  ros.somarr[0].name # => 'a'
24
24
  ros.somarr[1].name # => 'b'
25
25
 
26
- == Installation
26
+ ## Installation
27
27
 
28
28
  Available as a gem in rubygems, the default gem repository.
29
29
 
@@ -35,16 +35,14 @@ You may also install the gem manually :
35
35
 
36
36
  gem install recursive-open-struct
37
37
 
38
- == Note on Patches/Pull Requests
38
+ ## Contributing
39
39
 
40
40
  * Fork the project.
41
41
  * Make your feature addition or bug fix.
42
- * Add tests for it. This is important so I don't break it in a
43
- future version unintentionally.
44
- * Commit, do not mess with rakefile, version, or history.
45
- (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)
42
+ * Add tests for your new or changed functionality. Make sure the tests you add
43
+ provide clean and clear explanation of the feature.
46
44
  * Send me a pull request. Bonus points for topic branches.
47
45
 
48
- == Copyright
46
+ ## Copyright
49
47
 
50
- Copyright (c) 2010 William (B.J.) Snow Orvis. See LICENSE for details.
48
+ Copyright (c) 2009-2015 William (B.J.) Snow Orvis. See LICENSE for details.
@@ -1,11 +1,22 @@
1
1
  require 'ostruct'
2
+ require 'recursive_open_struct/version'
3
+
4
+ require 'recursive_open_struct/debug_inspect'
5
+ require 'recursive_open_struct/deep_dup'
2
6
 
3
7
  class RecursiveOpenStruct < OpenStruct
4
- VERSION = "0.5.0"
8
+ include DebugInspect
9
+
10
+ def initialize(hash=nil, args={})
11
+ @recurse_over_arrays = args.fetch(:recurse_over_arrays, false)
12
+ mutate_input_hash = args.fetch(:mutate_input_hash, false)
13
+
14
+ unless mutate_input_hash
15
+ hash = DeepDup.new(recurse_over_arrays: @recurse_over_arrays).call(hash)
16
+ end
17
+
18
+ super(hash)
5
19
 
6
- def initialize(h=nil, args={})
7
- @recurse_over_arrays = args.fetch(:recurse_over_arrays,false)
8
- super(h)
9
20
  @sub_elements = {}
10
21
  end
11
22
 
@@ -23,6 +34,10 @@ class RecursiveOpenStruct < OpenStruct
23
34
 
24
35
  alias_method :to_hash, :to_h
25
36
 
37
+ def [](name)
38
+ send name
39
+ end
40
+
26
41
  def new_ostruct_member(name)
27
42
  name = name.to_sym
28
43
  unless self.respond_to?(name)
@@ -30,9 +45,11 @@ class RecursiveOpenStruct < OpenStruct
30
45
  define_method(name) do
31
46
  v = @table[name]
32
47
  if v.is_a?(Hash)
33
- @sub_elements[name] ||= self.class.new(v, :recurse_over_arrays => @recurse_over_arrays)
48
+ @sub_elements[name] ||= self.class.new(v,
49
+ :recurse_over_arrays => @recurse_over_arrays,
50
+ :mutate_input_hash => true)
34
51
  elsif v.is_a?(Array) and @recurse_over_arrays
35
- @sub_elements[name] ||= recurse_over_array v
52
+ @sub_elements[name] ||= recurse_over_array(v)
36
53
  else
37
54
  v
38
55
  end
@@ -44,10 +61,11 @@ class RecursiveOpenStruct < OpenStruct
44
61
  name
45
62
  end
46
63
 
47
- def recurse_over_array array
64
+ # TODO: Make me private if/when we do an API-breaking change release
65
+ def recurse_over_array(array)
48
66
  array.map do |a|
49
67
  if a.is_a? Hash
50
- self.class.new(a, :recurse_over_arrays => true)
68
+ self.class.new(a, :recurse_over_arrays => true, :mutate_input_hash => true)
51
69
  elsif a.is_a? Array
52
70
  recurse_over_array a
53
71
  else
@@ -55,41 +73,5 @@ class RecursiveOpenStruct < OpenStruct
55
73
  end
56
74
  end
57
75
  end
58
-
59
- def debug_inspect(io = STDOUT, indent_level = 0, recursion_limit = 12)
60
- display_recursive_open_struct(io, @table, indent_level, recursion_limit)
61
- end
62
-
63
- def display_recursive_open_struct(io, ostrct_or_hash, indent_level, recursion_limit)
64
-
65
- if recursion_limit <= 0 then
66
- # protection against recursive structure (like in the tests)
67
- io.puts ' '*indent_level + '(recursion limit reached)'
68
- else
69
- #puts ostrct_or_hash.inspect
70
- if ostrct_or_hash.is_a?(self.class) then
71
- ostrct_or_hash = ostrct_or_hash.marshal_dump
72
- end
73
-
74
- # We'll display the key values like this : key = value
75
- # to align display, we look for the maximum key length of the data that will be displayed
76
- # (everything except hashes)
77
- data_indent = ostrct_or_hash \
78
- .reject { |k, v| v.is_a?(self.class) || v.is_a?(Hash) } \
79
- .max {|a,b| a[0].to_s.length <=> b[0].to_s.length}[0].to_s.length
80
- # puts "max length = #{data_indent}"
81
-
82
- ostrct_or_hash.each do |key, value|
83
- if (value.is_a?(self.class) || value.is_a?(Hash)) then
84
- io.puts ' '*indent_level + key.to_s + '.'
85
- display_recursive_open_struct(io, value, indent_level + 1, recursion_limit - 1)
86
- else
87
- io.puts ' '*indent_level + key.to_s + ' '*(data_indent - key.to_s.length) + ' = ' + value.inspect
88
- end
89
- end
90
- end
91
-
92
- true
93
- end
94
-
95
76
  end
77
+
@@ -0,0 +1,38 @@
1
+ module RecursiveOpenStruct::DebugInspect
2
+ def debug_inspect(io = STDOUT, indent_level = 0, recursion_limit = 12)
3
+ display_recursive_open_struct(io, @table, indent_level, recursion_limit)
4
+ end
5
+
6
+ def display_recursive_open_struct(io, ostrct_or_hash, indent_level, recursion_limit)
7
+
8
+ if recursion_limit <= 0 then
9
+ # protection against recursive structure (like in the tests)
10
+ io.puts ' '*indent_level + '(recursion limit reached)'
11
+ else
12
+ #puts ostrct_or_hash.inspect
13
+ if ostrct_or_hash.is_a?(self.class) then
14
+ ostrct_or_hash = ostrct_or_hash.marshal_dump
15
+ end
16
+
17
+ # We'll display the key values like this : key = value
18
+ # to align display, we look for the maximum key length of the data that will be displayed
19
+ # (everything except hashes)
20
+ data_indent = ostrct_or_hash \
21
+ .reject { |k, v| v.is_a?(self.class) || v.is_a?(Hash) } \
22
+ .max {|a,b| a[0].to_s.length <=> b[0].to_s.length}[0].to_s.length
23
+ # puts "max length = #{data_indent}"
24
+
25
+ ostrct_or_hash.each do |key, value|
26
+ if (value.is_a?(self.class) || value.is_a?(Hash)) then
27
+ io.puts ' '*indent_level + key.to_s + '.'
28
+ display_recursive_open_struct(io, value, indent_level + 1, recursion_limit - 1)
29
+ else
30
+ io.puts ' '*indent_level + key.to_s + ' '*(data_indent - key.to_s.length) + ' = ' + value.inspect
31
+ end
32
+ end
33
+ end
34
+
35
+ true
36
+ end
37
+
38
+ end
@@ -0,0 +1,30 @@
1
+ class RecursiveOpenStruct::DeepDup
2
+ def initialize(opts={})
3
+ @recurse_over_arrays = opts.fetch(:recurse_over_arrays, false)
4
+ end
5
+
6
+ def call(obj)
7
+ deep_dup(obj)
8
+ end
9
+
10
+ private
11
+
12
+ def deep_dup(obj, visited=[])
13
+ if obj.is_a?(Hash)
14
+ obj.each_with_object({}) do |(key, value), h|
15
+ h[key] = value_or_deep_dup(value, visited)
16
+ end
17
+ elsif obj.is_a?(Array) && @recurse_over_arrays
18
+ obj.each_with_object([]) do |value, arr|
19
+ arr << value_or_deep_dup(value, visited)
20
+ end
21
+ else
22
+ obj
23
+ end
24
+ end
25
+
26
+ def value_or_deep_dup(value, visited)
27
+ obj_id = value.object_id
28
+ visited.include?(obj_id) ? value : deep_dup(value, visited << obj_id)
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ # Necessary since the top-level class/module is a class that inherits from
2
+ # OpenStruct.
3
+ require 'ostruct'
4
+
5
+ class RecursiveOpenStruct < OpenStruct
6
+ VERSION = "0.6.0"
7
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require './lib/recursive_open_struct'
3
+ require './lib/recursive_open_struct/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "recursive-open-struct"
@@ -29,11 +29,12 @@ Gem::Specification.new do |s|
29
29
  s.test_files = `git ls-files spec`.split("\n")
30
30
  s.require_paths = ["lib"]
31
31
  s.extra_rdoc_files = [
32
+ "CHANGELOG.md",
32
33
  "LICENSE.txt",
33
- "README.rdoc"
34
+ "README.md"
34
35
  ]
35
36
 
36
- s.add_development_dependency(%q<rspec>, [">= 0"])
37
+ s.add_development_dependency('rspec', "~> 3.2")
37
38
  s.add_development_dependency(%q<bundler>, [">= 0"])
38
39
  s.add_development_dependency(%q<rdoc>, [">= 0"])
39
40
  s.add_development_dependency(%q<rake>, [">= 0"])
@@ -64,6 +64,10 @@ describe RecursiveOpenStruct do
64
64
  subject.blah.another.should == 'value'
65
65
  end
66
66
 
67
+ it "handles subscript notation the same way as dotted notation" do
68
+ subject.blah.another.should == subject[:blah].another
69
+ end
70
+
67
71
  it "uses #key_as_a_hash to return key as a Hash" do
68
72
  subject.blah_as_a_hash.should == { :another => 'value' }
69
73
  end
@@ -96,19 +100,30 @@ describe RecursiveOpenStruct do
96
100
 
97
101
  context "after a sub-element has been modified" do
98
102
  let(:hash) do
99
- {
100
- :blah => {
101
- :blargh => 'Brad'
102
- }
103
- }
103
+ { :blah => { :blargh => "Brad" }, :some_array => [ 1, 2, 3] }
104
+ end
105
+ let(:updated_hash) do
106
+ { :blah => { :blargh => "Janet" }, :some_array => [ 1, 2, 3] }
104
107
  end
108
+
105
109
  subject { RecursiveOpenStruct.new(hash) }
110
+
106
111
  before(:each) { subject.blah.blargh = "Janet" }
107
- it "returns a hash that contains those modifications" do
108
- subject.to_h.should == { :blah => { :blargh => "Janet" } }
112
+
113
+ it "returns a hash tree that contains those modifications" do
114
+ subject.to_h.should == updated_hash
115
+ end
116
+
117
+ it "does not mutate the input hash tree passed to the constructor" do
118
+ hash[:blah][:blargh].should == 'Brad'
109
119
  end
110
- end
111
120
 
121
+ it "limits the deep-copy to the initial hash tree" do
122
+ subject.some_array[0] = 4
123
+
124
+ hash[:some_array][0].should == 4
125
+ end
126
+ end
112
127
 
113
128
  describe 'recursing over arrays' do
114
129
  let(:blah_list) { [ { :foo => '1' }, { :foo => '2' }, 'baz' ] }
@@ -122,15 +137,30 @@ describe RecursiveOpenStruct do
122
137
  it { subject.blah[1].foo.should == '2' }
123
138
  it { subject.blah_as_a_hash.should == blah_list }
124
139
  it { subject.blah[2].should == 'baz' }
125
- it "Retains changes across Array lookups" do
126
- subject.blah[1].foo = "Dr Scott"
127
- subject.blah[1].foo.should == "Dr Scott"
128
- end
129
- it "propagates the changes through to .to_h across Array lookups" do
130
- subject.blah[1].foo = "Dr Scott"
131
- subject.to_h.should == {
132
- :blah => [ { :foo => '1' }, { :foo => "Dr Scott" }, 'baz' ]
133
- }
140
+
141
+ context "when an inner value changes" do
142
+ let(:updated_blah_list) { [ { :foo => '1' }, { :foo => 'Dr Scott' }, 'baz' ] }
143
+ let(:updated_h) { { :blah => updated_blah_list } }
144
+
145
+ before(:each) { subject.blah[1].foo = "Dr Scott" }
146
+
147
+ it "Retains changes across Array lookups" do
148
+ subject.blah[1].foo.should == "Dr Scott"
149
+ end
150
+
151
+ it "propagates the changes through to .to_h across Array lookups" do
152
+ subject.to_h.should == {
153
+ :blah => [ { :foo => '1' }, { :foo => "Dr Scott" }, 'baz' ]
154
+ }
155
+ end
156
+
157
+ it "does not mutate the input hash passed to the constructor" do
158
+ h[:blah][1][:foo].should == '2'
159
+ end
160
+
161
+ it "the deep copy recurses over Arrays as well" do
162
+ h[:blah][1][:foo].should == '2'
163
+ end
134
164
  end
135
165
 
136
166
  context "when array is nested deeper" do
data/spec/spec_helper.rb CHANGED
@@ -12,7 +12,7 @@ end
12
12
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
13
 
14
14
  RSpec.configure do |config|
15
- config.treat_symbols_as_metadata_keys_with_true_values = true
16
15
  config.run_all_when_everything_filtered = true
17
16
  config.filter_run :focus
17
+ config.expect_with(:rspec) { |c| c.syntax = :should }
18
18
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recursive-open-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - William (B.J.) Snow Orvis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-14 00:00:00.000000000 Z
11
+ date: 2015-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '3.2'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '3.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -81,19 +81,24 @@ email: aetherknight@gmail.com
81
81
  executables: []
82
82
  extensions: []
83
83
  extra_rdoc_files:
84
+ - CHANGELOG.md
84
85
  - LICENSE.txt
85
- - README.rdoc
86
+ - README.md
86
87
  files:
87
88
  - .document
88
89
  - .gitignore
89
90
  - .rspec
90
91
  - .travis.yml
92
+ - CHANGELOG.md
91
93
  - Gemfile
92
94
  - LICENSE.txt
93
- - README.rdoc
95
+ - README.md
94
96
  - Rakefile
95
97
  - lib/recursive-open-struct.rb
96
98
  - lib/recursive_open_struct.rb
99
+ - lib/recursive_open_struct/debug_inspect.rb
100
+ - lib/recursive_open_struct/deep_dup.rb
101
+ - lib/recursive_open_struct/version.rb
97
102
  - recursive-open-struct.gemspec
98
103
  - spec/recursive_open_struct_spec.rb
99
104
  - spec/spec_helper.rb