path-to 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/History.txt +4 -0
- data/LICENSE +20 -0
- data/Manifest.txt +17 -0
- data/PostInstall.txt +2 -0
- data/README.rdoc +52 -0
- data/Rakefile +38 -0
- data/lib/path-to/application.rb +121 -0
- data/lib/path-to/path.rb +71 -0
- data/lib/path-to/with_params.rb +91 -0
- data/lib/path-to.rb +6 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/path-to/test_application.rb +74 -0
- data/test/path-to/test_path.rb +48 -0
- data/test/path-to/test_with_params.rb +60 -0
- data/test/test_helper.rb +3 -0
- metadata +120 -0
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
path-to, Copyright (c) 2009 Mike Burrows
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
PostInstall.txt
|
5
|
+
README.rdoc
|
6
|
+
Rakefile
|
7
|
+
lib/path-to.rb
|
8
|
+
lib/path-to/application.rb
|
9
|
+
lib/path-to/path.rb
|
10
|
+
lib/path-to/with_params.rb
|
11
|
+
script/console
|
12
|
+
script/destroy
|
13
|
+
script/generate
|
14
|
+
test/path-to/test_application.rb
|
15
|
+
test/path-to/test_path.rb
|
16
|
+
test/path-to/test_with_params.rb
|
17
|
+
test/test_helper.rb
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
= path-to README
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
path-to allows web applications to be modelled via URI templates and then accessed through an application-specific Ruby API. It is
|
6
|
+
designed to be extended easily to support discovery mechanisms; support for XRD may be added at some future date.
|
7
|
+
|
8
|
+
== Synopsis
|
9
|
+
|
10
|
+
Here's a simple example that illustrates access to a CMS:
|
11
|
+
|
12
|
+
require "path-to"
|
13
|
+
|
14
|
+
class Users < PathTo::Path ; end
|
15
|
+
class Articles < PathTo::Path ; end
|
16
|
+
|
17
|
+
app = Application.new(
|
18
|
+
:users => "http://example.com/users/{user}",
|
19
|
+
:articles => "http://example.com/users/{user}/articles/{slug}") do |app|
|
20
|
+
def app.child_class_for(instance, method, params)
|
21
|
+
{
|
22
|
+
:users => Users,
|
23
|
+
:articles => Articles
|
24
|
+
}[method]
|
25
|
+
end
|
26
|
+
end #=> Application
|
27
|
+
|
28
|
+
Note that the Users and Articles classes and the overridden #child_class_for method above can be done away with (reducing the above
|
29
|
+
code to just four lines) if there is no need to define any class-specific behaviour.
|
30
|
+
|
31
|
+
Having defined URI template and class mappings for keys :users and :articles mapping to URI templates, calls to app.users and
|
32
|
+
app.articles cause objects of the appropriate class to be generated. These in turn support chaining and the collection of request
|
33
|
+
params, like this:
|
34
|
+
|
35
|
+
app.users #=> http://example.com/users/ <Users>
|
36
|
+
app.users(:user => "dojo") #=> http://example.com/users/dojo <Users>
|
37
|
+
app.users[:user => "dojo"] #=> http://example.com/users/dojo <Users>
|
38
|
+
app.articles(:user => "dojo", :slug => "my-article") #=> http://example.com/users/dojo/articles/my-article <Articles>
|
39
|
+
app.users[:user => "dojo"].articles[:slug => "my-article"] #=> http://example.com/users/dojo/articles/my-article <Articles>
|
40
|
+
|
41
|
+
With a little more work (overriding Users#[] and Articles#[] - as described in the documentation for the Path class), the last example
|
42
|
+
becomes simply:
|
43
|
+
|
44
|
+
app.users["dojo"].articles["my-article"] #=> http://example.com/users/dojo/articles/my-article <Articles>
|
45
|
+
|
46
|
+
HTTP support comes courtesy of HTTParty (the Path class includes it). To GET an article in the above example, just invoke the get method on the path object:
|
47
|
+
|
48
|
+
app.users["dojo"].articles["my-article"].get #=> "<html>...</html>"
|
49
|
+
|
50
|
+
== Installation, Dependencies, Testing
|
51
|
+
|
52
|
+
TODO
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
$:.push File.dirname(__FILE__) + '/lib'
|
3
|
+
require 'path-to'
|
4
|
+
|
5
|
+
# undefined method `empty?' for nil:NilClass
|
6
|
+
# /Library/Ruby/Site/1.8/rubygems/specification.rb:886:in `validate'
|
7
|
+
class NilClass
|
8
|
+
def empty?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generate all the Rake tasks
|
14
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
15
|
+
$hoe = Hoe.new('path-to', PathTo::VERSION) do |p|
|
16
|
+
p.developer('Mike Burrows (asplake)', 'mjb@asplake.co.uk')
|
17
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
18
|
+
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
19
|
+
p.rubyforge_name = p.name # TODO this is default value
|
20
|
+
p.extra_deps = [
|
21
|
+
['httparty','>= 0.4.2'],
|
22
|
+
['addressable','>= 2.0.2'],
|
23
|
+
]
|
24
|
+
p.extra_dev_deps = [
|
25
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
26
|
+
]
|
27
|
+
|
28
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
29
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
30
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
31
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
35
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
36
|
+
|
37
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
38
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "path-to/path"
|
2
|
+
require "path-to/http_client"
|
3
|
+
require "addressable/uri"
|
4
|
+
|
5
|
+
module PathTo
|
6
|
+
#
|
7
|
+
# Provides a Ruby client API interface to a web application. Method calls on this Application object generate Path objects that
|
8
|
+
# map (via URI templates held here on the Application) to the web application's URIs.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# app = PathTo::Application.new(
|
13
|
+
# :users => "http://example.com/users/{user}",
|
14
|
+
# :articles => "http://example.com/users/{user}/articles/{slug}") do |app|
|
15
|
+
# def app.child_class_for(instance, method, params)
|
16
|
+
# {
|
17
|
+
# :users => Users,
|
18
|
+
# :articles => Articles
|
19
|
+
# }[method]
|
20
|
+
# end
|
21
|
+
# end #=> PathTo::Application
|
22
|
+
#
|
23
|
+
# app.users #=> http://example.com/users/ <Users>
|
24
|
+
# app.users(:user => "dojo") #=> http://example.com/users/dojo <Users>
|
25
|
+
# app.articles(:user => "dojo", :slug => "my-article") #=> http://example.com/users/dojo/articles/my-article <Articles>
|
26
|
+
# app.users[:user => "dojo"].articles[:slug => "my-article"] #=> http://example.com/users/dojo/articles/my-article <Articles>
|
27
|
+
#
|
28
|
+
class Application < WithParams
|
29
|
+
# A Hash that maps method keys (Symbol) to URI templates (String)
|
30
|
+
attr_reader :templates
|
31
|
+
|
32
|
+
# A Class (or at least something with a #new method) from which child objects will be created
|
33
|
+
attr_reader :default_type
|
34
|
+
|
35
|
+
# An HTTParty or similar
|
36
|
+
attr_reader :http_client
|
37
|
+
|
38
|
+
#
|
39
|
+
# Initializes an Application. Parameters:
|
40
|
+
#
|
41
|
+
# [templates] Initial value for the templates attribute, defaults to {}
|
42
|
+
# [default_type] Initial value for the default_type attribute, defaults to Path
|
43
|
+
# [http_client] An object through which http calls are invoked. See HTTPClient and Path#http_client.
|
44
|
+
#
|
45
|
+
# Simple example:
|
46
|
+
#
|
47
|
+
# # Model an application with just a "users" collection that generates Path objects
|
48
|
+
# simple_app = PathTo::Application.new(:users => "http://example.com/users/{user}")
|
49
|
+
#
|
50
|
+
# The constructor yields self, utilised in this example:
|
51
|
+
#
|
52
|
+
# # Model an application with "users" and "articles" collections, represented here on the client side by Users and Articles objects
|
53
|
+
# bigger_app = PathTo::Application.new(
|
54
|
+
# :users => "http://example.com/users/{user}",
|
55
|
+
# :articles => "http://example.com/users/{user}/articles/{slug}") do |app|
|
56
|
+
# def app.child_class_for(instance, method, params)
|
57
|
+
# {
|
58
|
+
# :users => Users,
|
59
|
+
# :articles => Articles
|
60
|
+
# }[method]
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
def initialize(templates = {}, default_type = Path, http_client = HTTPClient)
|
65
|
+
super() # with default args
|
66
|
+
@templates, @default_type, @http_client = templates, default_type, http_client
|
67
|
+
yield self if block_given?
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Determines whether this application &/or its child objects should respond to the given method, and if so returns a class from
|
72
|
+
# which a new child instance (typically Path or a subclass thereof) will be created. This implementation (easily overridden)
|
73
|
+
# returns #default_type if there is a URI template defined for the method.
|
74
|
+
#
|
75
|
+
# Parameters:
|
76
|
+
#
|
77
|
+
# [instance] This application or (presumably) one of its child objects
|
78
|
+
# [method] The method invoked on the instance that has (presumably) been intercepted by instance#method_missing
|
79
|
+
# [params] The instance's params
|
80
|
+
#
|
81
|
+
def child_class_for(instance, method, params)
|
82
|
+
default_type if uri_template_for(method, params)
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Returns self. See Path#application.
|
87
|
+
#
|
88
|
+
def application
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Returns a URI template for the given method and params. Parameters:
|
94
|
+
#
|
95
|
+
# [method] The method invoked on the instance that has (presumably) been intercepted by instance#method_missing
|
96
|
+
# [params] The instance's params
|
97
|
+
#
|
98
|
+
# This implementation returns a value from the #templates Hash, keyed by method (params is ignored).
|
99
|
+
#
|
100
|
+
#--
|
101
|
+
# TODO Consider taking an instance as the first parameter, as #child_class_for does
|
102
|
+
#
|
103
|
+
def uri_template_for(method, params = {})
|
104
|
+
templates[method]
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Generates a URI, looking up a URI template (via #uri_template_for) and getting it formatted with the params.
|
109
|
+
#
|
110
|
+
#--
|
111
|
+
# TODO Consider taking an instance as the first parameter, as #child_class_for does
|
112
|
+
#
|
113
|
+
def uri_for(method, params = {})
|
114
|
+
# TODO it's a 1-line fix to Addressable to permit symbols (etc) as keys
|
115
|
+
if (t = uri_template_for(method, params))
|
116
|
+
string_keyed_params = params.keys.inject({}){|hash, key| hash[key.to_s] = params[key]; hash}
|
117
|
+
Addressable::URI.expand_template(t, string_keyed_params).to_s
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/path-to/path.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require "path-to/with_params"
|
2
|
+
|
3
|
+
module PathTo
|
4
|
+
#
|
5
|
+
# Builds on the chaining and param collection of WithParams to provide chainable references to URIs. Delegates the modelling of the
|
6
|
+
# web application to a parent (or ancestor) application object, so that this configuration is in one place (and perhaps the result of a
|
7
|
+
# discovery process).
|
8
|
+
#
|
9
|
+
class Path < WithParams
|
10
|
+
|
11
|
+
#
|
12
|
+
# Finds (once) the application in the parent hierarchy.
|
13
|
+
#
|
14
|
+
def application
|
15
|
+
@application ||= parent.application if parent
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Delegated to the application object (see Application#child_class_for for details).
|
20
|
+
#
|
21
|
+
def child_class_for(instance, service, args)
|
22
|
+
application.child_class_for(instance, service, args)
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Generate a URI for this object, using application.uri_for (see Application#uri_for for details).
|
27
|
+
#
|
28
|
+
def uri
|
29
|
+
application.uri_for(service, params)
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Returns the http_client of the application; override if necessary. See also HTTPClient.
|
34
|
+
#
|
35
|
+
def http_client
|
36
|
+
@http_client ||= application.http_client
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# GET request on this object's URI
|
41
|
+
#
|
42
|
+
def get(*args)
|
43
|
+
http_client.get(uri, *args)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# PUT request on this object's URI
|
48
|
+
#
|
49
|
+
def put(*args)
|
50
|
+
http_client.put(uri, *args)
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# POST request on this object's URI
|
55
|
+
#
|
56
|
+
def post(*args)
|
57
|
+
http_client.post(uri, *args)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# DELETE request on this object's URI
|
62
|
+
#
|
63
|
+
def delete(*args)
|
64
|
+
http_client.delete(uri, *args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def inspect #:nodoc:
|
68
|
+
"#{uri} #<#{self.class.name}:#{"0x%x" % object_id} service=#{service.inspect}, params=#{params.inspect}>"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module PathTo
|
2
|
+
#
|
3
|
+
# Chaining parameter collection. Example:
|
4
|
+
#
|
5
|
+
# p = WithParams.new.foo(:bar => "baz").fee[:fi => "fo"] #=> <WithParams>
|
6
|
+
# p.service #=> :fee
|
7
|
+
# p.params #=> {:bar => "baz", :fi => "fo"}
|
8
|
+
#
|
9
|
+
class WithParams
|
10
|
+
# Parent object, presumably of type WithParams or descendant
|
11
|
+
attr_reader :parent
|
12
|
+
|
13
|
+
# Service identifier, typically a method symbol intercepted in #method_missing
|
14
|
+
attr_reader :service
|
15
|
+
|
16
|
+
# Parameter hash
|
17
|
+
attr_reader :params
|
18
|
+
|
19
|
+
#
|
20
|
+
# Initialize a new WithParams object. Parameters:
|
21
|
+
#
|
22
|
+
# [parent] Value for the parent attribute
|
23
|
+
# [service] Value for the service attribute
|
24
|
+
# [params] Value for the params attribute
|
25
|
+
#
|
26
|
+
def initialize(parent = nil, service = nil, params = {})
|
27
|
+
@parent, @service, @params = parent, service, params
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Creates a child instance. Parameters:
|
32
|
+
#
|
33
|
+
# [child_class] The class of the new instance, calls #child_class_for to determine this if none supplied
|
34
|
+
# [service] Value for the new instance's service attribute, inherited from self (the parent) if none supplied
|
35
|
+
# [params] The new instance's params, will be merged with self's (the parent's) params
|
36
|
+
#
|
37
|
+
def child(child_class = nil, service = nil, params = {})
|
38
|
+
child_class ||= child_class_for(instance, service, params)
|
39
|
+
service ||= self.service
|
40
|
+
params = self.params.merge(params)
|
41
|
+
child_class.new(self, service, params)
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Creates a child instance with new params. Subclasses might override this to take values instead of hashes, as follows:
|
46
|
+
#
|
47
|
+
# def [](value)
|
48
|
+
# super(:key => value)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
def [](params = {})
|
52
|
+
child(self.class, self.service, params)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Determines the class of a new child, given the parent instance, service and params
|
57
|
+
#
|
58
|
+
def child_class_for(instance, service, params)
|
59
|
+
self.class
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Determines whether we can respond to method (missing or otherwise). We can respond to a missing method if #child_class_for
|
64
|
+
# returns a class for a new child.
|
65
|
+
#
|
66
|
+
def respond_to?(method)
|
67
|
+
child_class_for(self, method, params) || super
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Tries to respond to a missing method. We can do so if
|
72
|
+
#
|
73
|
+
# 1. any args are hashes (merged to make a params hash), and
|
74
|
+
# 2. #child_class_for returns a class or other factory object capable of creating a new child instance
|
75
|
+
#
|
76
|
+
# In all other cases and in the case of error we invoke super in the hope of avoiding any hard-to-debug behaviour!
|
77
|
+
#
|
78
|
+
def method_missing(method, *args)
|
79
|
+
begin
|
80
|
+
params = args.inject(Hash.new){|h, arg| h.merge(arg)}
|
81
|
+
if (child_class = child_class_for(self, method, params))
|
82
|
+
child(child_class, method, params)
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
rescue
|
87
|
+
super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/path-to.rb
ADDED
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/path-to.rb'}"
|
9
|
+
puts "Loading path-to gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "path-to/application"
|
3
|
+
|
4
|
+
module PathTo
|
5
|
+
class TestApplication < Test::Unit::TestCase
|
6
|
+
attr_reader :simple_app, :bigger_app
|
7
|
+
|
8
|
+
class Users < Path
|
9
|
+
def [](user)
|
10
|
+
super(:user => user)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Articles < Path
|
15
|
+
def [](slug)
|
16
|
+
super(:slug => slug)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@simple_app = Application.new(:users => "http://example.com/users/{user}")
|
22
|
+
|
23
|
+
@bigger_app = Application.new(
|
24
|
+
:users => "http://example.com/users/{user}",
|
25
|
+
:articles => "http://example.com/users/{user}/articles/{slug}") do |app|
|
26
|
+
def app.child_class_for(instance, method, params)
|
27
|
+
{
|
28
|
+
:users => Users,
|
29
|
+
:articles => Articles
|
30
|
+
}[method]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_uri_for
|
36
|
+
assert_equal("http://example.com/users/", simple_app.uri_for(:users))
|
37
|
+
assert_equal("http://example.com/users/", simple_app.uri_for(:users, :what => "ever"))
|
38
|
+
assert_equal("http://example.com/users/dojo", simple_app.uri_for(:users, :user => "dojo"))
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_child_uri
|
42
|
+
assert_equal("http://example.com/users/", simple_app.users.uri)
|
43
|
+
assert_equal("http://example.com/users/", simple_app.users(:what => "ever").uri)
|
44
|
+
assert_equal("http://example.com/users/", simple_app.users[:what => "ever"].uri)
|
45
|
+
assert_equal("http://example.com/users/dojo", simple_app.users(:user => "dojo").uri)
|
46
|
+
assert_equal("http://example.com/users/dojo", simple_app.users[:user => "dojo"].uri)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_no_child
|
50
|
+
assert_raises(NoMethodError) {simple_app.flooby}
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_bigger_app
|
54
|
+
assert_kind_of(Users, bigger_app.users)
|
55
|
+
assert_kind_of(Users, bigger_app.users["dojo"])
|
56
|
+
|
57
|
+
assert_kind_of(Articles, bigger_app.articles)
|
58
|
+
assert_kind_of(Articles, bigger_app.articles["a-title"])
|
59
|
+
|
60
|
+
assert_equal("http://example.com/users/", bigger_app.users.uri)
|
61
|
+
assert_equal("http://example.com/users/dojo", bigger_app.users["dojo"].uri)
|
62
|
+
|
63
|
+
assert_equal("http://example.com/users//articles/", bigger_app.articles.uri)
|
64
|
+
assert_equal("http://example.com/users/dojo/articles/", bigger_app.articles(:user => "dojo").uri)
|
65
|
+
assert_equal("http://example.com/users//articles/a-title", bigger_app.articles["a-title"].uri)
|
66
|
+
|
67
|
+
assert_equal("http://example.com/users/dojo/articles/a-title", bigger_app.articles(:user => "dojo", :slug => "a-title").uri)
|
68
|
+
assert_equal("http://example.com/users/dojo/articles/a-title", bigger_app.users["dojo"].articles["a-title"].uri)
|
69
|
+
|
70
|
+
assert_raises(NoMethodError) {bigger_app.flooby}
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "mocha"
|
3
|
+
require "path-to/path"
|
4
|
+
require "path-to/application"
|
5
|
+
|
6
|
+
module PathTo
|
7
|
+
class TestPath < Test::Unit::TestCase
|
8
|
+
attr_reader :app, :users
|
9
|
+
|
10
|
+
class TestPath < Path
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@app = Application.new(
|
15
|
+
:users => "http://example.com/users/{user}",
|
16
|
+
:articles => "http://example.com/users/{user}/articles/{slug}")
|
17
|
+
@users = @app.users
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_application
|
21
|
+
assert_equal(app, users.application)
|
22
|
+
assert_equal(app, users.articles.application)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_nil_application
|
26
|
+
assert_nil(Path.new.application)
|
27
|
+
assert_nil(Path.new[].application)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_child_class_for
|
31
|
+
params = {:slug => "a-title"}
|
32
|
+
app.expects(:child_class_for).with(users, :articles, params).returns(TestPath)
|
33
|
+
assert_equal(TestPath, users.child_class_for(users, :articles, params))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_uri
|
37
|
+
assert_equal("http://example.com/users/dojo", users[:user => "dojo"].uri)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_http_methods
|
41
|
+
request_options = {:body => {:bar => :baz}}
|
42
|
+
[:get, :put, :post, :delete].each do |method|
|
43
|
+
app.http_client.expects(method).with("http://example.com/users/", request_options)
|
44
|
+
users.send(method, request_options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "path-to/with_params"
|
3
|
+
|
4
|
+
module PathTo
|
5
|
+
|
6
|
+
class TestWithParams < Test::Unit::TestCase
|
7
|
+
attr_reader :root, :s1
|
8
|
+
|
9
|
+
class WithParamsSubclass1 < WithParams
|
10
|
+
def child_class_for(instance, service, params)
|
11
|
+
WithParamsSubclass2
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class WithParamsSubclass2 < WithParams
|
16
|
+
end
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@root = WithParams.new(nil, :root, :x => 1)
|
20
|
+
@s1 = WithParamsSubclass1.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_service
|
24
|
+
assert_nil(WithParams.new.service)
|
25
|
+
assert_equal(:root, root.service)
|
26
|
+
assert_equal(:root, root.child.service)
|
27
|
+
assert_equal(:child, root.child(nil, :child).service)
|
28
|
+
assert_equal(:child, root.child(nil, :child).child.service)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_params
|
32
|
+
assert_equal({}, WithParams.new.params)
|
33
|
+
assert_equal({:x => 1}, root.params)
|
34
|
+
assert_equal({:x => 1}, root.child.params)
|
35
|
+
assert_equal({:x => 2}, root.child(nil, nil, {:x => 2}).params)
|
36
|
+
assert_equal({:x => 2}, root.child(nil, nil, {:x => 2}).child!.params)
|
37
|
+
assert_equal({:x => 1, :y => 2}, root.child(nil, nil, {:y => 2}).params)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_child_type
|
41
|
+
assert_kind_of(WithParams, root.child)
|
42
|
+
assert_kind_of(WithParamsSubclass2, s1.child)
|
43
|
+
assert_kind_of(WithParamsSubclass2, s1.child.child)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_ary_params
|
47
|
+
assert_equal(:root, root[].service)
|
48
|
+
assert_equal({:x => 1}, root[].params)
|
49
|
+
assert_equal({:x => 2}, root[:x => 2].params)
|
50
|
+
assert_equal({:x => 1, :y => 2}, root[:y => 2].params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_method_missing
|
54
|
+
assert_kind_of(WithParams, root.flooby)
|
55
|
+
assert_equal(:flooby, root.flooby.service)
|
56
|
+
assert_equal({:x => 1}, root.flooby.params)
|
57
|
+
assert_equal({:x => 1, :y => 2}, root.flooby[:y => 2].params)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: path-to
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Burrows (asplake)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-18 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: httparty
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.4.2
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: addressable
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.2
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: newgem
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.3.0
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: hoe
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.8.0
|
54
|
+
version:
|
55
|
+
description: |-
|
56
|
+
path-to allows web applications to be modelled via URI templates and then accessed through an application-specific Ruby API. It is
|
57
|
+
designed to be extended easily to support discovery mechanisms; support for XRD may be added at some future date.
|
58
|
+
email:
|
59
|
+
- mjb@asplake.co.uk
|
60
|
+
executables: []
|
61
|
+
|
62
|
+
extensions: []
|
63
|
+
|
64
|
+
extra_rdoc_files:
|
65
|
+
- History.txt
|
66
|
+
- Manifest.txt
|
67
|
+
- PostInstall.txt
|
68
|
+
- README.rdoc
|
69
|
+
files:
|
70
|
+
- History.txt
|
71
|
+
- LICENSE
|
72
|
+
- Manifest.txt
|
73
|
+
- PostInstall.txt
|
74
|
+
- README.rdoc
|
75
|
+
- Rakefile
|
76
|
+
- lib/path-to.rb
|
77
|
+
- lib/path-to/application.rb
|
78
|
+
- lib/path-to/path.rb
|
79
|
+
- lib/path-to/with_params.rb
|
80
|
+
- script/console
|
81
|
+
- script/destroy
|
82
|
+
- script/generate
|
83
|
+
- test/path-to/test_application.rb
|
84
|
+
- test/path-to/test_path.rb
|
85
|
+
- test/path-to/test_with_params.rb
|
86
|
+
- test/test_helper.rb
|
87
|
+
has_rdoc: true
|
88
|
+
homepage:
|
89
|
+
licenses: []
|
90
|
+
|
91
|
+
post_install_message: PostInstall.txt
|
92
|
+
rdoc_options:
|
93
|
+
- --main
|
94
|
+
- README.rdoc
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
version:
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: "0"
|
108
|
+
version:
|
109
|
+
requirements: []
|
110
|
+
|
111
|
+
rubyforge_project: path-to
|
112
|
+
rubygems_version: 1.3.2
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: path-to allows web applications to be modelled via URI templates and then accessed through an application-specific Ruby API
|
116
|
+
test_files:
|
117
|
+
- test/path-to/test_application.rb
|
118
|
+
- test/path-to/test_path.rb
|
119
|
+
- test/path-to/test_with_params.rb
|
120
|
+
- test/test_helper.rb
|