jsi 0.6.0 → 0.8.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -1
  3. data/CHANGELOG.md +33 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +29 -23
  6. data/jsi.gemspec +29 -0
  7. data/lib/jsi/base/mutability.rb +44 -0
  8. data/lib/jsi/base/node.rb +348 -0
  9. data/lib/jsi/base.rb +497 -339
  10. data/lib/jsi/jsi_coder.rb +19 -17
  11. data/lib/jsi/metaschema_node/bootstrap_schema.rb +61 -26
  12. data/lib/jsi/metaschema_node.rb +161 -133
  13. data/lib/jsi/ptr.rb +80 -47
  14. data/lib/jsi/schema/application/child_application/contains.rb +11 -2
  15. data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
  16. data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
  17. data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
  18. data/lib/jsi/schema/application/child_application/items.rb +3 -3
  19. data/lib/jsi/schema/application/child_application/properties.rb +3 -3
  20. data/lib/jsi/schema/application/child_application.rb +0 -27
  21. data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
  22. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
  23. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
  24. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
  25. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +3 -3
  26. data/lib/jsi/schema/application/inplace_application/ref.rb +2 -2
  27. data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
  28. data/lib/jsi/schema/application/inplace_application.rb +0 -32
  29. data/lib/jsi/schema/draft04.rb +0 -1
  30. data/lib/jsi/schema/draft06.rb +0 -1
  31. data/lib/jsi/schema/draft07.rb +0 -1
  32. data/lib/jsi/schema/ref.rb +46 -19
  33. data/lib/jsi/schema/schema_ancestor_node.rb +69 -66
  34. data/lib/jsi/schema/validation/array.rb +3 -3
  35. data/lib/jsi/schema/validation/const.rb +1 -1
  36. data/lib/jsi/schema/validation/contains.rb +2 -2
  37. data/lib/jsi/schema/validation/dependencies.rb +1 -1
  38. data/lib/jsi/schema/validation/draft04/minmax.rb +8 -6
  39. data/lib/jsi/schema/validation/draft04.rb +0 -2
  40. data/lib/jsi/schema/validation/draft06.rb +0 -2
  41. data/lib/jsi/schema/validation/draft07.rb +0 -2
  42. data/lib/jsi/schema/validation/enum.rb +1 -1
  43. data/lib/jsi/schema/validation/ifthenelse.rb +5 -5
  44. data/lib/jsi/schema/validation/items.rb +7 -7
  45. data/lib/jsi/schema/validation/not.rb +1 -1
  46. data/lib/jsi/schema/validation/numeric.rb +5 -5
  47. data/lib/jsi/schema/validation/object.rb +2 -2
  48. data/lib/jsi/schema/validation/pattern.rb +2 -2
  49. data/lib/jsi/schema/validation/properties.rb +7 -7
  50. data/lib/jsi/schema/validation/property_names.rb +1 -1
  51. data/lib/jsi/schema/validation/ref.rb +2 -2
  52. data/lib/jsi/schema/validation/required.rb +1 -1
  53. data/lib/jsi/schema/validation/someof.rb +3 -3
  54. data/lib/jsi/schema/validation/string.rb +2 -2
  55. data/lib/jsi/schema/validation/type.rb +1 -1
  56. data/lib/jsi/schema/validation.rb +1 -3
  57. data/lib/jsi/schema.rb +443 -226
  58. data/lib/jsi/schema_classes.rb +241 -147
  59. data/lib/jsi/schema_registry.rb +78 -19
  60. data/lib/jsi/schema_set.rb +114 -28
  61. data/lib/jsi/simple_wrap.rb +18 -4
  62. data/lib/jsi/util/private/attr_struct.rb +141 -0
  63. data/lib/jsi/util/private/memo_map.rb +75 -0
  64. data/lib/jsi/util/private.rb +185 -0
  65. data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +79 -105
  66. data/lib/jsi/util.rb +157 -153
  67. data/lib/jsi/validation/error.rb +4 -0
  68. data/lib/jsi/validation/result.rb +18 -32
  69. data/lib/jsi/version.rb +1 -1
  70. data/lib/jsi.rb +65 -39
  71. data/lib/schemas/json-schema.org/draft-04/schema.rb +160 -3
  72. data/lib/schemas/json-schema.org/draft-06/schema.rb +162 -3
  73. data/lib/schemas/json-schema.org/draft-07/schema.rb +189 -3
  74. metadata +27 -11
  75. data/lib/jsi/metaschema.rb +0 -7
  76. data/lib/jsi/pathed_node.rb +0 -116
  77. data/lib/jsi/schema/validation/core.rb +0 -39
  78. data/lib/jsi/util/attr_struct.rb +0 -106
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # JSI::Util::Private classes, modules, constants, and methods are internal, and will be added and removed without warning.
5
+ #
6
+ # @api private
7
+ module Util::Private
8
+ autoload :AttrStruct, 'jsi/util/private/attr_struct'
9
+ autoload :MemoMap, 'jsi/util/private/memo_map'
10
+
11
+ extend self
12
+
13
+ EMPTY_ARY = [].freeze
14
+
15
+ EMPTY_HASH = {}.freeze
16
+
17
+ EMPTY_SET = Set[].freeze
18
+
19
+ CLASSES_ALWAYS_FROZEN = Set[TrueClass, FalseClass, NilClass, Integer, Float, BigDecimal, Rational, Symbol].freeze
20
+
21
+ # is a hash as the last argument passed to keyword params? (false in ruby 3; true before - generates
22
+ # a warning in 2.7 but no way to make 2.7 behave like 3 so the warning is useless)
23
+ #
24
+ # TODO remove eventually (keyword argument compatibility)
25
+ LAST_ARGUMENT_AS_KEYWORD_PARAMETERS = begin
26
+ if Object.const_defined?(:Warning)
27
+ warn = ::Warning.instance_method(:warn)
28
+ ::Warning.send(:remove_method, :warn)
29
+ ::Warning.send(:define_method, :warn) { |*, **| }
30
+ end
31
+
32
+ -> (k: ) { k }[{k: nil}]
33
+ true
34
+ rescue ArgumentError
35
+ false
36
+ ensure
37
+ if Object.const_defined?(:Warning)
38
+ ::Warning.send(:remove_method, :warn)
39
+ ::Warning.send(:define_method, :warn, warn)
40
+ end
41
+ end
42
+
43
+ # we won't use #to_json on classes where it is defined by
44
+ # JSON::Ext::Generator::GeneratorMethods / JSON::Pure::Generator::GeneratorMethods
45
+ # this is a bit of a kluge and disregards any singleton class to_json, but it will do.
46
+ USE_TO_JSON_METHOD = Hash.new do |h, klass|
47
+ h[klass] = klass.method_defined?(:to_json) &&
48
+ klass.instance_method(:to_json).owner.name !~ /\AJSON:.*:GeneratorMethods\b/
49
+ end
50
+
51
+ RUBY_REJECT_NAME_CODEPOINTS = [
52
+ 0..31, # C0 control chars
53
+ %q( !"#$%&'()*+,-./:;<=>?@[\\]^`{|}~).each_codepoint, # printable special chars (note: "_" not included)
54
+ 127..159, # C1 control chars
55
+ ].inject(Set[], &:merge).freeze
56
+
57
+ RUBY_REJECT_NAME_RE = Regexp.new('[' + Regexp.escape(RUBY_REJECT_NAME_CODEPOINTS.to_a.pack('U*')) + ']+').freeze
58
+
59
+ # is the given name ok to use as a ruby method name?
60
+ def ok_ruby_method_name?(name)
61
+ # must be a string
62
+ return false unless name.respond_to?(:to_str)
63
+ # must not begin with a digit
64
+ return false if name =~ /\A[0-9]/
65
+ # must not contain special or control characters
66
+ return false if name =~ RUBY_REJECT_NAME_RE
67
+
68
+ return true
69
+ end
70
+
71
+ def const_name_from_parts(parts, join: '')
72
+ parts = parts.map do |part|
73
+ part = part.dup
74
+ part[/\A[^a-zA-Z]*/] = ''
75
+ part[0] = part[0].upcase if part[0]
76
+ part.gsub!(RUBY_REJECT_NAME_RE, '_')
77
+ part
78
+ end
79
+ if !parts.all?(&:empty?)
80
+ parts.reject(&:empty?).join(join).freeze
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ # string or URI → frozen URI
87
+ # @return [Addressable::URI]
88
+ def uri(uri)
89
+ if uri.is_a?(Addressable::URI)
90
+ if uri.frozen?
91
+ uri
92
+ else
93
+ uri.dup.freeze
94
+ end
95
+ else
96
+ Addressable::URI.parse(uri).freeze
97
+ end
98
+ end
99
+
100
+ # this is the Y-combinator, which allows anonymous recursive functions. for a simple example,
101
+ # to define a recursive function to return the length of an array:
102
+ #
103
+ # length = ycomb do |len|
104
+ # proc { |list| list == [] ? 0 : 1 + len.call(list[1..-1]) }
105
+ # end
106
+ #
107
+ # length.call([0])
108
+ # # => 1
109
+ #
110
+ # see https://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator
111
+ # and chapter 9 of the little schemer, available as the sample chapter at
112
+ # https://felleisen.org/matthias/BTLS-index.html
113
+ def ycomb
114
+ proc { |f| f.call(f) }.call(proc { |f| yield proc { |*x| f.call(f).call(*x) } })
115
+ end
116
+
117
+ def require_jmespath
118
+ return if instance_variable_defined?(:@jmespath_required)
119
+ begin
120
+ require 'jmespath'
121
+ rescue ::LoadError => e
122
+ # :nocov:
123
+ msg = [
124
+ "please install and/or add to your Gemfile the `jmespath` gem to use this. jmespath is not a dependency of JSI.",
125
+ "original error message:",
126
+ e.message,
127
+ ].join("\n")
128
+ raise(e.class, msg, e.backtrace)
129
+ # :nocov:
130
+ end
131
+ hashlike = JSI::SchemaSet[].new_jsi({'test' => 0})
132
+ unless JMESPath.search('test', hashlike) == 0
133
+ # :nocov:
134
+ raise(::LoadError, [
135
+ "the loaded version of jmespath cannot be used with JSI.",
136
+ "jmespath is compatible with JSI objects as of version 1.5.0",
137
+ ].join("\n"))
138
+ # :nocov:
139
+ end
140
+ @jmespath_required = true
141
+ nil
142
+ end
143
+
144
+ # Defines equality methods and #hash (for Hash / Set), based on a method #jsi_fingerprint
145
+ # implemented by the includer. #jsi_fingerprint is to include the class and any properties
146
+ # of the instance which constitute its identity.
147
+ module FingerprintHash
148
+ # overrides BasicObject#==
149
+ def ==(other)
150
+ __id__ == other.__id__ || (other.is_a?(FingerprintHash) && jsi_fingerprint == other.jsi_fingerprint)
151
+ end
152
+
153
+ alias_method :eql?, :==
154
+
155
+ # overrides Kernel#hash
156
+ def hash
157
+ jsi_fingerprint.hash
158
+ end
159
+ end
160
+
161
+ module FingerprintHash::Immutable
162
+ include FingerprintHash
163
+
164
+ def ==(other)
165
+ return true if __id__ == other.__id__
166
+ return false unless other.is_a?(FingerprintHash)
167
+ # FingerprintHash::Immutable#hash being memoized, comparing that is basically free.
168
+ # not done with FingerprintHash, its #hash can be expensive.
169
+ return false if other.is_a?(FingerprintHash::Immutable) && hash != other.hash
170
+ jsi_fingerprint == other.jsi_fingerprint
171
+ end
172
+
173
+ alias_method :eql?, :==
174
+
175
+ def hash
176
+ @jsi_fingerprint_hash ||= jsi_fingerprint.hash
177
+ end
178
+
179
+ def freeze
180
+ hash
181
+ super
182
+ end
183
+ end
184
+ end
185
+ end
@@ -1,102 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- # a module relating to objects that act like Hash or Array instances
5
- module Typelike
6
- # yields the content of the given param `object`. for objects which have a #jsi_modified_copy
7
- # method of their own (JSI::Base, JSI::MetaschemaNode) that method is invoked with the given
8
- # block. otherwise the given object itself is yielded.
9
- #
10
- # the given block must result in a modified copy of its block parameter
11
- # (not destructively modifying the yielded content).
12
- #
13
- # @yield [Object] the content of the given object. the block should result
14
- # in a (nondestructively) modified copy of this.
15
- # @return [object.class] modified copy of the given object
16
- def self.modified_copy(object, &block)
17
- if object.respond_to?(:jsi_modified_copy)
18
- object.jsi_modified_copy(&block)
19
- else
20
- return yield(object)
21
- end
22
- end
23
-
24
- # recursive method to express the given argument object in json-compatible
25
- # types of Hash, Array, and basic types of String/boolean/numeric/nil. this
26
- # will raise TypeError if an object is given that is not a type that seems
27
- # to be expressable as json.
28
- #
29
- # similar effect could be achieved by requiring 'json/add/core' and using #as_json,
30
- # but I don't much care for how it represents classes that are
31
- # not naturally expressable in JSON, and prefer not to load its
32
- # monkey-patching.
33
- #
34
- # @param object [Object] the object to be converted to jsonifiability
35
- # @return [Array, Hash, String, Boolean, NilClass, Numeric] jsonifiable
36
- # expression of param object
37
- # @raise [TypeError] when the object (or an object nested with a hash or
38
- # array of object) cannot be expressed as json
39
- def self.as_json(object, *opt)
40
- if object.is_a?(JSI::PathedNode)
41
- as_json(object.jsi_node_content, *opt)
42
- elsif object.respond_to?(:to_hash)
43
- (object.respond_to?(:map) ? object : object.to_hash).map do |k, v|
44
- unless k.is_a?(Symbol) || k.respond_to?(:to_str)
45
- raise(TypeError, "json object (hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
46
- end
47
- {k.to_s => as_json(v, *opt)}
48
- end.inject({}, &:update)
49
- elsif object.respond_to?(:to_ary)
50
- (object.respond_to?(:map) ? object : object.to_ary).map { |e| as_json(e, *opt) }
51
- elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| object.is_a?(c) }
52
- object
53
- elsif object.is_a?(Symbol)
54
- object.to_s
55
- elsif object.is_a?(Set)
56
- as_json(object.to_a, *opt)
57
- elsif object.respond_to?(:as_json)
58
- as_json(object.as_json(*opt), *opt)
59
- else
60
- raise(TypeError, "cannot express object as json: #{object.pretty_inspect.chomp}")
61
- end
62
- end
63
- end
64
-
65
4
  # a module of methods for objects which behave like Hash but are not Hash.
66
5
  #
67
6
  # this module is intended to be internal to JSI. no guarantees or API promises
68
7
  # are made for non-JSI classes including this module.
69
- module Hashlike
8
+ module Util::Hashlike
9
+ include(Enumerable)
10
+
70
11
  # safe methods which can be delegated to #to_hash (which the includer is assumed to have defined).
71
12
  # 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
72
13
 
73
14
  # methods which do not need to access the value.
74
- SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size)
75
- SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at)
76
- DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift)
15
+ SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size).map(&:freeze).freeze
16
+ SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at).map(&:freeze).freeze
17
+ DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift).map(&:freeze).freeze
77
18
  # these return a modified copy
