luigi-httparty 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,21 @@
1
+ == 0.5.0 2009-12-07
2
+ * bug fixes
3
+ * inheritable attributes no longer mutable by subclasses (yyyc514)
4
+ * namespace BasicObject within HTTParty to avoid class name collisions (eric)
5
+
6
+ * major enhancements
7
+ * Custom Parsers via class or proc
8
+ * Deprecation warning on HTTParty::AllowedFormats
9
+ moved to HTTParty::Parser::SupportedFormats
10
+
11
+ * minor enhancements
12
+ * Curl inspired output when using the binary in verbose mode (alexvollmer)
13
+ * raise UnsupportedURIScheme when scheme is not HTTP or HTTPS (djspinmonkey)
14
+ * Allow SSL for ports other than 443 when scheme is HTTPS (stefankroes)
15
+ * Accept PEM certificates via HTTParty#pem (chrislo)
16
+ * Support HEAD and OPTION verbs (grempe)
17
+ * Verify SSL certificates when providing a PEM file (collectiveidea/danielmorrison)
18
+
1
19
  == 0.4.5 2009-09-12
2
20
  * bug fixes
3
21
  * Fixed class-level headers overwritten by cookie management code. Closes #19
@@ -138,4 +156,4 @@
138
156
  == 0.1.0 2008-07-27
139
157
 
140
158
  * 1 major enhancement:
141
- * Initial release
159
+ * Initial release
data/Rakefile CHANGED
@@ -11,29 +11,34 @@ begin
11
11
  gem.homepage = "http://httparty.rubyforge.org"
12
12
  gem.authors = ["John Nunemaker", "Sandro Turriate"]
13
13
  gem.add_dependency 'crack', '>= 0.1.1'
14
- gem.add_development_dependency "rspec", "1.2.8"
14
+ gem.add_development_dependency "activesupport", "~>2.3"
15
+ gem.add_development_dependency "cucumber", "~>0.4"
16
+ gem.add_development_dependency "fakeweb", "~>1.2"
17
+ gem.add_development_dependency "mongrel", "~>1.1"
18
+ gem.add_development_dependency "rspec", "1.2.9"
15
19
  gem.post_install_message = "When you HTTParty, you must party hard!"
16
20
  gem.rubyforge_project = 'httparty'
17
21
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
22
  end
19
23
  Jeweler::RubyforgeTasks.new do |rubyforge|
20
- rubyforge.doc_task = "rdoc"
24
+ rubyforge.doc_task = "rdoc"
21
25
  end
26
+ Jeweler::GemcutterTasks.new
22
27
  rescue LoadError
23
28
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
29
  end
25
30
 
26
31
  require 'spec/rake/spectask'
27
32
  Spec::Rake::SpecTask.new(:spec) do |spec|
28
- spec.libs << 'lib' << 'spec'
29
- spec.spec_files = FileList['spec/**/*_spec.rb']
30
- spec.spec_opts = ['--options', 'spec/spec.opts']
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.spec_files = FileList['spec/**/*_spec.rb']
35
+ spec.spec_opts = ['--options', 'spec/spec.opts']
31
36
  end
32
37
 
33
38
  Spec::Rake::SpecTask.new(:rcov) do |spec|
34
- spec.libs << 'lib' << 'spec'
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
39
+ spec.libs << 'lib' << 'spec'
40
+ spec.pattern = 'spec/**/*_spec.rb'
41
+ spec.rcov = true
37
42
  end
38
43
 
39
44
  task :spec => :check_dependencies
@@ -41,11 +46,11 @@ task :spec => :check_dependencies
41
46
  begin
42
47
  require 'cucumber/rake/task'
43
48
  Cucumber::Rake::Task.new(:features)
44
-
49
+
45
50
  task :features => :check_dependencies
46
51
  rescue LoadError
47
52
  task :features do
48
- abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
53
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
49
54
  end
50
55
  end
51
56
 
@@ -53,19 +58,19 @@ task :default => [:spec, :features]
53
58
 
54
59
  require 'rake/rdoctask'
55
60
  Rake::RDocTask.new do |rdoc|
56
- if File.exist?('VERSION')
57
- version = File.read('VERSION')
58
- else
59
- version = ""
60
- end
61
+ if File.exist?('VERSION')
62
+ version = File.read('VERSION')
63
+ else
64
+ version = ""
65
+ end
61
66
 
62
- rdoc.rdoc_dir = 'rdoc'
63
- rdoc.title = "httparty #{version}"
64
- rdoc.rdoc_files.include('README*')
65
- rdoc.rdoc_files.include('lib/**/*.rb')
67
+ rdoc.rdoc_dir = 'rdoc'
68
+ rdoc.title = "httparty #{version}"
69
+ rdoc.rdoc_files.include('README*')
70
+ rdoc.rdoc_files.include('lib/**/*.rb')
66
71
  end
67
72
 
68
73
  desc 'Upload website files to rubyforge'
69
74
  task :website do
70
- sh %{rsync -av website/ jnunemaker@rubyforge.org:/var/www/gforge-projects/httparty}
71
- end
75
+ sh %{rsync -av website/ jnunemaker@rubyforge.org:/var/www/gforge-projects/httparty}
76
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.6
1
+ 0.5.0
data/bin/httparty CHANGED
@@ -12,27 +12,22 @@ opts = {
12
12
  :verbose => false
13
13
  }
14
14
 
15
- def die(msg)
16
- STDERR.puts(msg)
17
- exit 1
18
- end
19
-
20
15
  OptionParser.new do |o|
21
16
  o.banner = "USAGE: #{$0} [options] [url]"
22
-
17
+
23
18
  o.on("-f",
24
19
  "--format [FORMAT]",
25
20
  "Output format to use instead of pretty-print ruby: " +
26
21
  "plain, json or xml") do |f|
27
22
  opts[:output_format] = f.downcase.to_sym
28
23
  end
29
-
24
+
30
25
  o.on("-a",
31
26
  "--action [ACTION]",
32
- "HTTP action: get (default), post, put or delete") do |a|
27
+ "HTTP action: get (default), post, put, delete, head, or options") do |a|
33
28
  opts[:action] = a.downcase.to_sym
34
29
  end
35
-
30
+
36
31
  o.on("-d",
37
32
  "--data [BODY]",
38
33
  "Data to put in request body (prefix with '@' for file)") do |d|
@@ -42,44 +37,60 @@ OptionParser.new do |o|
42
37
  opts[:data] = d
43
38
  end
44
39
  end
45
-
40
+
46
41
  o.on("-H", "--header [NAME=VALUE]", "Additional HTTP headers in NAME=VALUE form") do |h|
47
- die "Invalid header specification, should be Name:Value" unless h =~ /.+:.+/
42
+ abort "Invalid header specification, should be Name:Value" unless h =~ /.+:.+/
48
43
  name, value = h.split(':')
49
44
  opts[:headers][name.strip] = value.strip
50
45
  end
51
-
46
+
52
47
  o.on("-v", "--verbose", "If set, print verbose output") do |v|
53
48
  opts[:verbose] = true
54
49
  end
55
-
50
+
56
51
  o.on("-u", "--user [CREDS]", "Use basic authentication. Value should be user:password") do |u|
57
- die "Invalid credentials format. Must be user:password" unless u =~ /.+:.+/
52
+ abort "Invalid credentials format. Must be user:password" unless u =~ /.+:.+/
58
53
  user, password = u.split(':')
59
54
  opts[:basic_auth] = { :username => user, :password => password }
60
55
  end
61
-
56
+
62
57
  o.on("-h", "--help", "Show help documentation") do |h|
63
58
  puts o
64
59
  exit
65
60
  end
66
61
  end.parse!
67
62
 
68
- puts "Querying #{ARGV.first} with options: #{opts.inspect}" if opts[:verbose]
69
63
 
70
64
  if ARGV.empty?
71
65
  STDERR.puts "You need to provide a URL"
72
66
  STDERR.puts "USAGE: #{$0} [options] [url]"
73
67
  end
74
68
 
69
+ def dump_headers(response)
70
+ resp_type = Net::HTTPResponse::CODE_TO_OBJ[response.code.to_s]
71
+ puts "#{response.code} #{resp_type.to_s.sub(/^Net::HTTP/, '')}"
72
+ response.headers.each do |n,v|
73
+ puts "#{n}: #{v}"
74
+ end
75
+ puts
76
+ end
77
+
78
+ if opts[:verbose]
79
+ puts "#{opts[:action].to_s.upcase} #{ARGV.first}"
80
+ opts[:headers].each do |n,v|
81
+ puts "#{n}: #{v}"
82
+ end
83
+ puts
84
+ end
85
+
86
+ response = HTTParty.send(opts[:action], ARGV.first, opts)
75
87
  if opts[:output_format].nil?
