httparty 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of httparty might be problematic. Click here for more details.

@@ -0,0 +1,4 @@
1
+ == 0.1.0 2008-07-27
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.txt
6
+ Rakefile
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ examples/aaws.rb
10
+ examples/delicious.rb
11
+ examples/twitter.rb
12
+ examples/whoismyrep.rb
13
+ httparty.gemspec
14
+ lib/httparty.rb
15
+ lib/httparty/core_ext.rb
16
+ lib/httparty/core_ext/hash.rb
17
+ lib/httparty/version.rb
18
+ script/console
19
+ script/destroy
20
+ script/generate
21
+ script/txt2html
22
+ setup.rb
23
+ spec/hash_spec.rb
24
+ spec/httparty_spec.rb
25
+ spec/spec.opts
26
+ spec/spec_helper.rb
27
+ tasks/deployment.rake
28
+ tasks/environment.rake
29
+ tasks/website.rake
@@ -0,0 +1 @@
1
+ When you HTTParty, you must party hard!
@@ -0,0 +1,51 @@
1
+ = httparty
2
+
3
+ == DESCRIPTION:
4
+
5
+ Makes http fun again!
6
+
7
+ == FEATURES/PROBLEMS:
8
+
9
+ * Easy get, post, put, delete requests
10
+ * Basic http authentication
11
+ * Default request query string parameters (ie: for api keys that are needed on each request)
12
+ * Automatic parsing of JSON and XML into ruby hashes
13
+
14
+ == SYNOPSIS:
15
+
16
+ The following is a simple example of wrapping Twitter's API for posting updates.
17
+
18
+ class Twitter
19
+ include HTTParty
20
+ base_uri 'twitter.com'
21
+ basic_auth 'username', 'password'
22
+ end
23
+
24
+ Twitter.post('/statuses/update.json', :query => {:status => "It's an HTTParty and everyone is invited!"})
25
+
26
+ That is really it! The object returned is a ruby hash that is decoded from Twitter's json response. JSON parsing is used because of the .json extension in the path of the request. You can also explicitly set a format (see the examples).
27
+
28
+ That works and all but what if you don't want to embed your username and password in the class? Below is an example to fix that:
29
+
30
+ class Twitter
31
+ include HTTParty
32
+ base_uri 'twitter.com'
33
+
34
+ def initialize(user, pass)
35
+ self.class.basic_auth user, pass
36
+ end
37
+
38
+ def post(text)
39
+ self.class.post('/statuses/update.json', :query => {:status => text})
40
+ end
41
+ end
42
+
43
+ Twitter.new('username', 'password').post("It's an HTTParty and everyone is invited!")
44
+
45
+ == REQUIREMENTS:
46
+
47
+ * Active Support >= 2.1
48
+
49
+ == INSTALL:
50
+
51
+ * sudo gem install httparty
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,73 @@
1
+ require 'httparty/version'
2
+
3
+ AUTHOR = 'John Nunemaker' # can also be an array of Authors
4
+ EMAIL = "nunemaker@gmail.com"
5
+ DESCRIPTION = "Makes http fun! Also, makes consuming restful web services dead easy."
6
+ GEM_NAME = 'httparty' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'httparty' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ ['activesupport', '>= 2.1']
12
+ ] # An array of rubygem dependencies [name, version]
13
+
14
+ @config_file = "~/.rubyforge/user-config.yml"
15
+ @config = nil
16
+ RUBYFORGE_USERNAME = "unknown"
17
+ def rubyforge_username
18
+ unless @config
19
+ begin
20
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
21
+ rescue
22
+ puts <<-EOS
23
+ ERROR: No rubyforge config file found: #{@config_file}
24
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
25
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
26
+ EOS
27
+ exit
28
+ end
29
+ end
30
+ RUBYFORGE_USERNAME.replace @config["username"]
31
+ end
32
+
33
+
34
+ REV = nil
35
+ # UNCOMMENT IF REQUIRED:
36
+ # REV = YAML.load(`svn info`)['Revision']
37
+ VERS = HTTParty::VERSION::STRING + (REV ? ".#{REV}" : "")
38
+ RDOC_OPTS = ['--quiet', '--title', 'httparty documentation',
39
+ "--opname", "index.html",
40
+ "--line-numbers",
41
+ "--main", "README",
42
+ "--inline-source"]
43
+
44
+ class Hoe
45
+ def extra_deps
46
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
47
+ @extra_deps
48
+ end
49
+ end
50
+
51
+ # Generate all the Rake tasks
52
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
53
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
54
+ p.developer(AUTHOR, EMAIL)
55
+ p.description = DESCRIPTION
56
+ p.summary = DESCRIPTION
57
+ p.url = HOMEPATH
58
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
59
+ p.test_globs = ["test/**/test_*.rb"]
60
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
61
+
62
+ # == Optional
63
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
64
+ p.extra_deps = EXTRA_DEPENDENCIES
65
+
66
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
67
+ end
68
+
69
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
70
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
71
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
72
+ $hoe.rsync_args = '-av --delete --ignore-errors'
73
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -0,0 +1,30 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+ require 'pp'
4
+ config = YAML::load(File.read(File.join(ENV['HOME'], '.aaws')))
5
+
6
+ module AAWS
7
+ class Book
8
+ include HTTParty
9
+ base_uri 'http://ecs.amazonaws.com'
10
+ default_params :Service => 'AWSECommerceService', :Operation => 'ItemSearch', :SearchIndex => 'Books'
11
+ format :xml
12
+
13
+ def initialize(key)
14
+ self.class.default_params :AWSAccessKeyId => key
15
+ end
16
+
17
+ def search(options={})
18
+ raise ArgumentError, 'You must search for something' if options[:query].blank?
19
+
20
+ # amazon uses nasty camelized query params
21
+ options[:query] = options[:query].inject({}) { |h, q| h[q[0].to_s.camelize] = q[1]; h }
22
+
23
+ # make a request and return the items (NOTE: this doesn't handle errors at this point)
24
+ self.class.get('/onca/xml', options)['ItemSearchResponse']['Items']
25
+ end
26
+ end
27
+ end
28
+
29
+ aaws = AAWS::Book.new(config[:access_key])
30
+ pp aaws.search(:query => {:title => 'Ruby On Rails'})
@@ -0,0 +1,40 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+ require 'pp'
4
+ config = YAML::load(File.read(File.join(ENV['HOME'], '.delicious')))
5
+
6
+ class Delicious
7
+ include HTTParty
8
+ base_uri 'https://api.del.icio.us/v1'
9
+ format :xml
10
+
11
+ def initialize(user, pass)
12
+ self.class.basic_auth(user, pass)
13
+ end
14
+
15
+ # query params that filter the posts are:
16
+ # tag (optional). Filter by this tag.
17
+ # dt (optional). Filter by this date (CCYY-MM-DDThh:mm:ssZ).
18
+ # url (optional). Filter by this url.
19
+ # ie: posts(:query => {:tag => 'ruby'})
20
+ def posts(options={})
21
+ # get posts and convert to structs so we can do .key instead of ['key'] with results
22
+ self.class.get('/posts/get', options)['posts']['post'].map { |b| b.to_struct }
23
+ end
24
+
25
+ # query params that filter the posts are:
26
+ # tag (optional). Filter by this tag.
27
+ # count (optional). Number of items to retrieve (Default:15, Maximum:100).
28
+ def recent(options={})
29
+ self.class.get('/posts/recent', options)['posts']['post'].map { |b| b.to_struct }
30
+ end
31
+ end
32
+
33
+ delicious = Delicious.new(config['username'], config['password'])
34
+
35
+ pp delicious.posts(:query => {:tag => 'ruby'})
36
+
37
+ puts '', '*' * 50, ''
38
+
39
+ pp delicious.recent
40
+
@@ -0,0 +1,36 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+ require 'pp'
4
+ config = YAML::load(File.read(File.join(ENV['HOME'], '.twitter')))
5
+
6
+ class Twitter
7
+ include HTTParty
8
+ base_uri 'twitter.com'
9
+
10
+ def initialize(user, pass)
11
+ self.class.basic_auth user, pass
12
+ end
13
+
14
+ # which can be :friends, :user or :public
15
+ # options[:query] can be things like since, since_id, count, etc.
16
+ def timeline(which=:friends, options={})
17
+ self.class.get("/statuses/#{which}_timeline.xml", options)['statuses'].map { |s| s.to_struct }
18
+ end
19
+
20
+ def post(text)
21
+ self.class.post('/statuses/update.xml', :query => {:status => text})['status'].to_struct
22
+ end
23
+ end
24
+
25
+
26
+ twitter = Twitter.new(config['email'], config['password'])
27
+
28
+ twitter.timeline.each do |s|
29
+ puts s.user.name, s.text, "#{s.created_at} #{s.id}", ''
30
+ end
31
+
32
+ # twitter.timeline(:friends, :query => {:since_id => 868482746}).each do |s|
33
+ # puts s.user.name, s.text, "#{s.created_at} #{s.id}", ''
34
+ # end
35
+ #
36
+ # pp twitter.post('this is a test')
@@ -0,0 +1,10 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+ require 'pp'
4
+
5
+ class Rep
6
+ include HTTParty
7
+ format :xml
8
+ end
9
+
10
+ puts Rep.get('http://whoismyrepresentative.com/whoismyrep.php?zip=46544').inspect
@@ -0,0 +1,33 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{httparty}
3
+ s.version = "0.1.0"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["John Nunemaker"]
7
+ s.date = %q{2008-07-28}
8
+ s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
9
+ s.email = ["nunemaker@gmail.com"]
10
+ s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "PostInstall.txt", "README.txt"]
11
+ s.files = ["History.txt", "License.txt", "Manifest.txt", "PostInstall.txt", "README.txt", "Rakefile", "config/hoe.rb", "config/requirements.rb", "examples/aaws.rb", "examples/delicious.rb", "examples/twitter.rb", "examples/whoismyrep.rb", "httparty.gemspec", "lib/httparty.rb", "lib/httparty/core_ext.rb", "lib/httparty/core_ext/hash.rb", "lib/httparty/version.rb", "script/console", "script/destroy", "script/generate", "script/txt2html", "setup.rb", "spec/hash_spec.rb", "spec/httparty_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/deployment.rake", "tasks/environment.rake", "tasks/website.rake"]
12
+ s.has_rdoc = true
13
+ s.homepage = %q{http://httparty.rubyforge.org}
14
+ s.post_install_message = %q{When you HTTParty, you must party hard!}
15
+ s.rdoc_options = ["--main", "README.txt"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{httparty}
18
+ s.rubygems_version = %q{1.2.0}
19
+ s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 2
24
+
25
+ if current_version >= 3 then
26
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.1"])
27
+ else
28
+ s.add_dependency(%q<activesupport>, [">= 2.1"])
29
+ end
30
+ else
31
+ s.add_dependency(%q<activesupport>, [">= 2.1"])
32
+ end
33
+ end
@@ -0,0 +1,135 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'ostruct'
5
+ require 'rubygems'
6
+ require 'active_support'
7
+
8
+ $:.unshift(File.dirname(__FILE__)) unless
9
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
10
+
11
+ dir = File.expand_path(File.join(File.dirname(__FILE__), 'httparty'))
12
+ require dir + '/core_ext'
13
+
14
+ module HTTParty
15
+ def self.included(base)
16
+ base.extend ClassMethods
17
+ end
18
+
19
+ class UnsupportedFormat < StandardError; end
20
+
21
+ AllowedFormats = %w[xml json]
22
+
23
+ module ClassMethods
24
+ def base_uri(base_uri=nil)
25
+ return @base_uri unless base_uri
26
+ # don't want this to ever end with /
27
+ base_uri = base_uri.ends_with?('/') ? base_uri.chop : base_uri
28
+ @base_uri = normalize_base_uri(base_uri)
29
+ end
30
+
31
+ def basic_auth(u, p)
32
+ @auth = {:username => u, :password => p}
33
+ end
34
+
35
+ def default_params(h={})
36
+ raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
37
+ @default_params ||= {}
38
+ return @default_params if h.blank?
39
+ @default_params.merge!(h)
40
+ end
41
+
42
+ def headers(h={})
43
+ raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
44
+ @headers ||= {}
45
+ return @headers if h.blank?
46
+ @headers.merge!(h)
47
+ end
48
+
49
+ def format(f)
50
+ f = f.to_s
51
+ raise UnsupportedFormat, "Must be one of: #{AllowedFormats.join(', ')}" unless AllowedFormats.include?(f)
52
+ @format = f
53
+ end
54
+
55
+ # TODO: spec out this
56
+ def get(path, options={})
57
+ send_request 'get', path, options
58
+ end
59
+
60
+ # TODO: spec out this
61
+ def post(path, options={})
62
+ send_request 'post', path, options
63
+ end
64
+
65
+ # TODO: spec out this
66
+ def put(path, options={})
67
+ send_request 'put', path, options
68
+ end
69
+
70
+ # TODO: spec out this
71
+ def delete(path, options={})
72
+ send_request 'delete', path, options
73
+ end
74
+
75
+ private
76
+ def http(uri)
77
+ if @http.blank?
78
+ @http = Net::HTTP.new(uri.host, uri.port)
79
+ @http.use_ssl = (uri.port == 443)
80
+ # so we can avoid ssl warnings
81
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
82
+ end
83
+ @http
84
+ end
85
+
86
+ # options can be any or all of:
87
+ # query => hash of keys/values to be converted to query string
88
+ # body => string for raw post data
89
+ # headers => hash of headers to send request with
90
+ def send_request(method, path, options={})
91
+ raise ArgumentError, 'only get, post, put and delete methods are supported' unless %w[get post put delete].include?(method.to_s)
92
+ raise ArgumentError, ':query must be a hash' if options[:query] && !options[:query].is_a?(Hash)
93
+ raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
94
+ # we always want path that begins with /
95
+ path = path =~ /^(\/|https?:\/\/)/ ? path : "/#{path}"
96
+ @format ||= format_from_path(path)
97
+ uri = URI.parse("#{base_uri}#{path}")
98
+ current_qs = uri.query ? "#{uri.query}&" : ''
99
+ uri.query = current_qs + default_params.merge(options[:query] || {}).to_query
100
+ klass = Net::HTTP.const_get method.to_s.downcase.capitalize
101
+ request = klass.new(uri.request_uri)
102
+ request.body = options[:body] unless options[:body].blank?
103
+ request.initialize_http_header headers.merge(options[:headers] || {})
104
+ request.basic_auth(@auth[:username], @auth[:password]) if @auth
105
+ response = http(uri).start() { |conn| conn.request(request) }
106
+ parse_response(response.body)
107
+ end
108
+
109
+ def parse_response(body)
110
+ case @format
111
+ when 'xml'
112
+ Hash.from_xml(body)
113
+ when 'json'
114
+ ActiveSupport::JSON.decode(body)
115
+ else
116
+ # just return the response if no format
117
+ body
118
+ end
119
+ end
120
+
121
+ # Makes it so uri is sure to parse stuff like google.com with the http
122
+ def normalize_base_uri(str)
123
+ str =~ /^https?:\/\// ? str : "http#{'s' if str.include?(':443')}://#{str}"
124
+ end
125
+
126
+ # Returns a format that we can handle from the path if possible.
127
+ # Just does simple pattern matching on file extention:
128
+ # /foobar.xml => 'xml'
129
+ # /foobar.json => 'json'
130
+ def format_from_path(path)
131
+ ext = File.extname(path)[1..-1]
132
+ !ext.blank? && AllowedFormats.include?(ext) ? ext : nil
133
+ end
134
+ end
135
+ end