leto 2.1.0 → 2.3.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
  SHA256:
3
- metadata.gz: 0ed0d190b234b4976a219ba5b50a3e9cb4f394be7b1a27aa2f1d7bdfe93887b0
4
- data.tar.gz: eafafd24510514cf7ba4e59cdf1a967c7464d175b8ceed1f3cafa2e78fffcc1e
3
+ metadata.gz: 7a4c2fdd688142eb3f9db1a5d0d10cad624225413f6892a65eeef1a0aec3df00
4
+ data.tar.gz: 7ee194719f3912044b675f874f60d42bf4c6e7c60be5e83318cf3f2aaeca6225
5
5
  SHA512:
6
- metadata.gz: 79b0be7884c86be4a14977e5aaae6147da85b857f632013bd82f9fe95addf28a4014ea7adeba1c75dfc63b9988ab7426c622cb79bc2c2886dca04be246ee2db5
7
- data.tar.gz: 961a19be85b69aac0763828648c09100af4a9f84eff07a45e2b9c0c247c84e5b7ae9647a9bd4a7de791eb7cd0f0342b91f82d2cd50a85286425e4a8f915bd2fe
6
+ metadata.gz: 4edd7d0d8677b548585517b499a57ae630b5326053192f7b10b6de101c8899aab46d956de787df0a480cf66f73fd34c5da3518db2f1c61abc82b7758cdef7cbe
7
+ data.tar.gz: c3678597e116adf629f4229193c921a003e1f270030c2d076464af427806b76ce1560f6726a231eca7507af334be457e91f2c286d1bc2d0349d66696f73c25d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.3.0] - 2026-09-29
4
+
5
+ ### Added
6
+
7
+ - support for Ruby 4's native `Set` class
8
+
9
+ ## [2.2.0] - 2026-09-03
10
+
11
+ ### Added
12
+
13
+ - support for varargs in `::shared_mutable_state?`
14
+
3
15
  ## [2.1.0] - 2023-05-20
4
16
 
5
17
  ### Added
data/README.md CHANGED
@@ -12,9 +12,10 @@ Takes an object and recursively yields:
12
12
  - instance variables, class variables, constants
13
13
  - Hash keys and values
14
14
  - Enumerable members
15
+ - Range begins and ends
15
16
  - Struct members
