scorpio 0.0.4 → 0.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +37 -14
- data/bin/documents_to_yml.rb +26 -0
- data/documents/swagger.io/v2/schema.json +1607 -0
- data/documents/swagger.io/v2/schema.yml +1079 -0
- data/documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest +684 -0
- data/{getRest.yml → documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest.yml} +34 -72
- data/lib/scorpio.rb +30 -1
- data/lib/scorpio/google_api_document.rb +232 -0
- data/lib/scorpio/json-schema-fragments.rb +191 -0
- data/lib/scorpio/json.rb +5 -0
- data/lib/scorpio/json/node.rb +214 -0
- data/lib/scorpio/model.rb +178 -91
- data/lib/scorpio/openapi.rb +81 -0
- data/lib/scorpio/schema.rb +133 -0
- data/lib/scorpio/schema_object_base.rb +227 -0
- data/lib/scorpio/typelike_modules.rb +52 -0
- data/lib/scorpio/version.rb +1 -1
- data/scorpio.gemspec +2 -1
- metadata +20 -8
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'scorpio/schema_object_base'
|
2
|
+
|
3
|
+
module Scorpio
|
4
|
+
module OpenAPI
|
5
|
+
openapi_schema_doc = ::JSON.parse(Scorpio.root.join('documents/swagger.io/v2/schema.json').read)
|
6
|
+
openapi_class = proc do |*key|
|
7
|
+
Scorpio.class_for_schema(Scorpio::JSON::Node.new_by_type(openapi_schema_doc, key))
|
8
|
+
end
|
9
|
+
|
10
|
+
Document = openapi_class.call()
|
11
|
+
|
12
|
+
# naming these is not strictly necessary, but is nice to have.
|
13
|
+
# generated: puts Scorpio::OpenAPI::Document.schema_document['definitions'].select { |k,v| ['object', nil].include?(v['type']) }.keys.map { |k| "#{k[0].upcase}#{k[1..-1]} = openapi_class.call('definitions', '#{k}')" }
|
14
|
+
Info = openapi_class.call('definitions', 'info')
|
15
|
+
Contact = openapi_class.call('definitions', 'contact')
|
16
|
+
License = openapi_class.call('definitions', 'license')
|
17
|
+
Paths = openapi_class.call('definitions', 'paths')
|
18
|
+
Definitions = openapi_class.call('definitions', 'definitions')
|
19
|
+
ParameterDefinitions = openapi_class.call('definitions', 'parameterDefinitions')
|
20
|
+
ResponseDefinitions = openapi_class.call('definitions', 'responseDefinitions')
|
21
|
+
ExternalDocs = openapi_class.call('definitions', 'externalDocs')
|
22
|
+
Examples = openapi_class.call('definitions', 'examples')
|
23
|
+
Operation = openapi_class.call('definitions', 'operation')
|
24
|
+
PathItem = openapi_class.call('definitions', 'pathItem')
|
25
|
+
Responses = openapi_class.call('definitions', 'responses')
|
26
|
+
ResponseValue = openapi_class.call('definitions', 'responseValue')
|
27
|
+
Response = openapi_class.call('definitions', 'response')
|
28
|
+
Headers = openapi_class.call('definitions', 'headers')
|
29
|
+
Header = openapi_class.call('definitions', 'header')
|
30
|
+
VendorExtension = openapi_class.call('definitions', 'vendorExtension')
|
31
|
+
BodyParameter = openapi_class.call('definitions', 'bodyParameter')
|
32
|
+
HeaderParameterSubSchema = openapi_class.call('definitions', 'headerParameterSubSchema')
|
33
|
+
QueryParameterSubSchema = openapi_class.call('definitions', 'queryParameterSubSchema')
|
34
|
+
FormDataParameterSubSchema = openapi_class.call('definitions', 'formDataParameterSubSchema')
|
35
|
+
PathParameterSubSchema = openapi_class.call('definitions', 'pathParameterSubSchema')
|
36
|
+
NonBodyParameter = openapi_class.call('definitions', 'nonBodyParameter')
|
37
|
+
Parameter = openapi_class.call('definitions', 'parameter')
|
38
|
+
Schema = openapi_class.call('definitions', 'schema')
|
39
|
+
FileSchema = openapi_class.call('definitions', 'fileSchema')
|
40
|
+
PrimitivesItems = openapi_class.call('definitions', 'primitivesItems')
|
41
|
+
SecurityRequirement = openapi_class.call('definitions', 'securityRequirement')
|
42
|
+
Xml = openapi_class.call('definitions', 'xml')
|
43
|
+
Tag = openapi_class.call('definitions', 'tag')
|
44
|
+
SecurityDefinitions = openapi_class.call('definitions', 'securityDefinitions')
|
45
|
+
BasicAuthenticationSecurity = openapi_class.call('definitions', 'basicAuthenticationSecurity')
|
46
|
+
ApiKeySecurity = openapi_class.call('definitions', 'apiKeySecurity')
|
47
|
+
Oauth2ImplicitSecurity = openapi_class.call('definitions', 'oauth2ImplicitSecurity')
|
48
|
+
Oauth2PasswordSecurity = openapi_class.call('definitions', 'oauth2PasswordSecurity')
|
49
|
+
Oauth2ApplicationSecurity = openapi_class.call('definitions', 'oauth2ApplicationSecurity')
|
50
|
+
Oauth2AccessCodeSecurity = openapi_class.call('definitions', 'oauth2AccessCodeSecurity')
|
51
|
+
Oauth2Scopes = openapi_class.call('definitions', 'oauth2Scopes')
|
52
|
+
Title = openapi_class.call('definitions', 'title')
|
53
|
+
Description = openapi_class.call('definitions', 'description')
|
54
|
+
Default = openapi_class.call('definitions', 'default')
|
55
|
+
MultipleOf = openapi_class.call('definitions', 'multipleOf')
|
56
|
+
Maximum = openapi_class.call('definitions', 'maximum')
|
57
|
+
ExclusiveMaximum = openapi_class.call('definitions', 'exclusiveMaximum')
|
58
|
+
Minimum = openapi_class.call('definitions', 'minimum')
|
59
|
+
ExclusiveMinimum = openapi_class.call('definitions', 'exclusiveMinimum')
|
60
|
+
MaxLength = openapi_class.call('definitions', 'maxLength')
|
61
|
+
MinLength = openapi_class.call('definitions', 'minLength')
|
62
|
+
Pattern = openapi_class.call('definitions', 'pattern')
|
63
|
+
MaxItems = openapi_class.call('definitions', 'maxItems')
|
64
|
+
MinItems = openapi_class.call('definitions', 'minItems')
|
65
|
+
UniqueItems = openapi_class.call('definitions', 'uniqueItems')
|
66
|
+
Enum = openapi_class.call('definitions', 'enum')
|
67
|
+
JsonReference = openapi_class.call('definitions', 'jsonReference')
|
68
|
+
|
69
|
+
class Operation
|
70
|
+
attr_accessor :path
|
71
|
+
attr_accessor :http_method
|
72
|
+
|
73
|
+
# there should only be one body parameter; this returns it
|
74
|
+
def body_parameter
|
75
|
+
(parameters || []).detect do |parameter|
|
76
|
+
parameter['in'] == 'body'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Scorpio
|
2
|
+
class Schema
|
3
|
+
def initialize(schema_node)
|
4
|
+
@schema_node = schema_node
|
5
|
+
end
|
6
|
+
attr_reader :schema_node
|
7
|
+
|
8
|
+
def subschema_for_property(property_name)
|
9
|
+
if schema_node['properties'].respond_to?(:to_hash) && schema_node['properties'][property_name].respond_to?(:to_hash)
|
10
|
+
self.class.new(schema_node['properties'][property_name].deref)
|
11
|
+
else
|
12
|
+
if schema_node['patternProperties'].respond_to?(:to_hash)
|
13
|
+
_, pattern_schema_node = schema_node['patternProperties'].detect do |pattern, _|
|
14
|
+
property_name =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if pattern_schema_node
|
18
|
+
self.class.new(pattern_schema_node.deref)
|
19
|
+
else
|
20
|
+
if schema_node['additionalProperties'].is_a?(Scorpio::JSON::Node)
|
21
|
+
self.class.new(schema_node['additionalProperties'].deref)
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def match_to_object(object)
|
30
|
+
# matching oneOf is good here. one schema for one object.
|
31
|
+
# matching anyOf is okay. there could be more than one schema matched. it's often just one. if more
|
32
|
+
# than one is a match, the problems of allOf occur.
|
33
|
+
# matching allOf is questionable. all of the schemas must be matched but we just return the first match.
|
34
|
+
# there isn't really a better answer with the current implementation. merging the schemas together
|
35
|
+
# is a thought but is not practical.
|
36
|
+
%w(oneOf allOf anyOf).select { |k| schema_node[k].respond_to?(:to_ary) }.each do |someof_key|
|
37
|
+
schema_node[someof_key].map(&:deref).map do |someof_node|
|
38
|
+
someof_schema = self.class.new(someof_node)
|
39
|
+
if someof_schema.validate(object)
|
40
|
+
return someof_schema.match_to_object(object)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
return self
|
45
|
+
end
|
46
|
+
|
47
|
+
def subschema_for_index(index)
|
48
|
+
if schema_node['items'].is_a?(Scorpio::JSON::ArrayNode)
|
49
|
+
if index < schema_node['items'].size
|
50
|
+
self.class.new(schema_node['items'][index].deref)
|
51
|
+
elsif schema_node['additionalItems'].is_a?(Node)
|
52
|
+
self.class.new(schema_node['additionalItems'].deref)
|
53
|
+
end
|
54
|
+
elsif schema_node['items'].is_a?(Scorpio::JSON::Node)
|
55
|
+
self.class.new(schema_node['items'].deref)
|
56
|
+
else
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def describes_array?
|
62
|
+
schema_node['type'] == 'array' ||
|
63
|
+
schema_node['items'] ||
|
64
|
+
schema_node['additionalItems'] ||
|
65
|
+
schema_node['default'].respond_to?(:to_ary) || # TODO make sure this is right
|
66
|
+
(schema_node['enum'].respond_to?(:to_ary) && schema_node['enum'].all? { |enum| enum.respond_to?(:to_ary) }) ||
|
67
|
+
schema_node['maxItems'] ||
|
68
|
+
schema_node['minItems'] ||
|
69
|
+
schema_node.key?('uniqueItems') ||
|
70
|
+
schema_node['oneOf'].respond_to?(:to_ary) &&
|
71
|
+
schema_node['oneOf'].all? { |someof_node| self.class.new(someof_node).describes_array? } ||
|
72
|
+
schema_node['allOf'].respond_to?(:to_ary) &&
|
73
|
+
schema_node['allOf'].all? { |someof_node| self.class.new(someof_node).describes_array? } ||
|
74
|
+
schema_node['anyOf'].respond_to?(:to_ary) &&
|
75
|
+
schema_node['anyOf'].all? { |someof_node| self.class.new(someof_node).describes_array? }
|
76
|
+
end
|
77
|
+
def describes_hash?
|
78
|
+
schema_node['type'] == 'object' ||
|
79
|
+
schema_node['required'].respond_to?(:to_ary) ||
|
80
|
+
schema_node['properties'].respond_to?(:to_hash) ||
|
81
|
+
schema_node['additionalProperties'] ||
|
82
|
+
schema_node['patternProperties'] ||
|
83
|
+
schema_node['default'].respond_to?(:to_hash) ||
|
84
|
+
(schema_node['enum'].respond_to?(:to_ary) && schema_node['enum'].all? { |enum| enum.respond_to?(:to_hash) }) ||
|
85
|
+
schema_node['oneOf'].respond_to?(:to_ary) &&
|
86
|
+
schema_node['oneOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? } ||
|
87
|
+
schema_node['allOf'].respond_to?(:to_ary) &&
|
88
|
+
schema_node['allOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? } ||
|
89
|
+
schema_node['anyOf'].respond_to?(:to_ary) &&
|
90
|
+
schema_node['anyOf'].all? { |someof_node| self.class.new(someof_node).describes_hash? }
|
91
|
+
end
|
92
|
+
|
93
|
+
def described_hash_property_names
|
94
|
+
Set.new.tap do |property_names|
|
95
|
+
if schema_node['properties'].respond_to?(:to_hash)
|
96
|
+
property_names.merge(schema_node['properties'].keys)
|
97
|
+
end
|
98
|
+
if schema_node['required'].respond_to?(:to_ary)
|
99
|
+
property_names.merge(schema_node['required'].to_ary)
|
100
|
+
end
|
101
|
+
# we _could_ look at the properties of 'default' and each 'enum' but ... nah.
|
102
|
+
# we should look at dependencies (TODO).
|
103
|
+
%w(oneOf allOf anyOf).select { |k| schema_node[k].respond_to?(:to_ary) }.each do |schemas_key|
|
104
|
+
schema_node[schemas_key].map(&:deref).map do |someof_node|
|
105
|
+
property_names.merge(self.class.new(someof_node).described_hash_property_names)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def fully_validate(object)
|
112
|
+
::JSON::Validator.fully_validate(schema_node.document, object_to_content(object), fragment: schema_node.fragment)
|
113
|
+
end
|
114
|
+
def validate(object)
|
115
|
+
::JSON::Validator.validate(schema_node.document, object_to_content(object), fragment: schema_node.fragment)
|
116
|
+
end
|
117
|
+
def validate!(object)
|
118
|
+
::JSON::Validator.validate!(schema_node.document, object_to_content(object), fragment: schema_node.fragment)
|
119
|
+
end
|
120
|
+
|
121
|
+
def fingerprint
|
122
|
+
{class: self.class, schema_node: schema_node}
|
123
|
+
end
|
124
|
+
include FingerprintHash
|
125
|
+
|
126
|
+
private
|
127
|
+
def object_to_content(object)
|
128
|
+
object = object.object if object.is_a?(Scorpio::SchemaObjectBase)
|
129
|
+
object = object.content if object.is_a?(Scorpio::JSON::Node)
|
130
|
+
object
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'scorpio/typelike_modules'
|
3
|
+
|
4
|
+
module Scorpio
|
5
|
+
# base class for representing an instance of an object described by a schema
|
6
|
+
class SchemaObjectBase
|
7
|
+
def initialize(object)
|
8
|
+
if object.is_a?(Scorpio::JSON::Node)
|
9
|
+
@object = object
|
10
|
+
else
|
11
|
+
@object = Scorpio::JSON::Node.new_by_type(object, [])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :object
|
16
|
+
|
17
|
+
def fragment
|
18
|
+
object.fragment
|
19
|
+
end
|
20
|
+
|
21
|
+
def fully_validate
|
22
|
+
module_schema.fully_validate(object)
|
23
|
+
end
|
24
|
+
def validate
|
25
|
+
module_schema.validate(object)
|
26
|
+
end
|
27
|
+
def validate!
|
28
|
+
module_schema.validate!(object)
|
29
|
+
end
|
30
|
+
def inspect
|
31
|
+
"\#<#{self.class.name} #{object.inspect}>"
|
32
|
+
end
|
33
|
+
def pretty_print(q)
|
34
|
+
q.instance_exec(self) do |obj|
|
35
|
+
text "\#<#{obj.class.name}"
|
36
|
+
group_sub {
|
37
|
+
nest(2) {
|
38
|
+
breakable ' '
|
39
|
+
pp obj.object
|
40
|
+
}
|
41
|
+
}
|
42
|
+
breakable ''
|
43
|
+
text '>'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def fingerprint
|
48
|
+
{class: self.class, object: object}
|
49
|
+
end
|
50
|
+
include FingerprintHash
|
51
|
+
end
|
52
|
+
|
53
|
+
CLASS_FOR_SCHEMA = Hash.new do |h, schema_node_|
|
54
|
+
h[schema_node_] = Class.new(SchemaObjectBase).instance_exec(schema_node_) do |schema_node|
|
55
|
+
prepend(Scorpio.module_for_schema(schema_node))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.class_for_schema(schema_node)
|
60
|
+
schema_node = schema_node.object if schema_node.is_a?(Scorpio::SchemaObjectBase)
|
61
|
+
CLASS_FOR_SCHEMA[schema_node.deref]
|
62
|
+
end
|
63
|
+
|
64
|
+
# this invokes methods of type-like modules (Arraylike, Hashlike) but only if the #object
|
65
|
+
# is of the expected class. since the object may be anything - it will just not be a valid
|
66
|
+
# instance of its schema - we can't assume that the methods on the Xlike modules will work
|
67
|
+
# (e.g. trying to call #each_index on an #object that's not array-like)
|
68
|
+
module SchemaObjectMightBeLike
|
69
|
+
def inspect(*a, &b)
|
70
|
+
if object.is_a?(expected_object_class)
|
71
|
+
super
|
72
|
+
else
|
73
|
+
SchemaObjectBase.instance_method(:inspect).bind(self).call(*a, &b)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def pretty_print(*a, &b)
|
77
|
+
if object.is_a?(expected_object_class)
|
78
|
+
super
|
79
|
+
else
|
80
|
+
SchemaObjectBase.instance_method(:pretty_print).bind(self).call(*a, &b)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
module SchemaObjectBaseHash
|
85
|
+
def expected_object_class
|
86
|
+
Scorpio::JSON::HashNode
|
87
|
+
end
|
88
|
+
|
89
|
+
# Hash methods
|
90
|
+
def each
|
91
|
+
return to_enum(__method__) { object.size } unless block_given?
|
92
|
+
object.each_key { |k| yield(k, self[k]) }
|
93
|
+
self
|
94
|
+
end
|
95
|
+
include Enumerable
|
96
|
+
|
97
|
+
def to_hash
|
98
|
+
inject({}) { |h, (k, v)| h[k] = v; h }
|
99
|
+
end
|
100
|
+
|
101
|
+
include Hashlike
|
102
|
+
include SchemaObjectMightBeLike
|
103
|
+
|
104
|
+
# hash methods - define only those which do not modify the hash.
|
105
|
+
|
106
|
+
# methods that don't look at the value; can skip the overhead of #[]
|
107
|
+
key_methods = %w(each_key empty? include? has_key? key key? keys length member? size)
|
108
|
+
key_methods.each do |method_name|
|
109
|
+
define_method(method_name) { |*a, &b| object.public_send(method_name, *a, &b) }
|
110
|
+
end
|
111
|
+
|
112
|
+
# methods which use key and value
|
113
|
+
hash_methods = %w(compact each_pair each_value fetch fetch_values has_value? invert
|
114
|
+
rassoc reject select to_h transform_values value? values values_at)
|
115
|
+
hash_methods.each do |method_name|
|
116
|
+
define_method(method_name) { |*a, &b| to_hash.public_send(method_name, *a, &b) }
|
117
|
+
end
|
118
|
+
|
119
|
+
def [](property_name_)
|
120
|
+
@object_mapped ||= Hash.new do |hash, property_name|
|
121
|
+
hash[property_name] = begin
|
122
|
+
property_schema = module_schema.subschema_for_property(property_name)
|
123
|
+
property_schema = property_schema && property_schema.match_to_object(object[property_name])
|
124
|
+
|
125
|
+
if property_schema && object[property_name].is_a?(JSON::Node)
|
126
|
+
Scorpio.class_for_schema(property_schema.schema_node).new(object[property_name])
|
127
|
+
else
|
128
|
+
object[property_name]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
@object_mapped[property_name_]
|
133
|
+
end
|
134
|
+
|
135
|
+
def merge(other)
|
136
|
+
# we want to strip the containers from this before we merge
|
137
|
+
# this is kind of annoying. wish I had a better way.
|
138
|
+
other_stripped = ycomb do |striprec|
|
139
|
+
proc do |stripobject|
|
140
|
+
stripobject = stripobject.object if stripobject.is_a?(Scorpio::SchemaObjectBase)
|
141
|
+
stripobject = stripobject.content if stripobject.is_a?(Scorpio::JSON::Node)
|
142
|
+
if stripobject.is_a?(Hash)
|
143
|
+
stripobject.map { |k, v| {striprec.call(k) => striprec.call(v)} }.inject({}, &:update)
|
144
|
+
elsif stripobject.is_a?(Array)
|
145
|
+
stripobject.map(&striprec)
|
146
|
+
elsif stripobject.is_a?(Symbol)
|
147
|
+
stripobject.to_s
|
148
|
+
elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| stripobject.is_a?(c) }
|
149
|
+
stripobject
|
150
|
+
else
|
151
|
+
raise(TypeError, "bad (not jsonifiable) object: #{stripobject.pretty_inspect}")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end.call(other)
|
155
|
+
|
156
|
+
self.class.new(object.merge(other_stripped))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
module SchemaObjectBaseArray
|
161
|
+
def expected_object_class
|
162
|
+
Scorpio::JSON::ArrayNode
|
163
|
+
end
|
164
|
+
|
165
|
+
def each
|
166
|
+
return to_enum(__method__) { object.size } unless block_given?
|
167
|
+
object.each_index { |i| yield(self[i]) }
|
168
|
+
self
|
169
|
+
end
|
170
|
+
include Enumerable
|
171
|
+
|
172
|
+
def to_ary
|
173
|
+
to_a
|
174
|
+
end
|
175
|
+
|
176
|
+
include Arraylike
|
177
|
+
include SchemaObjectMightBeLike
|
178
|
+
|
179
|
+
def [](i_)
|
180
|
+
# it would make more sense for this to be an array here, but but Array doesn't have a nice memoizing
|
181
|
+
# constructor, so it's a hash with integer keys
|
182
|
+
@object_mapped ||= Hash.new do |hash, i|
|
183
|
+
hash[i] = begin
|
184
|
+
index_schema = module_schema.subschema_for_index(i)
|
185
|
+
index_schema = index_schema && index_schema.match_to_object(object[i])
|
186
|
+
|
187
|
+
if index_schema && object[i].is_a?(JSON::Node)
|
188
|
+
Scorpio.class_for_schema(index_schema.schema_node).new(object[i])
|
189
|
+
else
|
190
|
+
object[i]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
@object_mapped[i_]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.module_for_schema(schema_node_)
|
199
|
+
Module.new.tap do |m|
|
200
|
+
m.instance_exec(schema_node_) do |module_schema_node|
|
201
|
+
unless module_schema_node.is_a?(Scorpio::JSON::Node)
|
202
|
+
raise(ArgumentError, "expected instance of Scorpio::JSON::Node; got: #{module_schema_node.pretty_inspect.chomp}")
|
203
|
+
end
|
204
|
+
|
205
|
+
module_schema = Scorpio::Schema.new(module_schema_node)
|
206
|
+
|
207
|
+
define_method(:module_schema) { module_schema }
|
208
|
+
define_singleton_method(:module_schema) { module_schema }
|
209
|
+
define_singleton_method(:included) do |includer|
|
210
|
+
includer.send(:define_singleton_method, :module_schema) { module_schema }
|
211
|
+
end
|
212
|
+
|
213
|
+
if module_schema.describes_hash?
|
214
|
+
include SchemaObjectBaseHash
|
215
|
+
|
216
|
+
module_schema.described_hash_property_names.each do |property_name|
|
217
|
+
define_method(property_name) do
|
218
|
+
self[property_name]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
elsif module_schema.describes_array?
|
222
|
+
include SchemaObjectBaseArray
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Scorpio
|
2
|
+
module Hashlike
|
3
|
+
def inspect
|
4
|
+
object_group_text = respond_to?(:object_group_text) ? ' ' + self.object_group_text : ''
|
5
|
+
"\#{<#{self.class.name}#{object_group_text}> #{self.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join(', ')}}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def pretty_print(q)
|
9
|
+
q.instance_exec(self) do |obj|
|
10
|
+
object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
|
11
|
+
text "\#{<#{obj.class.name}#{object_group_text}>"
|
12
|
+
group_sub {
|
13
|
+
nest(2) {
|
14
|
+
breakable(obj.any? { true } ? ' ' : '')
|
15
|
+
seplist(obj, nil, :each_pair) { |k, v|
|
16
|
+
group {
|
17
|
+
pp k
|
18
|
+
text ' => '
|
19
|
+
pp v
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
breakable ''
|
25
|
+
text '}'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
module Arraylike
|
30
|
+
def inspect
|
31
|
+
object_group_text = respond_to?(:object_group_text) ? ' ' + self.object_group_text : ''
|
32
|
+
"\#[<#{self.class.name}#{object_group_text}> #{self.map { |e| e.inspect }.join(', ')}]"
|
33
|
+
end
|
34
|
+
|
35
|
+
def pretty_print(q)
|
36
|
+
q.instance_exec(self) do |obj|
|
37
|
+
object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
|
38
|
+
text "\#[<#{obj.class.name}#{object_group_text}>"
|
39
|
+
group_sub {
|
40
|
+
nest(2) {
|
41
|
+
breakable(obj.any? { true } ? ' ' : '')
|
42
|
+
seplist(obj, nil, :each) { |e|
|
43
|
+
pp e
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
breakable ''
|
48
|
+
text ']'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|