path-to 0.3.1 → 0.4.0

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 CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.4.0
2
+
3
+ * Positional parameters for path-to/described_routes (only), e.g. delicious.posts('ruby'); app.users['dojo']
4
+
1
5
  == 0.3.1 2009-05-08
2
6
 
3
7
  * Add gem dependency on described_routes
data/README.rdoc CHANGED
@@ -18,10 +18,17 @@ Create a client application configured from a server that supports described_rou
18
18
 
19
19
  app = PathTo::DescribedRoutes::Application.new(:json => Net::HTTP.get(URI.parse("http://example.com/described_routes.json")))
20
20
 
21
- app.users["user_id" => "dojo"].articles.recent #=> http://example.com/users/dojo/articles/recent
22
- app.users["user_id" => "dojo"].articles.recent.get #=> "<html>...</html>"
21
+ app.users["dojo"].articles.recent
22
+ #=> http://example.com/users/dojo/articles/recent
23
+ app.users["dojo"].articles.recent.get
24
+ #=> "<html>...</html>"
25
+
26
+ app.users["dojo"].articles.recent["format" => "json"]
27
+ #=> http://example.com/users/dojo/articles/recent.json
28
+ app.users["dojo"].articles.recent.get
29
+ #=> [...]
23
30
 
24
- See examples/delicious.rb for an example based on a partial YAML-based description of the Delicious API held locally.
31
+ See examples/delicious.rb for an example based on a partial YAML-based description of the Delicious API.
25
32
 
26
33
  === Local configuration
27
34
 
data/Rakefile CHANGED
@@ -13,7 +13,7 @@ $hoe = Hoe.new('path-to', PathTo::VERSION) do |p|
13
13
  p.extra_deps = [
14
14
  ['httparty','>= 0.4.2'],
15
15
  ['addressable','>= 2.0.2'],
16
- ['described_routes','>= 0.3.5']
16
+ ['described_routes','>= 0.3.6']
17
17
  ]
18
18
  p.extra_dev_deps = [
19
19
  ['newgem', ">= #{::Newgem::VERSION}"]
@@ -1,5 +1,4 @@
1
- # Adapted from jnunemaker/httparty/examples/delicious.rb to demonstrate path-to's metadata-driven REST client API capability
2
- # For more information see http://positiveincline.com/?tag=path-to
1
+ # Adapted from jnunemaker/httparty/examples/delicious.rb to demonstrate path-to's metadata-driven client API capability
3
2
 
4
3
  require 'path-to/described_routes'
5
4
  require 'pp'
@@ -38,6 +37,6 @@ delicious = PathTo::DescribedRoutes::Application.new(
38
37
  :username => config['username'],
39
38
  :password => config['password']}})
40
39
 
41
- pp delicious.posts['tag' => 'ruby'].get
42
- pp delicious.posts['tag' => 'ruby'].recent['count' => '5'].get
40
+ pp delicious.posts('ruby').get
41
+ pp delicious.posts('ruby').recent('count' => '5').get
43
42
  delicious.recent_posts.get['posts']['post'].each { |post| puts post['href'] }
data/lib/path-to.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module PathTo
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
4
4
 
5
5
  $:.push File.dirname(__FILE__)
@@ -2,10 +2,26 @@ require "path-to"
2
2
  require "described_routes/resource_template"
3
3
 
4
4
  module PathTo
5
+ #
6
+ # Application and Path implementations for DescribedRoutes, each resource described by a ResourceTemplate
7
+ #
5
8
  module DescribedRoutes
9
+ #
10
+ # Implements PathTo::Path, represents a resource described by a ResourceTemplate
11
+ #
6
12
  class TemplatedPath < PathTo::Path
7
13
  attr_reader :resource_template
8
14
 
15
+ #
16
+ # Initialize a TemplatedPath. Raises ArgumentError if params doesn't include all mandatory params expected by the resource
17
+ # template.
18
+ #
19
+ # Parameters:
20
+ # [parent] parent object path or application
21
+ # [service] unused - resource_template.name is passed to super() instead. TODO: refactor
22
+ # [params] hash of params; will be merged with the parent's params and passed when required to the resource template's URI template
23
+ # [resource_template] metadata describing the web resource
24
+ #
9
25
  def initialize(parent, service, params, resource_template)
