shogun 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/shogun +18 -0
- data/lib/shogun.rb +29 -0
- data/lib/shogun/authorizer.rb +7 -0
- data/lib/shogun/control.rb +82 -0
- data/lib/shogun/control/null.rb +15 -0
- data/lib/shogun/database.rb +44 -0
- data/lib/shogun/denormalizer.rb +54 -0
- data/lib/shogun/dispatch.rb +67 -0
- data/lib/shogun/dispatch/lookup.rb +57 -0
- data/lib/shogun/dispatch/payload.rb +18 -0
- data/lib/shogun/dispatch/route.rb +34 -0
- data/lib/shogun/dispatch/route/null.rb +11 -0
- data/lib/shogun/endpoint.rb +11 -0
- data/lib/shogun/normalizer.rb +31 -0
- data/lib/shogun/presenter.rb +57 -0
- data/lib/shogun/presenters.rb +28 -0
- data/lib/shogun/server.rb +49 -0
- data/lib/shogun/validator.rb +15 -0
- data/lib/shogun/verifier.rb +9 -0
- data/lib/shogun/verifier/allowed.rb +29 -0
- data/lib/shogun/verifier/match.rb +31 -0
- data/lib/shogun/verifier/presence.rb +26 -0
- data/lib/shogun/verifier/range.rb +30 -0
- data/lib/shogun/verifier/uniqueness.rb +23 -0
- data/lib/shogun/version.rb +3 -0
- data/spec/lib/shogun/authorizer_spec.rb +5 -0
- data/spec/lib/shogun/control/null_spec.rb +5 -0
- data/spec/lib/shogun/control_spec.rb +5 -0
- data/spec/lib/shogun/database_spec.rb +5 -0
- data/spec/lib/shogun/denormalizer_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/lookup_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/payload_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/route/null_spec.rb +5 -0
- data/spec/lib/shogun/dispatch/route_spec.rb +5 -0
- data/spec/lib/shogun/dispatch_spec.rb +5 -0
- data/spec/lib/shogun/endpoint_spec.rb +5 -0
- data/spec/lib/shogun/normalizer_spec.rb +5 -0
- data/spec/lib/shogun/presenter_spec.rb +5 -0
- data/spec/lib/shogun/presenters_spec.rb +5 -0
- data/spec/lib/shogun/server_spec.rb +5 -0
- data/spec/lib/shogun/validator_spec.rb +5 -0
- data/spec/lib/shogun/verifier/allowed_spec.rb +5 -0
- data/spec/lib/shogun/verifier/match_spec.rb +5 -0
- data/spec/lib/shogun/verifier/presence_spec.rb +5 -0
- data/spec/lib/shogun/verifier/range_spec.rb +5 -0
- data/spec/lib/shogun/verifier/uniqueness_spec.rb +5 -0
- data/spec/lib/shogun/verifier_spec.rb +5 -0
- data/spec/lib/shogun_spec.rb +5 -0
- data/spec/spec_helper.rb +6 -0
- metadata +385 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: caae485be3f9b7816128451965ff1acbe5d0ff37
|
4
|
+
data.tar.gz: ef5108915c2044a505a1f57f1bb3d8f965d22d28
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 42da9e33491a23787326a5c8ec8ce174b4430a7a7c9ebfa60d2b0d610d0cd4b2338e4f6b44416cf5053a3f35b266e990a8f3fd1a5ece4fda0a06058b526b0c73
|
7
|
+
data.tar.gz: 0a55db9e31daef5f4056f522f6ead270ce1b854fa83d76f7bbc487032603ddcaee5f99527d6237a86308cd714e6c1bb9887447bbddc8d0e6f66e7aa2b23a3cfd
|
data/bin/shogun
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "shogun"
|
4
|
+
require "pry"
|
5
|
+
require Dir["./lib/*.rb"].first
|
6
|
+
|
7
|
+
case ARGV.shift
|
8
|
+
when "database" then
|
9
|
+
case ARGV.shift
|
10
|
+
when "reset" then
|
11
|
+
Shogun::Database.setup!(logger: Logger.new(STDOUT))
|
12
|
+
when "console" then
|
13
|
+
Shogun::Database.new(logger: Logger.new(STDOUT))
|
14
|
+
binding.pry
|
15
|
+
end
|
16
|
+
when "console" then
|
17
|
+
binding.pry
|
18
|
+
end
|
data/lib/shogun.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "adamantium"
|
2
|
+
require "rack-protection"
|
3
|
+
require "rack-commonlogger"
|
4
|
+
require "rack-body_serializer"
|
5
|
+
require "rack-authentication_bearer"
|
6
|
+
require "rack-body_deserializer"
|
7
|
+
require "rack-accept_setter"
|
8
|
+
require "rack-chunker"
|
9
|
+
require "rack-content_length_setter"
|
10
|
+
require "rack-runtime"
|
11
|
+
require "rack-log"
|
12
|
+
require "scrawl"
|
13
|
+
require "active_record"
|
14
|
+
|
15
|
+
module Shogun
|
16
|
+
require_relative "shogun/authorizer"
|
17
|
+
require_relative "shogun/control"
|
18
|
+
require_relative "shogun/database"
|
19
|
+
require_relative "shogun/denormalizer"
|
20
|
+
require_relative "shogun/dispatch"
|
21
|
+
require_relative "shogun/endpoint"
|
22
|
+
require_relative "shogun/normalizer"
|
23
|
+
require_relative "shogun/presenter"
|
24
|
+
require_relative "shogun/presenters"
|
25
|
+
require_relative "shogun/server"
|
26
|
+
require_relative "shogun/validator"
|
27
|
+
require_relative "shogun/verifier"
|
28
|
+
require_relative "shogun/version"
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Control
|
3
|
+
CONTINUE = 100
|
4
|
+
SWITCHING_PROTOCOLS = 101
|
5
|
+
PROCESSING = 102
|
6
|
+
OK = 200
|
7
|
+
CREATED = 201
|
8
|
+
ACCEPTED = 202
|
9
|
+
NONAUTHORITATIVE_INFORMATION = 203
|
10
|
+
NO_CONTENT = 204
|
11
|
+
RESET_CONTENT = 205
|
12
|
+
PARTIAL_CONTENT = 206
|
13
|
+
MULTISTATUS = 207
|
14
|
+
ALREADY_REPORTED = 208
|
15
|
+
IM_USED = 226
|
16
|
+
MULTIPLE_CHOICES = 300
|
17
|
+
MOVED_PERMANENTLY = 301
|
18
|
+
FOUND = 302
|
19
|
+
SEE_OTHER = 303
|
20
|
+
NOT_MODIFIED = 304
|
21
|
+
USE_PROXY = 305
|
22
|
+
RESERVED = 306
|
23
|
+
TEMPORARY_REDIRECT = 307
|
24
|
+
PERMANENT_REDIRECT = 308
|
25
|
+
BAD_REQUEST = 400
|
26
|
+
UNAUTHORIZED = 401
|
27
|
+
PAYMENT_REQUIRED = 402
|
28
|
+
FORBIDDEN = 403
|
29
|
+
NOT_FOUND = 404
|
30
|
+
METHOD_NOT_ALLOWED = 405
|
31
|
+
NOT_ACCEPTABLE = 406
|
32
|
+
PROXY_AUTHENTICATION_REQUIRED = 407
|
33
|
+
REQUEST_TIMEOUT = 408
|
34
|
+
CONFLICT = 409
|
35
|
+
GONE = 410
|
36
|
+
LENGTH_REQUIRED = 411
|
37
|
+
PRECONDITION_FAILED = 412
|
38
|
+
REQUEST_ENTITY_TOO_LARGE = 413
|
39
|
+
REQUEST_URI_TOO_LONG = 414
|
40
|
+
UNSUPPORTED_MEDIA_TYPE = 415
|
41
|
+
REQUESTED_RANGE_NOT_SATISFIABLE = 416
|
42
|
+
EXPECTATION_FAILED = 417
|
43
|
+
IM_A_TEAPOT = 418
|
44
|
+
UNPROCESSABLE_ENTITY = 422
|
45
|
+
LOCKED = 423
|
46
|
+
FAILED_DEPENDENCY = 424
|
47
|
+
UPGRADE_REQUIRED = 426
|
48
|
+
PRECONDITION_REQUIRED = 428
|
49
|
+
TOO_MANY_REQUESTS = 429
|
50
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE = 431
|
51
|
+
INTERNAL_SERVER_ERROR = 500
|
52
|
+
NOT_IMPLEMENTED = 501
|
53
|
+
BAD_GATEWAY = 502
|
54
|
+
SERVICE_UNAVAILABLE = 503
|
55
|
+
GATEWAY_TIMEOUT = 504
|
56
|
+
HTTP_VERSION_NOT_SUPPORTED = 505
|
57
|
+
VARIANT_ALSO_NEGOTIATES = 506
|
58
|
+
INSUFFICIENT_STORAGE = 507
|
59
|
+
LOOP_DETECTED = 508
|
60
|
+
NOT_EXTENDED = 510
|
61
|
+
NETWORK_AUTHENTICATION_REQUIRED = 511
|
62
|
+
|
63
|
+
require_relative "control/null"
|
64
|
+
|
65
|
+
|
66
|
+
def to_a
|
67
|
+
[status, headers, body]
|
68
|
+
end
|
69
|
+
|
70
|
+
private def status
|
71
|
+
@status || INTERNAL_SERVER_ERROR
|
72
|
+
end
|
73
|
+
|
74
|
+
private def headers
|
75
|
+
@headers || {}
|
76
|
+
end
|
77
|
+
|
78
|
+
private def body
|
79
|
+
if @denormalizer then @denormalizer.as_document else "" end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Shogun
|
2
|
+
class Database
|
3
|
+
URL_NAME = "DATABASE_URL"
|
4
|
+
TIMEZONE_NAME = "DATABASE_TIMEZONE"
|
5
|
+
REAP_FREQUENCY_NAME = "DATABASE_REAP_FREQUENCY"
|
6
|
+
POOL_NAME = "DATABASE_POOL"
|
7
|
+
TIMEOUT_NAME = "DATABASE_TIMEOUT"
|
8
|
+
URL = ENV[URL_NAME]
|
9
|
+
TIMEZONE = ENV[TIMEZONE_NAME]
|
10
|
+
REAP_FREQUENCY = ENV[REAP_FREQUENCY_NAME]
|
11
|
+
POOL = ENV[POOL_NAME]
|
12
|
+
TIMEOUT = ENV[TIMEOUT_NAME]
|
13
|
+
DEFAULT_TIMEOUT = 20
|
14
|
+
DEFAULT_REAP_FREQUENCY = 15
|
15
|
+
DEFAULT_POOL = 10
|
16
|
+
DEFAULT_TIMEZONE = "utc"
|
17
|
+
CONFIGURATION = {
|
18
|
+
"reaping_frequency" => Integer(REAP_FREQUENCY || DEFAULT_REAP_FREQUENCY),
|
19
|
+
"pool" => Integer(POOL || DEFAULT_POOL),
|
20
|
+
"connect_timeout" => Integer(TIMEOUT || DEFAULT_TIMEOUT)
|
21
|
+
}
|
22
|
+
|
23
|
+
def self.connection
|
24
|
+
ActiveRecord::Base.connection
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.setup!(logger:)
|
28
|
+
new(logger: logger).tap do
|
29
|
+
connection.enable_extension("uuid-ossp")
|
30
|
+
ActiveRecord::Base.descendants.each(&:setup!)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(logger:)
|
35
|
+
@logger = logger
|
36
|
+
ActiveRecord::LogSubscriber.logger = @logger
|
37
|
+
ActiveRecord::LogSubscriber.colorize_logging = false
|
38
|
+
ActiveRecord::Base.time_zone_aware_attributes = true
|
39
|
+
ActiveRecord::Base.default_timezone = (self.class.const_get("TIMEZONE") || self.class.const_get("DEFAULT_TIMEZONE")).to_sym
|
40
|
+
ActiveRecord::Base.logger = @logger
|
41
|
+
ActiveRecord::Base.establish_connection("#{self.class.const_get("URL")}?#{self.class.const_get("CONFIGURATION").to_query}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Shogun
|
2
|
+
module Denormalizer
|
3
|
+
META_KEY = "meta"
|
4
|
+
LINKS_KEY = "links"
|
5
|
+
LINKED_KEY = "linked"
|
6
|
+
|
7
|
+
def initialize(object:, fields:)
|
8
|
+
@object = object
|
9
|
+
@fields = fields
|
10
|
+
@meta = object.meta
|
11
|
+
@links = object.links
|
12
|
+
@linked = object.linked
|
13
|
+
end
|
14
|
+
|
15
|
+
def as_document
|
16
|
+
{
|
17
|
+
document_key => document,
|
18
|
+
meta_key => @meta,
|
19
|
+
links_key => @links,
|
20
|
+
linked_key => @linked
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
private def document
|
25
|
+
if @fields.one?
|
26
|
+
@object.public_send(@fields.first)
|
27
|
+
else
|
28
|
+
@fields.inject({}) do |hash, field|
|
29
|
+
hash.merge(field => @object.public_send(mapping[field]))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private def document_key
|
35
|
+
self.class.const_get("DOCUMENT_KEY")
|
36
|
+
end
|
37
|
+
|
38
|
+
private def meta_key
|
39
|
+
self.class.const_get("META_KEY")
|
40
|
+
end
|
41
|
+
|
42
|
+
private def links_key
|
43
|
+
self.class.const_get("LINKS_KEY")
|
44
|
+
end
|
45
|
+
|
46
|
+
private def linked_key
|
47
|
+
self.class.const_get("LINKED_KEY")
|
48
|
+
end
|
49
|
+
|
50
|
+
private def mapping
|
51
|
+
self.class.const_get("MAPPING")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Shogun
|
2
|
+
class Dispatch
|
3
|
+
PATH_KEY = "REQUEST_PATH"
|
4
|
+
METHOD_KEY = "REQUEST_METHOD"
|
5
|
+
NO_MATCH = -> { // }
|
6
|
+
|
7
|
+
require_relative "dispatch/route"
|
8
|
+
require_relative "dispatch/payload"
|
9
|
+
require_relative "dispatch/lookup"
|
10
|
+
|
11
|
+
attr_reader :routes
|
12
|
+
private :routes
|
13
|
+
|
14
|
+
attr_reader :payload
|
15
|
+
private :payload
|
16
|
+
|
17
|
+
attr_reader :lookup
|
18
|
+
private :lookup
|
19
|
+
|
20
|
+
attr_reader :route
|
21
|
+
private :route
|
22
|
+
|
23
|
+
def initialize(logger:)
|
24
|
+
@routes = {}
|
25
|
+
@logger = logger
|
26
|
+
yield(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(state)
|
30
|
+
@state = state
|
31
|
+
|
32
|
+
lookup = Lookup.new(routes: routes, verb: state[METHOD_KEY], path: state[PATH_KEY])
|
33
|
+
|
34
|
+
payload = Payload.new(state: state, lookup: lookup)
|
35
|
+
|
36
|
+
route = lookup.to_route
|
37
|
+
|
38
|
+
control = route.control.new(payload: payload)
|
39
|
+
|
40
|
+
control.to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def push(route:)
|
44
|
+
routes[route.as_key] = route
|
45
|
+
end
|
46
|
+
|
47
|
+
private def stack
|
48
|
+
@stack
|
49
|
+
end
|
50
|
+
|
51
|
+
private def state
|
52
|
+
@state
|
53
|
+
end
|
54
|
+
|
55
|
+
private def headers
|
56
|
+
@headers
|
57
|
+
end
|
58
|
+
|
59
|
+
private def status
|
60
|
+
@status
|
61
|
+
end
|
62
|
+
|
63
|
+
private def body
|
64
|
+
@body
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Shogun
|
2
|
+
class Dispatch
|
3
|
+
class Lookup
|
4
|
+
include Adamantium
|
5
|
+
|
6
|
+
attr_reader :routes
|
7
|
+
private :routes
|
8
|
+
|
9
|
+
def initialize(routes:, verb:, path:)
|
10
|
+
@routes = routes
|
11
|
+
@verb = verb
|
12
|
+
@path = path
|
13
|
+
end
|
14
|
+
|
15
|
+
def request
|
16
|
+
"#{@verb} #{@path}"
|
17
|
+
end
|
18
|
+
memoize :request
|
19
|
+
|
20
|
+
def payload
|
21
|
+
if match.captures.any?
|
22
|
+
match.names.zip(match.captures).to_h
|
23
|
+
else
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
memoize :payload
|
28
|
+
|
29
|
+
def to_route
|
30
|
+
regular || regexp || Route::Null.new
|
31
|
+
end
|
32
|
+
memoize :to_route
|
33
|
+
|
34
|
+
private def regexp
|
35
|
+
routes[pattern]
|
36
|
+
end
|
37
|
+
memoize :regexp
|
38
|
+
|
39
|
+
private def regular
|
40
|
+
routes[request]
|
41
|
+
end
|
42
|
+
memoize :regular
|
43
|
+
|
44
|
+
private def match
|
45
|
+
request.match(pattern)
|
46
|
+
end
|
47
|
+
memoize :match
|
48
|
+
|
49
|
+
private def pattern
|
50
|
+
routes.keys.detect(NO_MATCH) do |key|
|
51
|
+
if key.is_a?(Regexp) then request =~ key else next end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
memoize :pattern
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Shogun
|
2
|
+
class Dispatch
|
3
|
+
class Payload
|
4
|
+
QUERY_KEY = "rack.request.query_hash"
|
5
|
+
|
6
|
+
def initialize(state:, lookup:)
|
7
|
+
@body = state[Rack::BodyDeserializer::RACK_KEY] || {}
|
8
|
+
@query = state[QUERY_KEY] || {}
|
9
|
+
@uri = lookup.payload
|
10
|
+
@sections = [state, @uri, @query, @body]
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_hash
|
14
|
+
@sections.inject(:merge)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Shogun
|
2
|
+
class Dispatch
|
3
|
+
class Route
|
4
|
+
require_relative "route/null"
|
5
|
+
|
6
|
+
attr_reader :control
|
7
|
+
|
8
|
+
def initialize(namespace:, verb:, matcher:, control:)
|
9
|
+
@namespace = namespace
|
10
|
+
@verb = verb
|
11
|
+
@matcher = matcher
|
12
|
+
@control = control
|
13
|
+
end
|
14
|
+
|
15
|
+
def as_key
|
16
|
+
if expression? then Regexp.new("^#{verb} /#{path}$") else "#{verb} /#{path}" end
|
17
|
+
end
|
18
|
+
|
19
|
+
private def path
|
20
|
+
@path ||= [@namespace, @matcher].compact.join("/")
|
21
|
+
end
|
22
|
+
|
23
|
+
private def verb
|
24
|
+
@verb.to_s.upcase
|
25
|
+
end
|
26
|
+
|
27
|
+
private def expression?
|
28
|
+
[@namespace, @matcher].any? do |item|
|
29
|
+
item.is_a?(Regexp)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|