weary 0.4.3 → 0.5.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/README.md CHANGED
@@ -8,14 +8,18 @@ The things it do:
8
8
 
9
9
  + Quickly build an interface to your favorite REST API.
10
10
  + Parse XML and JSON with the [Crack](http://github.com/jnunemaker/crack) library.
11
+ + Authenticate, basically, with Basic Authentication.
12
+ + Consume with [OAuth](http://oauth.net/)
11
13
 
12
14
  Browse the documentation here: [http://rdoc.info/projects/mwunsch/weary](http://rdoc.info/projects/mwunsch/weary)
15
+ Peruse the [Wiki](http://wiki.github.com/mwunsch/weary) to discover libraries built with Weary and a more thorough review of the API.
13
16
 
14
17
  ## Requirements
15
18
 
16
- + Crack >= 0.1.2
17
- + Nokogiri >= 1.3.1 (if you want to use the #search method)
18
- + Rspec (for running the tests)
19
+ + [Crack](http://github.com/jnunemaker/crack) >= 0.1.2
20
+ + [Nokogiri](http://github.com/tenderlove/nokogiri) >= 1.3.1 (if you want to use the #search method)
21
+ + [OAuth](http://github.com/mojodna/oauth) >= 0.3.5 (if you want to use OAuth)
22
+ + [RSpec](http://rspec.info/) (for running the tests)
19
23
 
20
24
  ## Installation
21
25
 
@@ -66,6 +70,8 @@ Besides the name of the resource, you can also give `declare_resource` a block l
66
70
  r.requires = [:id, :bar] # an array of params that the resource requires to be in the query/body
67
71
  r.with = [:blah] # an array of params that you can optionally send to the resource
68
72
  r.authenticates = false # does the method require basic authentication? defaults to false
73
+ r.oauth = false # does this resource use OAuth to authorize you? it's boolean
74
+ r.access_token = nil # if you're using OAuth, you should provide the user's access token.
69
75
  r.follows = false # if this is set to false, the formed request will not follow redirects.
70
76
  r.headers = {'Accept' => 'text/html'} # send custom headers. defaults to nil.
71
77
  end
@@ -116,4 +122,23 @@ There are many ways to form URLs in Weary. You can define URLs for the entire cl
116
122
  The string `<domain><resource>.<format>` helps define a simple pattern for creating URLs. These will be filled in by your resource declaration. The above `get` declaration creates a url that looks like: *http://foo.bar/show_users.xml*
117
123
 
118
124
  If you use the `<domain>` flag but don't define a domain, an exception will be raised.
119
-
125
+
126
+ ### Weary DSL
127
+
128
+ You can create some defaults for all of our resources easily:
129
+
130
+ class Foo
131
+ extend Weary
132
+
133
+ domain "http://foo.bar/"
134
+ url "<domain><resource>.<format>"
135
+ format :xml
136
+ headers {'Accept' => 'text/html'} # set headers
137
+ authenticates "basic_username","basic_password" # basic authentication
138
+ with [:login, :token] # params that should be sent with every request
139
+ oauth OAuth::AccessToken.new(consumer, "token", "secret") # an access token for OAuth
140
+
141
+ post "update" # uses the defaults defined above!
142
+ end
143
+
144
+ There's more to discover in the Wiki.
data/Rakefile CHANGED
@@ -54,6 +54,11 @@ rescue LoadError
54
54
  puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
55
55
  end
56
56
 
57
+ desc "Open an irb session preloaded with this library"
58
+ task :console do
59
+ sh "irb -rubygems -I lib -r weary.rb"
60
+ end
61
+
57
62
  Spec::Rake::SpecTask.new do |t|
58
63
  t.spec_files = FileList['spec/**/*_spec.rb']
59
64
  t.spec_opts = ['--color','--format nested']
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.3
1
+ 0.5.0
data/lib/weary.rb CHANGED
@@ -8,8 +8,10 @@ require 'rubygems'
8
8
  require 'crack'
9
9
 
10
10
  gem 'nokogiri'
11
+ gem 'oauth'
11
12
  autoload :Yaml, 'yaml'
12
13
  autoload :Nokogiri, 'nokogiri'
14
+ autoload :OAuth, 'oauth'
13
15
 
14
16
  require 'weary/request'
15
17
  require 'weary/response'
@@ -90,6 +92,14 @@ module Weary
90
92
  @headers = headers
91
93
  end
92
94
  alias set_headers headers
95
+
96
+ # Set the Access Token for OAuth. This must be an OAuth::AccessToken object.
97
+ # See http://github.com/mojodna/oauth/ to learn how to create Tokens
98
+ # Setting this will make resources use OAuth and this token by default.
99
+ def oauth(token)
100
+ raise ArgumentError, "Token needs to be an OAuth::AccessToken object" unless token.is_a?(OAuth::AccessToken)
101
+ @oauth = token
102
+ end
93
103
 
94
104
  # Declare a resource. Use it with a block to setup the resource
95
105
  #
@@ -98,10 +108,13 @@ module Weary
98
108
  # [<tt>with</tt>] An array of parameters that will be passed to the body or query of the request. If you pass a hash, it will define default <tt>values</tt> for params <tt>keys</tt>
99
109
  # [<tt>requires</tt>] Array of members of <tt>:with</tt> that are required by the resource.
100
110
  # [<tt>authenticates</tt>] Boolean value; does the resource require authentication?
111
+ # [<tt>oauth</tt>] Boolean value; does the resource use OAuth?
112
+ # [<tt>access_token</tt>] Provide the Token for OAuth. Must be an OAuth::AccessToken object.
101
113
  # [<tt>url</tt>] The url of the resource. You can use the same flags as #construct_url
102
114
  # [<tt>format</tt>] The format you would like to request. Defaults to json
103
115
  # [<tt>follows</tt>] Boolean; Does this follow redirects? Defaults to true
104
116
  # [<tt>domain</tt>] Sets the domain you would like this individual resource to be on (if you include the domain flag in <tt>url</tt>)
117
+ # [<tt>headers</tt>] Set headers for the HTTP Request
105
118
  def declare(name)
106
119
  resource = prepare_resource(name,:get)
107
120
  yield resource if block_given?
@@ -141,6 +154,10 @@ module Weary
141
154
  preparation.url = (@url_pattern || "<domain><resource>.<format>")
142
155
  preparation.with = @always_with unless @always_with.nil?
143
156
  preparation.headers = @headers unless (@headers.nil? || @headers.empty?)
157
+ if !@oauth.nil?
158
+ preparation.oauth = true
159
+ preparation.access_token = @oauth
160
+ end
144
161
  return preparation
145
162
  end
146
163
 
@@ -148,6 +165,12 @@ module Weary
148
165
  if resource.authenticates?
149
166
  raise StandardError, "Can not authenticate unless username and password are defined" unless (@username && @password)
150
167
  end
168
+ if resource.oauth?
169
+ if resource.access_token.nil?
170
+ raise StandardError, "Access Token is not provided" if @oauth.nil?
171
+ resource.access_token = @oauth
172
+ end
173
+ end
151
174
  @resources ||= []
152
175
  @resources << resource.to_hash
153
176
  craft_methods(resource)
@@ -214,6 +237,24 @@ module Weary
214
237
  if resource.authenticates?
215
238
  code << %Q{options[:basic_auth] = {:username => "#{@username}", :password => "#{@password}"} \n}
216
239
  end
240
+ if resource.oauth?
241
+ consumer_options = ""
242
+ resource.access_token.consumer.options.each_pair {|k,v|
243
+ if k.is_a?(Symbol)
244
+ k_string = ":#{k}"
245
+ else
246
+ k_string = "'#{k}'"
247
+ end
248
+ if v.is_a?(Symbol)
249
+ v_string = ":#{v}"
250
+ else
251
+ v_string = "'#{v}'"
252
+ end
253
+ consumer_options << "#{k_string} => #{v_string},"
254
+ }
255
+ code << %Q{ oauth_consumer = OAuth::Consumer.new("#{resource.access_token.consumer.key}","#{resource.access_token.consumer.secret}",#{consumer_options.chop}) \n}
256
+ code << %Q{ options[:oauth] = OAuth::AccessToken.new(oauth_consumer, "#{resource.access_token.token}", "#{resource.access_token.secret}") \n}
257
+ end
217
258
  unless resource.follows_redirects?
218
259
  code << %Q{options[:no_follow] = true \n}
219
260
  end
data/lib/weary/request.rb CHANGED
@@ -78,6 +78,9 @@ module Weary
78
78
  prepare[key] = value
79
79
  end
80
80
  end
81
+ if options[:oauth]
82
+ options[:oauth].sign!(prepare)
83
+ end
81
84
  prepare
82
85
  end
83
86
 
@@ -1,6 +1,6 @@
1
1
  module Weary
2
2
  class Resource
3
- attr_accessor :name, :domain, :with, :requires, :via, :format, :url, :authenticates, :follows, :headers
3
+ attr_accessor :name, :domain, :with, :requires, :via, :format, :url, :authenticates, :follows, :headers, :oauth, :access_token
4
4
 
5
5
  def initialize(name)
6
6
  self.name = name
@@ -9,6 +9,7 @@ module Weary
9
9
  self.follows = true
10
10
  self.with = []
11
11
  self.requires = []
12
+ self.oauth = false
12
13
  end
13
14
 
14
15
  def name=(resource_name)
@@ -82,14 +83,38 @@ module Weary
82
83
  @url = pattern
83
84
  end
84
85
 
85
- def authenticates?
86
- if @authenticates
86
+ def oauth=(bool)
87
+ @authenticates = false if bool
88
+ @oauth = if bool
89
+ true
90
+ else
91
+ false
92
+ end
93
+ end
94
+
95
+ def authenticates=(bool)
96
+ @oauth = false if bool
97
+ @authenticates = if bool
87
98
  true
88
99
  else
89
100
  false
90
101
  end
91
102
  end
92
103
 
104
+ def authenticates?
105
+ @authenticates
106
+ end
107
+
108
+ def oauth?
109
+ @oauth
110
+ end
111
+
112
+ def access_token=(token)
113
+ raise ArgumentError, "Token needs to be an OAuth::AccessToken object" unless token.is_a?(OAuth::AccessToken)
114
+ @oauth = true
115
+ @access_token = token
116
+ end
117
+
93
118
  def follows_redirects?
94
119
  if @follows
95
120
  true
@@ -107,7 +132,9 @@ module Weary
107
132
  :format => @format,
108
133
  :url => @url,
109
134
  :domain => @domain,
110
- :headers => @headers}}
135
+ :headers => @headers,
136
+ :oauth => oauth?,
137
+ :access_token => @access_token}}
111
138
  end
112
139
 
113
140
  end
@@ -17,14 +17,17 @@ module Weary
17
17
  self.format = http_response.content_type
18
18
  end
19
19
 
20
+ # Is this an HTTP redirect?
20
21
  def redirected?
21
22
  @raw.is_a?(Net::HTTPRedirection)
22
23
  end
23
24
 
25
+ # Was this Request successful?
24
26
  def success?
25
27
  (200..299).include?(@code)
26
28
  end
27
29
 
30
+ # Returns a symbol corresponding to the Response's Content Type
28
31
  def format=(type)
29
32
  @format = case type
30
33
  when *ContentTypes[:json]
@@ -42,6 +45,7 @@ module Weary
42
45
  end
43
46
  end
44
47
 
48
+ # Follow the Redirect
45
49
  def follow_redirect
46
50
  if redirected?
47
51
  Request.new(@raw['location'], @method).perform
@@ -66,11 +70,13 @@ module Weary
66
70
  end
67
71
  end
68
72
 
73
+ # Same as parse[key]
69
74
  def [](key)
70
75
  parse[key]
71
76
  end
72
77
 
73
- # Search the body with a CSS/XPath selector with Nokogiri
78
+ # Search the body with a CSS/XPath selector with Nokogiri.
79
+ # If the document is not XMLish, fall back to #parse and ditch the selector.
74
80
  def search(selector)
75
81
  if @format == (:xml || :html)
76
82
  doc = Nokogiri.parse(@body)
@@ -38,4 +38,12 @@ describe Weary::Request do
38
38
  # not exactly kosher.
39
39
  end
40
40
 
41
+ it "should prepare an oauth scheme if a token is provided" do
42
+ consumer = OAuth::Consumer.new("consumer_token","consumer_secret",{:site => 'http://foo.bar'})
43
+ token = OAuth::AccessToken.new(consumer, "token", "secret")
44
+ test = Weary::Request.new("http://foo.bar", :post, {:oauth => token})
45
+ test.send(:request).oauth_helper.options[:token].should == token
46
+ # seems a good a way as any to test if OAuth helpers have been added to the request
47
+ end
48
+
41
49
  end
@@ -35,6 +35,33 @@ describe Weary::Resource do
35
35
  @test.authenticates?.should == false
36
36
  end
37
37
 
38
+ it "oauth should be boolean" do
39
+ @test.oauth = "foobar"
40
+ @test.oauth?.should == true
41
+ @test.oauth = false
42
+ @test.oauth?.should == false
43
+ end
44
+
45
+ it "oauth should override basic authentication" do
46
+ @test.authenticates = true
47
+ @test.oauth = true
48
+ @test.authenticates?.should == false
49
+ @test.oauth?.should == true
50
+ end
51
+
52
+ it "providing an access token should set oauth to true" do
53
+ consumer = OAuth::Consumer.new("consumer_token","consumer_secret",{:site => 'http://foo.bar'})
54
+ token = OAuth::AccessToken.new(consumer, "token", "secret")
55
+ @test.oauth = false
56
+ @test.access_token = token
57
+ @test.oauth?.should == true
58
+ @test.access_token.should == token
59
+ end
60
+
61
+ it "an access token must contain an OAuth::AccessToken" do
62
+ lambda { @test.access_token = "foobar" }.should raise_error
63
+ end
64
+
38
65
  it 'follows_redirects? should be boolean' do
39
66
  @test.follows = "false"
40
67
  @test.follows_redirects?.should == true
data/spec/weary_spec.rb CHANGED
@@ -57,6 +57,39 @@ describe Weary do
57
57
  end
58
58
  end
59
59
 
60
+ describe "OAuth" do
61
+ before do
62
+ consumer = OAuth::Consumer.new("consumer_token","consumer_secret",{:site => 'http://foo.bar'})
63
+ @token = OAuth::AccessToken.new(consumer, "token", "secret")
64
+ @test.oauth @token
65
+ end
66
+
67
+ it "should accept an OAuth Access Token" do
68
+ @test.instance_variable_get(:@oauth).should == @token
69
+ lambda { @test.oauth "foobar" }.should raise_error
70
+ end
71
+ it "should notify the Resource that this is using OAuth" do
72
+ @test.domain "http://foo.bar"
73
+ r = @test.declare("show")
74
+ r.oauth?.should == true
75
+ r.access_token.should == @token
76
+ end
77
+ it "should be able to handle tokens set within the resource intelligently" do
78
+ test = Class.new
79
+ test.instance_eval { extend Weary }
80
+ test.domain "http://foo.bar"
81
+ r = test.declare("show")
82
+ r.oauth?.should == false
83
+ r.access_token.should == nil
84
+ r.oauth = true
85
+ lambda { test.send(:form_resource, r) }.should raise_error
86
+ r.access_token = @token
87
+ r.access_token.should == @token
88
+ test.send(:form_resource, r)[:show][:oauth].should == true
89
+ test.send(:form_resource, r)[:show][:access_token].should == @token
90
+ end
91
+ end
92
+
60
93
  describe "Set Headers" do
61
94
  it "should be a hash of values to pass in the Request head" do
62
95
  @test.on_domain "http://foo.bar"
data/weary.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{weary}
5
- s.version = "0.4.3"
5
+ s.version = "0.5.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Mark Wunsch"]
9
- s.date = %q{2009-06-30}
9
+ s.date = %q{2009-07-15}
10
10
  s.description = %q{The Weary need REST: a tiny DSL that makes the consumption of RESTful web services simple.}
11
11
  s.email = %q{mark@markwunsch.com}
12
12
  s.extra_rdoc_files = [
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Wunsch
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-30 00:00:00 -04:00
12
+ date: 2009-07-15 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency