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 ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in roy.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ roy (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ minitest (2.6.1)
10
+ rack (1.3.4)
11
+ rack-test (0.6.1)
12
+ rack (>= 1.0)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ minitest
19
+ rack-test
20
+ roy!
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.
@@ -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`
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = 'test/*_test.rb'
6
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Roy
2
+ VERSION = "0.0.1"
3
+ end
@@ -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
@@ -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
@@ -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)
@@ -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