Skip to content

Commit 56e214d

Browse files
committed
Resolve RuboCop updates
Apply remaining RuboCop fixes and configuration updates after the\ninitial autocorrections. Includes cache helpers, helper refactors,\nand spec/test adjustments to keep lint clean. Configure rubocop-rake and rubocop-rspec via the plugins list and drop Rake task requires that trigger extension warnings. This keeps the lint output clean while still running the extra cops.
1 parent fba400a commit 56e214d

13 files changed

Lines changed: 164 additions & 98 deletions

.rubocop.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
# Inherit from the working file.
44
inherit_from: .rubocop_todo.yml
55

6+
plugins:
7+
- rubocop-rake
8+
- rubocop-rspec
9+
610
AllCops:
711
NewCops: enable
812
TargetRubyVersion: 3.1
@@ -13,3 +17,6 @@ AllCops:
1317
Lint/UselessAssignment:
1418
Exclude:
1519
- 'examples/example_basic.rb'
20+
21+
Gemspec/DevelopmentDependencies:
22+
Enabled: false

Rakefile

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ GEM_SPEC = Bundler.load_gemspec(File.join(__dir__, 'rubytree.gemspec'))
4141

4242
PKG_NAME = GEM_SPEC.name
4343
PKG_VER = GEM_SPEC.version
44-
GEM_NAME = "#{PKG_NAME}-#{PKG_VER}.gem"
44+
GEM_NAME = "#{PKG_NAME}-#{PKG_VER}.gem".freeze
4545

4646
desc 'Default Task (Run the tests)'
47-
task :default => 'test:all'
47+
task default: 'test:all'
4848

4949
desc 'Display the current gem version'
5050
task :version do
@@ -154,8 +154,6 @@ begin
154154

155155
RuboCop::RakeTask.new(:rubocop) do |t|
156156
t.options = ['--display-cop-names']
157-
t.requires << 'rubocop-rake'
158-
t.requires << 'rubocop-rspec'
159157
end
160158
rescue LoadError
161159
# RuboCop not available.

lib/tree.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
# frozen_string_literal: true
4040

4141
require 'tree/tree_deps'
42+
require 'tree/utils/cache_methods'
4243
require 'tree/utils/structure_methods'
4344
require 'tree/utils/navigation_methods'
4445
require 'tree/utils/traversal_methods'
@@ -93,6 +94,7 @@ class TreeNode
9394
include Tree::Utils::JSONConverter
9495
include Tree::Utils::TreeMergeHandler
9596
include Tree::Utils::HashConverter
97+
include Tree::Utils::TreeCacheHandler
9698
include Tree::Utils::TreeStructureHandler
9799
include Tree::Utils::TreeNavigationHandler
98100
include Tree::Utils::TreeTraversalHandler
@@ -371,6 +373,5 @@ def <=>(other)
371373

372374
name <=> other.name
373375
end
374-
375376
end
376377
end

lib/tree/binarytree.rb

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -202,35 +202,50 @@ def inordered_each
202202
#
203203
# @raise [ArgumentError] If the index is out of limits.
204204
def set_child_at(child, at_index)
205-
raise ArgumentError,
206-
'A binary tree cannot have more than two children.' unless (0..1).include? at_index
205+
validate_binary_index!(at_index)
207206

208207
old_child = @children[at_index]
209-
if old_child && old_child != child
210-
still_present = @children.each_with_index.any? do |existing, idx|
211-
idx != at_index && existing.equal?(old_child)
212-
end
213-
214-
unless still_present
215-
@children_hash.delete(old_child.name)
216-
old_child.set_as_root!
217-
end
218-
end
208+
detach_old_child(old_child, at_index) if old_child && old_child != child
219209

220210
if child
221-
child.parent&.remove!(child) unless child.parent == self
222-
@children[at_index] = child
223-
@children_hash[child.name] = child # Assign the name mapping
224-
child.parent = self
211+
attach_child(child, at_index)
225212
else
226213
@children[at_index] = nil
227214
end
215+
228216
send(:invalidate_size_cache_upwards!)
229217
child
230218
end
231219

232220
protected :set_child_at
233221

222+
private
223+
224+
def validate_binary_index!(at_index)
225+
return if (0..1).include?(at_index)
226+
227+
raise ArgumentError, 'A binary tree cannot have more than two children.'
228+
end
229+
230+
def detach_old_child(old_child, at_index)
231+
still_present = @children.each_with_index.any? do |existing, idx|
232+
idx != at_index && existing.equal?(old_child)
233+
end
234+
return if still_present
235+
236+
@children_hash.delete(old_child.name)
237+
old_child.set_as_root!
238+
end
239+
240+
def attach_child(child, at_index)
241+
child.parent&.remove!(child) unless child.parent == self
242+
@children[at_index] = child
243+
@children_hash[child.name] = child
244+
child.parent = self
245+
end
246+
247+
public
248+
234249
# Sets the left child of the receiver node. If a previous child existed, it
235250
# is replaced.
236251
#