76
- response = HTTParty.send(opts[:action], ARGV.first, opts)
77
- puts "Status: #{response.code}"
88
+ dump_headers(response) if opts[:verbose]
78
89
  pp response
79
90
  else
80
91
  print_format = opts[:output_format]
81
- response = HTTParty.send(opts[:action], ARGV.first, opts)
82
- puts "Status: #{response.code}"
92
+ dump_headers(response) if opts[:verbose]
93
+
83
94
  case opts[:output_format]
84
95
  when :json
85
96
  begin
@@ -95,4 +106,4 @@ else
95
106
  else
96
107
  puts response
97
108
  end
98
- end
109
+ end
data/examples/basic.rb CHANGED
@@ -2,7 +2,7 @@ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  require File.join(dir, 'httparty')
3
3
  require 'pp'
4
4
 
5
- # You can also use post, put, delete in the same fashion
5
+ # You can also use post, put, delete, head, options in the same fashion
6
6
  response = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
7
7
  puts response.body, response.code, response.message, response.headers.inspect
8
8
 
@@ -0,0 +1,67 @@
1
+ class ParseAtom
2
+ include HTTParty
3
+
4
+ # Support Atom along with the default parsers: xml, json, yaml, etc.
5
+ class Parser::Atom < HTTParty::Parser
6
+ SupportedFormats.merge!({"application/atom+xml" => :atom})
7
+
8
+ protected
9
+
10
+ # perform atom parsing on body
11
+ def atom
12
+ body.to_atom
13
+ end
14
+ end
15
+
16
+ parser Parser::Atom
17
+ end
18
+
19
+
20
+ class OnlyParseAtom
21
+ include HTTParty
22
+
23
+ # Only support Atom
24
+ class Parser::OnlyAtom < HTTParty::Parser
25
+ SupportedFormats = {"application/atom+xml" => :atom}
26
+
27
+ protected
28
+
29
+ # perform atom parsing on body
30
+ def atom
31
+ body.to_atom
32
+ end
33
+ end
34
+
35
+ parser Parser::OnlyAtom
36
+ end
37
+
38
+
39
+ class SkipParsing
40
+ include HTTParty
41
+
42
+ # Parse the response body however you like
43
+ class Parser::Simple < HTTParty::Parser
44
+ def parse
45
+ body
46
+ end
47
+ end
48
+
49
+ parser Parser::Simple
50
+ end
51
+
52
+
53
+ class AdHocParsing
54
+ include HTTParty
55
+ parser(
56
+ Proc.new do |body, format|
57
+ case format
58
+ when :json
59
+ body.to_json
60
+ when :xml
61
+ body.to_xml
62
+ else
63
+ body
64
+ end
65
+ end
66
+ )
67
+ end
data/httparty.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{httparty}
8
- s.version = "0.4.6"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Nunemaker", "Sandro Turriate"]
12
- s.date = %q{2009-10-29}
12
+ s.date = %q{2009-12-07}
13
13
  s.default_executable = %q{httparty}
14
14
  s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
15
15
  s.email = %q{nunemaker@gmail.com}
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  "cucumber.yml",
30
30
  "examples/aaws.rb",
31
31
  "examples/basic.rb",
32
+ "examples/custom_parsers.rb",
32
33
  "examples/delicious.rb",
33
34
  "examples/google.rb",
34
35
  "examples/rubyurl.rb",
@@ -51,7 +52,7 @@ Gem::Specification.new do |s|
51
52
  "lib/httparty/core_extensions.rb",
52
53
  "lib/httparty/exceptions.rb",
53
54
  "lib/httparty/module_inheritable_attributes.rb",
54
- "lib/httparty/parsers.rb",
55
+ "lib/httparty/parser.rb",
55
56
  "lib/httparty/request.rb",
56
57
  "lib/httparty/response.rb",
57
58
  "lib/httparty/version.rb",
@@ -62,6 +63,7 @@ Gem::Specification.new do |s|
62
63
  "spec/fixtures/twitter.xml",
63
64
  "spec/fixtures/undefined_method_add_node_for_nil.xml",
