proc 0.1.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63fefe7d8d6f97ef667ca24f07ef5f38b402c6982d783582d6ea869dbfc67c76
4
- data.tar.gz: ceaeee3d8d634ba9d9ba6771266554e8dc7b8662b819751520658b0e657dbfa8
3
+ metadata.gz: a3b65698431242010b57cb9897c5ddf826cbd9c81dea9a3947505062e8060aeb
4
+ data.tar.gz: bf1d1caaa7fffda8b8eac8d65c50b29a4c95003d980b8c83962f65bed8e1749e
5
5
  SHA512:
6
- metadata.gz: 1c44c09c6526286bbcc8e67fefd1c2d13cb5dec65b4471452eb07c9845f36e8c2aa92dcd7c5080ff7070a2a1228b6f3667c7d9616f35e37ba7d14ec9f882ac22
7
- data.tar.gz: a38fe68ec69ed2b086741d46728245d3351fe58ed26c956fcb9ec67257e9089b9e2e383a8551530bed293188f6e14ca9799ebaf57859163bf1b9fbd0bfb26f82
6
+ metadata.gz: ba18a8388e58dfe25119b412274b13eaf7cf684fc33e2ca186c8527c85a6f8973ecf01d39c9f56b861456646262b0a0fbaf9272feaec44ce178f328e1c6349ca
7
+ data.tar.gz: 77ecd00cf5f48da5e17e80da46b2f0145ec1c8ae87d710d0b423d3e1908f7fdbc13fe251b9efee67289b5f20e1711b81a2c186fc0a48a9042aaff02413a8cdc8
data/lib/proc.rb CHANGED
@@ -6,4 +6,12 @@ class Proc
6
6
  def self.connect(authorization, **options)
7
7
  Client.new(authorization, **options)
8
8
  end
9
+
10
+ def self.undefined
11
+ @_undefined ||= Object.new
12
+ end
13
+
14
+ def self.undefined?(value)
15
+ value == undefined
16
+ end
9
17
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ class Argument
5
+ def initialize(name, **options)
6
+ @name = name
7
+ @options = options
8
+ end
9
+
10
+ def serialize
11
+ ["@@", @name.to_s, serialized_options]
12
+ end
13
+
14
+ def serialized_options
15
+ @options.each_pair.each_with_object({}) { |(key, value), hash|
16
+ hash[key.to_s] = serialize_value(value)
17
+ }
18
+ end
19
+
20
+ private def serialize_value(value)
21
+ if value.respond_to?(:serialize)
22
+ value.serialize
23
+ else
24
+ ["%%", value]
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/proc/callable.rb CHANGED
@@ -4,8 +4,8 @@ class Proc
4
4
  class Callable
5
5
  attr_reader :proc, :input, :arguments
6
6
 
7
- def initialize(proc, client:, input: nil, arguments: {})
8
- @proc = proc
7
+ def initialize(proc, client:, input: Proc.undefined, arguments: {})
8
+ @proc = proc.to_s
9
9
  @client = client
10
10
  @input = input
11
11
  @arguments = arguments
@@ -17,11 +17,23 @@ class Proc
17
17
  end
18
18
 
19
19
  def call(input = input_omitted = true, **arguments)
20
- @client.call(@proc, input_omitted ? @input : input, **@arguments.merge(arguments))
20
+ callable = self.class.new(
21
+ @proc,
22
+ client: @client,
23
+ input: input_omitted ? @input : input,
24
+ arguments: @arguments.merge(arguments)
25
+ )
26
+
27
+ @client.call(@proc, callable.input, **callable.arguments)
21
28
  end
22
29
 
23
30
  def with(input = input_omitted = true, **arguments)
24
- self.class.new(@proc, client: @client, input: input_omitted ? @input : input, arguments: @arguments.merge(arguments))
31
+ self.class.new(
32
+ @proc,
33
+ client: @client,
34
+ input: input_omitted ? @input : input,
35
+ arguments: @arguments.merge(arguments)
36
+ )
25
37
  end
