weary 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -9,6 +9,73 @@ The things it do:
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
11
 
12
+ Browse the documentation here: [http://rdoc.info/projects/mwunsch/weary](http://rdoc.info/projects/mwunsch/weary)
13
+
12
14
  ## Requirements
13
15
 
14
- + Crack
16
+ + Crack >= 0.1.2
17
+ + Nokogiri >= 1.3.1 (if you want to use the #search method)
18
+
19
+ ## Installation
20
+
21
+ You do have Rubygems right?
22
+
23
+ sudo gem install weary
24
+
25
+ ## How it works
26
+
27
+ Create a class and `extend Weary` to give it methods to craft a resource request:
28
+
29
+ class Foo
30
+ extend Weary
31
+
32
+ declare "foo" do |resource|
33
+ resource.url = "http://path/to/foo"
34
+ end
35
+ end
36
+
37
+ If you instantiate this class, you'll get an instance method named `foo` that crafts a GET request to "http://path/to/foo"
38
+
39
+ Besides the name of the resource, you can also give `declare_resource` a block like:
40
+
41
+ declare "foo" do |r|
42
+ r.url = "path/to/foo"
43
+ r.via = :post # defaults to :get
44
+ r.format = :xml # defaults to :json
45
+ r.requires = [:id, :bar] # an array of params that the resource requires to be in the query/body
46
+ r.with = [:blah] # an array of params that you can optionally send to the resource
47
+ r.authenticates = false # does the method require basic authentication? defaults to false
48
+ r.follows = false # if this is set to false, the formed request will not follow redirects.
49
+ end
50
+
51
+ So this would form a method:
52
+
53
+ x = Foo.new
54
+ x.foo(:id => "mwunsch", :bar => 123)
55
+
56
+ That method would return a Weary::Response object that you could then parse or examine.
57
+
58
+ ### Shortcuts
59
+
60
+ Of course, you don't always have to use `declare`; that is a little too ambiguous. You can also use `get`, `post`, `delete`, etc. Those do the obvious.
61
+
62
+ The `#requires` and `#with` methods can either be arrays of symbols, or a comma delimited list of strings.
63
+
64
+ ### Forming URLs
65
+
66
+ There are many ways to form URLs in Weary. You can define URLs for the entire class by typing:
67
+
68
+ class Foo
69
+ extend Weary
70
+
71
+ on_domain "http://foo.bar/"
72
+ construct_url "<domain><resource>.<format>"
73
+ as_format :xml
74
+
75
+ get "show_users"
76
+ end
77
+
78
+ 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*
79
+
80
+ If you use the `<domain>` flag but don't define a domain, an exception will be raised.
81
+
data/Rakefile CHANGED
@@ -22,6 +22,7 @@ begin
22
22
  gemspec.homepage = "http://github.com/mwunsch/weary"
23
23
  gemspec.description = "The Weary need REST: a tiny DSL that makes the consumption of RESTful web services simple."
24
24
  gemspec.authors = "Mark Wunsch"
25
+ gemspec.add_dependency('crack', '>= 0.1.2')
25
26
  end
26
27
  rescue LoadError
27
28
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
data/examples/repo.rb CHANGED
@@ -9,11 +9,13 @@ class Repository
9
9
  on_domain "http://github.com/api/v2/"
10
10
  as_format :yaml
11
11
 
12
- get "show",
13
- :url => "<domain><format>/repos/show/#{@gh_user}/#{@gh_repo}"
14
-
15
- get "network",
16
- :url => "<domain><format>/repos/show/#{@gh_user}/#{@gh_repo}/network"
12
+ get "show" do |r|
13
+ r.url = "<domain><format>/repos/show/#{@gh_user}/#{@gh_repo}"
14
+ end
15
+
16
+ get "network" do |r|
17
+ r.url = "<domain><format>/repos/show/#{@gh_user}/#{@gh_repo}/network"
18
+ end
17
19
 
18
20
  end
19
21
 
data/examples/status.rb CHANGED
@@ -5,9 +5,10 @@ class Status
5
5
 
6
6
  on_domain "http://twitter.com/statuses/"
7
7
 
8
- get "user_timeline",
9
- :requires => [:id],
10
- :with => [:user_id, :screen_name, :since_id, :max_id, :count, :page]
8
+ get "user_timeline" do |resource|
9
+ resource.requires = [:id]
10
+ resource.with = [:user_id, :screen_name, :since_id, :max_id, :count, :page]
11
+ end
11
12
 
12
13
  end
13
14
 
@@ -1,3 +1,15 @@
1
1
  module Weary
2
- class HTTPError < StandardError; end
2
+ class HTTPError < StandardError; end
3
+
4
+ class RedirectionError < HTTPError; end
5
+ class ClientError < HTTPError; end
6
+ class ServerError < HTTPError; end
7
+
8
+ class BadRequest < ClientError; end #400
9
+ class Unauthorized < ClientError; end #401
10
+ class Forbidden < ClientError; end #403
11
+ class NotFound < ClientError; end #404
12
+ class MethodNotAllowed < ClientError; end #405
13
+ class ResourceConflict < ClientError; end #409
14
+ class UnprocessableEntity < ClientError; end #422
3
15
  end
data/lib/weary/request.rb CHANGED
@@ -34,7 +34,7 @@ module Weary
34
34
  def method
35
35
  @http_verb
36
36
  end
37
-
37
+
38
38
  def perform
39
39
  req = http.request(request)
40
40
  response = Response.new(req, @http_verb)
@@ -1,35 +1,30 @@
1
1
  module Weary
2
2
  class Resource
3
- attr_reader :name, :with, :requires, :via, :format, :url
4
-
5
- def initialize(name,options={})
6
- @domain = options[:domain]
7
- self.name = name
8
- self.via = options[:via]
9
- self.with = options[:with]
10
- self.requires = options[:requires]
11
- self.format = options[:format]
12
- self.url = options[:url]
13
- @authenticates = (options[:authenticates] != false)
14
- @follows = (options[:no_follow] == false)
15
- end
16
-
17
- def name=(resource)
18
- @name = resource.to_s
19
- end
20
-
21
- def via=(verb)
22
- @via = verb
23
- end
24
-
3
+ attr_reader :name
4
+ attr_accessor :domain, :with, :requires, :via, :format, :url, :authenticates, :follows
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ self.via = :get
9
+ self.authenticates = false
10
+ self.follows = true
11
+ self.with = []
12
+ self.requires
13
+ end
14
+
25
15
  def with=(params)
26
- if params.empty?
27
- @with = nil
16
+ unless @requires.nil?
17
+ @with = params.collect {|x| x.to_sym} | @requires
28
18
  else
29
- @with = params.collect {|x| x.to_sym }
19
+ @with = params.collect {|x| x.to_sym}
30
20
  end
31
21
  end
32
22
 
23
+ def requires=(params)
24
+ @with = @with | params.collect {|x| x.to_sym}
25
+ @requires = params.collect {|x| x.to_sym}
26
+ end
27
+
33
28
  def url=(pattern)
34
29
  if pattern.index("<domain>")
35
30
  raise StandardError, "Domain flag found but the domain is not defined" if @domain.nil?
@@ -40,34 +35,23 @@ module Weary
40
35
  @url = pattern
41
36
  end
42
37
 
43
- def requires=(params)
44
- if (params.nil? || params.empty?)
45
- @requires = nil
46
- else
47
- @requires = params
48
- end
49
- end
50
-
51
- def format=(type)
52
- @format = type
53
- end
54
-
55
38
  def authenticates?
56
- @authenticates
39
+ @authenticates == true
57
40
  end
58
41
 
59
42
  def follows_redirects?
60
- @follows
43
+ @follows == true
61
44
  end
62
-
45
+
63
46
  def to_hash
64
- {@name.to_sym => {:via => @via,
65
- :with => @with,
66
- :requires => @requires,
67
- :no_follow => !follows_redirects?,
68
- :authenticates => authenticates?,
69
- :format => @format,
70
- :url => @url}}
47
+ {@name.to_sym => { :via => @via,
48
+ :with => @with,
49
+ :requires => @requires,
50
+ :follows => follows_redirects?,
51
+ :authenticates => authenticates?,
52
+ :format => @format,
53
+ :url => @url,
54
+ :domain => @domain}}
71
55
  end
72
56
 
73
57
  end
@@ -20,7 +20,7 @@ module Weary
20
20
  def redirected?
21
21
  @raw.is_a?(Net::HTTPRedirection)
22
22
  end
23
-
23
+
24
24
  def success?
25
25
  (200..299).include?(@code)
26
26
  end
@@ -54,6 +54,7 @@ module Weary
54
54
  end
55
55
  end
56
56
 
57
+ # Parse the body with Crack parsers (if XML/HTML) or Yaml parser
57
58
  def parse
58
59
  raise StandardError, "The Response has no body. #{@method.to_s.upcase} request sent." unless @body
59
60
  handle_errors
@@ -69,6 +70,7 @@ module Weary
69
70
  end
70
71
  end
71
72
 
73
+ # Search the body with a CSS/XPath selector with Nokogiri
72
74
  def search(selector)
73
75
  raise ArgumentError, "Search can only be used with an XML or HTML document." unless @format != (:xml || :html)
74
76
  doc = Nokogiri.parse(@body)
@@ -79,27 +81,27 @@ module Weary
79
81
  def handle_errors
80
82
  case @code
81
83
  when 301,302
82
- raise HTTPError, "#{@message} to #{@raw['location']}"
84
+ raise RedirectionError, "#{@message} to #{@raw['location']}"
83
85
  when 200...400
84
86
  return
85
87
  when 400
86
- raise HTTPError, "Failed with #{@code}: #{@message}"
88
+ raise BadRequest, "Failed with #{@code}: #{@message}"
87
89
  when 401
88
- raise HTTPError, "Failed with #{@code}: #{@message}"
90
+ raise Unauthorized, "Failed with #{@code}: #{@message}"
89
91
  when 403
90
- raise HTTPError, "Failed with #{@code}: #{@message}"
92
+ raise Forbidden, "Failed with #{@code}: #{@message}"
91
93
  when 404
92
- raise HTTPError, "Failed with #{@code}: #{@message}"
94
+ raise NotFound, "Failed with #{@code}: #{@message}"
93
95
  when 405
94
- raise HTTPError, "Failed with #{@code}: #{@message}"
96
+ raise MethodNotAllowed, "Failed with #{@code}: #{@message}"
95
97
  when 409
96
- raise HTTPError, "Failed with #{@code}: #{@message}"
98
+ raise ResourceConflict, "Failed with #{@code}: #{@message}"
97
99
  when 422
98
- raise HTTPError, "Failed with #{@code}: #{@message}"
100
+ raise UnprocessableEntity, "Failed with #{@code}: #{@message}"
99
101
  when 401...500
100
- raise HTTPError, "Failed with #{@code}: #{@message}"
102
+ raise ClientError, "Failed with #{@code}: #{@message}"
101
103
  when 500...600
102
- raise HTTPError, "Failed with #{@code}: #{@message}"
104
+ raise ServerError, "Failed with #{@code}: #{@message}"
103
105
  else
104
106
  raise HTTPError, "Unknown response code: #{@code}"
105
107
  end
data/lib/weary.rb CHANGED
@@ -24,8 +24,9 @@ module Weary
24
24
  :put => [:put, :PUT, /\bput\b/i],
25
25
  :delete => [:delete, :del, :DELETE, :DEL, /\bdelete\b/i],
26
26
  :head => [:head, :HEAD, /\bhead\b/i] }
27
+ UserAgents = { } # will be a collection of user agent strings
27
28
 
28
- # Weary::Query quickly performs a :get request on a URL and parses the request
29
+ # Weary::Query quickly performs a GET request on a URL and parses the request.
29
30
  def self.Query(url)
30
31
  req = Weary::Request.new(url, :get).perform
31
32
  req.parse
@@ -33,16 +34,29 @@ module Weary
33
34
 
34
35
  attr_reader :domain, :resources
35
36
 
37
+ # Sets the domain the resource is on.
38
+ #
39
+ # If the domain is not provided and you use a URL pattern that asks for it,
40
+ # an exception will be raised.
36
41
  def on_domain(domain)
37
42
  parse_domain = URI.extract(domain)
38
43
  raise ArgumentError, 'The domain must be a URL.' if parse_domain.empty?
39
44
  @domain = parse_domain[0]
45
+ return @domain
40
46
  end
41
-
47
+
48
+ # Sets a default format to make your Requests in.
49
+ # Defaults to JSON.
42
50
  def as_format(format)
43
51
  @default_format = format.to_sym
44
52
  end
45
53
 
54
+ # Construct a URL pattern for your resources to follow.
55
+ # You can use flags like
56
+ # * <domain>
57
+ # * <format>
58
+ # * <resource>
59
+ # To aid your construction. Defaults to "<domain><resource>.<format>"
46
60
  def construct_url(pattern)
47
61
  @url_pattern = pattern.to_s
48
62
  end
@@ -53,63 +67,65 @@ module Weary
53
67
  return nil
54
68
  end
55
69
 
56
- def declare_resource(resource, options={})
57
- # available options:
58
- # :via = get, post, etc. defaults to get
59
- # :with = paramaters passed to body or query
60
- # :requires = members of :with that must be in the action
61
- # :authenticates = boolean; uses basic_authentication
62
- # :url = a pattern
63
- # :format = to set format, defaults to :json
64
- # :no_follow = boolean; defaults to false. do not follow redirects
65
-
66
-
67
- @resources ||= []
68
-
69
- r = Weary::Resource.new(resource, set_defaults(options))
70
- declaration = r.to_hash
71
-
72
- @resources << declaration
73
-
74
- craft_methods(r)
75
- return declaration
70
+ # Declare a resource. Use it with a block to setup the resource
71
+ #
72
+ # Methods that are understood are:
73
+ # [<tt>via</tt>] Get, Post, etc. Defaults to a GET request
74
+ # [<tt>with</tt>] An array of parameters that will be passed to the body or query of the request.
75
+ # [<tt>requires</tt>] Members of <tt>:with</tt> that are required by the resource.
76
+ # [<tt>authenticates</tt>] Boolean value; does the resource require authentication?
77
+ # [<tt>url</tt>] The url of the resource. You can use the same flags as #construct_url
78
+ # [<tt>format</tt>] The format you would like to request. Defaults to json
79
+ # [<tt>follows</tt>] Boolean; Does this follow redirects? Defaults to true
80
+ # [<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>)
81
+ def declare(name)
82
+ resource = prepare_resource(name,:get)
83
+ yield resource if block_given?
84
+ form_resource(resource)
85
+ return resource
76
86
  end
87
+ alias get declare
77
88
 
78
- def get(resource,options={})
79
- options[:via] = :get
80
- declare_resource(resource,options)
89
+ def post(name)
90
+ resource = prepare_resource(name,:post)
91
+ yield resource if block_given?
92
+ form_resource(resource)
93
+ return resource
81
94
  end
82
95
 
83
- def post(resource,options={})
84
- options[:via] = :post
85
- declare_resource(resource,options)
96
+ def put(name)
97
+ resource = prepare_resource(name,:put)
98
+ yield resource if block_given?
99
+ form_resource(resource)
100
+ return resource
86
101
  end
87
102
 
88
- def put(resource,options={})
89
- options[:via] = :put
90
- declare_resource(resource,options)
91
- end
92
-
93
- def delete(resource,options={})
94
- options[:via] = :delete
95
- declare_resource(resource,options)
103
+ def delete(name)
104
+ resource = prepare_resource(name,:delete)
105
+ yield resource if block_given?
106
+ form_resource(resource)
107
+ return resource
96
108
  end
97
109
 
98
110
  private
99
- def set_defaults(hash)
100
- hash[:domain] = @domain
101
- hash[:via] ||= :get
102
- hash[:with] ||= []
103
- hash[:with] = hash[:with] | hash[:requires] unless hash[:requires].nil?
104
- hash[:format] ||= (@default_format || :json)
105
- hash[:authenticates] ||= false
106
- hash[:authenticates] = false if hash[:authenticates] == "false"
107
- if hash[:authenticates]
111
+
112
+ def prepare_resource(name,via)
113
+ preparation = Weary::Resource.new(name)
114
+ preparation.via = via
115
+ preparation.format = (@default_format || :json)
116
+ preparation.domain = @domain
117
+ preparation.url = (@url_pattern || "<domain><resource>.<format>")
118
+ return preparation
119
+ end
120
+
121
+ def form_resource(resource)
122
+ if resource.authenticates?
108
123
  raise StandardError, "Can not authenticate unless username and password are defined" unless (@username && @password)
109
124
  end
110
- hash[:url] ||= (@url_pattern || "<domain><resource>.<format>")
111
- hash[:no_follow] ||= false
112
- return hash
125
+ @resources ||= []
126
+ @resources << resource.to_hash
127
+ craft_methods(resource)
128
+ return resource.to_hash
113
129
  end
114
130
 
115
131
  def craft_methods(resource)
@@ -118,12 +134,12 @@ module Weary
118
134
  options ||= {}
119
135
  url = "#{resource.url}"
120
136
  }