64
65
  "spec/httparty/cookie_hash_spec.rb",
66
+ "spec/httparty/parser_spec.rb",
65
67
  "spec/httparty/request_spec.rb",
66
68
  "spec/httparty/response_spec.rb",
67
69
  "spec/httparty_spec.rb",
@@ -79,12 +81,14 @@ Gem::Specification.new do |s|
79
81
  s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
80
82
  s.test_files = [
81
83
  "spec/httparty/cookie_hash_spec.rb",
84
+ "spec/httparty/parser_spec.rb",
82
85
  "spec/httparty/request_spec.rb",
83
86
  "spec/httparty/response_spec.rb",
84
87
  "spec/httparty_spec.rb",
85
88
  "spec/spec_helper.rb",
86
89
  "examples/aaws.rb",
87
90
  "examples/basic.rb",
91
+ "examples/custom_parsers.rb",
88
92
  "examples/delicious.rb",
89
93
  "examples/google.rb",
90
94
  "examples/rubyurl.rb",
@@ -98,13 +102,26 @@ Gem::Specification.new do |s|
98
102
 
99
103
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
100
104
  s.add_runtime_dependency(%q<crack>, [">= 0.1.1"])
101
- s.add_development_dependency(%q<rspec>, ["= 1.2.8"])
105
+ s.add_development_dependency(%q<activesupport>, ["~> 2.3"])
106
+ s.add_development_dependency(%q<cucumber>, ["~> 0.4"])
107
+ s.add_development_dependency(%q<fakeweb>, ["~> 1.2"])
108
+ s.add_development_dependency(%q<mongrel>, ["~> 1.1"])
109
+ s.add_development_dependency(%q<rspec>, ["= 1.2.9"])
102
110
  else
103
111
  s.add_dependency(%q<crack>, [">= 0.1.1"])
104
- s.add_dependency(%q<rspec>, ["= 1.2.8"])
112
+ s.add_dependency(%q<activesupport>, ["~> 2.3"])
113
+ s.add_dependency(%q<cucumber>, ["~> 0.4"])
114
+ s.add_dependency(%q<fakeweb>, ["~> 1.2"])
115
+ s.add_dependency(%q<mongrel>, ["~> 1.1"])
116
+ s.add_dependency(%q<rspec>, ["= 1.2.9"])
105
117
  end
106
118
  else
107
119
  s.add_dependency(%q<crack>, [">= 0.1.1"])
108
- s.add_dependency(%q<rspec>, ["= 1.2.8"])
120
+ s.add_dependency(%q<activesupport>, ["~> 2.3"])
121
+ s.add_dependency(%q<cucumber>, ["~> 0.4"])
122
+ s.add_dependency(%q<fakeweb>, ["~> 1.2"])
123
+ s.add_dependency(%q<mongrel>, ["~> 1.1"])
124
+ s.add_dependency(%q<rspec>, ["= 1.2.9"])
109
125
  end
110
126
  end
127
+
@@ -1,7 +1,13 @@
1
- class BasicObject #:nodoc:
2
- instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
3
- end unless defined?(BasicObject)
4
-
1
+ module HTTParty
2
+ if defined?(::BasicObject)
3
+ BasicObject = ::BasicObject #:nodoc:
4
+ else
5
+ class BasicObject #:nodoc:
6
+ instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
7
+ end
8
+ end
9
+ end
10
+
5
11
  # 1.8.6 has mistyping of transitive in if statement
6
12
  require "rexml/document"
7
13
  module REXML #:nodoc:
@@ -1,7 +1,10 @@
1
1
  module HTTParty
2
2
  # Exception raised when you attempt to set a non-existant format
3
3
  class UnsupportedFormat < StandardError; end
4
-
4
+
5
+ # Exception raised when using a URI scheme other than HTTP or HTTPS
6
+ class UnsupportedURIScheme < StandardError; end
7
+
5
8
  # Exception that is raised when request has redirected too many times
6
9
  class RedirectionTooDeep < StandardError; end
7
- end
10
+ end
@@ -16,10 +16,10 @@ module HTTParty
16
16
 
17
17
  def inherited(subclass)
18
18
  @mattr_inheritable_attrs.each do |inheritable_attribute|
19
- instance_var = "@#{inheritable_attribute}"
19
+ instance_var = "@#{inheritable_attribute}"
20
20
  subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
21
21
  end
22
22
  end
23
23
  end
24
24
  end
25
- end
25
+ end
@@ -0,0 +1,140 @@
1
+ module HTTParty
2
+ # The default parser used by HTTParty, supports xml, json, html, yaml, and
3
+ # plain text.
4
+ #
5
+ # == Custom Parsers
6
+ #
7
+ # If you'd like to do your own custom parsing, subclassing HTTParty::Parser
8
+ # will make that process much easier. There are a few different ways you can
9
+ # utilize HTTParty::Parser as a superclass.
10
+ #
11
+ # @example Intercept the parsing for all formats
12
+ # class SimpleParser < HTTParty::Parser
13
+ # def parse
14
+ # perform_parsing
15
+ # end
16
+ # end
17
+ #
18
+ # @example Add the atom format and parsing method to the default parser
19
+ # class AtomParsingIncluded < HTTParty::Parser
20
+ # SupportedFormats.merge!(
21
+ # {"application/atom+xml" => :atom}
22
+ # )
23
+ #
24
+ # def atom
25
+ # perform_atom_parsing
26
+ # end
27
+ # end
28
+ #
29
+ # @example Only support the atom format
30
+ # class ParseOnlyAtom < HTTParty::Parser
31
+ # SupportedFormats = {"application/atom+xml" => :atom}
32
+ #
33
+ # def atom
34
+ # perform_atom_parsing
35
+ # end
36
+ # end
37
+ #
38
+ # @abstract Read the Custom Parsers section for more information.
39
+ class Parser
40
+ SupportedFormats = {
41
+ 'text/xml' => :xml,
42
+ 'application/xml' => :xml,
43
+ 'application/json' => :json,
44
+ 'text/json' => :json,
45
+ 'application/javascript' => :json,
46
+ 'text/javascript' => :json,
47
+ 'text/html' => :html,
48
+ 'application/x-yaml' => :yaml,
49
+ 'text/yaml' => :yaml,
50
+ 'text/plain' => :plain
51
+ }
52
+
53
+ # The response body of the request
54
+ # @return [String]
55
+ attr_reader :body
56
+
57
+ # The intended parsing format for the request
58
+ # @return [Symbol] e.g. :json
59
+ attr_reader :format
60
+
61
+ # @param [String] body the response body
62
+ # @param [Symbol] format the response format
63
+ # @return parsed response
64
+ def self.call(body, format)
65
+ new(body, format).parse
66
+ end
67
+
68
+ # @return [Hash] the SupportedFormats hash
69
+ def self.formats
70
+ const_get(:SupportedFormats)
71
+ end
72
+
73
+ # @param [String] mimetype response MIME type
74
+ # @return [Symbol]
75
+ # @return [nil] mime type not supported
76
+ def self.format_from_mimetype(mimetype)
77
+ formats[formats.keys.detect {|k| mimetype.include?(k)}]
78
+ end
79
+
80
+ # @return [Array<Symbol>] list of supported formats
81
+ def self.supported_formats
82
+ formats.values.uniq
83
+ end
84
+
85
+ # @param [Symbol] format e.g. :json, :xml
86
+ # @return [Boolean]
87
+ def self.supports_format?(format)
88
+ supported_formats.include?(format)
89
+ end
90
+
91
+ def initialize(body, format)
92
+ @body = body
93
+ @format = format
94
+ end
95
+ private_class_method :new
96
+
97
+ # @return [Object] the parsed body
98
+ # @return [nil] when the response body is nil or an empty string
99
+ def parse
100
+ return nil if body.nil? || body.empty?
101
+ if supports_format?
102
+ parse_supported_format
103
+ else
104
+ body
105
+ end
106
+ end
107
+
108
+ protected
109
+
110
+ def xml
111
+ Crack::XML.parse(body)
112
+ end
113
+
114
+ def json
115
+ Crack::JSON.parse(body)
116
+ end
117
+
118
+ def yaml
119
+ YAML.load(body)
120
+ end
121
+
122
+ def html
123
+ body
124
+ end
125
+
126
+ def plain
127
+ body
128
+ end
129
+
130
+ def supports_format?
131
+ self.class.supports_format?(format)
132
+ end
133
+
134
+ def parse_supported_format
135
+ send(format)
136
+ rescue NoMethodError
137
+ raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format."
138
+ end
139
+ end
140
+ end