26
38
 
27
39
  def >>(other)
@@ -32,20 +44,36 @@ class Proc
32
44
  end
33
45
 
34
46
  def serialize
35
- {
36
- "{}" => {
37
- "<<" => serialize_value(@input),
38
- "[]" => [[@proc, serialized_arguments]]
39
- }
40
- }
47
+ serialized = ["()", @proc]
48
+
49
+ unless Proc.undefined?(@input)
50
+ serialized << [">>", serialized_input]
51
+ end
52
+
53
+ serialized.concat(serialized_arguments)
54
+
55
+ ["{}", serialized]
56
+ end
57
+
58
+ def serialized_input
59
+ serialize_value(@input)
41
60
  end
42
61
 
43
62
  def serialized_arguments
44
- @arguments.each_pair.each_with_object({}) { |(key, value), hash|
45
- hash[key.to_s] = serialize_value(value)
63
+ @arguments.map { |key, value|
64
+ ["$$", key.to_s, serialize_value(value)]
46
65
  }
47
66
  end
48
67
 
68
+ def [](proc)
69
+ Callable.new(
70
+ [@proc, proc].join("."),
71
+ client: @client,
72
+ input: @input,
73
+ arguments: @arguments
74
+ )
75
+ end
76
+
49
77
  IGNORE_MISSING = %i[to_hash].freeze
50
78
 
51
79
  def method_missing(name, input = input_omitted = true, **arguments)
@@ -73,7 +101,7 @@ class Proc
73
101
  if value.respond_to?(:serialize)
74
102
  value.serialize
75
103
  else
76
- value
104
+ ["%%", value]
77
105
  end
78
106
  end
79
107
  end
data/lib/proc/client.rb CHANGED
@@ -3,17 +3,19 @@
3
3
  require "async"
4
4
  require "oj"
5
5
 
6
+ require_relative "argument"
6
7
  require_relative "callable"
7
8
  require_relative "composition"
8
- require_relative "null_logger"
9
9
 
10
10
  require_relative "http/client"
11
11
 
12
+ Console.logger.off!
13
+
12
14
  class Proc
13
15
  class Error < StandardError
14
16
  end
15
17
 
16
- class ArgumentError < ::ArgumentError
18
+ class Invalid < ::ArgumentError
17
19
  end
18
20
 
19
21
  class Undefined < ::NameError
@@ -22,22 +24,24 @@ class Proc
22
24
  class Unauthorized < Error
23
25
  end
24
26
 
27
+ class Forbidden < Error
28
+ end
29
+
25
30
  class Unavailable < Error
26
31
  end
27
32
 
28
- class RateLimited < Error
33
+ class Limited < Error
29
34
  end
30
35
 
31
36
  class Timeout < Error
32
37
  end
33
38
 
34
- class Client < Http::Client
39
+ class Client
35
40
  def initialize(authorization, scheme: "https", host: "proc.dev")
36
41
  @authorization = authorization
37
42
  @scheme = scheme
38
43
  @host = host
39
-
40
- super()
44
+ @internal = Http::Client.new
41
45
  end
42
46
 
43
47
  def [](proc)
@@ -60,66 +64,75 @@ class Proc
60
64
  @resets_at
61
65
  end
62
66
 
63
-
64
67
  DEFAULT_HEADERS = {
65
- "accept" => "application/json",
66
- "content-type" => "application/json"
68
+ "accept" => "application/vnd.proc+json",
69
+ "content-type" => "application/vnd.proc+json"
67
70
  }.freeze
68
71
 
69
- def call(proc = nil, input = nil, **arguments)
70
- Async(logger: NullLogger) { |task|
71
- body = { "<<" => serialize_value(input) }
72
-
73
- arguments.each_pair do |key, value|
74
- body[key.to_s] = serialize_value(value)
75
- end
76
-
77
- headers = {
78
- "authorization" => "bearer #{@authorization}"
79
- }.merge(DEFAULT_HEADERS)
80
-
81
- begin
82
- response = super(:post, build_uri(proc), headers: headers, body: Oj.dump(body, mode: :custom), task: task)
72
+ def call(proc = nil, input = Proc.undefined, **arguments)
73
+ body = []
83
74
 
84
- @remaining = response.headers["x-rate-limit-remaining"].to_s.to_i
85
- @resets_at = Time.at(response.headers["x-rate-limit-reset"].to_s.to_i)
75
+ unless Proc.undefined?(input)
76
+ body << [">>", serialize_value(input)]
77
+ end
86
78
 
87
- payload = Oj.load(response.read, mode: :compat)
88
- rescue => error
89
- raise Proc::Unavailable, error.message
90
- ensure
91
- response&.close
92
- end
79
+ arguments.each_pair do |key, value|
80
+ body << ["$$", key.to_s, serialize_value(value)]
81
+ end
93
82
 
94
- case response.status
95
- when 200
96
- payload[">>"]
97
- when 400
98
- raise Proc::ArgumentError, payload.dig("error", "message")
99
- when 403
100
- raise Proc::Unauthorized, payload.dig("error", "message")
101
- when 404
102
- raise Proc::Undefined, payload.dig("error", "message")
103
- when 408
104
- raise Proc::Timeout, payload.dig("error", "message")
105
- when 429
106
- raise Proc::RateLimited, payload.dig("error", "message")
107
- when 500
108
- raise Proc::Error, payload.dig("error", "message")
109
- else
110
- raise Proc::Error, "unhandled"
111
- end
112
- }.wait
83
+ headers = {
84
+ "authorization" => "bearer #{@authorization}"
85
+ }.merge(DEFAULT_HEADERS)
86
+
87
+ status, payload = get_payload(proc: proc, headers: headers, body: body)
88
+
89
+ case status
90
+ when 200
91
+ extract_output(payload)
92
+ when 400
93
+ raise Proc::Invalid, extract_error_message(payload)
94
+ when 401
95
+ raise Proc::Unauthorized, extract_error_message(payload)
96
+ when 403
97
+ raise Proc::Forbidden, extract_error_message(payload)
98
+ when 404
99
+ raise Proc::Undefined, extract_error_message(payload)
100
+ when 408
101
+ raise Proc::Timeout, extract_error_message(payload)
102
+ when 413
103
+ raise Proc::Invalid, extract_error_message(payload)
104
+ when 429
105
+ raise Proc::Limited, extract_error_message(payload)
106
+ when 500
107
+ raise Proc::Error, extract_error_message(payload)
108
+ when 508
109
+ raise Proc::Error, extract_error_message(payload)
110
+ else
111
+ raise Proc::Error, "unhandled"
112
+ end
113
113
  end
114
114
 
115
- def method_missing(name, input = nil, **arguments)
116
- Callable.new(name, client: self, input: input, arguments: arguments)
115
+ def method_missing(name, input = input_omitted = true, *, **arguments)
116
+ if input_omitted
117
+ Callable.new(name, client: self, arguments: arguments)
118
+ else
119
+ Callable.new(name, client: self, input: input, arguments: arguments)
120
+ end
117
121
  end
118
122
 
119
123
  def respond_to_missing?(name, *)
120
124
  true
121
125
  end
122
126
 
127
+ def argument(name, **options)
128
+ Argument.new(name, **options)
129
+ end
130
+ alias_method :arg, :argument
131
+
132
+ def close
133
+ @internal.close
134
+ end
135
+
123
136
  private def build_uri(proc)
124
137
  host_and_path = File.join(@host, proc.to_s.split(".").join("/"))
125
138
 
@@ -130,8 +143,44 @@ class Proc
130
143
  if value.respond_to?(:serialize)
131
144
  value.serialize
132
145
  else
133
- value
146
+ ["%%", value]
147
+ end
148
+ end
149
+
150
+ private def get_payload(proc:, headers:, body:)
151
+ @internal.call(:post, build_uri(proc), headers: headers, body: Oj.dump(body, mode: :custom)) do |response|
152
+ @remaining = response.headers["x-rate-limit-remaining"].to_s.to_i
153
+ @resets_at = Time.at(response.headers["x-rate-limit-reset"].to_s.to_i)
154
+ [response.status, Oj.load(response.read, mode: :compat, compat_bigdecimal: true)]
155
+ end
156
+ rescue
157
+ raise Proc::Unavailable
158
+ end
159
+
160
+ private def extract_output(payload)
161
+ payload.each do |tuple|
162
+ case tuple[0]
163
+ when "<<"
164
+ return tuple[1]
165
+ end
134
166
  end
167
+
168
+ nil
169
+ end
170
+
171
+ private def extract_error(payload)
172
+ payload.each do |tuple|
173
+ case tuple[0]
174
+ when "!!"
175
+ return tuple[1]
176
+ end
177
+ end
178
+
179
+ nil
180
+ end
181
+
182
+ private def extract_error_message(payload)
183
+ extract_error(payload)&.dig("message")
135
184
  end
136
185
  end
137
186
  end
@@ -2,10 +2,13 @@
2
2
 
3
3
  class Proc
4
4
  class Composition
5
- def initialize(client:, input:, callables: [])
5
+ attr_reader :input, :callables, :arguments
6
+
7
+ def initialize(client:, input:, callables: [], arguments: {})
6
8
  @client = client
7
9
  @input = input
8
10
  @callables = callables
11
+ @arguments = arguments
9
12
  end
10
13
 
11
14
  def initialize_copy(_)
@@ -13,11 +16,23 @@ class Proc
13
16
  end
14
17
 
15
18
  def call(input = input_omitted = true, **arguments)
16
- @client.call("exec", input_omitted ? @input : input, proc: serialized_calls)
19
+ callable = self.class.new(
20
+ client: @client,
21
+ input: input_omitted ? @input : input,
22
+ callables: @callables.dup,
23
+ arguments: @arguments.merge(arguments)
24
+ )
25
+
26
+ @client.call("proc.exec", Proc.undefined, proc: callable)
17
27
  end
18
28
 
19
- def with(input = input_omitted = true)
20
- self.class.new(client: @client, input: input_omitted ? @input : input, callables: @callables.dup)
29
+ def with(input = input_omitted = true, **arguments)
30
+ self.class.new(
31
+ client: @client,
32
+ input: input_omitted ? @input : input,
33
+ callables: @callables.dup,
34
+ arguments: @arguments.merge(arguments)
35
+ )
21
36
  end
22
37
 
23
38
  def >>(other)
@@ -27,22 +42,67 @@ class Proc
27
42
  end
28
43
 
29
44
  def <<(callable)
30
- @callables << callable
45
+ case callable
46
+ when Composition
47
+ merge(callable)
48
+ when Callable
49
+ @callables << callable
50
+ end
31
51
  end
32
52
 
33
53
  def serialize
34
- {
35
- "{}" => {
36
- "<<" => @input,
37
- "[]" => serialized_calls
38
- }
39
- }
54
+ # wrapped = {"[]" => serialized_calls}
55
+
56
+ # unless Proc.undefined?(@input)
57
+ # wrapped["<<"] = serialized_input
58
+ # end
59
+
60
+ # {"{}" => wrapped.merge(serialized_arguments)}
61
+
62
+ serialized = ["{}"]
63
+
64
+ unless Proc.undefined?(@input)
65
+ serialized << [">>", serialized_input]
66
+ end
67
+
68
+ serialized + serialized_arguments + serialized_calls
40
69
  end
41
70
 
42
71
  def serialized_calls
43
72
  @callables.map { |callable|
44
- [callable.proc, callable.serialized_arguments]
73
+ serialized = ["()", callable.proc]
74
+
75
+ unless Proc.undefined?(callable.input)
76
+ serialized << [">>", callable.serialized_input]
77
+ end
78
+
79
+ serialized.concat(callable.serialized_arguments)
45
80
  }
46
81
  end
82
+
83
+ def serialized_input
84
+ serialize_value(@input)
85
+ end
86
+
87
+ def serialized_arguments
88
+ @arguments.map { |key, value|
89
+ ["$$", key.to_s, serialize_value(value)]
90
+ }
91
+ end
92
+
93
+ def merge(composition)
94
+ raise ArgumentError, "expected a composition" unless composition.is_a?(self.class)
95
+
96
+ @callables.concat(composition.callables)
97
+ @arguments.merge!(composition.arguments)
98
+ end
99
+
100
+ private def serialize_value(value)
101
+ if value.respond_to?(:serialize)
102
+ value.serialize
103
+ else
104
+ ["%%", value]
105
+ end
106
+ end
47
107
  end
48
108
  end
@@ -5,50 +5,45 @@ require "async/http/internet"
5
5
 
6
6
  require "protocol/http/body/streamable"
7
7
 
8
+ require "core/async"
9
+
8
10
  require_relative "request"
9
11
  require_relative "response"
10
12
 
11
- require_relative "../null_logger"
12
-
13
13
  class Proc
14
14
  module Http
15
15
  class Client
16
+ include Is::Async
17
+
16
18
  def initialize
17
19
  @internet = Async::HTTP::Internet.new
18
- @responses = {}
19
20
  end
20
21
 
21
- def call(method, uri, params: {}, headers: {}, body: nil, task: nil)
22
+ def call(method, uri, params: {}, headers: {}, body: nil)
22
23
  request = Request.new(method: method, uri: uri, params: params, headers: headers, body: body)
23
24
 
24
- if task
25
- make_request(request)
26
- else
27
- Async(logger: NullLogger) {
28
- make_request(request)
29
- }.wait
30
- end
25
+ await {
26
+ begin
27
+ response = make_request(request)
28
+
29
+ yield response
30
+ ensure
31
+ response&.close
32
+ end
33
+ }
31
34
  end
32
35
 
33
36
  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
37
  @internet.close
39
38
  end
40
39
 
41
- def count
42
- @responses.count
43
- end
44
-
45
40
  private def make_request(request)
46
41
  async_response = @internet.call(*request.callable)
47
42
  wrap_async_response_for_request(async_response, request)
48
43
  end
49
44
 
50
45
  private def wrap_async_response_for_request(async_response, request)
51
- @responses[async_response] = Response.new(request, async_response)
46
+ Response.new(request, async_response)
52
47
  end
53
48
  end
54
49
  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.1.1"
4
+ VERSION = "0.5.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.1.1
4
+ version: 0.5.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-10-26 00:00:00.000000000 Z
11
+ date: 2021-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.52.5
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.0.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.0.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: oj
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -47,13 +61,13 @@ files:
47
61
  - LICENSE
48
62
  - README.md
49
63
  - lib/proc.rb
64
+ - lib/proc/argument.rb
50
65
  - lib/proc/callable.rb
51
66
  - lib/proc/client.rb
52
67
  - lib/proc/composition.rb
53
68
  - lib/proc/http/client.rb
54
69
  - lib/proc/http/request.rb
55
70
  - lib/proc/http/response.rb
56
- - lib/proc/null_logger.rb
57
71
  - lib/proc/version.rb
58
72
  homepage: https://proc.dev/
59
73
  licenses:
@@ -74,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
88
  - !ruby/object:Gem::Version
75
89
  version: '0'
76
90
  requirements: []
77
- rubygems_version: 3.1.2
91
+ rubygems_version: 3.1.4
78
92
  signing_key:
79
93
  specification_version: 4
80
94
  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