proc 0.1.0 → 0.4.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: 0f07ae5d056122c93826012896beb760372534d7c2c83193674580517ff31f86
4
- data.tar.gz: 5cd335938ec4121d5170c27518678d81c27dd3b65ff6b0235be72213e05613e2
3
+ metadata.gz: 63d146f97fd1dc28e5ff40ae924ac80e5f34838da4a3bb626b51023c2e23efbc
4
+ data.tar.gz: ebbab25f52bc3344c9fee6e10a00af6ed5022808d05f8516c41dc914987178d6
5
5
  SHA512:
6
- metadata.gz: '08b92c4b033fe05dea3a0db6321c5cea4ce5f8ce3587e3f1b7902b295e7778f95a433e84652f12d71f3eff0e60d2bc53d584cce308bd307e32b54f76ed3213e3'
7
- data.tar.gz: ef90d03381df7f8f016460cc83f15c14b3b1f9768dd5078588e85591e61d46df628fd63fd7e804d6d7e1975c7dd9749642f4b51ecaa5bf1e7c19896f64155d72
6
+ metadata.gz: 06f06ba059619e117f86021bfeda3b5520d6842664d5fb284c77e7920cae38b66d4e82874329243ee0e456216034a07111e04f8b49ffaf6828470eb8a0e7f2fc
7
+ data.tar.gz: d6b1423b158de5320e963c48a988db2936c5c576eb3e02b2ec9d2d675ff54ca6c428e270baaf566935bf8df2fd18b9b59e67449c5ed661d5c57d7a4b75a8f41b
@@ -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" => @name.to_s}.merge(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
@@ -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,27 +17,44 @@ 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.serialized_input, **callable.serialized_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)
28
- composed = Composition.new(client: @client)
40
+ composed = Composition.new(client: @client, input: @input)
29
41
  composed << self
30
42
  composed << other
31
43
  composed
32
44
  end
33
45
 
34
46
  def serialize
35
- {
36
- "{}" => {
37
- "<<" => serialize_value(@input),
38
- "[]" => [[@proc, serialized_arguments]]
39
- }
40
- }
47
+ wrapped = {"[]" => [[@proc, serialized_arguments]]}
48
+
49
+ unless Proc.undefined?(@input)
50
+ wrapped["<<"] = serialized_input
51
+ end
52
+
53
+ {"{}" => wrapped}
54
+ end
55
+
56
+ def serialized_input
57
+ serialize_value(@input)
41
58
  end
42
59
 
43
60
  def serialized_arguments
@@ -46,6 +63,15 @@ class Proc
46
63
  }
47
64
  end
48
65
 
66
+ def [](proc)
67
+ Callable.new(
68
+ [@proc, proc].join("."),
69
+ client: @client,
70
+ input: @input,
71
+ arguments: @arguments
72
+ )
73
+ end
74
+
49
75
  IGNORE_MISSING = %i[to_hash].freeze
50
76
 
51
77
  def method_missing(name, input = input_omitted = true, **arguments)
@@ -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,73 @@ class Proc
60
64
  @resets_at
61
65
  end
62
66
 
63
-
64
67
  DEFAULT_HEADERS = {
65
68
  "accept" => "application/json",
66
69
  "content-type" => "application/json"
67
70
  }.freeze
68
71
 
69
72
  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: :json), task: task)
83
-
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)
86
-
87
- payload = Oj.load(response.read, mode: :strict)
88
- rescue => error
89
- raise Proc::Unavailable, error.message
90
- ensure
91
- response&.close
92
- end
93
-
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
73
+ body = {"<>" => true}
74
+
75
+ unless Proc.undefined?(input)
76
+ body["<<"] = serialize_value(input)
77
+ end
78
+
79
+ arguments.each_pair do |key, value|
80
+ body[key.to_s] = serialize_value(value)
81
+ end
82
+
83
+ headers = {
84
+ "authorization" => "bearer #{@authorization}"
85
+ }.merge(DEFAULT_HEADERS)
86
+
87
+ payload = get_payload(proc: proc, headers: headers, body: body)
88
+
89
+ case payload["status"]
90
+ when 200
91
+ payload[">>"]
92
+ when 400
93
+ raise Proc::Invalid, payload.dig("error", "message")
94
+ when 401
95
+ raise Proc::Unauthorized, payload.dig("error", "message")
96
+ when 403
97
+ raise Proc::Forbidden, payload.dig("error", "message")
98
+ when 404
99
+ raise Proc::Undefined, payload.dig("error", "message")
100
+ when 408
101
+ raise Proc::Timeout, payload.dig("error", "message")
102
+ when 429
103
+ raise Proc::Limited, payload.dig("error", "message")
104
+ when 500
105
+ raise Proc::Error, payload.dig("error", "message")
106
+ when 508
107
+ raise Proc::Error, payload.dig("error", "message")
108
+ else
109
+ raise Proc::Error, "unhandled"
110
+ end
113
111
  end
