scorpio 0.1.0 → 0.2.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.
@@ -0,0 +1,127 @@
1
+ module Scorpio
2
+ # base class for representing an instance of an instance described by a schema
3
+ class SchemaInstanceBase
4
+ class << self
5
+ def class_comment
6
+ lines = []
7
+
8
+ description = schema &&
9
+ schema['description'].respond_to?(:to_str) &&
10
+ schema['description'].to_str
11
+ if description
12
+ description.split("\n", -1).each do |descline|
13
+ lines << "# " + descline
14
+ end
15
+ lines << "#"
16
+ end
17
+
18
+ schema.described_hash_property_names.each_with_index do |propname, i|
19
+ lines << "#" unless i == 0
20
+ lines << "# @!attribute [rw] #{propname}"
21
+
22
+ property_schema = schema['properties'].respond_to?(:to_hash) &&
23
+ schema['properties'][propname].respond_to?(:to_hash) &&
24
+ schema['properties'][propname]
25
+
26
+ required = property_schema && property_schema['required']
27
+ required ||= schema['required'].respond_to?(:to_ary) && schema['required'].include?(propname)
28
+ lines << "# @required" if required
29
+
30
+ type = property_schema &&
31
+ property_schema['type'].respond_to?(:to_str) &&
32
+ property_schema['type'].to_str
33
+ simple = {'string' => 'String', 'number' => 'Numeric', 'boolean' => 'Boolean', 'null' => 'nil'}
34
+ rettypes = []
35
+ if simple.key?(type)
36
+ rettypes << simple[type]
37
+ elsif type == 'object' || type == 'array'
38
+ rettypes = []
39
+ schema_class = Scorpio.class_for_schema(property_schema)
40
+ unless schema_class.name =~ /\AScorpio::SchemaClasses::/
41
+ rettypes << schema_class.name
42
+ end
43
+ rettypes << {'object' => '#to_hash', 'array' => '#to_ary'}[type]
44
+ elsif type
45
+ # not really valid, but there's some information in there. whatever it is.
46
+ rettypes << type
47
+ end
48
+ # we'll add Object to all because the accessor methods have no enforcement that their value is
49
+ # of the specified type, and may return anything really. TODO: consider if this is of any value?
50
+ rettypes << 'Object'
51
+ lines << "# @return [#{rettypes.join(', ')}]"
52
+
53
+ description = property_schema &&
54
+ property_schema['description'].respond_to?(:to_str) &&
55
+ property_schema['description'].to_str
56
+ if description
57
+ description.split("\n", -1).each do |descline|
58
+ lines << "# " + descline
59
+ end
60
+ end
61
+ end
62
+ lines.join("\n")
63
+ end
64
+
65
+ def to_rb
66
+ lines = []
67
+ description = schema &&
68
+ schema['description'].respond_to?(:to_str) &&
69
+ schema['description'].to_str
70
+ if description
71
+ description.split("\n", -1).each do |descline|
72
+ lines << "# " + descline
73
+ end
74
+ end
75
+ lines << "class #{name}"
76
+ schema.described_hash_property_names.each_with_index do |propname, i|
77
+ lines << "" unless i == 0
78
+ property_schema = schema['properties'].respond_to?(:to_hash) &&
79
+ schema['properties'][propname].respond_to?(:to_hash) &&
80
+ schema['properties'][propname]
81
+ description = property_schema &&
82
+ property_schema['description'].respond_to?(:to_str) &&
83
+ property_schema['description'].to_str
84
+ if description
85
+ description.split("\n", -1).each do |descline|
86
+ lines << " # " + descline
87
+ end
88
+ lines << " #" # blank comment line between description and @return
89
+ end
90
+
91
+ required = property_schema && property_schema['required']
92
+ required ||= schema['required'].respond_to?(:to_ary) && schema['required'].include?(propname)
93
+ lines << " # @required" if required
94
+
95
+ type = property_schema &&
96
+ property_schema['type'].respond_to?(:to_str) &&
97
+ property_schema['type'].to_str
98
+ simple = {'string' => 'String', 'number' => 'Numeric', 'boolean' => 'Boolean', 'null' => 'nil'}
99
+ rettypes = []
100
+ if simple.key?(type)
101
+ rettypes << simple[type]
102
+ elsif type == 'object' || type == 'array'
103
+ rettypes = []
104
+ schema_class = Scorpio.class_for_schema(property_schema)
105
+ unless schema_class.name =~ /\AScorpio::SchemaClasses::/
106
+ rettypes << schema_class.name
107
+ end
108
+ rettypes << {'object' => '#to_hash', 'array' => '#to_ary'}[type]
109
+ elsif type
110
+ # not really valid, but there's some information in there. whatever it is.
111
+ rettypes << type
112
+ end
113
+ # we'll add Object to all because the accessor methods have no enforcement that their value is
114
+ # of the specified type, and may return anything really. TODO: consider if this is of any value?
115
+ rettypes << 'Object'
116
+ lines << " # @return [#{rettypes.join(', ')}]"
117
+
118
+ lines << " def #{propname}"
119
+ lines << " super"
120
+ lines << " end"
121
+ end
122
+ lines << "end"
123
+ lines.join("\n")
124
+ end
125
+ end
126
+ end
127
+ end
@@ -1,14 +1,86 @@
1
1
  module Scorpio
