fray 0.0.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 +7 -0
- data/.gitignore +4 -0
- data/Gemfile +5 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config.ru +10 -0
- data/fray.gemspec +29 -0
- data/lib/fray.rb +8 -0
- data/lib/fray/abortable_pipe.rb +69 -0
- data/lib/fray/data.rb +41 -0
- data/lib/fray/data/dataset.rb +14 -0
- data/lib/fray/data/error_set.rb +19 -0
- data/lib/fray/data/filter.rb +6 -0
- data/lib/fray/data/query.rb +15 -0
- data/lib/fray/data/response.rb +6 -0
- data/lib/fray/data_structures/dataset.json +78 -0
- data/lib/fray/data_structures/error.json +18 -0
- data/lib/fray/data_structures/query.json +41 -0
- data/lib/fray/data_structures/request.json +55 -0
- data/lib/fray/data_structures/response.json +23 -0
- data/lib/fray/response_pipe.rb +38 -0
- data/lib/fray/version.rb +3 -0
- data/spec/fray_spec.rb +7 -0
- data/spec/lib/fray/abortable_pipe_spec.rb +65 -0
- data/spec/lib/fray/data_spec.rb +26 -0
- data/spec/lib/fray/response_pipe_spec.rb +75 -0
- data/spec/spec_helper.rb +9 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cbc5b801abec6fcb3adf401b109ba2139f77b551
|
4
|
+
data.tar.gz: fe0a8696dadca85d74ad19d58a2d323bd5642f74
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec0e0517aa367e0f83045a3ced7cdfd5d357d0b4a0cbec60ad6746ae9090cc1150f729ea7445bc981bf064539ff1b4f33425668ae1a720d9a6a5ad6c46f12ef5
|
7
|
+
data.tar.gz: 4100efae0591910023ad5f50b15f56789613880ea5f4acedd1e633dd48c8a777e2a32614e424ae3bcbd7d20054234d644f5d35b5e285c228cd521d42f77f856f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "fray"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require "pry"
|
11
|
+
Pry.start
|
12
|
+
|
13
|
+
# require "irb"
|
14
|
+
# IRB.start
|
data/bin/setup
ADDED
data/config.ru
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
app = ->(env) do
|
7
|
+
['200', {'Content-Type' => 'text/html'}, [Rack::Utils::parse_nested_query(env['QUERY_STRING']).inspect]]
|
8
|
+
end
|
9
|
+
|
10
|
+
Rack::Handler::WEBrick.run app
|
data/fray.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# stub: arcgis-ruby 0.0.1 ruby lib
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "fray"
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.require_paths = ["lib"]
|
12
|
+
s.authors = ["Jake Sower"]
|
13
|
+
s.date = Time.now.strftime("%Y-%m-%d")
|
14
|
+
s.email = "jakesower@gmail.com"
|
15
|
+
s.summary = "API building blocks."
|
16
|
+
s.homepage = 'https://github.com/jakesower/fray'
|
17
|
+
s.license = 'apache'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
|
24
|
+
s.add_dependency 'json-schema', '~> 2.6'
|
25
|
+
|
26
|
+
s.add_development_dependency 'rspec', '~> 3'
|
27
|
+
s.add_development_dependency 'rake', '~> 11'
|
28
|
+
s.add_development_dependency 'pry', '~> 0.10'
|
29
|
+
end
|
data/lib/fray.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#
|
2
|
+
# Functor for handling errors as part of a chain of functions. Works much like
|
3
|
+
# the pipe operator |> from Elixir, but also handles responses. If a function
|
4
|
+
# within a `then` returns somethings that fulfills the initializing predicate,
|
5
|
+
# the remainder of the pipe will not be used. Rather, the `catch` function will
|
6
|
+
# be called (like with a JS promise). This function defaults to the identity
|
7
|
+
# function.
|
8
|
+
#
|
9
|
+
# Anything that responds to #call can be passed to `then` or `catch` as an
|
10
|
+
# argument, including vanilla lambdas.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# AbortablePipe.new(->(v) { v.is_a?(Fray::Response) }).
|
15
|
+
# then(->(x) { x + 1 }).
|
16
|
+
# then(->(x, y) { x * y }, 2). # note how extra params are handled
|
17
|
+
# catch(->(_) { 5 })
|
18
|
+
# run(4)
|
19
|
+
#
|
20
|
+
# Returns 10
|
21
|
+
#
|
22
|
+
# AbortablePipe.new(->(v) { v.is_a?(Fray::Response) }).
|
23
|
+
# abort_when().
|
24
|
+
# then(->(x) { Fray::Response.new('hi') }).
|
25
|
+
# then(->(x) { x * 2 }).
|
26
|
+
# catch(->(_) { 5 })
|
27
|
+
# run(4)
|
28
|
+
#
|
29
|
+
# Returns 5
|
30
|
+
#
|
31
|
+
module Fray
|
32
|
+
class AbortablePipe
|
33
|
+
def initialize(abort_predicate, thens = [], c = nil)
|
34
|
+
@abort_predicate = abort_predicate
|
35
|
+
@thens = thens
|
36
|
+
@catch = c || ->(x) { x }
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def then(func, *args)
|
41
|
+
new_then = {
|
42
|
+
function: func,
|
43
|
+
args: args
|
44
|
+
}
|
45
|
+
|
46
|
+
AbortablePipe.new(@abort_predicate, @thens + [new_then], @catch)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def catch(func)
|
51
|
+
AbortablePipe.new(@abort_predicate, @thens, func)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def run(state = nil, thens = @thens)
|
56
|
+
if thens.empty?
|
57
|
+
state
|
58
|
+
else
|
59
|
+
first, rest = thens[0], thens[1..-1]
|
60
|
+
next_state = first[:function].call(state, *first[:args])
|
61
|
+
|
62
|
+
@abort_predicate.call(next_state) ?
|
63
|
+
@catch.call(next_state) :
|
64
|
+
run(next_state, rest)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/fray/data.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# This module simply reads the files in data_structures and reifies them as
|
3
|
+
# Structs with on-creation validations. The data strcture files use JSON schema
|
4
|
+
# for a format: http://json-schema.org
|
5
|
+
#
|
6
|
+
module Fray::Data
|
7
|
+
class Base < OpenStruct
|
8
|
+
def initialize(hash)
|
9
|
+
if JSON::Validator.validate(__schema, hash)
|
10
|
+
super(JSON.parse(JSON.generate(hash)))
|
11
|
+
self.freeze
|
12
|
+
else
|
13
|
+
e = "#{hash.inspect} failed schema validation:\n\n" +
|
14
|
+
JSON::Validator.fully_validate(__schema, hash).join("\n")
|
15
|
+
raise ArgumentError, e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def self.define_schema_class(name, schema)
|
22
|
+
const_name = name.split('_').map{|chunk| chunk[0].upcase + chunk[1..-1]}.join('')
|
23
|
+
klass = Class.new(Base) do
|
24
|
+
define_method :__schema do
|
25
|
+
schema
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Fray::Data.const_set(const_name.to_sym, klass.freeze)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
#
|
35
|
+
# Run through the json schemas and compile them
|
36
|
+
#
|
37
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
38
|
+
Dir["#{dir}/data_structures/*.json"].each do |path|
|
39
|
+
file_name = File.basename(path, '.json')
|
40
|
+
Fray::Data.define_schema_class(file_name, JSON.parse(File.read(path)))
|
41
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Fray::Data
|
2
|
+
class Dataset < Dry::Types::Struct
|
3
|
+
attribute :code, Types::Coercible::String.enum(
|
4
|
+
*((100..102).map(&:to_s) +
|
5
|
+
(200..206).map(&:to_s) +
|
6
|
+
(300..308).map(&:to_s) +
|
7
|
+
(400..417).map(&:to_s) + ['429'] +
|
8
|
+
(500..504).map(&:to_s))
|
9
|
+
)
|
10
|
+
|
11
|
+
attribute :headers, Types::Hash.default({})
|
12
|
+
attribute :body, Types::String.default('')
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# This module simply reads the files in data_structures and reifies them as
|
3
|
+
# Structs with on-creation validations. The data strcture files use JSON schema
|
4
|
+
# for a format: http://json-schema.org
|
5
|
+
#
|
6
|
+
module Fray::Data
|
7
|
+
class ErrorSet < Array
|
8
|
+
def initialize(array)
|
9
|
+
if array.all?{|elt| elt.is_a?(Fray::Data::Error)}
|
10
|
+
super(array)
|
11
|
+
self.freeze
|
12
|
+
else
|
13
|
+
msg = "All elements of a Fray::Data::ErrorSet must be instances of Fray::Data::Error. Got: #{array.inspect}"
|
14
|
+
|
15
|
+
raise ArgumentError, msg
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fray/data_structures/filter'
|
2
|
+
|
3
|
+
module Fray::Data
|
4
|
+
class Query < Dry::Types::Struct
|
5
|
+
attribute :search, Types::Maybe::Coercible::String.default('')
|
6
|
+
attribute :include, Types::Maybe::Strict::Array
|
7
|
+
attribute :filters, Types::Array.member(Fray::Filter)
|
8
|
+
attribute :sort, Types::Maybe::Coercible::Hash
|
9
|
+
attribute :fields, Types::Array.member(Types::String)
|
10
|
+
attribute :page, Types::Hash.schema(
|
11
|
+
size: Types::Coercible::Int,
|
12
|
+
number: Types::Coercible::Int
|
13
|
+
).default({size: 10, number: 1})
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Fray Dataset Schema",
|
4
|
+
"description": "Schema representing the results of a query",
|
5
|
+
"type": "object",
|
6
|
+
"required": [ "resource_type", "resources", "related" ],
|
7
|
+
"properties": {
|
8
|
+
"resource_type": {
|
9
|
+
"type": "string"
|
10
|
+
},
|
11
|
+
"resources": {
|
12
|
+
"description": "An array containing all of the *primary* resource results",
|
13
|
+
"type": "array",
|
14
|
+
"items": {
|
15
|
+
"$ref": "#/definitions/resource"
|
16
|
+
},
|
17
|
+
"uniqueItems": true
|
18
|
+
},
|
19
|
+
"related": {
|
20
|
+
"description": "An object containing linked resources by resource type",
|
21
|
+
"type": "object",
|
22
|
+
"patternProperties": {
|
23
|
+
"^[A-Za-z0-9_]+$": {
|
24
|
+
"description": "Object of resources of a particular type",
|
25
|
+
"type": "object",
|
26
|
+
"patternProperties": {
|
27
|
+
"^[A-Za-z0-9_]+": {
|
28
|
+
"description": "The resource with a type and ID",
|
29
|
+
"type": {
|
30
|
+
"$ref": "#/definitions/resource" }}}}}},
|
31
|
+
"statistics": {
|
32
|
+
"description": "All statistical meta-information about the request",
|
33
|
+
"type": "object",
|
34
|
+
"properties": {
|
35
|
+
"count": { "type": "integer" },
|
36
|
+
"total_count": { "type": "integer" }
|
37
|
+
}
|
38
|
+
},
|
39
|
+
"meta": {
|
40
|
+
"description": "Non-standard meta-information",
|
41
|
+
"type": "object",
|
42
|
+
"additionalProperties": true
|
43
|
+
}
|
44
|
+
},
|
45
|
+
|
46
|
+
"definitions": {
|
47
|
+
"resource": {
|
48
|
+
"description": "A single resource",
|
49
|
+
"type": "object",
|
50
|
+
"required": [ "id", "type", "attributes", "relationships" ],
|
51
|
+
"properties": {
|
52
|
+
"id": { "type": "string" },
|
53
|
+
"type": { "type": "string" },
|
54
|
+
"attributes": {
|
55
|
+
"type": "object",
|
56
|
+
"additionalProperties": true
|
57
|
+
},
|
58
|
+
"relationships": {
|
59
|
+
"description": "Related resources by type",
|
60
|
+
"type": "object",
|
61
|
+
"patternProperties": {
|
62
|
+
"^[A-Za-z0-9_]+$": {
|
63
|
+
"oneOf": [
|
64
|
+
{
|
65
|
+
"type": "string"
|
66
|
+
},
|
67
|
+
{
|
68
|
+
"type": "array",
|
69
|
+
"items": { "type": "string" }
|
70
|
+
}
|
71
|
+
]
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Fray Error",
|
4
|
+
"description": "Schema representing a single error that occured",
|
5
|
+
"type": "object",
|
6
|
+
"required": [
|
7
|
+
"status"
|
8
|
+
],
|
9
|
+
|
10
|
+
"properties": {
|
11
|
+
"status": {
|
12
|
+
"description": "HTTP status code (4xx or 5xx)",
|
13
|
+
"type": "string",
|
14
|
+
"pattern": "^[45][0-9]{2}$"
|
15
|
+
}
|
16
|
+
},
|
17
|
+
"additionalProperties": true
|
18
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Fray Query",
|
4
|
+
"description": "Schema representing query to be sent to a store",
|
5
|
+
"type": "object",
|
6
|
+
"required": [
|
7
|
+
"resource_type"
|
8
|
+
],
|
9
|
+
|
10
|
+
"properties": {
|
11
|
+
"resource_type": {
|
12
|
+
"description": "The type of the primary resource(s)",
|
13
|
+
"type": "string"
|
14
|
+
},
|
15
|
+
"id": {
|
16
|
+
"description": "The ID of a single resource (used when appropriate)",
|
17
|
+
"type": "string"
|
18
|
+
},
|
19
|
+
"fields": {
|
20
|
+
"description": "List of fields to restrict results to (if absent, all fields will be included)",
|
21
|
+
"type": "array",
|
22
|
+
"items": { "type": "string" }
|
23
|
+
},
|
24
|
+
"include": {
|
25
|
+
"description": "Resource types related to the primary",
|
26
|
+
"type": "string"
|
27
|
+
},
|
28
|
+
"filters": {
|
29
|
+
"description": "Filters to be applied to the query",
|
30
|
+
"type": "object"
|
31
|
+
},
|
32
|
+
"page": {
|
33
|
+
"type": "object",
|
34
|
+
"required": [ "number", "size" ],
|
35
|
+
"properties": {
|
36
|
+
"number": { "type": "string" },
|
37
|
+
"size": { "type": "string" }
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Fray Request",
|
4
|
+
"description": "Schema representing an initial request for processing",
|
5
|
+
"type": "object",
|
6
|
+
"required": [
|
7
|
+
"resource_type",
|
8
|
+
"uri",
|
9
|
+
"root_uri",
|
10
|
+
"headers",
|
11
|
+
"query_parameters"
|
12
|
+
],
|
13
|
+
|
14
|
+
"properties": {
|
15
|
+
"resource_type": {
|
16
|
+
"description": "The type of the primary resource(s)",
|
17
|
+
"type": "string"
|
18
|
+
},
|
19
|
+
"id": {
|
20
|
+
"description": "The ID of a single resource (used when appropriate)",
|
21
|
+
"type": "string"
|
22
|
+
},
|
23
|
+
"uri": {
|
24
|
+
"description": "The protocol, domain, and path of the incoming request",
|
25
|
+
"type": "string",
|
26
|
+
"format": "uri"
|
27
|
+
},
|
28
|
+
"root_uri": {
|
29
|
+
"description": "The root of the domain (used for links)",
|
30
|
+
"type": "string",
|
31
|
+
"format": "uri"
|
32
|
+
},
|
33
|
+
"headers": {
|
34
|
+
"description": "HTTP headers",
|
35
|
+
"type": "object",
|
36
|
+
"additionalProperties": true
|
37
|
+
},
|
38
|
+
"query_parameters": {
|
39
|
+
"description": "Query parameters from both query string and body",
|
40
|
+
"type": "object",
|
41
|
+
"required": [],
|
42
|
+
"properties": {
|
43
|
+
"page": {
|
44
|
+
"type": "object",
|
45
|
+
"required": [ "number", "size" ],
|
46
|
+
"properties": {
|
47
|
+
"number": { "type": "string" },
|
48
|
+
"size": { "type": "string" }
|
49
|
+
}
|
50
|
+
}
|
51
|
+
},
|
52
|
+
"additionalProperties": true
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Fray Response",
|
4
|
+
"description": "Schema representing a successful result of a Fray pipe",
|
5
|
+
"type": "object",
|
6
|
+
"required": [ "status", "headers", "body" ],
|
7
|
+
"properties": {
|
8
|
+
"status": {
|
9
|
+
"description": "HTTP status code",
|
10
|
+
"type": "string",
|
11
|
+
"pattern": "^[1-5][0-9]{2}$"
|
12
|
+
},
|
13
|
+
"headers": {
|
14
|
+
"description": "HTTP headers",
|
15
|
+
"type": "object",
|
16
|
+
"additionalProperties": true
|
17
|
+
},
|
18
|
+
"body": {
|
19
|
+
"description": "The contents of the HTTP body",
|
20
|
+
"type": "string"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#
|
2
|
+
# Functor for handling errors as part of a chain of functions. Works much like
|
3
|
+
# the pipe operator |> from Elixir, but also handles responses. If a function
|
4
|
+
# within a `then` returns a Fray::Response (or subclass thereof), the
|
5
|
+
# remainder of the pipe will not be used. Rather, the `catch` function will be
|
6
|
+
# called (like with a JS promise). This function defaults to the identity
|
7
|
+
# function.
|
8
|
+
#
|
9
|
+
# Anything that responds to #call can be passed to `then` or `catch` as an
|
10
|
+
# argument, including vanilla lambdas.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# FunctionPipe.new.
|
15
|
+
# then(->(x) { x + 1 }).
|
16
|
+
# then(->(x, y) { x * y }, 2). # note how extra params are handled
|
17
|
+
# catch(->(_) { 5 })
|
18
|
+
# run(4)
|
19
|
+
#
|
20
|
+
# Returns 10
|
21
|
+
#
|
22
|
+
# FunctionPipe.new.
|
23
|
+
# then(->(x) { Fray::Response.new('hi') }).
|
24
|
+
# then(->(x) { x * 2 }).
|
25
|
+
# catch(->(_) { 5 })
|
26
|
+
# run(4)
|
27
|
+
#
|
28
|
+
# Returns 5
|
29
|
+
#
|
30
|
+
module Fray
|
31
|
+
class ResponsePipe < AbortablePipe
|
32
|
+
def initialize(thens = [], c = nil)
|
33
|
+
abort_predicate = ->(state) { state.is_a?(Fray::Data::Response) }
|
34
|
+
super(abort_predicate, thens, c)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/fray/version.rb
ADDED
data/spec/fray_spec.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Fray::ResponsePipe do
|
4
|
+
context 'with some functions' do
|
5
|
+
subject(:pipe) { Fray::AbortablePipe.new(->(x) { x % 2 == 0 }) }
|
6
|
+
|
7
|
+
it 'runs a sequence of functions' do
|
8
|
+
result = pipe.
|
9
|
+
then(->(x) { x + 2 }).
|
10
|
+
then(->(x) { x * 5 }).
|
11
|
+
run(1)
|
12
|
+
|
13
|
+
expect(result).to eq(15)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
it 'is immutable' do
|
18
|
+
p2 = pipe.then(->(x) { x * 3 })
|
19
|
+
|
20
|
+
expect(pipe.run(1)).to eq(1)
|
21
|
+
expect(p2.run(1)).to eq(3)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
it 'is reusable' do
|
26
|
+
p = pipe.
|
27
|
+
then(->(x) { x + 2 })
|
28
|
+
|
29
|
+
expect(p.run(1)).to eq(3)
|
30
|
+
expect(p.run(3)).to eq(5)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it 'aborts when the predicate is true' do
|
35
|
+
result = pipe.
|
36
|
+
then(->(_) { 0 }).
|
37
|
+
then(->(_) { 1 }).
|
38
|
+
run
|
39
|
+
|
40
|
+
expect(result).to eq(0)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it 'catches even numbers' do
|
45
|
+
result = pipe.
|
46
|
+
then(->(x) { x * 2 }).
|
47
|
+
then(->(_) { raise "don't get here" }).
|
48
|
+
catch(->(_) { 'Inferno' }).
|
49
|
+
run(1)
|
50
|
+
|
51
|
+
expect(result).to eq('Inferno')
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
it "doesn't invoke catch if no even numbers are encountered" do
|
56
|
+
result = pipe.
|
57
|
+
then(->(x) { x * 3 }).
|
58
|
+
then(->(_) { 'Haven' }).
|
59
|
+
catch(->(_) { 'Inferno' }).
|
60
|
+
run(1)
|
61
|
+
|
62
|
+
expect(result).to eq('Haven')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Fray::Data do
|
4
|
+
context "A Fray::Response" do
|
5
|
+
it "works with good inputs" do
|
6
|
+
r = Fray::Data::Response.new(
|
7
|
+
status: '200',
|
8
|
+
headers: {},
|
9
|
+
body: ''
|
10
|
+
)
|
11
|
+
|
12
|
+
expect(r.status).to eq('200')
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
it "fails with bad inputs" do
|
17
|
+
expect {
|
18
|
+
r = Fray::Data::Response.new(
|
19
|
+
status: '200',
|
20
|
+
headers: {},
|
21
|
+
body: []
|
22
|
+
)
|
23
|
+
}.to raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Fray::ResponsePipe do
|
4
|
+
context 'with some functions' do
|
5
|
+
subject(:pipe) { Fray::ResponsePipe.new }
|
6
|
+
let(:response) {
|
7
|
+
Fray::Data::Response.new(
|
8
|
+
status: '200',
|
9
|
+
headers: {},
|
10
|
+
body: ''
|
11
|
+
)
|
12
|
+
}
|
13
|
+
|
14
|
+
it 'runs a sequence of functions' do
|
15
|
+
result = pipe.
|
16
|
+
then(->(x) { x * 2 }).
|
17
|
+
then(->(x) { x + 5 }).
|
18
|
+
run(1)
|
19
|
+
|
20
|
+
expect(result).to eq(7)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
it 'is immutable' do
|
25
|
+
p2 = pipe.then(->(x) { x * 2 })
|
26
|
+
|
27
|
+
expect(pipe.run(1)).to eq(1)
|
28
|
+
expect(p2.run(1)).to eq(2)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
it 'is reusable' do
|
33
|
+
p = pipe.
|
34
|
+
then(->(x) { x * 2 }).
|
35
|
+
then(->(x) { x + 5 })
|
36
|
+
|
37
|
+
expect(p.run(1)).to eq(7)
|
38
|
+
expect(p.run(2)).to eq(9)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
it 'aborts on getting a Fray::Data::Response' do
|
43
|
+
result = pipe.
|
44
|
+
then(->(x) { x * 2 }).
|
45
|
+
then(->(_) { response }).
|
46
|
+
then(->(_) { 1 }).
|
47
|
+
run(1)
|
48
|
+
|
49
|
+
expect(result).to be_instance_of(Fray::Data::Response)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
it 'catches Fray::Data::Responses' do
|
54
|
+
result = pipe.
|
55
|
+
then(->(x) { x * 2 }).
|
56
|
+
then(->(_) { response }).
|
57
|
+
then(->(_) { raise "don't get here" }).
|
58
|
+
catch(->(_) { 'Inferno' }).
|
59
|
+
run(1)
|
60
|
+
|
61
|
+
expect(result).to eq('Inferno')
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
it "doesn't invoke catch if no Fray::Data::Response is encountered" do
|
66
|
+
result = pipe.
|
67
|
+
then(->(x) { x * 2 }).
|
68
|
+
then(->(_) { 'Haven' }).
|
69
|
+
catch(->(_) { 'Inferno' }).
|
70
|
+
run(1)
|
71
|
+
|
72
|
+
expect(result).to eq('Haven')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fray
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jake Sower
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json-schema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '11'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '11'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.10'
|
69
|
+
description:
|
70
|
+
email: jakesower@gmail.com
|
71
|
+
executables:
|
72
|
+
- console
|
73
|
+
- setup
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- Rakefile
|
80
|
+
- bin/console
|
81
|
+
- bin/setup
|
82
|
+
- config.ru
|
83
|
+
- fray.gemspec
|
84
|
+
- lib/fray.rb
|
85
|
+
- lib/fray/abortable_pipe.rb
|
86
|
+
- lib/fray/data.rb
|
87
|
+
- lib/fray/data/dataset.rb
|
88
|
+
- lib/fray/data/error_set.rb
|
89
|
+
- lib/fray/data/filter.rb
|
90
|
+
- lib/fray/data/query.rb
|
91
|
+
- lib/fray/data/response.rb
|
92
|
+
- lib/fray/data_structures/dataset.json
|
93
|
+
- lib/fray/data_structures/error.json
|
94
|
+
- lib/fray/data_structures/query.json
|
95
|
+
- lib/fray/data_structures/request.json
|
96
|
+
- lib/fray/data_structures/response.json
|
97
|
+
- lib/fray/response_pipe.rb
|
98
|
+
- lib/fray/version.rb
|
99
|
+
- spec/fray_spec.rb
|
100
|
+
- spec/lib/fray/abortable_pipe_spec.rb
|
101
|
+
- spec/lib/fray/data_spec.rb
|
102
|
+
- spec/lib/fray/response_pipe_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
homepage: https://github.com/jakesower/fray
|
105
|
+
licenses:
|
106
|
+
- apache
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.4.8
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: API building blocks.
|
128
|
+
test_files: []
|