121
- unless resource.requires.nil?
137
+ unless resource.requires.empty?
122
138
  resource.requires.each do |required|
123
139
  code << %Q{raise ArgumentError, "This resource requires parameter: ':#{required}'" unless params.has_key?(:#{required}) \n}
124
140
  end
125
141
  end
126
- unless resource.with.nil?
142
+ unless resource.with.empty?
127
143
  with = %Q{[#{resource.with.collect {|x| ":#{x}"}.join(',')}]}
128
144
  code << %Q{unnecessary = params.keys - #{with} \n}
129
145
  code << %Q{unnecessary.each { |x| params.delete(x) } \n}
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.1.2"
5
+ s.version = "0.2.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-09}
9
+ s.date = %q{2009-06-11}
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 = [
@@ -30,12 +30,11 @@ Gem::Specification.new do |s|
30
30
  "spec/weary_spec.rb",
31
31
  "weary.gemspec"
32
32
  ]
33
- s.has_rdoc = true
34
33
  s.homepage = %q{http://github.com/mwunsch/weary}
35
34
  s.rdoc_options = ["--charset=UTF-8"]
36
35
  s.require_paths = ["lib"]
37
36
  s.rubyforge_project = %q{weary}
38
- s.rubygems_version = %q{1.3.1}
37
+ s.rubygems_version = %q{1.3.4}
39
38
  s.summary = %q{A little DSL for consuming RESTful web services}
40
39
  s.test_files = [
41
40
  "spec/weary/request_spec.rb",
@@ -46,11 +45,14 @@ Gem::Specification.new do |s|
46
45
 
47
46
  if s.respond_to? :specification_version then
48
47
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
- s.specification_version = 2
48
+ s.specification_version = 3
50
49
 
51
50
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<crack>, [">= 0.1.2"])
52
52
  else
53
+ s.add_dependency(%q<crack>, [">= 0.1.2"])
53
54
  end
54
55
  else
56
+ s.add_dependency(%q<crack>, [">= 0.1.2"])
55
57
  end
56
58
  end
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.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Wunsch
@@ -9,10 +9,19 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-09 00:00:00 -04:00
12
+ date: 2009-06-11 00:00:00 -04:00
13
13
  default_executable:
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: crack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.2
24
+ version:
16
25
  description: "The Weary need REST: a tiny DSL that makes the consumption of RESTful web services simple."
17
26
  email: mark@markwunsch.com
18
27
  executables: []
@@ -40,6 +49,8 @@ files:
40
49
  - weary.gemspec
41
50
  has_rdoc: true
42
51
  homepage: http://github.com/mwunsch/weary
52
+ licenses: []
53
+
43
54
  post_install_message:
44
55
  rdoc_options:
45
56
  - --charset=UTF-8
@@ -60,9 +71,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
71
  requirements: []
61
72
 
62
73
  rubyforge_project: weary
63
- rubygems_version: 1.3.1
74
+ rubygems_version: 1.3.4
64
75
  signing_key:
65
- specification_version: 2
76
+ specification_version: 3
66
77
  summary: A little DSL for consuming RESTful web services
67
78
  test_files:
68
79
  - spec/weary/request_spec.rb