2
+ module Typelike
3
+ def self.modified_copy(other, &block)
4
+ if other.respond_to?(:modified_copy)
5
+ other.modified_copy(&block)
6
+ else
7
+ return yield(other)
8
+ end
9
+ end
10
+
11
+ # I could require 'json/add/core' and use #as_json but I like this better.
12
+ def self.as_json(object)
13
+ if object.respond_to?(:to_hash)
14
+ object.map do |k, v|
15
+ unless k.is_a?(Symbol) || k.respond_to?(:to_str)
16
+ raise(TypeError, "json object (hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
17
+ end
18
+ {k.to_s => as_json(v)}
19
+ end.inject({}, &:update)
20
+ elsif object.respond_to?(:to_ary)
21
+ object.map { |e| as_json(e) }
22
+ elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| object.is_a?(c) }
23
+ object
24
+ elsif object.is_a?(Symbol)
25
+ object.to_s
26
+ elsif object.is_a?(Set)
27
+ as_json(object.to_a)
28
+ elsif object.respond_to?(:as_json)
29
+ as_json(object.as_json)
30
+ else
31
+ raise(TypeError, "cannot express object as json: #{object.pretty_inspect.chomp}")
32
+ end
33
+ end
34
+ end
2
35
  module Hashlike
36
+ include Enumerable
37
+
38
+ # safe methods which can be delegated to #to_hash (which the includer is assumed to have defined).
39
+ # 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
40
+
41
+ # methods which do not need to access the value.
42
+ SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size)
43
+ SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at)
44
+ DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift)
45
+ # these return a modified copy
46
+ safe_modified_copy_methods = %w(compact merge)
47
+ # select and reject will return a modified copy but need the yielded block variable value from #[]
48
+ safe_kv_block_modified_copy_methods = %w(select reject)
49
+ SAFE_METHODS = SAFE_KEY_ONLY_METHODS | SAFE_KEY_VALUE_METHODS
50
+ safe_to_hash_methods = SAFE_METHODS - safe_modified_copy_methods - safe_kv_block_modified_copy_methods
51
+ safe_to_hash_methods.each do |method_name|
52
+ define_method(method_name) { |*a, &b| to_hash.public_send(method_name, *a, &b) }
53
+ end
54
+ safe_modified_copy_methods.each do |method_name|
55
+ define_method(method_name) do |*a, &b|
56
+ Scorpio::Typelike.modified_copy(self) do |object_to_modify|
57
+ object_to_modify.public_send(method_name, *a, &b)
58
+ end
59
+ end
60
+ end
61
+ safe_kv_block_modified_copy_methods.each do |method_name|
62
+ define_method(method_name) do |*a, &b|
63
+ Scorpio::Typelike.modified_copy(self) do |object_to_modify|
64
+ object_to_modify.public_send(method_name, *a) do |k, _v|
65
+ b.call(k, self[k])
66
+ end
67
+ end
68
+ end
69
+ end
70
+
3
71
  def inspect
4
72
  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(', ')}}"
73
+ "\#{<#{self.class.to_s}#{object_group_text}>#{empty? ? '' : ' '}#{self.map { |k, v| "#{k.inspect} => #{v.inspect}" }.join(', ')}}"
74
+ end
75
+
76
+ def to_s
77
+ inspect
6
78
  end
7
79
 
8
80
  def pretty_print(q)
9
81
  q.instance_exec(self) do |obj|
