weary 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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