10
26
  super(parent, resource_template.name, params)
11
27
  @resource_template = resource_template
@@ -20,10 +36,16 @@ module PathTo
20
36
  end
21
37
  end
22
38
 
39
+ #
40
+ # Get and cache the uri template from the resource tamplte
41
+ #
23
42
  def uri_template
24
43
  @uri_template ||= resource_template.uri_template || (application.base + resource_template.path_template)
25
44
  end
26
45
 
46
+ #
47
+ # Create and cache the URI by filling in the URI template with params
48
+ #
27
49
  def uri
28
50
  @uri ||= begin
29
51
  Addressable::Template.new(uri_template).expand(params).to_s
@@ -43,15 +65,38 @@ module PathTo
43
65
  end
44
66
 
45
67
  #
46
- # Creates a child instance with new params, potentially finding a nested resource template that takes the additional params
68
+ # Creates a child instance with new params, potentially finding a nested resource template that takes the additional params.
69
+ # May take a combination of positional and named parameters, e.g.
70
+ #
71
+ # users["dojo", {"format" => "json"}]
47
72
  #
48
- def [](params = {})
49
- keys = self.params.merge(params).keys
50
- child_resource_template = resource_template.resource_templates.detect{ |t|
51
- t.rel.nil? && (t.params - keys).empty?
52
- } || resource_template
53
- child_class = child_class_for(self, nil, params, child_resource_template)
54
- child(child_class, nil, params, child_resource_template)
73
+ # Positional parameters are unsupported however if a new child template is not identified.
74
+ #
75
+ def [](*args)
76
+ positional_params, params_hash = extract_params(args, params)
77
+ known_keys = params_hash.keys
78
+
79
+ child_resource_template = resource_template.resource_templates.detect do |t|
80
+ if t.rel.nil?
81
+ (t.positional_params(resource_template)[positional_params.length..-1] - t.optional_params - known_keys).empty?
82
+ end
83
+ end
84
+
85
+ if child_resource_template
86
+ # we have a new child resource template; apply any positional params to the hash
87
+ complete_params_hash!(params_hash, child_resource_template.positional_params(resource_template), positional_params)
88
+ else
89
+ # we're just adding optional params, no new template identified
90
+ unless positional_params.empty?
91
+ raise ArgumentError.new(
92
+ "No matching child template; only named parameters can be used here. " +
93
+ "positional_params=#{positional_params.inspect}, params_hash=#{params_hash.inspect}")
94
+ end
95
+ child_resource_template = resource_template
96
+ end
97
+
98
+ child_class = child_class_for(self, nil, params_hash, child_resource_template)
99
+ child(child_class, nil, params_hash, child_resource_template)
55
100
  end
56
101
 
57
102
  #
@@ -62,11 +107,16 @@ module PathTo
62
107
  #
63
108
  # Otherwise we invoke super in the hope of avoiding any hard-to-debug behaviour!
64
109
  #
110
+ # May take a combination of positional and named parameters, e.g.
111
+ #
112
+ # users("dojo", "format" => "json")
113
+ #
65
114
  def method_missing(method, *args)
66
115
  child_resource_template = resource_template.resource_templates.detect{|t| t.rel == method.to_s}
67
116
  if child_resource_template && (child_class = child_class_for(self, method, params, child_resource_template))
68
- params = args.inject(Hash.new){|h, arg| h.merge(arg)}
69
- child(child_class, method, params, child_resource_template)
117
+ positional_params, params_hash = extract_params(args, params)
118
+ complete_params_hash!(params_hash, child_resource_template.positional_params(resource_template), positional_params)
119
+ child(child_class, method, params_hash, child_resource_template)
70
120
  else
71
121
  super
72
122
  end
@@ -74,6 +124,9 @@ module PathTo
74
124
 
75
125
  end
76
126
 
127
+ #
128
+ # DescribedRoutes implementation of PathTo::Application.
129
+ #
77
130
  class Application < WithParams