10
82
  object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
11
- text "\#{<#{obj.class.name}#{object_group_text}>"
83
+ text "\#{<#{obj.class.to_s}#{object_group_text}>"
12
84
  group_sub {
13
85
  nest(2) {
14
86
  breakable(obj.any? { true } ? ' ' : '')
@@ -27,15 +99,59 @@ module Scorpio
27
99
  end
28
100
  end
29
101
  module Arraylike
102
+ include Enumerable
103
+
104
+ # safe methods which can be delegated to #to_ary (which the includer is assumed to have defined).
105
+ # 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
106
+
107
+ # methods which do not need to access the element.
108
+ SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size)
109
+ # there are some ambiguous ones that are omitted, like #sort, #map / #collect.
110
+ SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev assoc at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip)
111
+ DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift)
112
+
113
+ # methods (well, method) that returns a modified copy and doesn't need any handling of block variable(s)
114
+ safe_modified_copy_methods = %w(compact)
115
+
116
+ # methods that return a modified copy and do need handling of block variables
117
+ safe_el_block_methods = %w(reject select)
118
+
119
+ SAFE_METHODS = SAFE_INDEX_ONLY_METHODS | SAFE_INDEX_ELEMENT_METHODS
120
+ safe_to_ary_methods = SAFE_METHODS - safe_modified_copy_methods - safe_el_block_methods
121
+ safe_to_ary_methods.each do |method_name|
122
+ define_method(method_name) { |*a, &b| to_ary.public_send(method_name, *a, &b) }
123
+ end
124
+ safe_modified_copy_methods.each do |method_name|
125
+ define_method(method_name) do |*a, &b|
126
+ Scorpio::Typelike.modified_copy(self) do |object_to_modify|
127
+ object_to_modify.public_send(method_name, *a, &b)
128
+ end
129
+ end
130
+ end
131
+ safe_el_block_methods.each do |method_name|
132
+ define_method(method_name) do |*a, &b|
133
+ Scorpio::Typelike.modified_copy(self) do |object_to_modify|
134
+ i = 0
135
+ object_to_modify.public_send(method_name, *a) do |_e|
136
+ b.call(self[i]).tap { i += 1 }
137
+ end
138
+ end
139
+ end
140
+ end
141
+
30
142
  def inspect
31
143
  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(', ')}]"
144
+ "\#[<#{self.class.to_s}#{object_group_text}>#{empty? ? '' : ' '}#{self.map { |e| e.inspect }.join(', ')}]"
145
+ end
146
+
147
+ def to_s
148
+ inspect
33
149
  end
34
150
 
35
151
  def pretty_print(q)
36
152
  q.instance_exec(self) do |obj|
37
153
  object_group_text = obj.respond_to?(:object_group_text) ? ' ' + obj.object_group_text : ''
