proc 0.3.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3a73eafa61306bca405c0eaec0f8df0d145b22d27a98ee8dcf28db94cdadfa3
4
- data.tar.gz: b1e95096528fe529ec664a370095269ae26672994178ed62e3c78159c46ed173
3
+ metadata.gz: ece2d4c0f8e4e0aa122ca1df9e751d061afaa802dc056e5dd2c0ba7b272ff3e8
4
+ data.tar.gz: 4f9ce5d28d966c23097dd3445d5ea5fc37fe9d0ecea1c5b13771b9633ac28d0c
5
5
  SHA512:
6
- metadata.gz: 9708b1585d8c95deb6016cc5dbed10d5c7bffdfa55421d0392d3c51ec2af5ce334eb5b41283c585795874c7cdf5b035e6eb41ca145ee56d7409147546b61a6bf
7
- data.tar.gz: ec138355afed48dde883579c73d304196b3cb2b6b8ac7d5a0986b43c5bffb6591fd33c019b516807c46b2c1ef3c6d75df7fc10d5bae10ecfbc38428ea89977c9
6
+ metadata.gz: c084f766e97cced8633a74379d700d06efa54bc6f0eb81cf6351af9e2528aea465ebff4dc4d3dd3631c20016ce70a1a36c2245607fe14f85aaa29d115d3ac9a5
7
+ data.tar.gz: 62303f9d27683c07bc0ff8f5df8c8697a669fa8df7ac496929457e3e572384ded1c779580ddf09082d830b8658c6c1be07eadb3478255de9b013e318a51b2755
data/lib/proc.rb CHANGED
@@ -8,7 +8,7 @@ class Proc
8
8
  end
9
9
 
10
10
  def self.undefined
11
- @_undefined ||= Object.new
11
+ @_undefined ||= ::Object.new
12
12
  end
13
13
 
14
14
  def self.undefined?(value)
data/lib/proc/argument.rb CHANGED
@@ -8,7 +8,7 @@ class Proc
8
8
  end
9
9
 
10
10
  def serialize
11
- {"::" => {"name" => @name.to_s}.merge(serialized_options)}
11
+ ["@@", @name.to_s, serialized_options]
12
12
  end
13
13
 
14
14
  def serialized_options
@@ -21,7 +21,7 @@ class Proc
21
21
  if value.respond_to?(:serialize)
22
22
  value.serialize
23
23
  else
24
- value
24
+ ["%%", value]
25
25
  end
26
26
  end
27
27
  end
data/lib/proc/callable.rb CHANGED
@@ -16,7 +16,7 @@ class Proc
16
16
  @arguments = arguments.dup
17
17
  end
18
18
 
19
- def call(input = input_omitted = true, **arguments)
19
+ def call(input = input_omitted = true, **arguments, &block)
20
20
  callable = self.class.new(
21
21
  @proc,
22
22
  client: @client,
@@ -24,7 +24,7 @@ class Proc
24
24
  arguments: @arguments.merge(arguments)
25
25
  )
26
26
 
27
- @client.call(@proc, callable.serialized_input, **callable.serialized_arguments)
27
+ @client.call(@proc, callable.input, **callable.arguments, &block)
28
28
  end
29
29
 
30
30
  def with(input = input_omitted = true, **arguments)
@@ -43,14 +43,20 @@ class Proc
43
43
  composed
44
44
  end
45
45
 
46
- def serialize
47
- wrapped = {"[]" => [[@proc, serialized_arguments]]}
46
+ def serialize(unwrapped: false)
47
+ serialized = ["()", @proc]
48
48
 
49
49
  unless Proc.undefined?(@input)
50
- wrapped["<<"] = serialized_input
50
+ serialized << [">>", serialized_input]
51
51
  end
52
52
 
53
- {"{}" => wrapped}
53
+ serialized.concat(serialized_arguments)
54
+
55
+ if unwrapped
56
+ serialized
57
+ else
58
+ ["{}", serialized]
59
+ end
54
60
  end
55
61
 
56
62
  def serialized_input
@@ -58,8 +64,8 @@ class Proc
58
64
  end
59
65
 
60
66
  def serialized_arguments
