scorpio 0.1.0 → 0.2.0

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