78
19
  safe_modified_copy_methods = %w(compact)
79
20
  # select and reject will return a modified copy but need the yielded block variable value from #[]
80
21
  safe_kv_block_modified_copy_methods = %w(select reject)
81
22
  SAFE_METHODS = SAFE_KEY_ONLY_METHODS | SAFE_KEY_VALUE_METHODS
82
- safe_to_hash_methods = SAFE_METHODS - safe_modified_copy_methods - safe_kv_block_modified_copy_methods
23
+ custom_methods = %w(merge) # defined below
24
+ safe_to_hash_methods = SAFE_METHODS -
25
+ safe_modified_copy_methods -
26
+ safe_kv_block_modified_copy_methods -
27
+ custom_methods
83
28
  safe_to_hash_methods.each do |method_name|
84
- define_method(method_name) { |*a, &b| to_hash.public_send(method_name, *a, &b) }
29
+ if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
30
+ define_method(method_name) { |*a, &b| to_hash.public_send(method_name, *a, &b) }
31
+ else
32
+ define_method(method_name) { |*a, **kw, &b| to_hash.public_send(method_name, *a, **kw, &b) }
33
+ end
85
34
  end
86
35
  safe_modified_copy_methods.each do |method_name|
87
- define_method(method_name) do |*a, &b|
88
- jsi_modified_copy do |object_to_modify|
89
- responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
90
- responsive_object.public_send(method_name, *a, &b)
36
+ if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
37
+ define_method(method_name) do |*a, &b|
38
+ jsi_modified_copy do |object_to_modify|
39
+ responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
40
+ responsive_object.public_send(method_name, *a, &b)
41
+ end
42
+ end
43
+ else
44
+ define_method(method_name) do |*a, **kw, &b|
45
+ jsi_modified_copy do |object_to_modify|
46
+ responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
47
+ responsive_object.public_send(method_name, *a, **kw, &b)
48
+ end
91
49
  end
