proc 0.1.1 → 0.5.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: 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