jsi 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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