92
50
  end
93
51
  end
94
52
  safe_kv_block_modified_copy_methods.each do |method_name|
95
- define_method(method_name) do |*a, &b|
53
+ define_method(method_name) do |**kw, &b|
96
54
  jsi_modified_copy do |object_to_modify|
97
55
  responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
98
56
  responsive_object.public_send(method_name) do |k, _v|
99
- b.call(k, self[k, *a])
57
+ b.call(k, self[k, **kw])
100
58
  end
101
59
  end
102
60
  end
@@ -112,9 +70,8 @@ module JSI
112
70
  unless other.respond_to?(:to_hash)
113
71
  raise(TypeError, "cannot update with argument that does not respond to #to_hash: #{other.pretty_inspect.chomp}")
114
72
  end
115
- self_respondingto_key = self.respond_to?(:key?) ? self : to_hash
116
73
  other.to_hash.each_pair do |key, value|
117
- if block && self_respondingto_key.key?(key)
74
+ if block && key?(key)
118
75
  value = yield(key, self[key], value)
119
76
  end
120
77
  self[key] = value
@@ -131,27 +88,30 @@ module JSI
131
88
  # @return duplicate of this hash with the other hash merged in
132
89
  # @raise [TypeError] when `other` does not respond to #to_hash
133
90
  def merge(other, &block)