78
131
  # An Array of DescribedRoutes::Resource objects
79
132
  attr_reader :resource_templates
@@ -122,7 +175,7 @@ module PathTo
122
175
  #
123
176
  # Creates a copy of self with additional params
124
177
  #
125
- def [](params = {})
178
+ def [](params)
126
179
  self.class.new(:parent => self, :params => params)
127
180
  end
128
181
 
@@ -135,10 +188,11 @@ module PathTo
135
188
  # Otherwise we invoke super in the hope of avoiding any hard-to-debug behaviour!
136
189
  #
137
190
  def method_missing(method, *args)
138
- resource_template = resource_templates_by_name[method.to_s]
139
- if resource_template && (child_class = child_class_for(self, method, params, resource_template))
140
- params = args.inject(Hash.new){|h, arg| h.merge(arg)}
141
- child(child_class, method, params, resource_template)
191
+ child_resource_template = resource_templates_by_name[method.to_s]
192
+ if child_resource_template && (child_class = child_class_for(self, method, params, child_resource_template))
193
+ positional_params, params_hash = extract_params(args, params)
194
+ complete_params_hash!(params_hash, child_resource_template.positional_params(nil), positional_params)
195
+ child(child_class, method, params_hash, child_resource_template)
142
196
  else
143
197
  super
144
198
  end
@@ -87,5 +87,32 @@ module PathTo
87
87
  super
88
88
  end
89
89
  end
90
+
91
+ #
92
+ # Separates positional params from hash params
93
+ # TODO: this is initially just for the DescribedRoutes implementation but there will be some refactoring to do
94
+ #
95
+ def extract_params(args, params_hash={})#:nodoc:
96
+ positional_params = []
97
+ params_hash = params_hash.clone
98
+ args.each do |arg|
99
+ if arg.kind_of?(Hash)
100
+ params_hash.merge!(arg)
101
+ else
102
+ positional_params << arg
103
+ end
104
+ end
105
+ [positional_params, params_hash]
106
+ end
107
+
108
+ #
109
+ # Updates params_hash with positional parameters
110
+ # TODO: this is initially just for the DescribedRoutes implementation but there will be some refactoring to do
111
+ #
112
+ def complete_params_hash!(params_hash, names, values)#:nodoc:
113
+ names[0...values.length].each_with_index do |k, i|
114
+ params_hash[k] = values[i]
115
+ end
116
+ end
90
117
  end
91
118
  end
@@ -102,13 +102,22 @@ class TestDescribedRoutes < Test::Unit::TestCase
102
102
  end
103
103
 
104
104
  def test_path_optional_params
105
- users_json = app.users["format" => "json"]
106
- user_json = app.users["user_id" => "dojo"]["format" => "json"]
105
+ # more complicated than would be ideal, but the app has a different #method_missing d
106
+ user_articles = app.users["user_id" => "dojo"].articles("json")
107
107
 
108
- assert_equal("users", users_json.service)
109
- assert_equal("user", user_json.service)
110
- assert_equal("http://localhost:3000/users.json", users_json.uri)
111
- assert_equal("http://localhost:3000/users/dojo.json", user_json.uri)
108
+ assert_equal("user_articles", user_articles.service)
109
+ assert_equal({"user_id" => "dojo", "format" => "json"}, user_articles.params)
110
+ end
111
+
112
+ def test_path_collection_positional_params
113
+ article_json = app.users["dojo"].articles["article-1"]["format" => "json"]
114
+ assert_equal("user_article", article_json.service)
115
+ assert_equal({"user_id" => "dojo", "article_id" => "article-1", "format" => "json"}, article_json.params)
116
+ assert_equal("http://localhost:3000/users/dojo/articles/article-1.json", article_json.uri)
117
+
118
+ assert_raises(ArgumentError) do
119
+ article_json = app.users["dojo"]["json"]
120
+ end
112
121
  end
113
122
 
114
123
  def test_app_params
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: path-to
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Burrows (asplake)
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-08 00:00:00 +01:00
12
+ date: 2009-05-12 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.3.5
43
+ version: 0.3.6
44
44
  version:
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: newgem