16
17
  - [Data](https://docs.ruby-lang.org/en/3.2/Data.html) members
17
- - Range begins and ends
18
+ - Set members (for [native Sets on Ruby >= 4.0](https://bugs.ruby-lang.org/issues/21216))
18
19
 
19
20
  This makes stuff like deep-freezing fairly easy to implement:
20
21
 
@@ -37,22 +38,29 @@ Basic example:
37
38
  ```ruby
38
39
  object = [{ a: ['b', 'c'..'d'] }]
39
40
 
40
- Leto.call(object) { |el| p el }
41
+ Leto.call(object) { |el| puts "#{el.class}: #{el}" }
41
42
  # prints:
42
43
  #
43
- # [{:a=>["b", ["c".."d"]]}]
44
- # {:a=>["b", ["c".."d"]]}
45
- # :a
46
- # ["b", ["c".."d"]]
47
- # "b"
48
- # "c".."d"
49
- # "c"
50
- # "d"
44
+ # Array: [{a: ["b", "c".."d"]}]
45
+ # Hash: {a: ["b", "c".."d"]}
46
+ # Symbol: a
47
+ # Array: ["b", "c".."d"]
48
+ # String: b
49
+ # Range: c..d
50
+ # String: c
51
+ # String: d
52
+ ```
53
+
54
+ Calls without a block return an `Enumerator`:
51
55
 
56
+ ```ruby
52
57
  Leto.call(object).to_a
53
58
  # => [[{:a=>["b", ["c".."d"]]}], {:a=>["b", ["c".."d"]]}, :a, ...]
59
+ ```
54
60
 
55
- # Leto::trace behaves like ::call, but also yields each (sub-)object's path:
61
+ `Leto.trace` behaves like `.call`, but also yields each (sub-)object's `Path`:
62
+
63
+ ```ruby
56
64
  Leto.trace(object) { |el, path| puts "#{el.inspect.ljust(23)} @#{path}" }
57
65
  # prints:
58
66
  #
@@ -64,11 +72,19 @@ Leto.trace(object) { |el, path| puts "#{el.inspect.ljust(23)} @#{path}" }
64
72
  # "c".."d" @#<Leto::Path [{:a=>["b", "c".."d"]}][0][:a][1]>
65
73
  # "c" @#<Leto::Path [{:a=>["b", "c".."d"]}][0][:a][1].begin>
66
74
  # "d" @#<Leto::Path [{:a=>["b", "c".."d"]}][0][:a][1].end>
75
+ ```
76
+
77
+ Paths can be looked up with `Leto::Path#resolve` or `Leto.dig`:
67
78
 
68
- # paths can be looked up with Leto::Path#resolve or Leto::dig
79
+ ```ruby
69
80
  path = Leto.trace(object).map { |_el, path| path }.last # => #<Leto::Path...>
70
81
  path.resolve # => "d"
71
82
  Leto.dig(object, path) # => "d"
83
+ ```
84
+
85
+ Arrays of Arrays, where the latter ones contain method names and arguments, are valid paths:
86
+
87
+ ```ruby
72
88
  Leto.dig(object, [[:[], 0], [:[], :a], [:[], 1], [:end]]) # => "d"
73
89
  ```
74
90
 
@@ -82,7 +98,7 @@ Leto.dig(object, [[:[], 0], [:[], :a], [:[], 1], [:end]]) # => "d"
82
98
  - stricter version of `#eql?` that takes all ivars into consideration
83
99
  - [`Leto.deep_dup(obj)`](https://github.com/search?q=deep_dup+repo%3Ajaynetics%2Fleto+path%3Alib%2Fleto%2Futils.rb&type=code)
84
100
  - more thorough than [`active_support`](https://www.rubydoc.info/search/gems/activesupport?q=deep_dup) or [`deep_dup`](https://github.com/ollie/deep_dup) gems, e.g. dups ivars
85
- - [`Leto.shared_mutable_state?(obj1, obj2)`](https://github.com/search?q=shared_mutable_state+repo%3Ajaynetics%2Fleto+path%3Alib%2Fleto%2Futils.rb&type=code)
101
+ - [`Leto.shared_mutable_state?(obj1, obj2, ...)`](https://github.com/search?q=shared_mutable_state+repo%3Ajaynetics%2Fleto+path%3Alib%2Fleto%2Futils.rb&type=code)
86
102
  - useful for debugging or verifying that a `#dup` implementation is sane
87
103
  - [`Leto.shared_mutables(obj1, obj2)`](https://github.com/search?q=shared_mutables+repo%3Ajaynetics%2Fleto+path%3Alib%2Fleto%2Futils.rb&type=code)
88
104
  - useful for debugging or verifying that a `#dup` implementation is sane
data/lib/leto/call.rb CHANGED
@@ -60,12 +60,19 @@ module Leto
60
60
  obj.members.each do |member|
61
61
  traverse(obj[member], path&.+([[:[], member]]), depth, max_depth, seen, block)
62
62
  end
63
- when Enumerable
64
- obj.each_with_index do |el, idx|
65
- traverse(el, path&.+([[:[], idx]]), depth, max_depth, seen, block)
66
- end
67
63
  #{
68
- defined?(Data) && Data.respond_to?(:define) && <<-DATA_FEATURE_RUBY
64
+ set_feature? && <<-SET_FEATURE_RUBY
65
+ when Set
66
+ obj.each_with_index do |el, idx|
67
+ traverse(
68
+ el, path&.+([[:to_a], [:[], idx]]),
69
+ depth, max_depth, seen, block
70
+ )
71
+ end
72
+ SET_FEATURE_RUBY
73
+ }
74
+ #{
75
+ data_feature? && <<-DATA_FEATURE_RUBY
69
76
  when Data
70
77
  obj.members.each do |member|
71
78
  traverse(
@@ -76,6 +83,10 @@ module Leto
76
83
  end
77
84
  DATA_FEATURE_RUBY
78
85
  }
86
+ when Enumerable
87
+ obj.each_with_index do |el, idx|
88
+ traverse(el, path&.+([[:[], idx]]), depth, max_depth, seen, block)
89
+ end
79
90
  end
80
91
  end
81
92
 
@@ -0,0 +1,12 @@
1
+ # feature detection code for ruby
2
+ module Leto
3
+ def self.data_feature?
4
+ defined?(Data) && Data.respond_to?(:define)
5
+ end
6
+
7
+ # Pre-Ruby-4.0, Hash-backed Sets are traversed through their
8
+ # @hash instance variable and don't need custom handling.
9
+ def self.set_feature?
10
+ defined?(Set) && Set.respond_to?(:[]) && !Set[].instance_variable_defined?(:@hash)
11
+ end
12
+ end
data/lib/leto/utils.rb CHANGED
@@ -10,8 +10,7 @@ module Leto
10
10
  def self.deep_print(obj, print_method: :inspect, indent: 4, show_path: true)
11
11
  trace(obj) do |el, path|
12
12
  puts "#{' ' * path.count * indent}#{el.send(print_method)}" \
13
- "#{" @ #{path.inspect}" if show_path}" \
14
- [0..78]
13
+ "#{" @ #{path.inspect}" if show_path}"[0..78]
15
14
  end
16
15
  nil
17
16
  end
@@ -35,16 +34,24 @@ module Leto
35
34
  copy[*args] = deep_dup(el, include_modules: include_modules)
36
35
  when :send # Data
37
36
  copy = copy.with(args[0] => deep_dup(el, include_modules: include_modules))
37
+ when :to_a # Set
38
+ copy = (copy - [el]) + [deep_dup(el, include_modules: include_modules)]
38
39
  when :begin
39
- return Range.new(deep_dup(obj.begin), deep_dup(obj.end), obj.exclude_end?)
40
+ return Range.new(
41
+ deep_dup(obj.begin, include_modules: include_modules),
42
+ deep_dup(obj.end, include_modules: include_modules),
43
+ obj.exclude_end?,
44
+ )
40
45
  end
41
46
  end
42
47
 
43
48
  copy
44
49
  end
45
50
 
46
- def self.shared_mutable_state?(obj1, obj2)
47
- each_shared_object(obj1, obj2, filter: method(:mutable?)).any?
51
+ def self.shared_mutable_state?(*objects)
52
+ objects.combination(2).any? do |obj1, obj2|
53
+ each_shared_object(obj1, obj2, filter: method(:mutable?)).any?
54
+ end
48
55
  end
49
56
 
50
57
  # returns [[shared_object, path1, path2], ...], e.g.:
@@ -87,7 +94,7 @@ module Leto
87
94
  if defined?(Integer)
88
95
  Integer
89
96
  else
90
- Fixnum # rubocop:disable Lint/UnifiedInteger for Ruby < 2.4
97
+ Fixnum # rubocop:disable Lint/UnifiedInteger -- for Ruby < 2.4
91
98
  end,
92
99
  NilClass,
93
100
  Symbol,
data/lib/leto/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Leto
4
- VERSION = "2.1.0"
4
+ VERSION = "2.3.0"
5
5
  end
data/lib/leto.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'leto/features'
3
4
  require_relative 'leto/call'
4
5
  require_relative 'leto/dig'
5
6
  require_relative 'leto/path'
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leto
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janosch Müller
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-05-20 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
13
  - janosch84@gmail.com
16
14
  executables: []
@@ -29,6 +27,7 @@ files:
29
27
  - lib/leto.rb
30
28
  - lib/leto/call.rb
31
29
  - lib/leto/dig.rb
30
+ - lib/leto/features.rb
32
31
  - lib/leto/path.rb
33
32
  - lib/leto/utils.rb
34
33
  - lib/leto/version.rb
@@ -38,7 +37,6 @@ licenses:
38
37
  metadata:
39
38
  changelog_uri: https://github.com/jaynetics/leto/blob/main/CHANGELOG.md
40
39
  homepage_uri: https://github.com/jaynetics/leto
41
- post_install_message:
42
40
  rdoc_options: []
43
41
  require_paths:
44
42
  - lib
@@ -53,8 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
51
  - !ruby/object:Gem::Version
54
52
  version: '0'
55
53
  requirements: []
56
- rubygems_version: 3.4.13
57
- signing_key:
54
+ rubygems_version: 4.0.20
58
55
  specification_version: 4
59
56
  summary: Generic object traverser
60
57
  test_files: []