proc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9b1cac83c7cff9f09dc94086b6b58e43cf686c4faa28e95aecda09c73e9eebbc
4
+ data.tar.gz: e349377c73af33cdff5dfb488dbfd199ec37b9c1db84132e82dde7d65889b71b
5
+ SHA512:
6
+ metadata.gz: e9b7322deb5945ddac4cff0c12c444d8c90d88b917619176bc2983370e215f54ebafdf83e82a4778424fc94a9ed67149596ebb7fd7378d0bea8d0fcdac611a67
7
+ data.tar.gz: 91286717d2ee7d67a8021f3aedf2debec4c2da88e2f9abbe34dadd751f02c64f227a40dbdb8e3f6b95ed0ca5ead33557b096683594b3b5e32f10aab5eaf23367
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ This software is licensed under the MIT License.
2
+
3
+ Copyright 2020 Metabahn.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a
6
+ copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to permit
10
+ persons to whom the Software is furnished to do so, subject to the
11
+ following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included
14
+ in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
19
+ NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,61 @@
1
+ # Pass `token: "..."` or default to `PROC_ACCESS_TOKEN`:
2
+ #
3
+ client = Proc.connect
4
+
5
+ # Not chainable, returns the value:
6
+ #
7
+ client.stdlib.echo("bar")
8
+ # => bar
9
+
10
+ # Create chains using `chain` (perhaps alias as `with`):
11
+ #
12
+ client.chain("foo") { |chain| chain.stdlib.reverse.capitalize.truncate(2) }
13
+ # => Oo
14
+
15
+ # Or return a chain to pass around:
16
+ #
17
+ chain = client.chain("foo")
18
+ # => <Proc::Chain ...>
19
+ chain.stdlib.echo("bar")
20
+
21
+ # Call `value` or `perform` to evaluate the chain:
22
+ #
23
+ chain.value
24
+
25
+ # Calling a non-existent proc raises a Proc::NameError,
26
+ #
27
+ chain.nonexistent
28
+
29
+ # Pass values through different contexts:
30
+ #
31
+ client.chain("foo") { |chain| chain.stdlib.reverse >> chain.whatever.other }
32
+
33
+ # Use from the cli (included with the ruby gem OR as a go library):
34
+ #
35
+ # proc echo "bar" -t access-token
36
+ # proc chain "bar" "stdlib.reverse >> whatever.other" -t access-token
37
+
38
+ #########################################################
39
+ # alternatives so that we don't have to fetch metadata: #
40
+ #########################################################
41
+
42
+ proc = Proc.connect
43
+
44
+ # I like this because it's obvious how to get a reference as well as just make the call.
45
+ #
46
+ proc["stdlib.echo"].call("foo")
47
+
48
+ # This works for simple composition, where the value is simply passed through.
49
+ #
50
+ my_proc = proc["stdlib.reverse"] >> proc["stdlib.capitalize"]
51
+
52
+ my_proc.call("foo")
53
+
54
+ # Perhaps something like this, where we compose a proc with arguments. Each is implicitly called, or
55
+ # call explicitly to pass an argument to the proc. I sort of like the simplicity of this.
56
+ #
57
+ my_proc = proc.compose { |context, length:|
58
+ context["stdlib.reverse"] >> context["stdlib.capitalize"] >> context["stdlib.truncate"].call(length)
59
+ }
60
+
61
+ my_proc.call("foo", length: 2)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TODO: Introduce inspectable everywhere (just bundle from pakyow).
4
+
5
+ require_relative "proc/client"
6
+
7
+ class Proc
8
+ def self.connect(authorization, **options)
9
+ Client.new(authorization, **options)
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ class Callable
5
+ attr_reader :proc
6
+
7
+ def initialize(proc, client:)
8
+ @proc = proc
9
+ @client = client
10
+ end
11
+
12
+ def call(input = nil, **arguments)
13
+ @client.perform(@proc, input, **arguments)
14
+ end
15
+
16
+ def >>(callable)
17
+ composed = Composition.new(client: @client)
18
+ composed << self
19
+ composed << callable
20
+ composed
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "oj"
4
+
5
+ require_relative "callable"
6
+ require_relative "composition"
7
+
8
+ require_relative "http/client"
9
+
10
+ class Proc
11
+ class Error < StandardError
12
+ end
13
+
14
+ class ArgumentError < ::ArgumentError
15
+ end
16
+
17
+ class Undefined < ::NameError
18
+ end
19
+
20
+ class Unauthorized < Error
21
+ end
22
+
23
+ class Unavailable < Error
24
+ end
25
+
26
+ class Client < Http::Client
27
+ def initialize(authorization, scheme: "https", host: "proc.dev")
28
+ @authorization = authorization
29
+ @scheme = scheme
30
+ @host = host
31
+
32
+ super()
33
+ end
34
+
35
+ def [](proc)
36
+ Callable.new(proc, client: self)
37
+ end
38
+
39
+ def compose(&block)
40
+ Composition.new(client: self, &block)
41
+ end
42
+
43
+ DEFAULT_HEADERS = {
44
+ "content-type" => "application/json"
45
+ }.freeze
46
+
47
+ def perform(proc, input, **arguments)
48
+ body = {
49
+ input: input, arguments: arguments
50
+ }
51
+
52
+ headers = {
53
+ "authorization" => "Bearer #{@authorization}"
54
+ }.merge(DEFAULT_HEADERS)
55
+
56
+ begin
57
+ response = call(:post, build_uri(proc), headers: headers, body: Oj.dump(body, mode: :json))
58
+ rescue => error
59
+ raise Proc::Unavailable, error.message
60
+ end
61
+
62
+ payload = if response.body
63
+ Oj.load(response.body.read, mode: :strict)
64
+ end
65
+
66
+ case response.status
67
+ when 200
68
+ payload["value"]
69
+ when 400
70
+ raise Proc::ArgumentError, payload.dig("error", "message")
71
+ when 403
72
+ raise Proc::Unauthorized, payload.dig("error", "message")
73
+ when 404
74
+ raise Proc::Undefined, payload.dig("error", "message")
75
+ when 500
76
+ raise Proc::Error, payload.dig("error", "message")
77
+ end
78
+ end
79
+
80
+ private def build_uri(proc)
81
+ host_and_path = File.join(@host, proc.split(".").join("/"))
82
+
83
+ "#{@scheme}://#{host_and_path}"
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ class Composition
5
+ require_relative "composition/deferable"
6
+ require_relative "composition/evaluator"
7
+
8
+ def initialize(client:, &block)
9
+ @client = client
10
+ @block = block
11
+ @callables = []
12
+ end
13
+
14
+ def initialize_copy(_)
15
+ @callables = @callables.dup
16
+ end
17
+
18
+ def call(input, **arguments)
19
+ if @block
20
+ evaluator = @block.call(Evaluator.new, **arguments)
21
+ @client.perform("compose", input, procs: evaluator.serialize.concat(serialize))
22
+ else
23
+ @client.perform("compose", input, procs: serialize)
24
+ end
25
+ end
26
+
27
+ def >>(callable)
28
+ composed = dup
29
+ composed << callable
30
+ composed
31
+ end
32
+
33
+ def <<(callable)
34
+ @callables << callable
35
+ end
36
+
37
+ protected def serialize
38
+ @callables.map(&:proc)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ class Composition
5
+ class Deferable
6
+ attr_reader :proc
7
+ attr_writer :arguments
8
+
9
+ def initialize(proc)
10
+ @proc = proc
11
+ @arguments = {}
12
+ end
13
+
14
+ def serialize
15
+ [@proc, @arguments]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ class Composition
5
+ class Evaluator
6
+ attr_reader :callables
7
+
8
+ def initialize
9
+ @callables = []
10
+ end
11
+
12
+ def initialize_copy(_)
13
+ @callables = @callables.map(&:dup)
14
+ end
15
+
16
+ def [](proc)
17
+ evaluator = dup
18
+ evaluator << Deferable.new(proc)
19
+ evaluator
20
+ end
21
+
22
+ def <<(proc)
23
+ @callables << proc
24
+ end
25
+
26
+ def >>(callable)
27
+ evaluator = dup
28
+
29
+ case callable
30
+ when Evaluator
31
+ callable.callables.each do |each_callable|
32
+ evaluator << each_callable
33
+ end
34
+ when Callable
35
+ evaluator << Deferable.new(callable.proc)
36
+ end
37
+
38
+ evaluator
39
+ end
40
+
41
+ def call(**arguments)
42
+ evaluator = dup
43
+ evaluator.callables.last.arguments = arguments
44
+ evaluator
45
+ end
46
+
47
+ def serialize
48
+ @callables.map(&:serialize)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "async"
4
+ require "async/http/internet"
5
+
6
+ require "protocol/http/body/streamable"
7
+
8
+ require_relative "request"
9
+ require_relative "response"
10
+
11
+ class Proc
12
+ module Http
13
+ class Client
14
+ class NullLogger
15
+ class << self
16
+ def method_missing(*, **)
17
+ end
18
+
19
+ def respond_to_missing?(*)
20
+ true
21
+ end
22
+ end
23
+ end
24
+
25
+ def initialize
26
+ @internet = Async::HTTP::Internet.new
27
+ @responses = {}
28
+ end
29
+
30
+ def call(method, uri, params: {}, headers: {}, body: nil)
31
+ request = Request.new(method: method, uri: uri, params: params, headers: headers, body: body)
32
+
33
+ Async(logger: NullLogger) {
34
+ async_request = @internet.call(*request.callable)
35
+ wrap_async_response_for_request(async_request, request)
36
+ }.wait
37
+ end
38
+
39
+ def close
40
+ @responses.each_value(&:close)
41
+ @responses.clear
42
+ @internet.close
43
+ end
44
+
45
+ def count
46
+ @responses.count
47
+ end
48
+
49
+ private def wrap_async_response_for_request(async_response, request)
50
+ Protocol::HTTP::Body::Streamable.wrap(async_response) do
51
+ @responses.delete(async_response)
52
+ end
53
+
54
+ response = Response.new(
55
+ request: request,
56
+ status: async_response.status,
57
+ version: async_response.version,
58
+ headers: async_response.headers,
59
+ body: async_response.body
60
+ )
61
+
62
+ @responses[async_response] = response
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ module Http
5
+ class Request
6
+ attr_reader :method, :uri, :params, :headers, :body
7
+
8
+ def initialize(method:, uri:, params:, headers:, body:)
9
+ @method = method
10
+ @uri = uri
11
+ @params = params
12
+ @headers = headers
13
+ @body = body
14
+ end
15
+
16
+ def callable
17
+ return @method.to_s.upcase, finalize_uri(@uri, @params), @headers, @body
18
+ end
19
+
20
+ private def finalize_uri(uri, params)
21
+ "#{uri}#{query_string(params)}"
22
+ end
23
+
24
+ private def query_string(params)
25
+ params = params.compact
26
+
27
+ if params.any?
28
+ "?" + params.compact.map { |key, value|
29
+ "#{key}=#{value}"
30
+ }.join("&")
31
+ else
32
+ ""
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ module Http
5
+ class Response
6
+ attr_reader :request, :status, :version, :headers, :body
7
+
8
+ def initialize(request:, status:, version:, headers:, body:)
9
+ @request = request
10
+ @status = status
11
+ @version = version
12
+ @headers = headers
13
+ @body = body
14
+
15
+ @stream = nil
16
+ @stream_blocks = []
17
+ end
18
+
19
+ def stream(&block)
20
+ @stream_blocks << block
21
+
22
+ @stream ||= Async {
23
+ @body.each do |chunk|
24
+ @stream_blocks.each do |stream_callback|
25
+ stream_callback.call(chunk)
26
+ end
27
+ end
28
+ }
29
+ end
30
+
31
+ def close
32
+ @stream&.stop
33
+ @stream_blocks.clear
34
+ @body.close
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Proc
4
+ VERSION = "0.0.1"
5
+
6
+ def self.version
7
+ VERSION
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Bryan Powell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-10-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: async-http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.52.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.52.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: oj
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.10'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.10'
41
+ description: Proc client library.
42
+ email: bryan@metabahn.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - LICENSE
48
+ - lib/examples/scratch.rb
49
+ - lib/proc.rb
50
+ - lib/proc/callable.rb
51
+ - lib/proc/client.rb
52
+ - lib/proc/composition.rb
53
+ - lib/proc/composition/deferable.rb
54
+ - lib/proc/composition/evaluator.rb
55
+ - lib/proc/http/client.rb
56
+ - lib/proc/http/request.rb
57
+ - lib/proc/http/response.rb
58
+ - lib/proc/version.rb
59
+ homepage: https://proc.dev/
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 2.5.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.1.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Proc client library.
82
+ test_files: []