114
112
 
115
- def method_missing(name, input = nil, **arguments)
116
- Callable.new(name, client: self, input: input, arguments: arguments)
113
+ def method_missing(name, input = input_omitted = true, *, **arguments)
114
+ if input_omitted
115
+ Callable.new(name, client: self, arguments: arguments)
116
+ else
117
+ Callable.new(name, client: self, input: input, arguments: arguments)
118
+ end
117
119
  end
118
120
 
119
121
  def respond_to_missing?(name, *)
120
122
  true
121
123
  end
122
124
 
125
+ def argument(name, **options)
126
+ Argument.new(name, **options)
127
+ end
128
+ alias_method :arg, :argument
129
+
130
+ def close
131
+ @internal.close
132
+ end
133
+
123
134
  private def build_uri(proc)
124
135
  host_and_path = File.join(@host, proc.to_s.split(".").join("/"))
125
136
 
@@ -133,5 +144,18 @@ class Proc
133
144
  value
134
145
  end
135
146
  end
147
+
148
+ private def get_payload(proc:, headers:, body:)
149
+ @internal.call(:post, build_uri(proc), headers: headers, body: Oj.dump(body, mode: :custom)) { |response|
150
+ @remaining = response.headers["x-rate-limit-remaining"].to_s.to_i
151
+ @resets_at = Time.at(response.headers["x-rate-limit-reset"].to_s.to_i)
152
+
153
+ payload = Oj.load(response.read, mode: :compat, compat_bigdecimal: true)
154
+ payload["status"] = response.status
155
+ payload
156
+ }
157
+ rescue
158
+ raise Proc::Unavailable
159
+ end
136
160
  end
137
161
  end
@@ -2,9 +2,13 @@
2
2
 
3
3
  class Proc
4
4
  class Composition
5
- def initialize(client:)
5
+ attr_reader :input, :callables, :arguments
6
+
7
+ def initialize(client:, input:, callables: [], arguments: {})
6
8
  @client = client
7
- @callables = []
9
+ @input = input
10
+ @callables = callables
11
+ @arguments = arguments
8
12
  end
9
13
 
10
14
  def initialize_copy(_)
@@ -12,7 +16,23 @@ class Proc
12
16
  end
13
17
 
14
18
  def call(input = input_omitted = true, **arguments)
15
- @client.call("exec", input_omitted ? @callables.first.input : input, proc: serialize)
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", nil, proc: callable.serialize)
27
+ end
28
+
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
+ )
16
36
  end
17
37
 
18
38
  def >>(other)
@@ -22,13 +42,59 @@ class Proc
22
42
  end
23
43
 
24
44
  def <<(callable)
25
- @callables << callable
45
+ case callable
46
+ when Composition
47
+ merge(callable)
48
+ when Callable
49
+ @callables << callable
50
+ end
26
51
  end
27
52
 
28
53
  def serialize
54
+ wrapped = {"[]" => serialized_calls}
55
+
56
+ unless Proc.undefined?(@input)
57
+ wrapped["<<"] = serialized_input
58
+ end
59
+
60
+ {"{}" => wrapped.merge(serialized_arguments)}
61
+ end
62
+
63
+ def serialized_calls
29
64
  @callables.map { |callable|
30
- [callable.proc, callable.serialized_arguments]
65
+ arguments = callable.serialized_arguments
66
+
67
+ unless Proc.undefined?(callable.input)
68
+ arguments["<<"] = callable.serialized_input
69
+ end
70
+
71
+ [callable.proc, arguments]
31
72
  }
32
73
  end
74
+
75
+ def serialized_input
76
+ serialize_value(@input)
77
+ end
78
+
79
+ def serialized_arguments
80
+ @arguments.each_pair.each_with_object({}) { |(key, value), hash|
81
+ hash[key.to_s] = serialize_value(value)
82
+ }
83
+ end
84
+
85
+ def merge(composition)
86
+ raise ArgumentError, "expected a composition" unless composition.is_a?(self.class)
87
+
88
+ @callables.concat(composition.callables)
89
+ @arguments.merge!(composition.arguments)
90
+ end
91
+
92
+ private def serialize_value(value)
93
+ if value.respond_to?(:serialize)
94
+ value.serialize
95
+ else
96
+ value
97
+ end
98
+ end
33
99
  end
34
100
  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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Proc
4
- VERSION = "0.1.0"
4
+ VERSION = "0.4.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.0
4
+ version: 0.4.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-01-20 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