weary 0.1.2 → 0.2.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
@@ -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