• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

notEthan / jsi / 5109670048

pending completion
5109670048

push

github

notEthan
doc Schema::SchemaAncestorNode#jsi_anchor_subschema, #jsi_anchor_subschemas

3708 of 3809 relevant lines covered (97.35%)

357145.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.98
/lib/jsi/base/node.rb
1
# frozen_string_literal: true
2

3
module JSI
42✔
4
  module Base::Enumerable
42✔
5
    include ::Enumerable
42✔
6

7
    # an Array containing each item in this JSI.
8
    #
9
    # @param kw keyword arguments are passed to {Base#[]} - see its keyword params
10
    # @return [Array]
11
    def to_a(**kw)
42✔
12
      # TODO remove eventually (keyword argument compatibility)
13
      # discard when all supported ruby versions Enumerable#to_a delegate keywords to #each (3.0.1 breaks; 2.7.x warns)
14
      # https://bugs.ruby-lang.org/issues/18289
15
      ary = []
1,184✔
16
      each(**kw) do |e|
848✔
17
        ary << e
3,452✔
18
      end
19
      ary.freeze
1,184✔
20
    end
21

22
    alias_method :entries, :to_a
42✔
23

24
    # a jsonifiable representation of the node content
25
    # @return [Object]
26
    def as_json(*opt)
42✔
27
      # include Enumerable (above) means, if ActiveSupport is loaded, its undesirable #as_json is included
28
      # https://github.com/rails/rails/blob/v7.0.0/activesupport/lib/active_support/core_ext/object/json.rb#L139-L143
29
      # although Base#as_json does clobber activesupport's, I want as_json defined correctly on the module too.
30
      Util.as_json(jsi_node_content, *opt)
84✔
31
    end
32
  end
33

34
  # module extending a {JSI::Base} object when its instance (its {Base#jsi_node_content})
35
  # is a Hash (or responds to `#to_hash`)
36
  module Base::HashNode
42✔
37
    include Base::Enumerable
42✔
38

39
    # instantiates and yields each property name (hash key) as a JSI described by any `propertyNames` schemas.
40
    #
41
    # @yield [JSI::Base]
42
    # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
43
    def jsi_each_propertyName
42✔
44
      return to_enum(__method__) { jsi_node_content_hash_pubsend(:size) } unless block_given?
336✔
45

46
      property_schemas = SchemaSet.build do |schemas|
196✔
47
        jsi_schemas.each do |s|
196✔
48
          if s.keyword?('propertyNames') && s['propertyNames'].is_a?(Schema)
322✔
49
            schemas << s['propertyNames']
168✔
50
          end
51
        end
52
      end
53
      jsi_node_content_hash_pubsend(:each_key) do |key|
140✔
54
        yield property_schemas.new_jsi(key)
490✔
55
      end
56

57
      nil
78✔
58
    end
59

60
    # See {Base#jsi_hash?}. Always true for HashNode.
61
    def jsi_hash?
42✔
62
      true
490✔
63
    end
64

65
    # Yields each key - see {Base#jsi_each_child_token}
66
    def jsi_each_child_token(&block)
42✔
67
      return to_enum(__method__) { jsi_node_content_hash_pubsend(:size) } unless block
160,662✔
68
      jsi_node_content_hash_pubsend(:each_key, &block)
160,662✔
69
      nil
68,512✔
70
    end
71

72
    # See {Base#jsi_child_token_in_range?}
73
    def jsi_child_token_in_range?(token)
42✔
74
      jsi_node_content_hash_pubsend(:key?, token)
×
75
    end
76

77
    # See {Base#jsi_node_content_child}
78
    def jsi_node_content_child(token)
42✔
79
      # I could check token_in_range? and return nil here (as ArrayNode does).
80
      # without that check, if the instance defines Hash#default or #default_proc, that result is returned.
81
      # the preferred mechanism for a JSI's default value should be its schema.
82
      # but there's no compelling reason not to support both, so I'll return what #[] returns.
83
      jsi_node_content_hash_pubsend(:[], token)
559,670✔
84
    end
85

86
    # See {Base#[]}
87
    def [](token, as_jsi: :auto, use_default: true)
42✔
88
      if jsi_node_content_hash_pubsend(:key?, token)
559,698✔
89
        jsi_child(token, as_jsi: as_jsi)
557,778✔
90
      else
91
        if use_default
1,920✔
92
          jsi_default_child(token, as_jsi: as_jsi)
1,892✔
93
        else
94
          nil
12✔
95
        end
96
      end
97
    end
98

99
    # yields each hash key and value of this node.