134
- dup.update(other, &block)
91
+ jsi_modified_copy do |instance|
92
+ instance.merge(other.is_a?(Base) ? other.jsi_node_content : other, &block)
93
+ end
135
94
  end
136
95
 
137
96
  # basically the same #inspect as Hash, but has the class name and, if responsive,
138
97
  # self's #jsi_object_group_text
139
98
  # @return [String]
140
99
  def inspect
141
- object_group_str = (respond_to?(:jsi_object_group_text) ? self.jsi_object_group_text : [self.class]).join(' ')
142
- "\#{<#{object_group_str}>#{self.map { |k, v| " #{k.inspect} => #{v.inspect}" }.join(',')}}"
100
+ object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
101
+ -"\#{<#{object_group_str}>#{map { |k, v| " #{k.inspect} => #{v.inspect}" }.join(',')}}"
143
102
  end
144
103
 
145
- alias_method :to_s, :inspect
104
+ def to_s
105
+ inspect
106
+ end
146
107
 
147
108
  # pretty-prints a representation of this hashlike to the given printer
148
109
  # @return [void]
149
110
  def pretty_print(q)
150
- object_group_str = (respond_to?(:jsi_object_group_text) ? jsi_object_group_text : [self.class]).join(' ')
111
+ object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
151
112
  q.text "\#{<#{object_group_str}>"
