jimmy 0.5.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +35 -0
- data/.gitignore +4 -3
- data/.rspec +3 -0
- data/.rubocop.yml +61 -0
- data/.ruby-version +1 -1
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +13 -0
- data/LICENSE +1 -1
- data/README.md +22 -134
- data/Rakefile +91 -1
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/jimmy.gemspec +25 -21
- data/lib/jimmy.rb +23 -15
- data/lib/jimmy/declaration.rb +150 -0
- data/lib/jimmy/declaration/assertion.rb +81 -0
- data/lib/jimmy/declaration/casting.rb +34 -0
- data/lib/jimmy/declaration/composites.rb +41 -0
- data/lib/jimmy/declaration/number.rb +27 -0
- data/lib/jimmy/declaration/object.rb +11 -0
- data/lib/jimmy/declaration/string.rb +100 -0
- data/lib/jimmy/declaration/types.rb +57 -0
- data/lib/jimmy/error.rb +9 -0
- data/lib/jimmy/file_map.rb +166 -0
- data/lib/jimmy/index.rb +78 -0
- data/lib/jimmy/json/array.rb +93 -0
- data/lib/jimmy/json/collection.rb +90 -0
- data/lib/jimmy/json/hash.rb +118 -0
- data/lib/jimmy/json/pointer.rb +119 -0
- data/lib/jimmy/json/uri.rb +144 -0
- data/lib/jimmy/loaders/base.rb +30 -0
- data/lib/jimmy/loaders/json.rb +15 -0
- data/lib/jimmy/loaders/ruby.rb +21 -0
- data/lib/jimmy/macros.rb +37 -0
- data/lib/jimmy/schema.rb +106 -87
- data/lib/jimmy/schema/array.rb +95 -0
- data/lib/jimmy/schema/casting.rb +17 -0
- data/lib/jimmy/schema/json.rb +40 -0
- data/lib/jimmy/schema/number.rb +47 -0
- data/lib/jimmy/schema/object.rb +108 -0
- data/lib/jimmy/schema/operators.rb +96 -0
- data/lib/jimmy/schema/string.rb +44 -0
- data/lib/jimmy/schema_with_uri.rb +53 -0
- data/lib/jimmy/schemer_factory.rb +65 -0
- data/lib/jimmy/version.rb +3 -1
- data/schema07.json +172 -0
- metadata +50 -101
- data/circle.yml +0 -11
- data/lib/jimmy/combination.rb +0 -34
- data/lib/jimmy/definitions.rb +0 -38
- data/lib/jimmy/domain.rb +0 -111
- data/lib/jimmy/link.rb +0 -93
- data/lib/jimmy/reference.rb +0 -39
- data/lib/jimmy/schema_creation.rb +0 -121
- data/lib/jimmy/schema_type.rb +0 -100
- data/lib/jimmy/schema_types.rb +0 -42
- data/lib/jimmy/schema_types/array.rb +0 -30
- data/lib/jimmy/schema_types/boolean.rb +0 -6
- data/lib/jimmy/schema_types/integer.rb +0 -8
- data/lib/jimmy/schema_types/null.rb +0 -6
- data/lib/jimmy/schema_types/number.rb +0 -34
- data/lib/jimmy/schema_types/object.rb +0 -45
- data/lib/jimmy/schema_types/string.rb +0 -40
- data/lib/jimmy/symbol_array.rb +0 -17
- data/lib/jimmy/transform_keys.rb +0 -39
- data/lib/jimmy/type_reference.rb +0 -14
- data/lib/jimmy/validation_error.rb +0 -20
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jimmy
|
4
|
+
module Declaration
|
5
|
+
FORMATS = Set.new(
|
6
|
+
%w[
|
7
|
+
date-time
|
8
|
+
date
|
9
|
+
time
|
10
|
+
email
|
11
|
+
idn-email
|
12
|
+
hostname
|
13
|
+
idn-hostname
|
14
|
+
ipv4
|
15
|
+
ipv6
|
16
|
+
uri
|
17
|
+
uri-reference
|
18
|
+
iri
|
19
|
+
iri-reference
|
20
|
+
uri-template
|
21
|
+
json-pointer
|
22
|
+
relative-json-pointer
|
23
|
+
regex
|
24
|
+
]
|
25
|
+
).freeze
|
26
|
+
|
27
|
+
# Set the pattern for a string value.
|
28
|
+
# @param [Regexp] expression The pattern for a string value. Cannot include
|
29
|
+
# any options such as +/i+.
|
30
|
+
# @return [self] self, for chaining
|
31
|
+
def pattern(expression)
|
32
|
+
assert_regexp expression
|
33
|
+
string
|
34
|
+
set pattern: expression.source
|
35
|
+
end
|
36
|
+
|
37
|
+
FORMATS.each do |format|
|
38
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
39
|
+
def #{format.gsub '-', '_'}
|
40
|
+
string
|
41
|
+
format '#{format}'
|
42
|
+
end
|
43
|
+
RUBY
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generated by `rake yard`. Do not modify anything below here.
|
47
|
+
#
|
48
|
+
# @!method date_time()
|
49
|
+
# Validate a string with format "date-time".
|
50
|
+
# @return [Schema]
|
51
|
+
# @!method date()
|
52
|
+
# Validate a string with format "date".
|
53
|
+
# @return [Schema]
|
54
|
+
# @!method time()
|
55
|
+
# Validate a string with format "time".
|
56
|
+
# @return [Schema]
|
57
|
+
# @!method email()
|
58
|
+
# Validate a string with format "email".
|
59
|
+
# @return [Schema]
|
60
|
+
# @!method idn_email()
|
61
|
+
# Validate a string with format "idn-email".
|
62
|
+
# @return [Schema]
|
63
|
+
# @!method hostname()
|
64
|
+
# Validate a string with format "hostname".
|
65
|
+
# @return [Schema]
|
66
|
+
# @!method idn_hostname()
|
67
|
+
# Validate a string with format "idn-hostname".
|
68
|
+
# @return [Schema]
|
69
|
+
# @!method ipv4()
|
70
|
+
# Validate a string with format "ipv4".
|
71
|
+
# @return [Schema]
|
72
|
+
# @!method ipv6()
|
73
|
+
# Validate a string with format "ipv6".
|
74
|
+
# @return [Schema]
|
75
|
+
# @!method uri()
|
76
|
+
# Validate a string with format "uri".
|
77
|
+
# @return [Schema]
|
78
|
+
# @!method uri_reference()
|
79
|
+
# Validate a string with format "uri-reference".
|
80
|
+
# @return [Schema]
|
81
|
+
# @!method iri()
|
82
|
+
# Validate a string with format "iri".
|
83
|
+
# @return [Schema]
|
84
|
+
# @!method iri_reference()
|
85
|
+
# Validate a string with format "iri-reference".
|
86
|
+
# @return [Schema]
|
87
|
+
# @!method uri_template()
|
88
|
+
# Validate a string with format "uri-template".
|
89
|
+
# @return [Schema]
|
90
|
+
# @!method json_pointer()
|
91
|
+
# Validate a string with format "json-pointer".
|
92
|
+
# @return [Schema]
|
93
|
+
# @!method relative_json_pointer()
|
94
|
+
# Validate a string with format "relative-json-pointer".
|
95
|
+
# @return [Schema]
|
96
|
+
# @!method regex()
|
97
|
+
# Validate a string with format "regex".
|
98
|
+
# @return [Schema]
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jimmy
|
4
|
+
module Declaration
|
5
|
+
# Acceptable values for +#type+.
|
6
|
+
SIMPLE_TYPES =
|
7
|
+
Set.new(%w[array boolean integer null number object string]).freeze
|
8
|
+
|
9
|
+
# Set the type(s) of the schema.
|
10
|
+
# @param [String, Array<String>] types The type(s) of the schema.
|
11
|
+
# @return [self] self, for chaining
|
12
|
+
def type(*types)
|
13
|
+
types = types.flatten
|
14
|
+
types.each &method(:assert_simple_type)
|
15
|
+
assert_array types, unique: true, minimum: 1
|
16
|
+
types = Array(get('type') { [] }) | types.flatten
|
17
|
+
types = types.first if types.one?
|
18
|
+
set type: types
|
19
|
+
end
|
20
|
+
|
21
|
+
alias types type
|
22
|
+
|
23
|
+
SIMPLE_TYPES.each do |type|
|
24
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
25
|
+
def #{type}
|
26
|
+
type '#{type}'
|
27
|
+
end
|
28
|
+
RUBY
|
29
|
+
end
|
30
|
+
|
31
|
+
alias nullable null
|
32
|
+
|
33
|
+
# Generated by `rake yard`. Do not modify anything below here.
|
34
|
+
#
|
35
|
+
# @!method array()
|
36
|
+
# Make the schema allow type "array".
|
37
|
+
# @return [Schema]
|
38
|
+
# @!method boolean()
|
39
|
+
# Make the schema allow type "boolean".
|
40
|
+
# @return [Schema]
|
41
|
+
# @!method integer()
|
42
|
+
# Make the schema allow type "integer".
|
43
|
+
# @return [Schema]
|
44
|
+
# @!method null()
|
45
|
+
# Make the schema allow type "null".
|
46
|
+
# @return [Schema]
|
47
|
+
# @!method number()
|
48
|
+
# Make the schema allow type "number".
|
49
|
+
# @return [Schema]
|
50
|
+
# @!method object()
|
51
|
+
# Make the schema allow type "object".
|
52
|
+
# @return [Schema]
|
53
|
+
# @!method string()
|
54
|
+
# Make the schema allow type "string".
|
55
|
+
# @return [Schema]
|
56
|
+
end
|
57
|
+
end
|
data/lib/jimmy/error.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jimmy/index'
|
4
|
+
require 'jimmy/loaders/ruby'
|
5
|
+
require 'jimmy/loaders/json'
|
6
|
+
require 'jimmy/schema_with_uri'
|
7
|
+
require 'jimmy/schemer_factory'
|
8
|
+
|
9
|
+
module Jimmy
|
10
|
+
# Maps a directory of files to schemas with URIs. Can be used as a URI
|
11
|
+
# resolver with {SchemerFactory}.
|
12
|
+
#
|
13
|
+
# Given +~/schemas/user.rb+ as a schema file:
|
14
|
+
#
|
15
|
+
# file_map = FileMap.new('~/schemas', 'http://example.com/schemas/', suffix: '.json')
|
16
|
+
# file_map.resolve('user.json') # => SchemaWithURI
|
17
|
+
#
|
18
|
+
# Calling {SchemaWithURI#as_json} on the above will include the full ID
|
19
|
+
# +http://example.com/schemas/user.json#+ in the +$id+ key.
|
20
|
+
#
|
21
|
+
# Including the suffix in the call to {FileMap#resolve} is optional.
|
22
|
+
#
|
23
|
+
# If you initialize a {FileMap} with +live: true+, files will be loaded
|
24
|
+
# lazily and repeatedly, every time {FileMap#resolve} or {FileMap#index} is
|
25
|
+
# called. This is intended as convenience for development environments.
|
26
|
+
class FileMap
|
27
|
+
DEFAULT_LOADERS = {
|
28
|
+
'rb' => Loaders::Ruby,
|
29
|
+
'json' => Loaders::JSON
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
# @param [Pathname, String] base_dir The directory that should map to the
|
33
|
+
# given URI.
|
34
|
+
# @param [Json::URI, URI, String] base_uri The URI that should resolve to
|
35
|
+
# the given directory. If omitted, a +file://+ URI will be used for the
|
36
|
+
# absolute path of the given directory.
|
37
|
+
# @param [true, false] live When true, schemas are not stored in memory, and
|
38
|
+
# are instead live-reloaded on demand. Typically only useful for
|
39
|
+
# development environments.
|
40
|
+
# @param [Hash{String => #call}] loaders Loaders for one or more file types.
|
41
|
+
# By default, +.json+ files are parsed as-is, and +.rb+ files are
|
42
|
+
# evaluated in the context of a {Loaders::Ruby} instance, which
|
43
|
+
# exposes the methods in {Macros}.
|
44
|
+
# @param [String] suffix Optional suffix that will be appended to each
|
45
|
+
# schema ID. This can be set to +".json"+ if, for example, you want your
|
46
|
+
# schemas to have +.json+ suffixes when you serve them over HTTP.
|
47
|
+
def initialize(
|
48
|
+
base_dir,
|
49
|
+
base_uri = nil,
|
50
|
+
live: false,
|
51
|
+
loaders: DEFAULT_LOADERS,
|
52
|
+
suffix: ''
|
53
|
+
)
|
54
|
+
@dir = Pathname(base_dir).realpath
|
55
|
+
unless @dir.directory? && @dir.readable?
|
56
|
+
raise Error::BadArgument, 'Expected a readable directory'
|
57
|
+
end
|
58
|
+
|
59
|
+
base_uri ||= uri_for_dir
|
60
|
+
@uri = Json::URI.new(base_uri.to_s, container: true)
|
61
|
+
|
62
|
+
@live = live
|
63
|
+
@loaders = loaders
|
64
|
+
@suffix = suffix
|
65
|
+
|
66
|
+
index unless live
|
67
|
+
end
|
68
|
+
|
69
|
+
# Given a URI, either absolute or relative to the file map's base URI,
|
70
|
+
# returns a {SchemaWithURI} if a matching schema is found.
|
71
|
+
# @param [Json::URI, URI, String] uri
|
72
|
+
# @return [Jimmy::SchemaWithURI, nil]
|
73
|
+
def resolve(uri)
|
74
|
+
uri = make_child_uri(uri)
|
75
|
+
absolute_uri = @uri + uri
|
76
|
+
|
77
|
+
return index.resolve(absolute_uri) unless live?
|
78
|
+
|
79
|
+
schema = load_file(path_for_uri uri)&.get_fragment(uri.fragment)
|
80
|
+
schema && SchemaWithURI.new(absolute_uri, schema)
|
81
|
+
end
|
82
|
+
|
83
|
+
alias [] resolve
|
84
|
+
|
85
|
+
# Get an index of all schemas in the file map's directory.
|
86
|
+
# @return [Jimmy::Index]
|
87
|
+
def index
|
88
|
+
return @index if @index
|
89
|
+
|
90
|
+
index = build_index
|
91
|
+
@index = index unless live?
|
92
|
+
|
93
|
+
index
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns true if live-reloading is enabled.
|
97
|
+
# @return [true, false]
|
98
|
+
def live?
|
99
|
+
@live
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def load_file(file_base)
|
105
|
+
@loaders.each do |ext, loader|
|
106
|
+
file = Pathname("#{file_base}.#{ext}")
|
107
|
+
next unless file.file?
|
108
|
+
return loader.call(file) if file.readable?
|
109
|
+
|
110
|
+
warn "Jimmy cannot read #{file}"
|
111
|
+
end
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def uri_for_dir
|
116
|
+
Json::URI.new 'file://' + fs_to_rfc3968(@dir)
|
117
|
+
end
|
118
|
+
|
119
|
+
def path_for_uri(uri)
|
120
|
+
path = uri.path[0..(-@suffix.length - 1)]
|
121
|
+
parts = path.split('/').map(&URI.method(:decode_www_form_component))
|
122
|
+
@dir.join(*parts)
|
123
|
+
end
|
124
|
+
|
125
|
+
def make_child_uri(uri)
|
126
|
+
uri = @uri.route_to(@uri + uri)
|
127
|
+
|
128
|
+
unless uri.host.nil? && !uri.path.match?(%r{\A(\.\.|/)})
|
129
|
+
raise Error::BadArgument, 'The given URI is outside this FileMap'
|
130
|
+
end
|
131
|
+
|
132
|
+
uri.path += @suffix unless uri.path.end_with? @suffix
|
133
|
+
uri
|
134
|
+
end
|
135
|
+
|
136
|
+
def build_index
|
137
|
+
index = Index.new
|
138
|
+
|
139
|
+
Dir[@dir + "**/*.{#{@loaders.keys.join ','}}"].sort.each do |file|
|
140
|
+
relative_uri = relative_uri_for_file(file)
|
141
|
+
uri = @uri + relative_uri
|
142
|
+
|
143
|
+
index[uri] = @loaders.fetch(File.extname(file)[1..]).call(file)
|
144
|
+
end
|
145
|
+
|
146
|
+
index
|
147
|
+
end
|
148
|
+
|
149
|
+
def relative_uri_for_file(file)
|
150
|
+
path = Pathname(file)
|
151
|
+
.relative_path_from(@dir)
|
152
|
+
.to_s
|
153
|
+
.sub(/\.[^.]+\z/, '')
|
154
|
+
|
155
|
+
Json::URI.new fs_to_rfc3968(path) + @suffix
|
156
|
+
end
|
157
|
+
|
158
|
+
def fs_to_rfc3968(path)
|
159
|
+
path
|
160
|
+
.to_s
|
161
|
+
.split(File::SEPARATOR)
|
162
|
+
.map { |p| URI.encode_www_form_component p.sub(/:\z/, '') }
|
163
|
+
.join('/')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/jimmy/index.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jimmy/schema'
|
4
|
+
require 'jimmy/schema_with_uri'
|
5
|
+
|
6
|
+
module Jimmy
|
7
|
+
# Represents an in-memory collection of schemas
|
8
|
+
class Index
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
def initialize # rubocop:disable Style/DocumentationMethod
|
12
|
+
@by_uri = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Json::URI, URI, String] uri
|
16
|
+
# @return [Jimmy::SchemaWithURI, nil]
|
17
|
+
def resolve(uri)
|
18
|
+
uri = Json::URI.new(uri)
|
19
|
+
raise Error::BadArgument, 'Cannot resolve relative URIs' if uri.relative?
|
20
|
+
|
21
|
+
return @by_uri[uri] if @by_uri.key? uri
|
22
|
+
return if uri.pointer.empty?
|
23
|
+
|
24
|
+
resolve(uri / -1)&.resolve uri
|
25
|
+
end
|
26
|
+
|
27
|
+
alias [] resolve
|
28
|
+
|
29
|
+
# @param [Json::URI, URI, String] uri
|
30
|
+
# @param [Jimmy::Schema] schema
|
31
|
+
# @return [self] self, for chaining
|
32
|
+
def add(uri, schema)
|
33
|
+
uri = Json::URI.new(uri)
|
34
|
+
raise Error::BadArgument, 'Expected a schema' unless schema.is_a? Schema
|
35
|
+
raise Error::BadArgument, 'Cannot index relative URIs' if uri.relative?
|
36
|
+
|
37
|
+
push SchemaWithURI.new(uri, schema)
|
38
|
+
end
|
39
|
+
|
40
|
+
alias []= add
|
41
|
+
|
42
|
+
# @param [Array<Jimmy::SchemaWithURI>] schemas_with_uris
|
43
|
+
# @return [self]
|
44
|
+
def push(*schemas_with_uris)
|
45
|
+
schemas_with_uris.each do |schema_with_uri|
|
46
|
+
unless schema_with_uri.is_a? SchemaWithURI
|
47
|
+
raise Error::BadArgument, 'Expected a SchemaWithURI'
|
48
|
+
end
|
49
|
+
|
50
|
+
@by_uri[schema_with_uri.uri] = schema_with_uri
|
51
|
+
end
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
alias << push
|
56
|
+
|
57
|
+
# @return [Array<Json::URI>]
|
58
|
+
def uris
|
59
|
+
@by_uri.keys
|
60
|
+
end
|
61
|
+
|
62
|
+
alias keys uris
|
63
|
+
|
64
|
+
# @param [Json::URI, URI, String] uri
|
65
|
+
# @return [true, false]
|
66
|
+
def uri?(uri)
|
67
|
+
!resolve(uri).nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
alias key? uri?
|
71
|
+
alias has_key? key?
|
72
|
+
|
73
|
+
# @yieldparam [Jimmy::SchemaWithURI] schema_with_uri
|
74
|
+
def each(&block)
|
75
|
+
@by_uri.each_value &block
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jimmy/json/collection'
|
4
|
+
|
5
|
+
module Jimmy
|
6
|
+
module Json
|
7
|
+
# Represents an array in a JSON schema.
|
8
|
+
class Array
|
9
|
+
include Collection
|
10
|
+
|
11
|
+
KEY_PATTERN = /\A(?:\d|[1-9]\d+)\z/.freeze
|
12
|
+
|
13
|
+
# @param [Array, ::Array, Set] array Items to be included in the array.
|
14
|
+
def initialize(array = [])
|
15
|
+
super()
|
16
|
+
@members = []
|
17
|
+
concat array
|
18
|
+
end
|
19
|
+
|
20
|
+
# Append items in +array+ to self.
|
21
|
+
# @param [Array, ::Array, Set] array
|
22
|
+
# @return [self]
|
23
|
+
def concat(array)
|
24
|
+
array = array.to_a if array.is_a? Set
|
25
|
+
push *array
|
26
|
+
end
|
27
|
+
|
28
|
+
# Add one or more items to self.
|
29
|
+
# @param [Array] members Things to add.
|
30
|
+
# @return [self]
|
31
|
+
def push(*members)
|
32
|
+
@members.concat members.map(&method(:cast_value))
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
alias << push
|
37
|
+
|
38
|
+
# Iterate over items in the array. If a block with a single argument is
|
39
|
+
# given, only values will be yielded. Otherwise, indexes and values will
|
40
|
+
# be yielded.
|
41
|
+
# @yieldparam [Integer] index The index of each item.
|
42
|
+
# @yieldparam [Object] member Each item.
|
43
|
+
# @return [Enumerable, self] If no block is given, an {::Enumerable} is
|
44
|
+
# returned. Otherwise, +self+ is returned.
|
45
|
+
def each(&block)
|
46
|
+
return enum_for :each unless block
|
47
|
+
|
48
|
+
if block.arity == 1
|
49
|
+
@members.each { |member| yield member }
|
50
|
+
else
|
51
|
+
@members.each.with_index { |member, i| yield i, member }
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Integer] The length of the array.
|
57
|
+
def length
|
58
|
+
@members.length
|
59
|
+
end
|
60
|
+
|
61
|
+
# Dig into the array.
|
62
|
+
# @param [Integer] key Index of the item to be dug or returned.
|
63
|
+
# @param [Array<String, Integer>] rest Keys or indexes to be passed to
|
64
|
+
# resolved hashes/arrays.
|
65
|
+
def dig(key, *rest)
|
66
|
+
key = key.to_i if key.is_a?(String) && key.match?(KEY_PATTERN)
|
67
|
+
super key, *rest
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Array] Get a regular array.
|
71
|
+
def to_a
|
72
|
+
@members.dup
|
73
|
+
end
|
74
|
+
|
75
|
+
alias count length
|
76
|
+
alias size length
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def export_pairs(pairs)
|
81
|
+
pairs.map &:last
|
82
|
+
end
|
83
|
+
|
84
|
+
def cast_key(key)
|
85
|
+
unless key.is_a? Integer
|
86
|
+
raise Error::WrongType, "Invalid array index of type #{key.class}"
|
87
|
+
end
|
88
|
+
|
89
|
+
key
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|