61
- @arguments.each_pair.each_with_object({}) { |(key, value), hash|
62
- hash[key.to_s] = serialize_value(value)
67
+ @arguments.map { |key, value|
68
+ ["$$", key.to_s, serialize_value(value)]
63
69
  }
64
70
  end
65
71
 
@@ -99,7 +105,7 @@ class Proc
99
105
  if value.respond_to?(:serialize)
100
106
  value.serialize
101
107
  else
102
- value
108
+ ["%%", value]
103
109
  end
104
110
  end
105
111
  end
data/lib/proc/client.rb CHANGED
@@ -6,10 +6,19 @@ require "oj"
6
6
  require_relative "argument"
7
7
  require_relative "callable"
8
8
  require_relative "composition"
9
- require_relative "null_logger"
9
+ require_relative "enumerator"
10
10
 
11
11
  require_relative "http/client"
12
12
 
13
+ Console.logger.off!
14
+
15
+ Oj.default_options = {
16
+ mode: :custom,
17
+ bigdecimal_load: :auto,
18
+ float_precision: 0,
19
+ second_precision: 6
20
+ }.freeze
21
+
13
22
  class Proc
14
23
  class Error < StandardError
15
24
  end
@@ -35,13 +44,14 @@ class Proc
35
44
  class Timeout < Error
36
45
  end
37
46
 
38
- class Client < Http::Client
47
+ class Client
48
+ attr_reader :authorization, :scheme, :host
49
+
39
50
  def initialize(authorization, scheme: "https", host: "proc.dev")
40
51
  @authorization = authorization
41
52
  @scheme = scheme
42
53
  @host = host
43
-
44
- super()
54
+ @internal = Http::Client.new
45
55
  end
46
56
 
47
57
  def [](proc)
@@ -65,65 +75,69 @@ class Proc
65
75
  end
66
76
 
67
77
  DEFAULT_HEADERS = {
68
- "accept" => "application/json",
69
- "content-type" => "application/json"
78
+ "accept" => "application/vnd.proc+json",
79
+ "content-type" => "application/vnd.proc+json"
70
80
  }.freeze
71
81
 
72
- def call(proc = nil, input = nil, **arguments)
73
- Async(logger: NullLogger) { |task|
74
- body = {"<>" => true}
82
+ def call(proc = nil, input = Proc.undefined, **arguments, &block)
83
+ body = []
75
84
 
76
- unless Proc.undefined?(input)
77
- body["<<"] = serialize_value(input)
78
- end
85
+ unless Proc.undefined?(input)
86
+ body << [">>", serialize_value(input)]
87
+ end
79
88
 
80
- arguments.each_pair do |key, value|
81
- body[key.to_s] = serialize_value(value)
82
- end
89
+ arguments.each_pair do |key, value|
90
+ body << ["$$", key.to_s, serialize_value(value)]
91
+ end
83
92
 
84
- headers = {
85
- "authorization" => "bearer #{@authorization}"
86
- }.merge(DEFAULT_HEADERS)
93
+ headers = {
94
+ "authorization" => "bearer #{@authorization}"
95
+ }.merge(DEFAULT_HEADERS)
87
96
 
88
- begin
89
- response = super(:post, build_uri(proc), headers: headers, body: Oj.dump(body, mode: :custom), task: task)
97
+ status, headers, payload = get_payload(proc: proc, headers: headers, body: body)
90
98
 
91
- @remaining = response.headers["x-rate-limit-remaining"].to_s.to_i
92
- @resets_at = Time.at(response.headers["x-rate-limit-reset"].to_s.to_i)
99
+ case status
100
+ when 200
101
+ result = extract_output(payload)
93
102
 
94
- payload = Oj.load(response.read, mode: :compat)
95
- rescue
96
- raise Proc::Unavailable
97
- ensure
98
- response&.close
99
- end
103
+ if (cursor = headers["x-cursor"])
104
+ enumerator = Enumerator.new(result) {
105
+ arguments[:cursor] = cursor.to_s
106
+ call(proc, input, **arguments)
107
+ }
100
108
 
101
- case response.status
102
- when 200
103
- payload[">>"]
104
- when 400
105
- raise Proc::Invalid, payload.dig("error", "message")
106
- when 401
107
- raise Proc::Unauthorized, payload.dig("error", "message")
108
- when 403
109
- raise Proc::Forbidden, payload.dig("error", "message")
110
- when 404
111
- raise Proc::Undefined, payload.dig("error", "message")
112
- when 408
113
- raise Proc::Timeout, payload.dig("error", "message")
114
- when 429
115
- raise Proc::Limited, payload.dig("error", "message")
116
- when 500
117
- raise Proc::Error, payload.dig("error", "message")
118
- when 508
119
- raise Proc::Error, payload.dig("error", "message")
109
+ if block
110
+ enumerator.each(&block)
111
+ else
112
+ enumerator
113
+ end
120
114
  else
121
- raise Proc::Error, "unhandled"
115
+ result
122
116
  end
123
- }.wait
117
+ when 400
118
+ raise Proc::Invalid, extract_error_message(payload)
119
+ when 401
120
+ raise Proc::Unauthorized, extract_error_message(payload)
121
+ when 403
122
+ raise Proc::Forbidden, extract_error_message(payload)
123
+ when 404
124
+ raise Proc::Undefined, extract_error_message(payload)
125
+ when 408
126
+ raise Proc::Timeout, extract_error_message(payload)
127
+ when 413
128
+ raise Proc::Invalid, extract_error_message(payload)
129
+ when 429
130
+ raise Proc::Limited, extract_error_message(payload)
131
+ when 500
132
+ raise Proc::Error, extract_error_message(payload)
133
+ when 508
134
+ raise Proc::Error, extract_error_message(payload)
135
+ else
136
+ raise Proc::Error, "unhandled"
137
+ end
124
138
  end
125
139
 
126
- def method_missing(name, input = input_omitted = true, **arguments)
140
+ def method_missing(name, input = input_omitted = true, *, **arguments)
127
141
  if input_omitted
128
142
  Callable.new(name, client: self, arguments: arguments)
129
143
  else
@@ -140,6 +154,10 @@ class Proc
140
154
  end
141
155
  alias_method :arg, :argument
142
156
 
157
+ def close
158
+ @internal.close
159
+ end
160
+
143
161
  private def build_uri(proc)
144
162
  host_and_path = File.join(@host, proc.to_s.split(".").join("/"))
145
163
 
@@ -150,8 +168,44 @@ class Proc
150
168
  if value.respond_to?(:serialize)
151
169
  value.serialize
152
170
  else
153
- value
171
+ ["%%", value]
172
+ end
173
+ end
174
+
175
+ private def get_payload(proc:, headers:, body:)
176
+ @internal.call(:post, build_uri(proc), headers: headers, body: Oj.dump(body)) do |response|
177
+ @remaining = response.headers["x-rate-limit-remaining"].to_s.to_i
178
+ @resets_at = Time.at(response.headers["x-rate-limit-reset"].to_s.to_i)
179
+ [response.status, response.headers, Oj.load(response.read)]
180
+ end
181
+ rescue
182
+ raise Proc::Unavailable
183
+ end
184
+
185
+ private def extract_output(payload)
186
+ payload.each do |tuple|
187
+ case tuple[0]
188
+ when "<<"
189
+ return tuple[1]
190
+ end
191
+ end
192
+
193
+ nil
194
+ end
195
+
196
+ private def extract_error(payload)
197
+ payload.each do |tuple|
198
+ case tuple[0]
199
+ when "!!"
200
+ return tuple[1]
201
+ end
154
202
  end
203
+
204
+ nil
205
+ end
206
+
207
+ private def extract_error_message(payload)
208
+ extract_error(payload)&.dig("message")
155
209
  end
156
210
  end
157
211
  end
@@ -15,7 +15,7 @@ class Proc
15
15
  @callables = @callables.dup
16
16
  end
17
17
 
18
- def call(input = input_omitted = true, **arguments)
18
+ def call(input = input_omitted = true, **arguments, &block)
19
19
  callable = self.class.new(
20
20
  client: @client,
21
21
  input: input_omitted ? @input : input,
@@ -23,7 +23,7 @@ class Proc
23
23
  arguments: @arguments.merge(arguments)
24
24
  )
25
25
 
26
- @client.call("proc.exec", nil, proc: callable.serialize)
26
+ @client.call("exec", Proc.undefined, proc: callable, &block)
27
27
  end
28
28
 
29
29
  def with(input = input_omitted = true, **arguments)
@@ -51,25 +51,13 @@ class Proc
51
51
  end
52
52
 
53
53
  def serialize
54
- wrapped = {"[]" => serialized_calls}
54
+ serialized = ["{}"]
55
55
 
56
56
  unless Proc.undefined?(@input)
57
- wrapped["<<"] = serialized_input
57
+ serialized << [">>", serialized_input]
58
58
  end
59
59
 
60
- {"{}" => wrapped.merge(serialized_arguments)}
61
- end
62
-
63
- def serialized_calls
64
- @callables.map { |callable|
65
- arguments = callable.serialized_arguments
66
-
67
- unless Proc.undefined?(callable.input)
68
- arguments["<<"] = callable.serialized_input
69
- end
70
-
71
- [callable.proc, arguments]
72
- }
60
+ serialized + serialized_arguments + @callables.map { |callable| callable.serialize(unwrapped: true) }
73
61
  end
74
62
 
75
63
  def serialized_input
@@ -77,8 +65,8 @@ class Proc
77
65
  end
78
66
 
79
67
  def serialized_arguments
80
- @arguments.each_pair.each_with_object({}) { |(key, value), hash|
81
- hash[key.to_s] = serialize_value(value)
68
+ @arguments.map { |key, value|
69
+ ["$$", key.to_s, serialize_value(value)]
82
70
  }
83
71
  end
84
72
 
@@ -93,7 +81,7 @@ class Proc
93
81
  if value.respond_to?(:serialize)
94
82
  value.serialize
95
83
  else
96
- value
84
+ ["%%", value]
97
85
  end
98
86
  end
99
87
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ class Enumerator
5
+ include Enumerable
6
+
7
+ def initialize(values, &next_block)
8
+ @values = values
9
+ @next_block = next_block
10
+ end
11
+
12
+ def each(&block)
13
+ return to_enum(:each) unless block
14
+
15
+ @values.each(&block)
16
+ @next_block.call.each(&block)
17
+ end
18
+ end
19
+ end
@@ -3,52 +3,39 @@
3
3
  require "async"
4
4
  require "async/http/internet"
5
5
 
6
- require "protocol/http/body/streamable"
6
+ require "core/async"
7
7
 
8
8
  require_relative "request"
9
9
  require_relative "response"
10
10
 
11
- require_relative "../null_logger"
12
-
13
11
  class Proc
14
12
  module Http
15
13
  class Client
16
- def initialize
17
- @internet = Async::HTTP::Internet.new
18
- @responses = {}
19
- end
14
+ include Is::Async
20
15
 
21
- def call(method, uri, params: {}, headers: {}, body: nil, task: nil)
16
+ def call(method, uri, params: {}, headers: {}, body: nil)
17
+ internet = Async::HTTP::Internet.new
22
18
  request = Request.new(method: method, uri: uri, params: params, headers: headers, body: body)
23
19
 
24
- if task
25
- make_request(request)
26
- else
27
- Async(logger: NullLogger) {
28
- make_request(request)
29
- }.wait
30
- end
31
- end
32
-
33
- def close
34
- # TODO: Make sure this works. We should also close / clear after accumulating some amount.
35
- #
36
- @responses.each_value(&:close)
37
- @responses.clear
38
- @internet.close
39
- end
20
+ await {
21
+ begin
22
+ response = make_request(internet, request)
40
23
 
41
- def count
42
- @responses.count
24
+ yield response
25
+ ensure
26
+ response&.close
27
+ internet&.close
28
+ end
29
+ }
43
30
  end
44
31
 
45
- private def make_request(request)
46
- async_response = @internet.call(*request.callable)
32
+ private def make_request(internet, request)
33
+ async_response = internet.call(*request.callable)
47
34
  wrap_async_response_for_request(async_response, request)
48
35
  end
49
36
 
50
37
  private def wrap_async_response_for_request(async_response, request)
51
- @responses[async_response] = Response.new(request, async_response)
38
+ Response.new(request, async_response)
52
39
  end
53
40
  end
54
41
  end
data/lib/proc/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Proc
4
- VERSION = "0.3.0"
4
+ VERSION = "0.8.0"
5
5
 
6
6
  def self.version
7
7
  VERSION
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Powell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-23 00:00:00.000000000 Z
11
+ date: 2021-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -16,28 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.52.5
19
+ version: 0.54.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.52.5
26
+ version: 0.54.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: core-async
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.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.1.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: oj
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '3.10'
47
+ version: '3.11'
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '3.10'
54
+ version: '3.11'
41
55
  description: Proc client library.
42
56
  email: bryan@metabahn.com
43
57
  executables: []
@@ -51,10 +65,10 @@ files:
51
65
  - lib/proc/callable.rb
52
66
  - lib/proc/client.rb
53
67
  - lib/proc/composition.rb
68
+ - lib/proc/enumerator.rb
54
69
  - lib/proc/http/client.rb
55
70
  - lib/proc/http/request.rb
56
71
  - lib/proc/http/response.rb
57
- - lib/proc/null_logger.rb
58
72
  - lib/proc/version.rb
59
73
  homepage: https://proc.dev/
60
74
  licenses:
@@ -75,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
89
  - !ruby/object:Gem::Version
76
90
  version: '0'
77
91
  requirements: []
78
- rubygems_version: 3.1.4
92
+ rubygems_version: 3.2.4
79
93
  signing_key:
80
94
  specification_version: 4
81
95
  summary: Proc client library.
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Proc
4
- class NullLogger
5
- class << self
6
- def method_missing(*, **)
7
- end
8
-
9
- def respond_to_missing?(*)
10
- true
11
- end
12
- end
13
- end
14
- end