jsi 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +6 -1
- data/CHANGELOG.md +15 -0
- data/README.md +19 -18
- data/jsi.gemspec +2 -3
- data/lib/jsi/base/mutability.rb +44 -0
- data/lib/jsi/base/node.rb +199 -34
- data/lib/jsi/base.rb +412 -228
- data/lib/jsi/jsi_coder.rb +18 -16
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +57 -23
- data/lib/jsi/metaschema_node.rb +138 -107
- data/lib/jsi/ptr.rb +59 -37
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/child_application.rb +0 -25
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/ref.rb +1 -1
- data/lib/jsi/schema/application/inplace_application/someof.rb +1 -1
- data/lib/jsi/schema/application/inplace_application.rb +0 -27
- data/lib/jsi/schema/draft04.rb +0 -1
- data/lib/jsi/schema/draft06.rb +0 -1
- data/lib/jsi/schema/draft07.rb +0 -1
- data/lib/jsi/schema/ref.rb +44 -18
- data/lib/jsi/schema/schema_ancestor_node.rb +65 -56
- data/lib/jsi/schema/validation/contains.rb +1 -1
- data/lib/jsi/schema/validation/draft04/minmax.rb +2 -0
- data/lib/jsi/schema/validation/draft04.rb +0 -2
- data/lib/jsi/schema/validation/draft06.rb +0 -2
- data/lib/jsi/schema/validation/draft07.rb +0 -2
- data/lib/jsi/schema/validation/items.rb +3 -3
- data/lib/jsi/schema/validation/pattern.rb +1 -1
- data/lib/jsi/schema/validation/properties.rb +4 -4
- data/lib/jsi/schema/validation/ref.rb +1 -1
- data/lib/jsi/schema/validation.rb +0 -2
- data/lib/jsi/schema.rb +405 -194
- data/lib/jsi/schema_classes.rb +196 -127
- data/lib/jsi/schema_registry.rb +66 -17
- data/lib/jsi/schema_set.rb +76 -30
- data/lib/jsi/simple_wrap.rb +2 -7
- data/lib/jsi/util/private/attr_struct.rb +28 -14
- data/lib/jsi/util/private/memo_map.rb +75 -0
- data/lib/jsi/util/private.rb +73 -92
- data/lib/jsi/util/typelike.rb +28 -28
- data/lib/jsi/util.rb +120 -36
- data/lib/jsi/validation/error.rb +4 -0
- data/lib/jsi/validation/result.rb +18 -32
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +67 -25
- data/lib/schemas/json-schema.org/draft-04/schema.rb +159 -4
- data/lib/schemas/json-schema.org/draft-06/schema.rb +161 -4
- data/lib/schemas/json-schema.org/draft-07/schema.rb +188 -4
- metadata +19 -5
- data/lib/jsi/metaschema.rb +0 -6
- data/lib/jsi/schema/validation/core.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e18f4241f5f0013f657912295cb85db30095223a25030da3bbb690e68a5954d9
|
4
|
+
data.tar.gz: '083cbd8436424eeab11c36aeabb42f27ad9072d44450a0b8bfca391503131f9e'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1030a1013ae2209a7c6b1df083efe2466a670e1f644d64fd349475ba07331c182629c41a77d4d7220aaffcfb669f0c13bc5c49e6cb23a4f231747fbdc4b5ec7a
|
7
|
+
data.tar.gz: d9f24dec46a507e9d2f40046e891acec2bbef92150e72dd29bb1245618bb6be0ea9beb140cc4f0a465f187bd65e6e52c68bf82d906f8df0edaff67f062dd4c15
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# v0.8.0
|
2
|
+
|
3
|
+
- Immutable JSIs with new_jsi param `mutable`
|
4
|
+
- JSIs are still mutable by default, but in the next release they will default to immutable
|
5
|
+
- Base#jsi_indicated_schemas
|
6
|
+
- Base::StringNode
|
7
|
+
- rename metaschema modules /JSONSchemaOrgDraft0X/JSONSchemaDraft0X/
|
8
|
+
- terminology: /Metaschema/Meta-Schema/ and /metaschema/meta-schema/ (where hyphen is allowed)
|
9
|
+
- Base::HashNode#jsi_each_propertyName
|
10
|
+
- new_schema and/or new_jsi params register, schema_registry, stringify_symbol_keys, to_immutable
|
11
|
+
- new_schema block param will module_exec on schema module
|
12
|
+
- Base#[] param use_default default false, overridable
|
13
|
+
- SchemaModule::Connects, SchemaModule::Connection
|
14
|
+
- rm Schema#jsi_schema_instance_modules
|
15
|
+
|
1
16
|
# v0.7.0
|
2
17
|
|
3
18
|
- JSI::Base instances include Array/Hash-like modules on subclasses rather than extending each instance; are only Enumerable when appropriate instead of always
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
JSI offers an Object-Oriented representation for JSON data using JSON Schemas. Given your JSON Schemas, JSI constructs Ruby modules and classes which are used to instantiate your JSON data. These modules let you use JSON with all the niceties of OOP such as property accessors and application-defined instance methods.
|
7
7
|
|
8
|
-
To learn more about JSON Schema see
|
8
|
+
To learn more about JSON Schema see <https://json-schema.org/>.
|
9
9
|
|
10
10
|
JSI marries object-oriented programming with JSON Schemas by associating a module with each schema, and extending every instance described by a schema with that module. When an application adds methods to a schema module, those methods can be used on its instances.
|
11
11
|
|
@@ -15,7 +15,9 @@ Note: The canonical location of this README is on [RubyDoc](http://rubydoc.info/
|
|
15
15
|
|
16
16
|
## Example
|
17
17
|
|
18
|
-
Words are boring, let's code.
|
18
|
+
Words are boring, let's code. You can follow along from the code blocks - install the gem (`gem install jsi`), load an irb (`irb -r jsi`), and copy/paste/hack.
|
19
|
+
|
20
|
+
Here's a schema in yaml:
|
19
21
|
|
20
22
|
```yaml
|
21
23
|
$schema: "http://json-schema.org/draft-07/schema"
|
@@ -91,7 +93,7 @@ bill.phone.map(&:location)
|
|
91
93
|
# => ["home"]
|
92
94
|
```
|
93
95
|
|
94
|
-
We also get validations, as you'd expect given that's largely what
|
96
|
+
We also get validations, as you'd expect given that's largely what JSON Schema exists to do:
|
95
97
|
|
96
98
|
```ruby
|
97
99
|
bill.jsi_valid?
|
@@ -115,7 +117,7 @@ bad.phone.jsi_validate
|
|
115
117
|
# #<Set: {#<JSI::Validation::Error
|
116
118
|
# message: "instance type does not match `type` value",
|
117
119
|
# keyword: "type",
|
118
|
-
# schema: #{<JSI (JSI::
|
120
|
+
# schema: #{<JSI (JSI::JSONSchemaDraft07) Schema> "type" => "string"},
|
119
121
|
# instance_ptr: JSI::Ptr["phone", 0, "number"],
|
120
122
|
# instance_document: {"phone"=>[{"number"=>[5, 5, 5]}]}
|
121
123
|
# >,
|
@@ -140,7 +142,7 @@ There's plenty more JSI has to offer, but this should give you a pretty good ide
|
|
140
142
|
## Terminology and Concepts
|
141
143
|
|
142
144
|
- `JSI::Base` is the base class for each JSI schema class representing instances of JSON Schemas.
|
143
|
-
- a "JSI Schema" is a JSON Schema, instantiated as (usually) a JSI::Base described by a
|
145
|
+
- a "JSI Schema" is a JSON Schema, instantiated as (usually) a JSI::Base described by a meta-schema (see the section on meta-schemas below). A JSI Schema is an instance of the module `JSI::Schema`.
|
144
146
|
- a "JSI Schema Module" is a module which represents one schema, dynamically created by that Schema. Instances of that schema are extended with its JSI schema module. applications may reopen these modules to add functionality to JSI instances described by a given schema.
|
145
147
|
- a "JSI schema class" is a subclass of `JSI::Base` representing one or more JSON schemas. Instances of such a class are described by all of the represented schemas. A JSI schema class includes the JSI schema module of each represented schema.
|
146
148
|
- "instance" is a term that is significantly overloaded in this space, so documentation will attempt to be clear what kind of instance is meant:
|
@@ -154,9 +156,9 @@ JSI supports these JSON Schema specification versions:
|
|
154
156
|
|
155
157
|
| Version | `$schema` URI | JSI Schema Module |
|
156
158
|
| --- | --- | --- |
|
157
|
-
| Draft 4 | `http://json-schema.org/draft-04/schema#` | {JSI::
|
158
|
-
| Draft 6 | `http://json-schema.org/draft-06/schema#` | {JSI::
|
159
|
-
| Draft 7 | `http://json-schema.org/draft-07/schema#` | {JSI::
|
159
|
+
| Draft 4 | `http://json-schema.org/draft-04/schema#` | {JSI::JSONSchemaDraft04} |
|
160
|
+
| Draft 6 | `http://json-schema.org/draft-06/schema#` | {JSI::JSONSchemaDraft06} |
|
161
|
+
| Draft 7 | `http://json-schema.org/draft-07/schema#` | {JSI::JSONSchemaDraft07} |
|
160
162
|
|
161
163
|
## JSI and Object Oriented Programming
|
162
164
|
|
@@ -190,7 +192,7 @@ bill['name']
|
|
190
192
|
|
191
193
|
For `#name` and `#name=`, we're overriding existing accessor methods. note the use of `super` - this invokes the accessor methods defined by JSI which these override. You could alternatively use `self['name']` and `self['name']=` in these methods, with the same effect as `super`.
|
192
194
|
|
193
|
-
Working with subschemas is just about as easy as with root schemas.
|
195
|
+
Working with subschemas to add methods is just about as easy as with root schemas.
|
194
196
|
|
195
197
|
You can subscript or use property accessors on a JSI schema module to refer to the schema modules of its subschemas, e.g.:
|
196
198
|
|
@@ -211,8 +213,7 @@ bill.phone.first.number_with_dashes
|
|
211
213
|
# => "5-5-5"
|
212
214
|
```
|
213
215
|
|
214
|
-
A recommended convention for naming subschemas is to define them in the namespace of the module of their
|
215
|
-
parent schema. The module can then be opened to add methods to the subschema's module.
|
216
|
+
A recommended convention for naming subschemas is to define them in the namespace of the module of their parent schema. The module can then be opened to add methods to the subschema's module.
|
216
217
|
|
217
218
|
```ruby
|
218
219
|
module Contact
|
@@ -242,9 +243,9 @@ The classes used to instantiate JSIs are dynamically generated subclasses of JSI
|
|
242
243
|
|
243
244
|
## Registration
|
244
245
|
|
245
|
-
In order for references across documents (generally from a `$ref` schema keyword) to resolve, JSI provides a registry which associates URIs with schemas (or resources containing schemas).
|
246
|
+
In order for references across documents (generally from a `$ref` schema keyword) to resolve, JSI provides a registry (a {JSI::SchemaRegistry}) which associates URIs with schemas (or resources containing schemas). The default registry is accessible on {JSI.schema_registry}.
|
246
247
|
|
247
|
-
Schemas instantiated with `.new_schema`, and their subschemas, are
|
248
|
+
Schemas instantiated with `.new_schema`, and their subschemas, are by default registered with `JSI.schema_registry` if they are identified by an absolute URI. This can be controlled by params `register` and `schema_registry`.
|
248
249
|
|
249
250
|
Schemas can automatically be lazily loaded by registering a block which instantiates them with {JSI::SchemaRegistry#autoload_uri} (see its documentation).
|
250
251
|
|
@@ -258,13 +259,13 @@ The following optional features are not completely supported:
|
|
258
259
|
- Regular expressions are interpreted by Ruby's Regexp class, whereas JSON Schema recommends interpreting these as ECMA 262 regular expressions. Certain expressions behave differently, particularly `^` and `$`.
|
259
260
|
- Keywords `contentMediaType` and `contentEncoding` do not perform validation.
|
260
261
|
|
261
|
-
##
|
262
|
+
## Meta-Schemas
|
262
263
|
|
263
|
-
A
|
264
|
+
A meta-schema is a schema that describes schemas. Likewise, a schema is an instance of a meta-schema.
|
264
265
|
|
265
|
-
In JSI, a schema is generally a JSI::Base instance whose schemas include a
|
266
|
+
In JSI, a schema is generally a JSI::Base instance whose schemas include a meta-schema.
|
266
267
|
|
267
|
-
A self-descriptive
|
268
|
+
A self-descriptive meta-schema - most commonly one of the JSON schema draft meta-schemas - is an object whose schemas include itself. This is instantiated in JSI as a JSI::MetaSchemaNode, a special subclass of JSI::Base.
|
268
269
|
|
269
270
|
## ActiveRecord serialization
|
270
271
|
|
@@ -282,7 +283,7 @@ class User < ActiveRecord::Base
|
|
282
283
|
end
|
283
284
|
```
|
284
285
|
|
285
|
-
Now `user.contact_info` will be instantiated as a `Contact` JSI instance, from the JSON type in the database, with Contact's accessors, validations, and
|
286
|
+
Now `user.contact_info` will be instantiated as a `Contact` JSI instance, from the JSON type in the database, with Contact's accessors, validations, and application-defined instance methods.
|
286
287
|
|
287
288
|
See the gem [`arms`](https://github.com/notEthan/arms) if you wish to serialize the dumped JSON-compatible objects further as text.
|
288
289
|
|
data/jsi.gemspec
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require "jsi/version"
|
1
|
+
require_relative "lib/jsi/version"
|
4
2
|
|
5
3
|
Gem::Specification.new do |spec|
|
6
4
|
spec.name = "jsi"
|
@@ -27,4 +25,5 @@ Gem::Specification.new do |spec|
|
|
27
25
|
spec.require_paths = ["lib"]
|
28
26
|
|
29
27
|
spec.add_dependency "addressable", '~> 2.3'
|
28
|
+
spec.add_dependency "bigdecimal"
|
30
29
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JSI
|
4
|
+
module Base::Mutable
|
5
|
+
include(Util::FingerprintHash)
|
6
|
+
|
7
|
+
def jsi_node_content
|
8
|
+
jsi_ptr.evaluate(jsi_document)
|
9
|
+
end
|
10
|
+
|
11
|
+
def jsi_mutable?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def jsi_mutability_initialize
|
18
|
+
end
|
19
|
+
|
20
|
+
def jsi_memomap_class
|
21
|
+
Util::MemoMap::Mutable
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Base::Immutable
|
26
|
+
include(Util::FingerprintHash::Immutable)
|
27
|
+
|
28
|
+
attr_reader(:jsi_node_content)
|
29
|
+
|
30
|
+
def jsi_mutable?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def jsi_mutability_initialize
|
37
|
+
@jsi_node_content = @jsi_ptr.evaluate(@jsi_document)
|
38
|
+
end
|
39
|
+
|
40
|
+
def jsi_memomap_class
|
41
|
+
Util::MemoMap::Immutable
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/jsi/base/node.rb
CHANGED
@@ -1,47 +1,73 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JSI
|
4
|
-
module Base
|
5
|
-
|
6
|
-
|
7
|
-
#
|
4
|
+
# module extending a {JSI::Base} object when its instance (its {Base#jsi_node_content})
|
5
|
+
# is a Hash (or responds to `#to_hash`)
|
6
|
+
module Base::HashNode
|
7
|
+
# instantiates and yields each property name (hash key) as a JSI described by any `propertyNames` schemas.
|
8
8
|
#
|
9
|
-
# @
|
10
|
-
# @return [
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
# @yield [JSI::Base]
|
10
|
+
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
11
|
+
def jsi_each_propertyName
|
12
|
+
return to_enum(__method__) { jsi_node_content_hash_pubsend(:size) } unless block_given?
|
13
|
+
|
14
|
+
property_schemas = SchemaSet.build do |schemas|
|
15
|
+
jsi_schemas.each do |s|
|
16
|
+
if s.keyword?('propertyNames') && s['propertyNames'].is_a?(Schema)
|
17
|
+
schemas << s['propertyNames']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
jsi_node_content_hash_pubsend(:each_key) do |key|
|
22
|
+
yield property_schemas.new_jsi(key)
|
18
23
|
end
|
19
|
-
|
24
|
+
|
25
|
+
nil
|
20
26
|
end
|
21
27
|
|
22
|
-
|
28
|
+
# See {Base#jsi_hash?}. Always true for HashNode.
|
29
|
+
def jsi_hash?
|
30
|
+
true
|
31
|
+
end
|
23
32
|
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# although Base#as_json does clobber activesupport's, I want as_json defined correctly on the module too.
|
30
|
-
Typelike.as_json(jsi_node_content, *opt)
|
33
|
+
# Yields each key - see {Base#jsi_each_child_token}
|
34
|
+
def jsi_each_child_token(&block)
|
35
|
+
return to_enum(__method__) { jsi_node_content_hash_pubsend(:size) } unless block
|
36
|
+
jsi_node_content_hash_pubsend(:each_key, &block)
|
37
|
+
nil
|
31
38
|
end
|
32
|
-
end
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
# See {Base#jsi_child_token_in_range?}
|
41
|
+
def jsi_child_token_in_range?(token)
|
42
|
+
jsi_node_content_hash_pubsend(:key?, token)
|
43
|
+
end
|
44
|
+
|
45
|
+
# See {Base#jsi_node_content_child}
|
46
|
+
def jsi_node_content_child(token)
|
47
|
+
# I could check token_in_range? and return nil here (as ArrayNode does).
|
48
|
+
# without that check, if the instance defines Hash#default or #default_proc, that result is returned.
|
49
|
+
# the preferred mechanism for a JSI's default value should be its schema.
|
50
|
+
# but there's no compelling reason not to support both, so I'll return what #[] returns.
|
51
|
+
jsi_node_content_hash_pubsend(:[], token)
|
52
|
+
end
|
53
|
+
|
54
|
+
# See {Base#[]}
|
55
|
+
def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
|
56
|
+
if jsi_node_content_hash_pubsend(:key?, token)
|
57
|
+
jsi_child(token, as_jsi: as_jsi)
|
58
|
+
else
|
59
|
+
if use_default
|
60
|
+
jsi_default_child(token, as_jsi: as_jsi)
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
38
66
|
|
39
|
-
# yields each
|
67
|
+
# yields each Hash key (JSON object property name) and value of this node.
|
40
68
|
#
|
41
69
|
# each yielded key is a key of the instance hash, and each yielded value is the result of {Base#[]}.
|
42
70
|
#
|
43
|
-
# returns an Enumerator if no block is given.
|
44
|
-
#
|
45
71
|
# @param kw keyword arguments are passed to {Base#[]}
|
46
72
|
# @yield [Object, Object] each key and value of this hash node
|
47
73
|
# @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
|
@@ -59,7 +85,22 @@ module JSI
|
|
59
85
|
# @param kw keyword arguments are passed to {Base#[]}
|
60
86
|
# @return [Hash]
|
61
87
|
def to_hash(**kw)
|
62
|
-
|
88
|
+
hash = {}
|
89
|
+
jsi_node_content_hash_pubsend(:each_key) { |k| hash[k] = self[k, **kw] }
|
90
|
+
hash.freeze
|
91
|
+
end
|
92
|
+
|
93
|
+
# See {Base#as_json}
|
94
|
+
def as_json(options = {})
|
95
|
+
hash = {}
|
96
|
+
each_key do |k|
|
97
|
+
ks = k.is_a?(String) ? k :
|
98
|
+
k.is_a?(Symbol) ? k.to_s :
|
99
|
+
k.respond_to?(:to_str) && (kstr = k.to_str).is_a?(String) ? kstr :
|
100
|
+
raise(TypeError, "JSON object (Hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
|
101
|
+
hash[ks] = jsi_child_node(k).as_json(**options)
|
102
|
+
end
|
103
|
+
hash
|
63
104
|
end
|
64
105
|
|
65
106
|
include Util::Hashlike
|
@@ -110,14 +151,99 @@ module JSI
|
|
110
151
|
# module extending a {JSI::Base} object when its instance (its {Base#jsi_node_content})
|
111
152
|
# is an Array (or responds to `#to_ary`)
|
112
153
|
module Base::ArrayNode
|
113
|
-
|
154
|
+
# See {Base#jsi_array?}. Always true for ArrayNode.
|
155
|
+
def jsi_array?
|
156
|
+
true
|
157
|
+
end
|
158
|
+
|
159
|
+
# Yields each index - see {Base#jsi_each_child_token}
|
160
|
+
def jsi_each_child_token(&block)
|
161
|
+
return to_enum(__method__) { jsi_node_content_ary_pubsend(:size) } unless block
|
162
|
+
jsi_node_content_ary_pubsend(:each_index, &block)
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
166
|
+
# See {Base#jsi_child_token_in_range?}
|
167
|
+
def jsi_child_token_in_range?(token)
|
168
|
+
token.is_a?(Integer) && token >= 0 && token < jsi_node_content_ary_pubsend(:size)
|
169
|
+
end
|
170
|
+
|
171
|
+
# See {Base#jsi_node_content_child}
|
172
|
+
def jsi_node_content_child(token)
|
173
|
+
# we check token_in_range? here (unlike HashNode) because we do not want to pass
|
174
|
+
# negative indices, Ranges, or non-Integers to Array#[]
|
175
|
+
if jsi_child_token_in_range?(token)
|
176
|
+
jsi_node_content_ary_pubsend(:[], token)
|
177
|
+
else
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# See {Base#[]}
|
183
|
+
def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
|
184
|
+
size = jsi_node_content_ary_pubsend(:size)
|
185
|
+
if token.is_a?(Integer)
|
186
|
+
if token < 0
|
187
|
+
if token < -size
|
188
|
+
nil
|
189
|
+
else
|
190
|
+
jsi_child(token + size, as_jsi: as_jsi)
|
191
|
+
end
|
192
|
+
else
|
193
|
+
if token < size
|
194
|
+
jsi_child(token, as_jsi: as_jsi)
|
195
|
+
else
|
196
|
+
if use_default
|
197
|
+
jsi_default_child(token, as_jsi: as_jsi)
|
198
|
+
else
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
elsif token.is_a?(Range)
|
204
|
+
type_err = proc do
|
205
|
+
raise(TypeError, [
|
206
|
+
"given range does not contain Integers",
|
207
|
+
"range: #{token.inspect}",
|
208
|
+
].join("\n"))
|
209
|
+
end
|
210
|
+
|
211
|
+
start_idx = token.begin
|
212
|
+
if start_idx.is_a?(Integer)
|
213
|
+
start_idx += size if start_idx < 0
|
214
|
+
return Util::EMPTY_ARY if start_idx == size
|
215
|
+
return nil if start_idx < 0 || start_idx > size
|
216
|
+
elsif start_idx.nil?
|
217
|
+
start_idx = 0
|
218
|
+
else
|
219
|
+
type_err.call
|
220
|
+
end
|
221
|
+
|
222
|
+
end_idx = token.end
|
223
|
+
if end_idx.is_a?(Integer)
|
224
|
+
end_idx += size if end_idx < 0
|
225
|
+
end_idx += 1 unless token.exclude_end?
|
226
|
+
end_idx = size if end_idx > size
|
227
|
+
return Util::EMPTY_ARY if start_idx >= end_idx
|
228
|
+
elsif end_idx.nil?
|
229
|
+
end_idx = size
|
230
|
+
else
|
231
|
+
type_err.call
|
232
|
+
end
|
233
|
+
|
234
|
+
(start_idx...end_idx).map { |i| jsi_child(i, as_jsi: as_jsi) }.freeze
|
235
|
+
else
|
236
|
+
raise(TypeError, [
|
237
|
+
"expected `token` param to be an Integer or Range",
|
238
|
+
"token: #{token.inspect}",
|
239
|
+
].join("\n"))
|
240
|
+
end
|
241
|
+
end
|
114
242
|
|
115
243
|
# yields each array element of this node.
|
116
244
|
#
|
117
245
|
# each yielded element is the result of {Base#[]} for each index of the instance array.
|
118
246
|
#
|
119
|
-
# returns an Enumerator if no block is given.
|
120
|
-
#
|
121
247
|
# @param kw keyword arguments are passed to {Base#[]}
|
122
248
|
# @yield [Object] each element of this array node
|
123
249
|
# @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
|
@@ -135,6 +261,11 @@ module JSI
|
|
135
261
|
to_a(**kw)
|
136
262
|
end
|
137
263
|
|
264
|
+
# See {Base#as_json}
|
265
|
+
def as_json(options = {})
|
266
|
+
each_index.map { |i| jsi_child_node(i).as_json(**options) }
|
267
|
+
end
|
268
|
+
|
138
269
|
include Util::Arraylike
|
139
270
|
|
140
271
|
if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
|
@@ -180,4 +311,38 @@ module JSI
|
|
180
311
|
end
|
181
312
|
end
|
182
313
|
end
|
314
|
+
|
315
|
+
module Base::StringNode
|
316
|
+
delegate_methods = %w(% * + << =~ [] []=
|
317
|
+
ascii_only? b byteindex byterindex bytes bytesize byteslice bytesplice capitalize capitalize!
|
318
|
+
casecmp casecmp? center chars chomp chomp! chop chop! chr clear codepoints concat count delete delete!
|
319
|
+
delete_prefix delete_prefix! delete_suffix delete_suffix! downcase downcase!
|
320
|
+
each_byte each_char each_codepoint each_grapheme_cluster each_line
|
321
|
+
empty? encode encode! encoding end_with? force_encoding getbyte grapheme_clusters gsub gsub! hex
|
322
|
+
include? index insert intern length lines ljust lstrip lstrip! match match? next next! oct ord
|
323
|
+
partition prepend replace reverse reverse! rindex rjust rpartition rstrip rstrip! scan scrub scrub!
|
324
|
+
setbyte size slice slice! split squeeze squeeze! start_with? strip strip! sub sub! succ succ! sum
|
325
|
+
swapcase swapcase! to_c to_f to_i to_r to_s to_str to_sym tr tr! tr_s tr_s!
|
326
|
+
unicode_normalize unicode_normalize! unicode_normalized? unpack unpack1 upcase upcase! upto valid_encoding?
|
327
|
+
)
|
328
|
+
delegate_methods.each do |method_name|
|
329
|
+
if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
|
330
|
+
define_method(method_name) do |*a, &b|
|
331
|
+
if jsi_node_content.respond_to?(method_name)
|
332
|
+
jsi_node_content.public_send(method_name, *a, &b)
|
333
|
+
else
|
334
|
+
jsi_node_content.to_str.public_send(method_name, *a, &b)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
else
|
338
|
+
define_method(method_name) do |*a, **kw, &b|
|
339
|
+
if jsi_node_content.respond_to?(method_name)
|
340
|
+
jsi_node_content.public_send(method_name, *a, **kw, &b)
|
341
|
+
else
|
342
|
+
jsi_node_content.to_str.public_send(method_name, *a, **kw, &b)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
183
348
|
end
|