100
    #
101
    # each yielded key is a key of the instance hash, and each yielded value is the result of {Base#[]}.
102
    #
103
    # @param kw keyword arguments are passed to {Base#[]}
104
    # @yield [Object, Object] each key and value of this hash node
105
    # @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
106
    def each(**kw, &block)
42✔
107
      return to_enum(__method__, **kw) { jsi_node_content_hash_pubsend(:size) } unless block
13,202✔
108
      if block.arity > 1
13,202✔
109
        jsi_node_content_hash_pubsend(:each_key) { |k| yield k, self[k, **kw] }
29,286✔
110
      else
111
        jsi_node_content_hash_pubsend(:each_key) { |k| yield [k, self[k, **kw]] }
5,932✔
112
      end
113
      self
9,430✔
114
    end
115

116
    # a hash in which each key is a key of the instance hash and each value is the result of {Base#[]}
117
    # @param kw keyword arguments are passed to {Base#[]}
118
    # @return [Hash]
119
    def to_hash(**kw)
42✔
120
      hash = {}
1,384✔
121
      jsi_node_content_hash_pubsend(:each_key) { |k| hash[k] = self[k, **kw] }
3,838✔
122
      hash.freeze
1,384✔
123
    end
124

125
    include Util::Hashlike
42✔
126

127
    if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
42✔
128
      # invokes the method with the given name on the jsi_node_content (if defined) or its #to_hash
129
      # @param method_name [String, Symbol]
130
      # @param a positional arguments are passed to the invocation of method_name
131
      # @param b block is passed to the invocation of method_name
132
      # @return [Object] the result of calling method method_name on the jsi_node_content or its #to_hash
133
      def jsi_node_content_hash_pubsend(method_name, *a, &b)
12✔
134
        if jsi_node_content.respond_to?(method_name)
390,752✔
135
          jsi_node_content.public_send(method_name, *a, &b)
390,416✔
136
        else
137
          jsi_node_content.to_hash.public_send(method_name, *a, &b)
336✔
138
        end
139
      end
140
    else
141
      # invokes the method with the given name on the jsi_node_content (if defined) or its #to_hash
142
      # @param method_name [String, Symbol]
143
      # @param a positional arguments are passed to the invocation of method_name
144
      # @param kw keyword arguments are passed to the invocation of method_name
145
      # @param b block is passed to the invocation of method_name
146
      # @return [Object] the result of calling method method_name on the jsi_node_content or its #to_hash
147
      def jsi_node_content_hash_pubsend(method_name, *a, **kw, &b)
30✔
148
        if jsi_node_content.respond_to?(method_name)
972,246✔
149
          jsi_node_content.public_send(method_name, *a, **kw, &b)
971,406✔
150
        else
151
          jsi_node_content.to_hash.public_send(method_name, *a, **kw, &b)
840✔
152
        end
153
      end
154
    end
155

156
    # methods that don't look at the value; can skip the overhead of #[] (invoked by #to_hash)
157
    SAFE_KEY_ONLY_METHODS.each do |method_name|
42✔
158
      if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
378✔
159
        define_method(method_name) do |*a, &b|
108✔
160
          jsi_node_content_hash_pubsend(method_name, *a, &b)
19,564✔
161
        end
162
      else
163
        define_method(method_name) do |*a, **kw, &b|
162✔
164
          jsi_node_content_hash_pubsend(method_name, *a, **kw, &b)
48,622✔
165
        end
166
      end
167
    end
168
  end
169

170
  # module extending a {JSI::Base} object when its instance (its {Base#jsi_node_content})
171
  # is an Array (or responds to `#to_ary`)
172
  module Base::ArrayNode
42✔
173
    include Base::Enumerable
42✔
174

175
    # See {Base#jsi_array?}. Always true for ArrayNode.
176
    def jsi_array?
42✔
177
      true
434✔
178
    end
179

180
    # Yields each index - see {Base#jsi_each_child_token}
181
    def jsi_each_child_token(&block)
42✔
182
      return to_enum(__method__) { jsi_node_content_ary_pubsend(:size) } unless block
25,348✔
183
      jsi_node_content_ary_pubsend(:each_index, &block)
25,348✔
184
      nil
10,862✔
185
    end
186

187
    # See {Base#jsi_child_token_in_range?}
188
    def jsi_child_token_in_range?(token)
42✔
189
      token.is_a?(Integer) && token >= 0 && token < jsi_node_content_ary_pubsend(:size)
125,158✔
190
    end
191

192
    # See {Base#jsi_node_content_child}