152
- q.group_sub {
153
- q.nest(2) {
154
- q.breakable(empty? ? '' : ' ')
113
+ q.group(2) {
114
+ q.breakable ' ' if !empty?
155
115
  q.seplist(self, nil, :each_pair) { |k, v|
156
116
  q.group {
157
117
  q.pp k
@@ -159,9 +119,8 @@ module JSI
159
119
  q.pp v
160
120
  }
161
121
  }
162
- }
163
122
  }
164
- q.breakable ''
123
+ q.breakable '' if !empty?
165
124
  q.text '}'
166
125
  end
167
126
  end
@@ -170,15 +129,17 @@ module JSI
170
129
  #
171
130
  # this module is intended to be internal to JSI. no guarantees or API promises
172
131
  # are made for non-JSI classes including this module.
173
- module Arraylike
132
+ module Util::Arraylike
133
+ include(Enumerable)
134
+
174
135
  # safe methods which can be delegated to #to_ary (which the includer is assumed to have defined).
175
136
  # 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
176
137
 
177
138
  # methods which do not need to access the element.
178
- SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size)
139
+ SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size).map(&:freeze).freeze
179
140
  # there are some ambiguous ones that are omitted, like #sort, #map / #collect.
180
- SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip)
181
- DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift)
141
+ SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip).map(&:freeze).freeze
142
+ DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift).map(&:freeze).freeze
182
143
 
183
144
  # methods (well, method) that returns a modified copy and doesn't need any handling of block variable(s)
184
145
  safe_modified_copy_methods = %w(compact)
@@ -189,23 +150,36 @@ module JSI
189
150
  SAFE_METHODS = SAFE_INDEX_ONLY_METHODS | SAFE_INDEX_ELEMENT_METHODS
190
151
  safe_to_ary_methods = SAFE_METHODS - safe_modified_copy_methods - safe_el_block_methods
191
152
  safe_to_ary_methods.each do |method_name|
192
- define_method(method_name) { |*a, &b| to_ary.public_send(method_name, *a, &b) }
153
+ if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
154
+ define_method(method_name) { |*a, &b| to_ary.public_send(method_name, *a, &b) }
155
+ else
156
+ define_method(method_name) { |*a, **kw, &b| to_ary.public_send(method_name, *a, **kw, &b) }
157
+ end
193
158
  end
194
159
  safe_modified_copy_methods.each do |method_name|
195
- define_method(method_name) do |*a, &b|
196
- jsi_modified_copy do |object_to_modify|
197
- responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
198
- responsive_object.public_send(method_name, *a, &b)
160
+ if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
161
+ define_method(method_name) do |*a, &b|
162
+ jsi_modified_copy do |object_to_modify|
163
+ responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
164
+ responsive_object.public_send(method_name, *a, &b)
165
+ end
166
+ end
167
+ else
168
+ define_method(method_name) do |*a, **kw, &b|
169
+ jsi_modified_copy do |object_to_modify|
170
+ responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
171
+ responsive_object.public_send(method_name, *a, **kw, &b)
172
+ end
199
173
  end
200
174
  end
201
175
  end
202
176
  safe_el_block_methods.each do |method_name|
203
- define_method(method_name) do |*a, &b|
177
+ define_method(method_name) do |**kw, &b|
204
178
  jsi_modified_copy do |object_to_modify|
205
179
  i = 0
206
180
  responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
207
181
  responsive_object.public_send(method_name) do |_e|
208
- b.call(self[i, *a]).tap { i += 1 }
182
+ b.call(self[i, **kw]).tap { i += 1 }
209
183
  end
210
184
  end
211
185
  end
@@ -229,26 +203,26 @@ module JSI
229
203
  # self's #jsi_object_group_text
230
204
  # @return [String]
231
205
  def inspect
232
- object_group_str = (respond_to?(:jsi_object_group_text) ? jsi_object_group_text : [self.class]).join(' ')
233
- "\#[<#{object_group_str}>#{self.map { |e| ' ' + e.inspect }.join(',')}]"
206
+ object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
207
+ -"\#[<#{object_group_str}>#{map { |e| ' ' + e.inspect }.join(',')}]"
234
208
  end
235
209
 
236
- alias_method :to_s, :inspect
210
+ def to_s
211
+ inspect
212
+ end
237
213
 
238
214
  # pretty-prints a representation of this arraylike to the given printer
239
215
  # @return [void]
240
216
  def pretty_print(q)
241
- object_group_str = (respond_to?(:jsi_object_group_text) ? jsi_object_group_text : [self.class]).join(' ')
217
+ object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
242
218
  q.text "\#[<#{object_group_str}>"
243
- q.group_sub {
244
- q.nest(2) {
245
- q.breakable(empty? ? '' : ' ')
219
+ q.group(2) {
220
+ q.breakable ' ' if !empty?
246
221
  q.seplist(self, nil, :each) { |e|
247
222
  q.pp e
248
223
  }
249
- }
250
224
  }
251
- q.breakable ''
225
+ q.breakable '' if !empty?
252
226
  q.text ']'
253
227
  end
254
228
  end