rester 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rester.rb +23 -0
- data/lib/rester/client.rb +61 -0
- data/lib/rester/client/adapters.rb +8 -0
- data/lib/rester/client/adapters/adapter.rb +114 -0
- data/lib/rester/client/adapters/http_adapter.rb +31 -0
- data/lib/rester/client/adapters/http_adapter/connection.rb +58 -0
- data/lib/rester/errors.rb +52 -0
- data/lib/rester/middleware.rb +6 -0
- data/lib/rester/middleware/base.rb +44 -0
- data/lib/rester/middleware/error_handling.rb +49 -0
- data/lib/rester/railtie.rb +12 -0
- data/lib/rester/service.rb +137 -0
- data/lib/rester/utils.rb +49 -0
- data/lib/rester/version.rb +3 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 891fe3d740381cb54c48e89ad69fca06e1fc06d6
|
4
|
+
data.tar.gz: db402da2918dc425d879b3719fc41e976b1de4bf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1a4992cf642165b17d1e77e854ac9fdaadef82ccc45337548d0a902048bb372dc9574713ff5109f5baba43e3f0c730b1c299c005932314d1ff5bc803ca6d087b
|
7
|
+
data.tar.gz: 4b9ef3355867f57f67632b24dffd84b9d39027d94544d8681058b27e01fe204418dc04b3a9b843ad48fdcf9b412460dda07707bdfbc74ed77bac057b38a347e2
|
data/lib/rester.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rester/version'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module Rester
|
5
|
+
require 'rester/railtie' if defined?(Rails)
|
6
|
+
autoload(:Service, 'rester/service')
|
7
|
+
autoload(:Errors, 'rester/errors')
|
8
|
+
autoload(:Client, 'rester/client')
|
9
|
+
autoload(:Utils, 'rester/utils')
|
10
|
+
autoload(:Middleware, 'rester/middleware')
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def load_tasks
|
14
|
+
Dir[
|
15
|
+
File.expand_path("../../tasks", __FILE__) + '/**.rake'
|
16
|
+
].each { |rake_file| load rake_file }
|
17
|
+
end
|
18
|
+
|
19
|
+
def connect(*args)
|
20
|
+
Client.new(*args)
|
21
|
+
end
|
22
|
+
end # Class Methods
|
23
|
+
end # Rester
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Rester
|
4
|
+
class Client
|
5
|
+
autoload(:Adapters, 'rester/client/adapters')
|
6
|
+
|
7
|
+
attr_reader :adapter
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
case args.first
|
11
|
+
when Adapters::Adapter
|
12
|
+
self.adapter = args.first
|
13
|
+
else
|
14
|
+
self.adapter = Adapters::HttpAdapter.new(*args)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def connect(*args)
|
19
|
+
adapter.connect(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def connected?
|
23
|
+
adapter.connected? && adapter.get(:test_connection).first == 200
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def adapter=(adapter)
|
29
|
+
@adapter = adapter
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
##
|
35
|
+
# Submits the method to the adapter.
|
36
|
+
def method_missing(meth, *args, &block)
|
37
|
+
verb, meth = Utils.extract_method_verb(meth)
|
38
|
+
_process_response(meth, *adapter.request(verb, meth, *args, &block))
|
39
|
+
end
|
40
|
+
|
41
|
+
def _process_response(meth, status, body)
|
42
|
+
if status.between?(200, 299)
|
43
|
+
_parse_json(body)
|
44
|
+
elsif status == 400
|
45
|
+
raise Errors::RequestError, _parse_json(body)[:message]
|
46
|
+
elsif status == 404
|
47
|
+
raise Errors::InvalidMethodError, meth.to_s
|
48
|
+
else
|
49
|
+
raise Errors::ServerError, _parse_json(body)[:message]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def _parse_json(data)
|
54
|
+
if data.is_a?(String) && !data.empty?
|
55
|
+
JSON.parse(data, symbolize_names: true)
|
56
|
+
else
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end # Client
|
61
|
+
end # Rester
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Rester
|
2
|
+
module Client::Adapters
|
3
|
+
class Adapter
|
4
|
+
def initialize(*args)
|
5
|
+
connect(*args) unless args.empty?
|
6
|
+
end
|
7
|
+
|
8
|
+
##
|
9
|
+
# Returns the headers defined for this Adapter. Optionally, you may also
|
10
|
+
# define additional headers you'd like to add/override.
|
11
|
+
def headers(new_headers={})
|
12
|
+
(@headers ||= {}).merge!(new_headers)
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Connect to a service. The specific arguments depend on the Adapter
|
17
|
+
# subclass.
|
18
|
+
def connect(*args)
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Returns whether or not the Adapter is connected to a service.
|
24
|
+
def connected?
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
def request(verb, method, *args, &block)
|
29
|
+
_validate_verb(verb)
|
30
|
+
params = _validate_params(args.pop) if args.last.is_a?(Hash)
|
31
|
+
_validate_args(args)
|
32
|
+
|
33
|
+
public_send(
|
34
|
+
"#{verb}!",
|
35
|
+
"/#{method}/#{args.map(&:to_s).join('/')}",
|
36
|
+
params
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(method, *args, &block)
|
41
|
+
request(:get, method, *args, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def post(method, *args, &block)
|
45
|
+
request(:post, method, *args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get!(path, params={})
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
def post!(path, params={})
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def headers=(h)
|
59
|
+
@headers = h
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
VALID_VERBS = {
|
65
|
+
get: true,
|
66
|
+
post: true
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
VALID_ARG_TYPES = {
|
70
|
+
String => true,
|
71
|
+
Symbol => true,
|
72
|
+
Fixnum => true,
|
73
|
+
Integer => true,
|
74
|
+
Float => true
|
75
|
+
}.freeze
|
76
|
+
|
77
|
+
VALID_PARAM_KEY_TYPES = {
|
78
|
+
String => true,
|
79
|
+
Symbol => true
|
80
|
+
}.freeze
|
81
|
+
|
82
|
+
VALID_PARAM_VALUE_TYPES = {
|
83
|
+
String => true,
|
84
|
+
Symbol => true,
|
85
|
+
Fixnum => true,
|
86
|
+
Integer => true,
|
87
|
+
Float => true,
|
88
|
+
DateTime => true
|
89
|
+
}.freeze
|
90
|
+
|
91
|
+
def _validate_verb(verb)
|
92
|
+
VALID_VERBS[verb] or
|
93
|
+
raise ArgumentError, "Invalid verb: #{verb.inspect}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def _validate_args(args)
|
97
|
+
args.each { |arg|
|
98
|
+
VALID_ARG_TYPES[arg.class] or
|
99
|
+
raise ArgumentError, "Invalid argument type: #{arg.inspect}"
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def _validate_params(params)
|
104
|
+
params.each { |key, value|
|
105
|
+
VALID_PARAM_KEY_TYPES[key.class] or
|
106
|
+
raise ArgumentError, "Invalid param key type: #{key.inspect}"
|
107
|
+
|
108
|
+
VALID_PARAM_VALUE_TYPES[value.class] or
|
109
|
+
raise ArgumentError, "Invalid param value type: #{value.inspect}"
|
110
|
+
}
|
111
|
+
end
|
112
|
+
end # Adapter
|
113
|
+
end # Client::Adapters
|
114
|
+
end # Rester
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rester
|
2
|
+
module Client::Adapters
|
3
|
+
class HttpAdapter < Adapter
|
4
|
+
autoload(:Connection, 'rester/client/adapters/http_adapter/connection')
|
5
|
+
|
6
|
+
attr_reader :connection
|
7
|
+
|
8
|
+
def connect(*args)
|
9
|
+
nil.tap { @connection = Connection.new(*args) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def connected?
|
13
|
+
!!connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def get!(path, params={})
|
17
|
+
_prepare_response(connection.get(path, headers: headers, query: params))
|
18
|
+
end
|
19
|
+
|
20
|
+
def post!(path, params={})
|
21
|
+
_prepare_response(connection.post(path, headers: headers, data: params))
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _prepare_response(response)
|
27
|
+
[response.code.to_i, response.body]
|
28
|
+
end
|
29
|
+
end # HttpAdapter
|
30
|
+
end # Client::Adapters
|
31
|
+
end # Rester
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Rester
|
6
|
+
module Client::Adapters
|
7
|
+
class HttpAdapter::Connection
|
8
|
+
DEFAULT_POST_HEADERS = {
|
9
|
+
"Content-Type".freeze => "application/x-www-form-urlencoded".freeze
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
attr_reader :url
|
13
|
+
|
14
|
+
def initialize(url)
|
15
|
+
@url = url.is_a?(String) ? URI(url) : url
|
16
|
+
@url.path = @url.path[0..-2] if @url.path[-1] == '/'
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(path, params={})
|
20
|
+
_http.get(
|
21
|
+
_path(path, params[:query]),
|
22
|
+
_prepare_headers(params[:headers])
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def post(path, params={})
|
27
|
+
headers = DEFAULT_POST_HEADERS.merge(_prepare_headers(params[:headers]))
|
28
|
+
encoded_data = URI.encode_www_form(params[:data] || {})
|
29
|
+
_http.post(_path(path), encoded_data, headers)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def _path(path, query=nil)
|
35
|
+
u = url.dup
|
36
|
+
u.path += path
|
37
|
+
u.query = URI.encode_www_form(query) if query && !query.empty?
|
38
|
+
u.request_uri
|
39
|
+
end
|
40
|
+
|
41
|
+
def _http
|
42
|
+
Net::HTTP.new(url.hostname, url.port).tap { |http|
|
43
|
+
if (http.use_ssl=url.is_a?(URI::HTTPS))
|
44
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
45
|
+
|
46
|
+
http.cert_store = OpenSSL::X509::Store.new.tap { |s|
|
47
|
+
s.set_default_paths
|
48
|
+
}
|
49
|
+
end
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def _prepare_headers(headers)
|
54
|
+
Hash[(headers || {}).map { |k, v| [k.to_s, v.to_s] }]
|
55
|
+
end
|
56
|
+
end # HttpAdapter::Connection
|
57
|
+
end # Client::Adapters
|
58
|
+
end # Rester
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rester
|
2
|
+
module Errors
|
3
|
+
class << self
|
4
|
+
##
|
5
|
+
# Throws an error instead of raising it, which is more performant. Must
|
6
|
+
# be caught by an appropriate error handling wrapper.
|
7
|
+
def throw_error!(klass, message=nil)
|
8
|
+
error = message ? klass.new(message) : klass.new
|
9
|
+
throw :error, error
|
10
|
+
end
|
11
|
+
end # Class Methods
|
12
|
+
|
13
|
+
class Error < StandardError; end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Packet errors
|
17
|
+
class PacketError < Error; end
|
18
|
+
class InvalidEncodingError < PacketError; end
|
19
|
+
|
20
|
+
#############
|
21
|
+
# Http Errors
|
22
|
+
class HttpError < Error; end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Request Errors
|
26
|
+
|
27
|
+
# 400 Error
|
28
|
+
class RequestError < HttpError; end
|
29
|
+
|
30
|
+
# 401 Error
|
31
|
+
class AuthenticationError < RequestError; end
|
32
|
+
|
33
|
+
# 403 Error
|
34
|
+
class ForbiddenError < RequestError; end
|
35
|
+
|
36
|
+
# 404 Not Found
|
37
|
+
class NotFoundError < RequestError; end
|
38
|
+
class InvalidMethodError < NotFoundError; end
|
39
|
+
|
40
|
+
# 500 ServerError
|
41
|
+
class ServerError < RequestError; end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Server Errors
|
45
|
+
|
46
|
+
# General Errors
|
47
|
+
class InvalidValueError < Error; end
|
48
|
+
|
49
|
+
# Rester Errors
|
50
|
+
class ServiceNotDefinedError < Error; end
|
51
|
+
end # Errors
|
52
|
+
end # Rester
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rester
|
2
|
+
module Middleware
|
3
|
+
class Base
|
4
|
+
attr_reader :app
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(app, options = {})
|
8
|
+
@app = app
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
app.call(env)
|
14
|
+
end
|
15
|
+
|
16
|
+
def service
|
17
|
+
@__service ||= _find_service
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def _find_service
|
23
|
+
service = app
|
24
|
+
|
25
|
+
loop {
|
26
|
+
break if service.is_a?(Service)
|
27
|
+
|
28
|
+
[:app, :target].each { |meth|
|
29
|
+
if service.respond_to?(meth)
|
30
|
+
service = service.public_send(meth)
|
31
|
+
break
|
32
|
+
end
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
service.is_a?(Service) && service
|
37
|
+
end
|
38
|
+
|
39
|
+
def _error!(klass, message=nil)
|
40
|
+
Errors.throw_error!(klass, message)
|
41
|
+
end
|
42
|
+
end # Base
|
43
|
+
end # Middleware
|
44
|
+
end # Rester
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Rester
|
4
|
+
module Middleware
|
5
|
+
##
|
6
|
+
# Provides error handling for Rester. Should be mounted above all other
|
7
|
+
# Rester middleware.
|
8
|
+
class ErrorHandling < Base
|
9
|
+
def call(env)
|
10
|
+
error = catch(:error) {
|
11
|
+
begin
|
12
|
+
return super
|
13
|
+
rescue Exception => error
|
14
|
+
throw :error, error
|
15
|
+
end
|
16
|
+
}
|
17
|
+
|
18
|
+
_error_to_response(error).finish
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def _error_to_response(error)
|
24
|
+
Rack::Response.new(
|
25
|
+
[JSON.dump(message: error.message)],
|
26
|
+
_error_to_http_code(error),
|
27
|
+
{ "Content-Type" => "application/json"}
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _error_to_http_code(error)
|
32
|
+
case error
|
33
|
+
when Errors::NotFoundError
|
34
|
+
404
|
35
|
+
when Errors::ForbiddenError
|
36
|
+
403
|
37
|
+
when Errors::AuthenticationError
|
38
|
+
401
|
39
|
+
when Errors::RequestError
|
40
|
+
400
|
41
|
+
when Errors::ServerError
|
42
|
+
500
|
43
|
+
else
|
44
|
+
500
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end # ErrorHandling
|
48
|
+
end # Middleware
|
49
|
+
end # Rester
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module Rester
|
5
|
+
class Service
|
6
|
+
##
|
7
|
+
# The base set of middleware to use for every service.
|
8
|
+
# Middleware will be executed in the order specified.
|
9
|
+
BASE_MIDDLEWARE = [
|
10
|
+
Rack::Head,
|
11
|
+
Middleware::ErrorHandling
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
# Used to signify an empty body
|
15
|
+
class EmptyResponse; end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def instance
|
19
|
+
@instance ||= new
|
20
|
+
end
|
21
|
+
|
22
|
+
# The call method needs to call the rack_call method, which adds additional
|
23
|
+
# rack middleware.
|
24
|
+
def call(env)
|
25
|
+
instance.rack_call(env)
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(meth, *args, &block)
|
29
|
+
instance.public_send(meth, *args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
###
|
33
|
+
# Middleware DSL
|
34
|
+
###
|
35
|
+
|
36
|
+
def use(klass, *args)
|
37
|
+
_middleware << [klass, *args]
|
38
|
+
end
|
39
|
+
|
40
|
+
def _middleware
|
41
|
+
@__middleware ||= BASE_MIDDLEWARE.dup
|
42
|
+
end
|
43
|
+
end # Class methods
|
44
|
+
|
45
|
+
attr_reader :request
|
46
|
+
|
47
|
+
def initialize(opts={})
|
48
|
+
@_opts = opts.dup
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# To be called by Rack. Wraps the app in middleware.
|
53
|
+
def rack_call(env)
|
54
|
+
_rack_app.call(env)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Call the service app directly.
|
59
|
+
#
|
60
|
+
# Duplicates the instance before processing the request so individual requests
|
61
|
+
# can't impact each other.
|
62
|
+
def call(env)
|
63
|
+
dup.call!(env)
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Actually process the request.
|
68
|
+
#
|
69
|
+
# Calls methods that may modify instance variables, so the instance should
|
70
|
+
# be dup'd beforehand.
|
71
|
+
def call!(env)
|
72
|
+
@request = Rack::Request.new(env)
|
73
|
+
_process_request
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Built in service method called by Client#connected?
|
78
|
+
def test_connection(params={})
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def _rack_app
|
84
|
+
@__rack_app ||= _build_rack_app
|
85
|
+
end
|
86
|
+
|
87
|
+
def _build_rack_app
|
88
|
+
Rack::Builder.new.tap { |app|
|
89
|
+
self.class._middleware.each { |m| app.use(*m) }
|
90
|
+
app.run self
|
91
|
+
}.to_app
|
92
|
+
end
|
93
|
+
|
94
|
+
def _process_request
|
95
|
+
error!(Errors::NotFoundError) unless request.get? || request.post?
|
96
|
+
method, *args = _parse_path
|
97
|
+
params = _parse_params
|
98
|
+
method = "#{method}!" if request.post?
|
99
|
+
retval = public_send(method, *args, params)
|
100
|
+
_response(request.post? ? 201 : 200, _prepare_response(retval))
|
101
|
+
end
|
102
|
+
|
103
|
+
def _prepare_response(retval)
|
104
|
+
retval ||= {}
|
105
|
+
|
106
|
+
unless retval.is_a?(Hash)
|
107
|
+
error!(Errors::ServerError, "Invalid response: #{retval.inspect}")
|
108
|
+
end
|
109
|
+
|
110
|
+
JSON.dump(retval)
|
111
|
+
end
|
112
|
+
|
113
|
+
def _parse_path
|
114
|
+
path = request.path
|
115
|
+
uri = URI(path)
|
116
|
+
uri.path.split('/')[1..-1]
|
117
|
+
end
|
118
|
+
|
119
|
+
def _parse_params
|
120
|
+
if request.get?
|
121
|
+
request.GET
|
122
|
+
elsif request.post?
|
123
|
+
request.POST
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def _response(status, body=EmptyResponse, headers={})
|
128
|
+
body = body == EmptyResponse ? [] : [body]
|
129
|
+
headers = headers.merge("Content-Type" => "application/json")
|
130
|
+
Rack::Response.new(body, status, headers).finish
|
131
|
+
end
|
132
|
+
|
133
|
+
def _error!(klass, message=nil)
|
134
|
+
Errors.throw_error!(klass, message)
|
135
|
+
end
|
136
|
+
end # Service
|
137
|
+
end # Rester
|
data/lib/rester/utils.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Rester
|
4
|
+
module Utils
|
5
|
+
class << self
|
6
|
+
##
|
7
|
+
# Determines the HTTP method/verb based on the method name.
|
8
|
+
# Defaults to GET but if the method ends with "!" it uses POST.
|
9
|
+
def extract_method_verb(meth)
|
10
|
+
meth = meth.to_s
|
11
|
+
|
12
|
+
if meth[-1] == '!'
|
13
|
+
[:post, meth[0..-2]]
|
14
|
+
else
|
15
|
+
[:get, meth]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def walk(object, context=nil, &block)
|
20
|
+
case object
|
21
|
+
when Hash
|
22
|
+
Hash[
|
23
|
+
object.map { |key, val|
|
24
|
+
[walk(key, :hash_key, &block), walk(val, :hash_value, &block)]
|
25
|
+
}
|
26
|
+
]
|
27
|
+
when Array
|
28
|
+
object.map { |obj| walk(obj, :array_elem, &block) }
|
29
|
+
when Range
|
30
|
+
Range.new(
|
31
|
+
walk(object.begin, :range_begin, &block),
|
32
|
+
walk(object.end, :range_end, &block),
|
33
|
+
object.exclude_end?
|
34
|
+
)
|
35
|
+
else
|
36
|
+
yield object, context
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def symbolize_keys(hash)
|
41
|
+
hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
42
|
+
end
|
43
|
+
|
44
|
+
def classify(str)
|
45
|
+
str.to_s.split("_").map(&:capitalize).join
|
46
|
+
end
|
47
|
+
end # Class methods
|
48
|
+
end # Utils
|
49
|
+
end # Rester
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rester
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Honer
|
8
|
+
- Kayvon Ghaffari
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-09-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.5'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.5.2
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.5'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.5.2
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
type: :development
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec-rails
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sqlite3
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
type: :development
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: rails
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 4.0.0
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.0.0
|
90
|
+
description: A framework for creating simple RESTful interfaces between services.
|
91
|
+
email:
|
92
|
+
- robert@ribbonpayments.com
|
93
|
+
- kayvon@ribbon.co
|
94
|
+
executables: []
|
95
|
+
extensions: []
|
96
|
+
extra_rdoc_files: []
|
97
|
+
files:
|
98
|
+
- lib/rester.rb
|
99
|
+
- lib/rester/client.rb
|
100
|
+
- lib/rester/client/adapters.rb
|
101
|
+
- lib/rester/client/adapters/adapter.rb
|
102
|
+
- lib/rester/client/adapters/http_adapter.rb
|
103
|
+
- lib/rester/client/adapters/http_adapter/connection.rb
|
104
|
+
- lib/rester/errors.rb
|
105
|
+
- lib/rester/middleware.rb
|
106
|
+
- lib/rester/middleware/base.rb
|
107
|
+
- lib/rester/middleware/error_handling.rb
|
108
|
+
- lib/rester/railtie.rb
|
109
|
+
- lib/rester/service.rb
|
110
|
+
- lib/rester/utils.rb
|
111
|
+
- lib/rester/version.rb
|
112
|
+
homepage: http://github.com/ribbon/rester
|
113
|
+
licenses:
|
114
|
+
- BSD
|
115
|
+
metadata: {}
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
requirements: []
|
131
|
+
rubyforge_project:
|
132
|
+
rubygems_version: 2.4.8
|
133
|
+
signing_key:
|
134
|
+
specification_version: 4
|
135
|
+
summary: A framework for creating simple RESTful interfaces between services.
|
136
|
+
test_files: []
|