Skip to content

Commit f03722c

Browse files
transclaude
andcommitted
Rename Hash#to_proc to setter, consolidate arrange/to_ranges
- Rename Hash#to_proc to Hash#setter to avoid clash with Ruby's Hash#to_proc (key lookup vs attribute assignment) - Fix bug in setter where respond_to? checked self instead of receiver - Consolidate Array#arrange and Array#to_ranges; to_ranges is now primary with arrange/rangify as aliases - to_ranges now handles mixed ranges and values, and compacts nils Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9242533 commit f03722c

File tree

7 files changed

+63
-79
lines changed

7 files changed

+63
-79
lines changed

HISTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ Changes:
1717
* Add `Binding#caller_locations`.
1818
* Add `Kernel#tee` — block-less method chaining via Tee/Functor, replaces `tap` override.
1919
* Add `Tee` as alias for `Functor` (gradual rename).
20+
* Rename `Hash#to_proc` to `Hash#setter` (avoids clash with Ruby 2.3's `Hash#to_proc`
21+
which does key lookup; Facets' version does attribute assignment).
22+
* Consolidate `Array#arrange` and `Array#to_ranges`; `to_ranges` is now primary,
23+
`arrange` and `rangify` are aliases. Now handles mixed ranges and values.
2024

2125
* Improved Features
2226

lib/core/facets/array/arrange.rb

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1 @@
1-
class Array
2-
3-
# The `arrange` method produces appropriate ranges from the objects in the array.
4-
#
5-
# Examples
6-
#
7-
# [1,2,3,6,7,8].arrange #=> [1..3, 6..8]
8-
#
9-
# [10..15, 16..20, 21, 22].arrange #=> [10..22]
10-
#
11-
# Assumes inclusive ranges (ie. 1..4) and range.first <= range.last.
12-
#
13-
# Works with integers, dates and strings. However, all the objects in the array must
14-
# be of the same class.
15-
#
16-
# CREDIT: monocle
17-
18-
def arrange
19-
array = uniq.sort_by { |e| Range === e ? e.first : e }
20-
array.inject([]) do |c, value|
21-
unless c.empty?
22-
last = c.last
23-
last_value = (Range === last ? last.last : last)
24-
current_value = (Range === value ? value.first : value)
25-
if (last_value.succ <=> current_value) == -1
26-
c << value
27-
else
28-
first = (Range === last ? last.first : last)
29-
second = [Range === last ? last.last : last, Range === value ? value.last : value].max
30-
c[-1] = [first..second]
31-
c.flatten!
32-
end
33-
else
34-
c << value
35-
end
36-
end
37-
end
38-
39-
alias rangify arrange
40-
41-
end
42-
1+
require 'facets/array/to_ranges'

lib/core/facets/array/to_ranges.rb

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,45 @@
11
class Array
22

3-
# Convert an array of values (which must respond to #succ) to an
4-
# array of ranges.
3+
# Produces an array of ranges from the values in the array.
4+
# Contiguous values and overlapping ranges are merged.
55
#
6-
# [3,4,5,1,6,9,8].to_ranges => [1..1,3..6,8..9]
6+
# Examples
77
#
8-
# CREDIT: Adapted and debugged by Ryan Duryea
9-
# from https://dzone.com/articles/convert-ruby-array-ranges
8+
# [1,2,3,6,7,8].to_ranges #=> [1..3, 6..8]
9+
#
10+
# [3,4,5,1,6,9,8].to_ranges #=> [1..6, 8..9]
11+
#
12+
# [10..15, 16..20, 21, 22].to_ranges #=> [10..22]
13+
#
14+
# Assumes inclusive ranges (i.e. 1..4) and range.first <= range.last.
15+
#
16+
# Works with integers, dates and strings. However, all the objects
17+
# in the array must be of the same class.
18+
#
19+
# CREDIT: monocle, Ryan Duryea
1020

1121
def to_ranges
12-
array = self.compact.uniq.sort
13-
ranges = []
14-
if !array.empty?
15-
# Initialize the left and right endpoints of the range
16-
left, right = array.first, nil
17-
array.each do |obj|
18-
# If the right endpoint is set and obj is not equal to right's successor
19-
# then we need to create a range.
20-
if right && obj != right.succ
21-
ranges << Range.new(left,right)
22-
left = obj
22+
array = compact.uniq.sort_by { |e| Range === e ? e.first : e }
23+
array.inject([]) do |c, value|
24+
unless c.empty?
25+
last = c.last
26+
last_value = (Range === last ? last.last : last)
27+
current_value = (Range === value ? value.first : value)
28+
if (last_value.succ <=> current_value) == -1
29+
c << value
30+
else
31+
first = (Range === last ? last.first : last)
32+
second = [Range === last ? last.last : last, Range === value ? value.last : value].max
33+
c[-1] = [first..second]
34+
c.flatten!
2335
end
24-
right = obj
36+
else
37+
c << value
2538
end
26-
ranges << Range.new(left,right)
2739
end
28-
ranges
2940
end
41+
42+
alias arrange to_ranges
43+
alias rangify to_ranges
44+
3045
end
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
class Hash
22

3-
# Constructs a Proc object from a hash such that the parameter
4-
# of the Proc is assigned the hash keys as attributes.
3+
# Constructs a Proc that assigns the hash's key-value pairs as
4+
# attributes on a given object.
55
#
66
# c = Class.new do
77
# attr_accessor :a
88
# end
99
#
1010
# h = {:a => 1}
1111
# o = c.new
12-
# h.to_proc.call(o)
12+
# h.setter.call(o)
1313
# o.a #=> 1
1414
#
15-
# If +response+ is set to +true+, then assignment will only occur
16-
# if receiver responds_to? the writer method.
15+
# If +safe+ is set to +true+, then assignment will only occur
16+
# if the receiver responds_to? the writer method.
1717
#
1818
# CREDIT: Trans
1919

20-
def to_proc(response=false)
21-
if response
20+
def setter(safe=false)
21+
if safe
2222
lambda do |o|
2323
self.each do |k,v|
2424
ke = "#{k}="
25-
o.__send__(ke, v) if respond_to?(ke)
25+
o.__send__(ke, v) if o.respond_to?(ke)
2626
end
2727
end
2828
else
@@ -36,4 +36,3 @@ def to_proc(response=false)
3636
end
3737

3838
end
39-

test/core/array/test_arrange.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require 'facets/array/arrange'
1+
covers 'facets/array/arrange'
22

33
test_case Array do
44

test/core/array/test_to_ranges.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44

55
method :to_ranges do
66

7-
test do
8-
[3,4,5,1,6,9,8].to_ranges.assert == [1..1,3..6,8..9]
7+
test "flat values" do
8+
[3,4,5,1,6,9,8].to_ranges.assert == [1..1, 3..6, 8..9]
9+
end
10+
11+
test "consecutive" do
12+
[1,2,3,4,5].to_ranges.assert == [1..5]
13+
end
14+
15+
test "mixed ranges and values" do
16+
[10..15, 16..20, 21, 22].to_ranges.assert == [10..22]
917
end
1018

1119
end
12-
end
1320

21+
end
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
covers 'facets/hash/to_proc'
1+
covers 'facets/hash/setter'
22

33
test_case Hash do
44

5-
method :to_proc do
5+
method :setter do
66

77
test do
88
c = Class.new do
@@ -12,12 +12,11 @@
1212
h = {:a => 1}
1313
o = c.new
1414

15-
h.to_proc.call(o)
15+
h.setter.call(o)
1616

1717
o.a.assert == 1
1818
end
1919

2020
end
21-
22-
end
2321

22+
end

0 commit comments

Comments
 (0)