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