lib/tree/utils/cache_methods.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# cache_methods.rb - This file is part of the RubyTree package.
2+
#
3+
# Copyright (c) 2006-2026 Anupam Sengupta. All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without
6+
# modification, are permitted provided that the following conditions are met:
7+
#
8+
# - Redistributions of source code must retain the above copyright notice, this
9+
# list of conditions and the following disclaimer.
10+
#
11+
# - Redistributions in binary form must reproduce the above copyright notice,
12+
# this list of conditions and the following disclaimer in the documentation
13+
# and/or other materials provided with the distribution.
14+
#
15+
# - Neither the name of the organization nor the names of its contributors may
16+
# be used to endorse or promote products derived from this software without
17+
# specific prior written permission.
18+
#
19+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
#
30+
# frozen_string_literal: true
31+
32+
module Tree
33+
module Utils
34+
# Provides cache helpers for TreeNode.
35+
module TreeCacheHandler
36+
private
37+
38+
def clear_root_cache!
39+
@root_cache = nil
40+
return unless @children
41+
42+
children_array.each do |child|
43+
child&.__send__(:clear_root_cache!)
44+
end
45+
end
46+
47+
def invalidate_size_cache_upwards!
48+
node = self
49+
while node
50+
node.instance_variable_set(:@size, nil)
51+
node = node.parent
52+
end
53+
end
54+
end
55+
end
56+
end

lib/tree/utils/navigation_methods.rb

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,21 @@ def last_sibling?
9191
# An array of siblings for this node. This node is excluded.
9292
#
9393
# @note Nil child slots (e.g., in binary trees) are skipped.
94-
def siblings
95-
if block_given?
96-
return self if root?
94+
def siblings(&block)
95+
return [] if root?
96+
97+
siblings = []
98+
parent.send(:children_array).each do |sibling|
99+
next unless sibling
100+
next if sibling == self
97101

98-
parent.send(:children_array).each do |sibling|
99-
next unless sibling
100-
next if sibling == self
102+
siblings << sibling
103+
end
101104

102-
yield sibling
103-
end
105+
if block
106+
siblings.each(&block)
104107
self
105108
else
106-
return [] if root?
107-
108-
siblings = []
109-
parent.send(:children_array).each do |my_sibling|
110-
next unless my_sibling
111-
112-
siblings << my_sibling if my_sibling != self
113-
end
114109
siblings
115110
end
116111
end

lib/tree/utils/structure_methods.rb

Lines changed: 40 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
module Tree
3333
module Utils
3434
# Provides structure-modification helpers for TreeNode.
35+
# rubocop:disable Metrics/ModuleLength
3536
module TreeStructureHandler
3637
# Convenience synonym for {Tree::TreeNode#add} method.
3738
def <<(child)
@@ -40,39 +41,12 @@ def <<(child)
4041

4142
# Adds the specified child node to this node.
4243
def add(child, at_index = -1)
43-
# Only handles the immediate child scenario
44-
raise ArgumentError, 'Attempting to add a nil node' unless child
45-
46-
raise ArgumentError, 'Attempting add node to itself' if equal?(child)
47-
48-
raise ArgumentError, 'Attempting add root as a child' if child.equal?(root)
49-
50-
if (ancestors = parentage) && ancestors.include?(child)
51-
raise ArgumentError, 'Attempting add ancestor as a child'
52-
end
53-
54-
# Lazy man's unique test, won't test if children of child are unique in
55-
# this tree too.
56-
raise "Child #{child.name} already added!" if @children_hash.include?(child.name)
44+
validate_add_child!(child)
45+
ensure_unique_child_name!(child)
5746

5847
child.parent&.remove! child # Detach from the old parent
59-
60-
if insertion_range.include?(at_index)
61-
@children.insert(at_index, child)
62-
else
63-
message = [
64-
'Attempting to insert a child at a non-existent location',
65-
"(#{at_index})",
66-
'when only positions from',
67-
"#{insertion_range.min} to #{insertion_range.max} exist."
68-
].join(' ')
69-
raise message
70-
end
71-
72-
@children_hash[child.name] = child
73-
child.parent = self
74-
invalidate_size_cache_upwards!
75-
child
48+
insert_child_at!(child, at_index)
49+
attach_child!(child)
7650
end
7751

7852
# Return a range of valid insertion positions. Used in the #add method.
@@ -97,11 +71,9 @@ def rename(new_name)
9771

9872
# Renames the specified child node
9973
def rename_child(old_name, new_name)
100-
raise ArgumentError,
101-
"Invalid child name specified: #{old_name}" unless @children_hash.key?(old_name)
74+
raise ArgumentError, "Invalid child name specified: #{old_name}" unless @children_hash.key?(old_name)
10275

103-
raise ArgumentError,
104-
"Child name already exists: #{new_name}" if @children_hash.key?(new_name)
76+
raise ArgumentError, "Child name already exists: #{new_name}" if @children_hash.key?(new_name)
10577

10678
@children_hash[new_name] = @children_hash.delete(old_name)
10779
@children_hash[new_name].name = new_name
@@ -170,25 +142,45 @@ def freeze_tree!
170142
each(&:freeze)
171143
end
172144

173-
def clear_root_cache!
174-
@root_cache = nil
175-
return unless @children
145+
def validate_add_child!(child)
146+
raise ArgumentError, 'Attempting to add a nil node' unless child
147+
raise ArgumentError, 'Attempting add node to itself' if equal?(child)
148+
raise ArgumentError, 'Attempting add root as a child' if child.equal?(root)
176149

177-
children_array.each do |child|
178-
child&.__send__(:clear_root_cache!)
179-
end
150+
return unless (ancestors = parentage) && ancestors.include?(child)
151+
152+
raise ArgumentError, 'Attempting add ancestor as a child'
180153
end
181154

182-
def invalidate_size_cache_upwards!
183-
node = self
184-
while node
185-
node.instance_variable_set(:@size, nil)
186-
node = node.parent
187-
end
155+
def ensure_unique_child_name!(child)
156+
return unless @children_hash.include?(child.name)
157+
158+
raise "Child #{child.name} already added!"
159+
end
160+
161+
def insert_child_at!(child, at_index)
162+
return @children.insert(at_index, child) if insertion_range.include?(at_index)
163+
164+
message = [
165+
'Attempting to insert a child at a non-existent location',
166+
"(#{at_index})",
167+
'when only positions from',
168+
"#{insertion_range.min} to #{insertion_range.max} exist."
169+
].join(' ')
170+
raise message
171+
end
172+
173+
def attach_child!(child)
174+
@children_hash[child.name] = child
175+
child.parent = self
176+
invalidate_size_cache_upwards!
177+
child
188178
end
189179

190-
private :insertion_range, :clear_root_cache!, :invalidate_size_cache_upwards!
180+
private :insertion_range, :validate_add_child!, :ensure_unique_child_name!,
181+
:insert_child_at!, :attach_child!
191182
protected :parent=, :set_as_root!
192183
end
184+
# rubocop:enable Metrics/ModuleLength
193185
end
194186
end

rubytree.gemspec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,17 @@ Gem::Specification.new do |s|
6464
'--main', 'README.md',
6565
'--quiet']
6666

67-
s.add_runtime_dependency 'json', '~> 2.18'
67+
s.add_dependency 'json', '~> 2.18'
6868

6969
# NOTE: Rake is added as a development and test dependency in the Gemfile.
7070
s.add_development_dependency 'bundler', '~> 2.6'
7171
s.add_development_dependency 'rake', '~> 13.3'
7272
s.add_development_dependency 'rdoc', '~> 7.1'
73+
s.add_development_dependency 'rspec', '~> 3.13'
7374
s.add_development_dependency 'rtagstask', '~> 0.0'
7475
s.add_development_dependency 'rubocop', '~> 1.84'
75-
s.add_development_dependency 'rubocop-rspec', '~> 3.0'
7676
s.add_development_dependency 'rubocop-rake', '~> 0.7'
77-
s.add_development_dependency 'rspec', '~> 3.13'
77+
s.add_development_dependency 'rubocop-rspec', '~> 3.0'
7878
s.add_development_dependency 'simplecov', '~> 0.22'
7979
s.add_development_dependency 'simplecov-lcov', '~> 0.9'
8080
s.add_development_dependency 'test-unit', '~> 3.7'

spec/spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@
3939
enable_coverage :branch
4040
end
4141
rescue LoadError => e
42-
puts "Could not load simplecov; continuing without code coverage #{e.cause}"
42+
warn "Could not load simplecov; continuing without code coverage #{e.cause}"
4343
end
4444
end

0 commit comments

Comments
 (0)