193
    def jsi_node_content_child(token)
42✔
194
      # we check token_in_range? here (unlike HashNode) because we do not want to pass
195
      # negative indices, Ranges, or non-Integers to Array#[]
196
      if jsi_child_token_in_range?(token)
125,158✔
197
        jsi_node_content_ary_pubsend(:[], token)
125,116✔
198
      else
199
        nil
18✔
200
      end
201
    end
202

203
    # See {Base#[]}
204
    def [](token, as_jsi: :auto, use_default: true)
42✔
205
      size = jsi_node_content_ary_pubsend(:size)
125,284✔
206
      if token.is_a?(Integer)
125,284✔
207
        if token < 0
125,228✔
208
          if token < -size
70✔
209
            nil
18✔
210
          else
211
            jsi_child(token + size, as_jsi: as_jsi)
28✔
212
          end
213
        else
214
          if token < size
125,158✔
215
            jsi_child(token, as_jsi: as_jsi)
125,088✔
216
          else
217
            if use_default
70✔
218
              jsi_default_child(token, as_jsi: as_jsi)
42✔
219
            else
220
              nil
12✔
221
            end
222
          end
223
        end
224
      else
225
        raise(TypeError, [
64✔
226
          "expected `token` param to be an Integer",
227
          "token: #{token.inspect}",
16✔
228
        ].join("\n"))
6✔
229
      end
230
    end
231

232
    # yields each array element of this node.
233
    #
234
    # each yielded element is the result of {Base#[]} for each index of the instance array.
235
    #
236
    # @param kw keyword arguments are passed to {Base#[]}
237
    # @yield [Object] each element of this array node
238
    # @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
239
    def each(**kw, &block)
42✔
240
      return to_enum(__method__, **kw) { jsi_node_content_ary_pubsend(:size) } unless block
14,930✔
241
      jsi_node_content_ary_pubsend(:each_index) { |i| yield(self[i, **kw]) }
66,312✔
242
      self
10,698✔
243
    end
244

245
    # an array, the same size as the instance array, in which the element at each index is the
246
    # result of {Base#[]}.
247
    # @param kw keyword arguments are passed to {Base#[]}
248
    # @return [Array]
249
    def to_ary(**kw)
42✔
250
      to_a(**kw)
1,072✔
251
    end
252

253
    include Util::Arraylike
42✔
254

255
    if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
42✔
256
      # invokes the method with the given name on the jsi_node_content (if defined) or its #to_ary
257
      # @param method_name [String, Symbol]
258
      # @param a positional arguments are passed to the invocation of method_name
259
      # @param b block is passed to the invocation of method_name
260
      # @return [Object] the result of calling method method_name on the jsi_node_content or its #to_ary
261
      def jsi_node_content_ary_pubsend(method_name, *a, &b)
12✔
262
        if jsi_node_content.respond_to?(method_name)
124,504✔
263
          jsi_node_content.public_send(method_name, *a, &b)
124,144✔
264
        else
265
          jsi_node_content.to_ary.public_send(method_name, *a, &b)
360✔
266
        end
267
      end
268
    else
269
      # invokes the method with the given name on the jsi_node_content (if defined) or its #to_ary
270
      # @param method_name [String, Symbol]
271
      # @param a positional arguments are passed to the invocation of method_name
272
      # @param kw keyword arguments are passed to the invocation of method_name
273
      # @param b block is passed to the invocation of method_name
274
      # @return [Object] the result of calling method method_name on the jsi_node_content or its #to_ary
275
      def jsi_node_content_ary_pubsend(method_name, *a, **kw, &b)
30✔
276
        if jsi_node_content.respond_to?(method_name)
310,346✔
277
          jsi_node_content.public_send(method_name, *a, **kw, &b)
309,446✔
278
        else
279
          jsi_node_content.to_ary.public_send(method_name, *a, **kw, &b)
900✔
280
        end
281
      end
282
    end
283

284
    # methods that don't look at the value; can skip the overhead of #[] (invoked by #to_a).
285
    # we override these methods from Arraylike
286
    SAFE_INDEX_ONLY_METHODS.each do |method_name|
42✔
287
      if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
168✔
288
        define_method(method_name) do |*a, &b|
48✔
289
          jsi_node_content_ary_pubsend(method_name, *a, &b)
5,432✔
290
        end
291
      else
292
        define_method(method_name) do |*a, **kw, &b|
72✔
293
          jsi_node_content_ary_pubsend(method_name, *a, **kw, &b)
13,582✔
294
        end
295
      end
296
    end
297
  end
298
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc