roy 0.0.1
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.
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +22 -0
- data/README.md +83 -0
- data/Rakefile +6 -0
- data/lib/roy.rb +67 -0
- data/lib/roy/version.rb +3 -0
- data/roy.gemspec +27 -0
- data/test/base_test.rb +91 -0
- data/test/helper.rb +20 -0
- data/test/prefix_test.rb +24 -0
- metadata +90 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2009-2011 François "madx" Vaux <madx@yapok.org>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
Roy
|
2
|
+
===
|
3
|
+
|
4
|
+
Roy is a tiny module that aims to make any Ruby object Rack-friendly and provite
|
5
|
+
it with a REST-like interface.
|
6
|
+
|
7
|
+
Roy tries to be as less invasive as possible. In provides your objects with a
|
8
|
+
`#call` method that takes a Rack environment and dispatchs to a regular method
|
9
|
+
named after the HTTP method you want to catch.
|
10
|
+
|
11
|
+
## Tests
|
12
|
+
|
13
|
+
You can run the tests by running `rake test`. They are written with Minitest.
|
14
|
+
|
15
|
+
## Example
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
class MessageQueue
|
19
|
+
include Roy
|
20
|
+
|
21
|
+
roy allow: [:get, :post, :delete]
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@stack = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def get
|
28
|
+
@stack.inspect
|
29
|
+
end
|
30
|
+
|
31
|
+
def post
|
32
|
+
halt 403 unless roy.params[:item]
|
33
|
+
@stack << roy.params[:item].strip
|
34
|
+
get
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete
|
38
|
+
@stack.shift.inspect
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
## Docs
|
44
|
+
|
45
|
+
### Configuration
|
46
|
+
|
47
|
+
The `roy` class method is used to define access control and method prefix. The
|
48
|
+
following example should be self-explanatory enough:
|
49
|
+
|
50
|
+
``` ruby
|
51
|
+
class Example
|
52
|
+
include Roy
|
53
|
+
roy allow: [:get], prefix: :http_
|
54
|
+
|
55
|
+
def http_get(*args)
|
56
|
+
"get"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
### Environement
|
62
|
+
|
63
|
+
Inside your handler methods, you have access to a `roy` readable attribute which
|
64
|
+
is a struct containing the following fields:
|
65
|
+
|
66
|
+
* `env`: the Rack environment
|
67
|
+
* `response`: a `Rack::Response` object that will be returned by `call`
|
68
|
+
* `request`: a `Rack::Request` build from the environment
|
69
|
+
* `header`: a hash of headers that is part of `response`
|
70
|
+
* `params`: parameters extracted from the query string and the request body
|
71
|
+
* `conf`: the configuration set via `::roy`
|
72
|
+
|
73
|
+
The keys for `params` can be accessed either via a `String` or a `Symbol`
|
74
|
+
|
75
|
+
### Control flow
|
76
|
+
|
77
|
+
Your handler methods are run inside a `catch` block which will catch the `:halt`
|
78
|
+
symbol. You can then use `throw` to abort a method but you must return an array
|
79
|
+
composed of a status code and a message.
|
80
|
+
|
81
|
+
Roy provides a `halt` method that takes a status code and an optional message.
|
82
|
+
If there is no message it uses the default message from
|
83
|
+
`Rack::Utils::HTTP_STATUS_CODES`
|
data/Rakefile
ADDED
data/lib/roy.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'rack'
|
3
|
+
require 'roy/version'
|
4
|
+
|
5
|
+
module Roy
|
6
|
+
Env = Struct.new(:env, :request, :response, :headers, :params, :conf)
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.send(:extend, ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :roy
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
@roy = Env.new.tap { |e|
|
16
|
+
e.env = env
|
17
|
+
e.request = Rack::Request.new(env)
|
18
|
+
e.response = Rack::Response.new
|
19
|
+
e.headers = e.response.header
|
20
|
+
e.params = e.request.GET.merge(e.request.POST)
|
21
|
+
e.params.default_proc = proc do |hash, key|
|
22
|
+
hash[key.to_s] if Symbol === key
|
23
|
+
end
|
24
|
+
e.conf = self.class.conf
|
25
|
+
}
|
26
|
+
|
27
|
+
method = roy.env['REQUEST_METHOD'].downcase.to_sym
|
28
|
+
args = roy.env['PATH_INFO'].sub(/^\/+/, '').split(/\/+/).map { |arg|
|
29
|
+
Rack::Utils.unescape(arg)
|
30
|
+
}
|
31
|
+
|
32
|
+
method, was_head = :get, true if method == :head
|
33
|
+
|
34
|
+
roy.response.status, body = catch(:halt) do
|
35
|
+
halt(405) unless roy.conf.allow.include?(method)
|
36
|
+
prefixed_method = :"#{roy.conf.prefix}#{method}"
|
37
|
+
[roy.response.status, send(prefixed_method, *args)]
|
38
|
+
end
|
39
|
+
|
40
|
+
roy.response.write(body) unless was_head
|
41
|
+
roy.response.finish
|
42
|
+
end
|
43
|
+
|
44
|
+
def halt(code, message=nil)
|
45
|
+
throw :halt, [code, message || Rack::Utils::HTTP_STATUS_CODES[code]]
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
attr_reader :conf
|
50
|
+
|
51
|
+
def roy(options={})
|
52
|
+
@conf ||= Struct.new(:allow, :prefix).new
|
53
|
+
conf.allow ||= Set.new
|
54
|
+
conf.prefix ||= :''
|
55
|
+
|
56
|
+
options.each do |k,v|
|
57
|
+
case k
|
58
|
+
when :allow
|
59
|
+
conf.allow.merge(v)
|
60
|
+
conf.allow.add(:head) if v.member?(:get)
|
61
|
+
when :prefix
|
62
|
+
conf.prefix = v
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/roy/version.rb
ADDED
data/roy.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "roy/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "roy"
|
7
|
+
s.version = Roy::VERSION
|
8
|
+
s.authors = ["madx"]
|
9
|
+
s.email = ["madx@yapok.org"]
|
10
|
+
s.homepage = "https://github.com/madx/roy"
|
11
|
+
s.summary = 'make your objects REST-friendly'
|
12
|
+
s.description =
|
13
|
+
"roy is a small library which allows every Ruby object to be used\n" <<
|
14
|
+
"as a Rack application."
|
15
|
+
|
16
|
+
s.rubyforge_project = "roy"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
# specify any dependencies here; for example:
|
24
|
+
s.add_development_dependency "minitest"
|
25
|
+
s.add_development_dependency "rack-test"
|
26
|
+
# s.add_runtime_dependency "rest-client"
|
27
|
+
end
|
data/test/base_test.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class RemoteLog
|
4
|
+
include Roy
|
5
|
+
|
6
|
+
attr_reader :history
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@history = []
|
10
|
+
end
|
11
|
+
|
12
|
+
roy allow: [:get, :put, :custom]
|
13
|
+
|
14
|
+
def get(*args)
|
15
|
+
history.inspect
|
16
|
+
end
|
17
|
+
|
18
|
+
def put(*args)
|
19
|
+
Roy.halt 400 unless roy.params[:body]
|
20
|
+
history << roy.params[:body]
|
21
|
+
history << roy.params[:foo] if roy.params[:foo]
|
22
|
+
get
|
23
|
+
end
|
24
|
+
|
25
|
+
def custom(*args)
|
26
|
+
args.join('+')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class RoyTest < MiniTest::Unit::TestCase
|
31
|
+
include Rack::Test::Methods
|
32
|
+
|
33
|
+
def app
|
34
|
+
RemoteLog.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_provide_call
|
38
|
+
assert_respond_to app, :call
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_provide_roy
|
42
|
+
assert_respond_to app.class, :roy
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_forward_allowed_methods
|
46
|
+
get '/'
|
47
|
+
ok!
|
48
|
+
assert_equal app.get, last_response.body
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_block_forbidden_methods
|
52
|
+
post '/'
|
53
|
+
fail!
|
54
|
+
assert_equal 405, last_response.status
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def test_set_allowed_methods
|
59
|
+
assert_includes app.class.conf.allow, :get
|
60
|
+
assert_includes app.class.conf.allow, :put
|
61
|
+
refute_includes app.class.conf.allow, :post
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_allowing_get_allows_head
|
65
|
+
assert_includes app.class.conf.allow, :head
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_roy_halt
|
69
|
+
assert_throws :halt do
|
70
|
+
app.halt 200
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_head_does_not_have_contents
|
75
|
+
head '/'
|
76
|
+
ok!
|
77
|
+
assert_equal '', last_response.body
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_params
|
81
|
+
put '/?foo=bar', :body => 'hello'
|
82
|
+
ok!
|
83
|
+
assert_equal %w(hello bar).inspect, last_response.body
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_path_components_as_method_arguments
|
87
|
+
request '/a/b/c', :method => 'CUSTOM'
|
88
|
+
ok!
|
89
|
+
assert_equal 'a+b+c', last_response.body
|
90
|
+
end
|
91
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require(:default, :development)
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'rack/test'
|
6
|
+
require 'roy'
|
7
|
+
|
8
|
+
module CustomTestMethods
|
9
|
+
private
|
10
|
+
|
11
|
+
def ok!
|
12
|
+
assert_predicate last_response, :ok?
|
13
|
+
end
|
14
|
+
|
15
|
+
def fail!
|
16
|
+
refute_predicate last_response, :ok?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Rack::Test::Methods.send(:include, CustomTestMethods)
|
data/test/prefix_test.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class TestObject
|
4
|
+
include Roy
|
5
|
+
|
6
|
+
roy allow: [:get], prefix: :http_
|
7
|
+
|
8
|
+
def http_get(*args)
|
9
|
+
'success'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class PrefixTest < MiniTest::Unit::TestCase
|
14
|
+
include Rack::Test::Methods
|
15
|
+
|
16
|
+
def app
|
17
|
+
TestObject.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_prefixing
|
21
|
+
get '/'
|
22
|
+
assert_equal 'success', last_response.body
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: roy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- madx
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-10-19 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: minitest
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :development
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rack-test
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id002
|
37
|
+
description: |-
|
38
|
+
roy is a small library which allows every Ruby object to be used
|
39
|
+
as a Rack application.
|
40
|
+
email:
|
41
|
+
- madx@yapok.org
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
extra_rdoc_files: []
|
47
|
+
|
48
|
+
files:
|
49
|
+
- Gemfile
|
50
|
+
- Gemfile.lock
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- lib/roy.rb
|
55
|
+
- lib/roy/version.rb
|
56
|
+
- roy.gemspec
|
57
|
+
- test/base_test.rb
|
58
|
+
- test/helper.rb
|
59
|
+
- test/prefix_test.rb
|
60
|
+
homepage: https://github.com/madx/roy
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project: roy
|
83
|
+
rubygems_version: 1.8.5
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: make your objects REST-friendly
|
87
|
+
test_files:
|
88
|
+
- test/base_test.rb
|
89
|
+
- test/helper.rb
|
90
|
+
- test/prefix_test.rb
|