38
- text "\#[<#{obj.class.name}#{object_group_text}>"
154
+ text "\#[<#{obj.class.to_s}#{object_group_text}>"
39
155
  group_sub {
40
156
  nest(2) {
41
157
  breakable(obj.any? { true } ? ' ' : '')
@@ -0,0 +1,83 @@
1
+ module Scorpio
2
+ module Util
3
+ def stringify_symbol_keys(hash)
4
+ unless hash.respond_to?(:to_hash)
5
+ raise(ArgumentError, "expected argument to be a hash; got #{hash.class.inspect}: #{hash.pretty_inspect.chomp}")
6
+ end
7
+ Scorpio::Typelike.modified_copy(hash) do |hash_|
8
+ changed = false
9
+ out = {}
10
+ hash_.each do |k, v|
11
+ if k.is_a?(Symbol)
12
+ changed = true
13
+ k = k.to_s
14
+ end
15
+ out[k] = v
16
+ end
17
+ changed ? out : hash_
18
+ end
19
+ end
20
+
21
+ def deep_stringify_symbol_keys(object)
22
+ if object.respond_to?(:to_hash)
23
+ Scorpio::Typelike.modified_copy(object) do |hash|
24
+ changed = false
25
+ out = {}
26
+ hash.each do |k, v|
27
+ if k.is_a?(Symbol)
28
+ changed = true
29
+ k = k.to_s
30
+ end
31
+ out_k = deep_stringify_symbol_keys(k)
32
+ out_v = deep_stringify_symbol_keys(v)
33
+ changed = true if out_k.object_id != k.object_id
34
+ changed = true if out_v.object_id != v.object_id
35
+ out[out_k] = out_v
36
+ end
37
+ changed ? out : hash
38
+ end
39
+ elsif object.respond_to?(:to_ary)
40
+ Scorpio::Typelike.modified_copy(object) do |ary|
41
+ changed = false
42
+ out = ary.map do |e|
43
+ out_e = deep_stringify_symbol_keys(e)
44
+ changed = true if out_e.object_id != e.object_id
45
+ out_e
46
+ end
47
+ changed ? out : ary
48
+ end
49
+ else
50
+ object
51
+ end
52
+ end
53
+ end
54
+ extend Util
55
+
56
+ module FingerprintHash
57
+ def ==(other)
58
+ object_id == other.object_id || (other.respond_to?(:fingerprint) && other.fingerprint == self.fingerprint)
59
+ end
60
+
61
+ alias_method :eql?, :==
62
+
63
+ def hash
64
+ fingerprint.hash
65
+ end
66
+ end
67
+
68
+ module Memoize
69
+ def memoize(key, *args_)
70
+ @memos ||= {}
71
+ @memos[key] ||= Hash.new do |h, args|
72
+ h[args] = yield(*args)
73
+ end
74
+ @memos[key][args_]
75
+ end
76
+
77
+ def clear_memo(key)
78
+ @memos ||= {}
79
+ @memos[key].clear if @memos[key]
80
+ end
81
+ end
82
+ extend Memoize
83
+ end
@@ -0,0 +1,15 @@
1
+ require 'api_hammer'
2
+
3
+ # hax
4
+ class Faraday::Response
5
+ def media_type
6
+ content_type_attrs.media_type
7
+ end
8
+ def content_type_attrs
9
+ ApiHammer::ContentTypeAttrs.new(content_type)
10
+ end
11
+ def content_type
12
+ _, ct = env.response_headers.detect { |k,v| k =~ /\Acontent[-_]type\z/i }
13
+ ct
14
+ end
15
+ end
@@ -1,3 +1,3 @@
1
1
  module Scorpio
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/scorpio.gemspec CHANGED
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ["lib"]
22
22
 
23
23
  spec.add_dependency "faraday"
24
- spec.add_dependency "faraday_middleware"
25
24
  # we are monkey patching json-schema with a fix that has not been merged in a timely fashion.
26
25
  spec.add_dependency "json-schema", "~> 2.8"
27
26
  spec.add_development_dependency "bundler", "~> 1.12"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scorpio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-12 00:00:00.000000000 Z
11
+ date: 2018-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: faraday_middleware
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: json-schema
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -261,6 +247,8 @@ files:
261
247
  - README.md
262
248
  - Rakefile
263
249
  - bin/documents_to_yml.rb
250
+ - documents/openapis.org/v3/schema.json
251
+ - documents/openapis.org/v3/schema.yml
264
252
  - documents/swagger.io/v2/schema.json
265
253
  - documents/swagger.io/v2/schema.yml
266
254
  - documents/www.googleapis.com/discovery/v1/apis/discovery/v1/rest
@@ -270,12 +258,15 @@ files:
270
258
  - lib/scorpio/json-schema-fragments.rb
271
259
  - lib/scorpio/json.rb
272
260
  - lib/scorpio/json/node.rb
273
- - lib/scorpio/model.rb
274
261
  - lib/scorpio/openapi.rb
275
262
  - lib/scorpio/pickle_adapter.rb
263
+ - lib/scorpio/resource_base.rb
276
264
  - lib/scorpio/schema.rb
277
- - lib/scorpio/schema_object_base.rb
265
+ - lib/scorpio/schema_instance_base.rb
266
+ - lib/scorpio/schema_instance_base/to_rb.rb
278
267
  - lib/scorpio/typelike_modules.rb
268
+ - lib/scorpio/util.rb
269
+ - lib/scorpio/util/faraday/response_media_type.rb
279
270
  - lib/scorpio/version.rb
280
271
  - scorpio.gemspec
281
272
  homepage: https://github.com/notEthan/scorpio
@@ -298,7 +289,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
289
  version: '0'
299
290
  requirements: []
300
291
  rubyforge_project:
301
- rubygems_version: 2.6.14
292
+ rubygems_version: 2.4.5.5
302
293
  signing_key:
303
294
  specification_version: 4
304
295
  summary: Scorpio REST client