itrigga-net_helper 0.2.0 → 0.3.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/Gemfile CHANGED
@@ -5,13 +5,17 @@ source "http://rubygems.org"
5
5
 
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
+ gem "rest-client"
9
+ gem "hpricot"
10
+ gem 'itrigga-core_ext', :require=>'core_ext'
11
+
8
12
  group :development do
9
13
  gem "hpricot"
10
14
  gem "bundler", "~> 1.0.0"
11
15
  gem "jeweler", "~> 1.6.4"
16
+ gem "rspec", "1.3.0"
17
+ gem "rspec-rails", "1.3.2"
12
18
  gem "rcov", ">= 0"
13
- gem 'rspec'
14
19
  gem 'json_pure'
15
- gem 'itrigga-core_ext', ">= 0"
16
20
  gem "fastercsv", ">= 1.5.4"
17
21
  end
data/README.rdoc CHANGED
@@ -1,6 +1,41 @@
1
1
  = net_helper
2
2
 
3
- Description goes here.
3
+ Provides a wrapper around HTTP GET requests. Uses Net::HTTP (under RestClient) by default, but will use Typhoeus if available
4
+ Will return the raw body (unparsed) for each request.
5
+
6
+ == Usage
7
+ Rails2
8
+ gem.config 'itrigga-net_helper', :lib => 'net_helper'
9
+
10
+ Rails3 (bundler)
11
+ gem 'itrigga-net_helper', :require => 'net_helper'
12
+
13
+ === Basic example
14
+ Itrigga::NetHelper.get :url => "http://www.google.com"
15
+
16
+ === Adding params
17
+ Itrigga::NetHelper.get :url => "http://www.google.com", :params => {:p1 => "123"}
18
+ Gives url http://www.google.com?p1=123
19
+
20
+ === Variables
21
+
22
+ timeout - in seconds (default 5)
23
+ retries_on_timeout - how many times to try again (default 5)
24
+ max_redirects - number of redirects to follow (default 3)
25
+
26
+ The SystemTimer gem will be used for timeout is available.
27
+
28
+ Itrigga::NetHelper.get :url => "http://www.google.com", :timeout => 300, :max_redirects => 1
29
+
30
+ == Using Typhoeus
31
+ The Typhoeus gem needs to be installed (see https://github.com/dbalatero/typhoeus)
32
+ Rails2 - gem.config 'typhoeus'
33
+ Rails3 - gem 'typhoeus'
34
+
35
+ To enable for all requests set the constant ITNH_HTTP_ENGINE = "Typhoeus"
36
+ To use on a per request basis
37
+
38
+ Itrigga::NetHelper.get :url => "http://www.google.com", :http_engine => "Typhoeus"
4
39
 
5
40
  == Contributing to net_helper
6
41
 
data/Rakefile CHANGED
@@ -26,22 +26,27 @@ Jeweler::Tasks.new do |gem|
26
26
  end
27
27
  Jeweler::RubygemsDotOrgTasks.new
28
28
 
29
- require 'rake/testtask'
30
- Rake::TestTask.new(:test) do |test|
31
- test.libs << 'lib' << 'test'
32
- test.pattern = 'test/**/test_*.rb'
33
- test.verbose = true
34
- end
29
+ require 'spec/version'
30
+ require 'spec/rake/spectask'
31
+ require 'spec/ruby'
35
32
 
36
- require 'rcov/rcovtask'
37
- Rcov::RcovTask.new do |test|
38
- test.libs << 'test'
39
- test.pattern = 'test/**/test_*.rb'
40
- test.verbose = true
41
- test.rcov_opts << '--exclude "gems/*"'
33
+ Spec::Rake::SpecTask.new(:spec) do |spec|
34
+ RAILS_ENV = "test"
35
+ spec.spec_files = FileList['spec/**/*_spec.rb']
36
+ spec.spec_opts = ['--options', 'spec/spec.opts']
42
37
  end
43
38
 
44
- task :default => :test
39
+ task :default => :spec
40
+
41
+ desc "Run all specs with rcov"
42
+ Spec::Rake::SpecTask.new(:rcov) do |t|
43
+ t.spec_files = FileList['spec/**/*_spec.rb']
44
+ t.spec_opts = ['--options', 'spec/spec.opts']
45
+ t.rcov = true
46
+ t.rcov_dir = 'coverage'
47
+ t.rcov_opts = ['--exclude', "features,kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
48
+ t.rcov_opts << '--sort coverage --text-summary --aggregate coverage.data'
49
+ end
45
50
 
46
51
  require 'rake/rdoctask'
47
52
  Rake::RDocTask.new do |rdoc|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -1,3 +1,7 @@
1
+ require 'rest-client'
2
+ require 'itrigga/core_ext'
3
+ Dir[File.join(File.expand_path(File.dirname(__FILE__)),"net_helper","*.rb")].each{|f| require f}
4
+
1
5
  require 'uri'
2
6
  require 'hpricot'
3
7
  require 'json'
@@ -10,171 +14,74 @@ module Itrigga
10
14
 
11
15
  module_function
12
16
 
13
- def typhoeus_present?
14
- if defined?(Typhoeus)
15
- true
16
- else
17
- false
18
- end
17
+ # gets a list of engines available (each is a different class in this module that implements 'get' method)
18
+ def available_http_engines
19
+ Itrigga::NetHelper.constants
19
20
  end
20
-
21
- def do_get(url, time_out=5, retries_on_timeout=5, max_redirects = 3)
22
- retrycount = 0
23
- resp = nil
24
- begin
25
- resp = get_with_timeout( url, time_out )
26
-
27
- handle_response( url, resp, retries_on_timeout, max_redirects )
28
-
29
- rescue Timeout::Error
30
- if(retrycount.to_i < retries_on_timeout.to_i)
31
- retrycount+=1
32
- retry
33
- else
34
- raise IOError.new( "HTTP request timed out #{retrycount} times" )
35
- end
36
- end
37
21
 
22
+ def default_http_engine
23
+ "RestClient"
38
24
  end
39
25
 
40
- def typhoeus_request( opts={} )
41
- opts[:timeout] ||= 30
42
- opts[:timeout] *= 1000 # Typhoeus does its timeouts in ms
43
- opts[:follow_location] ||= true
44
- opts[:disable_ssl_peer_verification] = true if opts[:disable_ssl_peer_verification].nil?
45
-
46
- request = Typhoeus::Request.new(opts[:url], opts)
47
-
48
- request.on_complete do |response|
49
- if response.success?
50
- return response.body
51
- elsif response.timed_out?
52
- # aw hell no
53
- raise IOError.new("Timed out request to #{opts[:url]} after #{opts[:timeout]}ms")
54
- elsif response.code == 0
55
- # Could not get an http response, something's wrong.
56
- raise IOError.new(response.curl_error_message)
57
- else
58
- # Received a non-successful http response.
59
- raise IOError.new("HTTP request failed: " + response.code.to_s)
60
- end
61
- end
62
-
63
- hydra = Typhoeus::Hydra.new
64
- hydra.queue(request)
65
- hydra.run
66
- end
67
26
 
68
- def get_response( url, timeout=nil )
69
- if typhoeus_present?
70
- typhoeus_request( :url=>url, :timeout=>timeout )
71
- else
72
- Net::HTTP.get_response(URI.parse(url))
73
- end
74
- end
75
-
76
- def get_with_timeout( url, time_out)
77
- resp = nil
78
- if defined?(SystemTimer)
79
- resp = SystemTimer.timeout_after(time_out) do
80
- get_response(url, time_out)
81
- end
82
- else
83
- resp = timeout(time_out) do
84
- get_response(url, time_out)
85
- end
86
- end
87
- resp
27
+ # wrapper method for backwards compat.
28
+ def do_get(url, timeout=5, retries_on_timeout=5, max_redirects = 3)
29
+ get :url => url, :timeout => timeout, :retries_on_timeout => retries_on_timeout, :max_redirects => max_redirects
88
30
  end
31
+
32
+
33
+ def get(options = {})
34
+ opts = { :timeout => 5, :retries_on_timeout => 5, :max_redirects => 3, :headers => {} }.merge(options)
35
+ raise ArgumentError.new(":url is required" ) unless opts[:url]
89
36
 
90
-
91
-
92
- def handle_response( url, resp, retries_on_timeout=5, max_redirects = 3 )
93
- if resp.is_a? Net::HTTPSuccess then resp.body
94
- elsif resp.is_a? String then resp
95
- elsif resp.is_a? Net::HTTPRedirection
96
- if max_redirects > 0
97
- do_get( URI.parse(url).merge(resp['location']).to_s, retries_on_timeout, max_redirects - 1 )
98
- else
99
- raise IOError.new("too many redirects!")
100
- end
101
- else
102
- resp.error!
103
- end
37
+ engine_klass = get_engine opts
38
+ with_timeout(opts) { engine_klass.get(opts) }
104
39
  end
105
-
106
-
107
- def get( options = {} )
108
- opts = {:timeout=>5, :retries_on_timeout=>5, :max_redirects => 3, :headers=>{} }.merge(options)
109
- raise ArgumentError.new(":url is required" ) unless opts[:url]
110
40
 
111
- if (opts[:username] || opts[:headers]).to_s.empty?
112
- do_get(opts[:url], opts[:timeout], opts[:retries_on_timeout], opts[:max_redirects])
113
- else
114
-
115
- retrycount = 0
116
- resp = begin
117
- if defined?(SystemTimer)
118
- SystemTimer.timeout_after(opts[:timeout]) do
119
- raw_get(opts)
120
- end
121
- else
122
- timeout( opts[:timeout] ) do
123
- raw_get(opts)
124
- end
41
+
42
+
43
+ def with_timeout(opts, &block)
44
+ retrycount = 0
45
+ resp = begin
46
+ if defined?(SystemTimer)
47
+ SystemTimer.timeout_after(opts[:timeout]) do
48
+ yield
125
49
  end
126
- rescue TimeoutError
127
- if(retrycount < opts[:retries_on_timeout])
128
- retrycount+=1
129
- retry
130
- else
131
- raise IOError.new( "HTTP request timed out #{retrycount} times" )
50
+ else
51
+ timeout( opts[:timeout] ) do
52
+ yield
132
53
  end
133
54
  end
134
- resp
55
+ rescue ::TimeoutError, ::RestClient::RequestTimeout
56
+ if(retrycount < opts[:retries_on_timeout])
57
+ retrycount+=1
58
+ retry
59
+ else
60
+ raise IOError.new( "HTTP request timed out #{retrycount} times" )
61
+ end
135
62
  end
63
+ resp
136
64
  end
137
-
138
- def raw_get(opts)
65
+
66
+
67
+
68
+ def get_engine(opts = {})
69
+ # default to rest_client if not given an engine
70
+ opts[:http_engine] ||= ( defined?(ITNH_HTTP_ENGINE) ? ITNH_HTTP_ENGINE : default_http_engine )
139
71
 
140
- if typhoeus_present?
141
- return typhoeus_request(opts)
142
- end
72
+ # only use typhoeus if it is actually been required
73
+ opts[:http_engine] = default_http_engine if opts[:http_engine] == "Typhoeus" && defined?(::Typhoeus) == nil
143
74
 
144
- resp = nil
145
- establish_session_if_needed(opts)
146
-
147
- if opts[:username]
148
- resp = get_with_auth(opts)
149
- retries = 0
150
- while resp.is_a? Net::HTTPRedirection do
151
- retries += 1
152
- raise IOError.new( "HTTP request timed out #{retries} times" ) if retries > (opts[:max_redirects] || 3)
153
-
154
- resp = get_with_auth(opts.merge(:parsed_url=>URI.parse(resp['location'])))
155
- end
156
-
157
- resp.body
158
-
75
+ if available_http_engines.include?(opts[:http_engine])
76
+ Itrigga::NetHelper.const_get opts[:http_engine]
159
77
  else
160
- response = opts[:http_session].request_get(opts[:parsed_url].path, opts[:headers])
161
- response.body
78
+ puts "[NetHelper] Could not find http_engine '#{opts[:http_engine]}'. Available engines are: #{available_http_engines.join(',')}. Defaulting to Net::HTTP"
79
+ Itrigga::NetHelper.const_get default_http_engine
162
80
  end
81
+
163
82
  end
164
-
165
- def get_with_auth( opts )
166
- establish_session_if_needed(opts)
167
83
 
168
- req = Net::HTTP::Get.new(opts[:parsed_url].path)
169
- req.basic_auth( opts[:username], opts[:password] ) if opts[:username]
170
- resp = opts[:http_session].request(req, opts[:headers])
171
- end
172
-
173
- def establish_session_if_needed(opts)
174
- opts[:parsed_url] ||= URI.parse(opts[:url])
175
- opts[:http_session] ||= Net::HTTP.new(opts[:parsed_url].host, opts[:parsed_url].port)
176
- opts[:http_session].use_ssl = true if opts[:parsed_url].scheme == 'https'
177
- end
84
+
178
85
 
179
86
  def query_string( h, opts={:encode_values=>false, :skip_empty=>false} )
180
87
  params = []
@@ -0,0 +1,19 @@
1
+ module Itrigga
2
+ module NetHelper
3
+ class RestClient
4
+
5
+ def self.get( opts={} )
6
+ raise ArgumentError.new(":url is required") unless opts[:url]
7
+
8
+ # rest_client expects :user and not :username
9
+ opts[:user] ||= opts.delete(:username) if opts[:username]
10
+
11
+ opts[:headers] = {:params => opts.delete(:params)} if opts[:params]
12
+
13
+ response = ::RestClient::Request.new(opts.merge(:method => :get)).execute
14
+ response.to_str
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ module Itrigga
2
+ module NetHelper
3
+ class Typhoeus
4
+
5
+ def self.get( opts={} )
6
+ opts[:timeout] ||= 30
7
+ opts[:timeout] *= 1000 # Typhoeus does its timeouts in ms
8
+ opts[:follow_location] ||= true
9
+ opts[:disable_ssl_peer_verification] = true if opts[:disable_ssl_peer_verification].nil?
10
+
11
+ request = ::Typhoeus::Request.new(opts[:url], opts)
12
+
13
+ request.on_complete do |response|
14
+ if response.success?
15
+ return response.body
16
+ elsif response.timed_out?
17
+ # aw hell no
18
+ raise ::TimeoutError.new("Timed out request to #{opts[:url]} after #{opts[:timeout]}ms")
19
+ elsif response.code == 0
20
+ # Could not get an http response, something's wrong.
21
+ raise IOError.new(response.curl_error_message)
22
+ else
23
+ # Received a non-successful http response.
24
+ raise IOError.new("HTTP request failed: " + response.code.to_s)
25
+ end
26
+ end
27
+
28
+ hydra = ::Typhoeus::Hydra.new
29
+ hydra.queue(request)
30
+ hydra.run
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+ describe "Itrigga::NetHelper::RestClient" do
4
+ before do
5
+ @mock_response = mock("Response", :to_str => "stuff")
6
+ @client = mock("RestClient::Request")
7
+ @client.stub!(:execute).and_return(@mock_response)
8
+ RestClient::Request.stub!(:new).and_return(@client)
9
+ end
10
+
11
+ it "should raise error if no url" do
12
+ lambda { Itrigga::NetHelper::RestClient.get }.should raise_error(ArgumentError,":url is required")
13
+ end
14
+
15
+ it "should call execute on new Request" do
16
+ RestClient::Request.should_receive(:new).with(hash_including(:method => :get, :url => "abc", :param1 => "blart")).and_return(@client)
17
+ Itrigga::NetHelper::RestClient.get :url => "abc", :param1 => "blart"
18
+ end
19
+
20
+ it "should return the correct value" do
21
+ @mock_response.should_receive(:to_str).and_return("stuff")
22
+ Itrigga::NetHelper::RestClient.get(:url => "abc").should == "stuff"
23
+ end
24
+
25
+ context "when given a :username" do
26
+ it "should convert it to :user" do
27
+ RestClient::Request.should_receive(:new).with(hash_including(:method => :get, :url => "abc", :user => "blart")).and_return(@client)
28
+ Itrigga::NetHelper::RestClient.get :url => "abc", :username => "blart"
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,90 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper' )
2
+
3
+
4
+
5
+ describe "Itrigga::NetHelper::Typhoeus" do
6
+ before do
7
+ # define Typhoeus stubs as not installed as gem dependancy
8
+ Object.class_eval %{
9
+ module Typhoeus
10
+ class Request
11
+ attr_accessor :on_complete
12
+ def initialize(url,opts = {}); end
13
+ def on_complete(&block); @on_complete = block; end
14
+ end
15
+
16
+ class Hydra
17
+ attr_accessor :request
18
+ def queue(request); end
19
+ def run; end
20
+ end
21
+ end
22
+ }
23
+
24
+ @opts = {:url => "blart"}
25
+ @response = mock("Response", :success? => true, :body => "stuff")
26
+ @request = Typhoeus::Request.new("blart", @opts)
27
+ # @request.stub!(:on_complete).and_yield(@response)
28
+ Typhoeus::Request.stub!(:new).and_return(@request)
29
+ @hydra = Typhoeus::Hydra.new
30
+ Typhoeus::Hydra.stub!(:new).and_return(@hydra)
31
+ end
32
+
33
+ after do
34
+ Object.send(:remove_const, :Typhoeus)
35
+ end
36
+
37
+ it "should create a Request object" do
38
+ Typhoeus::Request.should_receive(:new).with("blart",@opts).and_return(@request)
39
+ Itrigga::NetHelper::Typhoeus.get @opts
40
+ end
41
+
42
+ it "should call queue on hydra" do
43
+ @hydra.should_receive(:queue).with(@request)
44
+ Itrigga::NetHelper::Typhoeus.get(@opts)
45
+ end
46
+
47
+ it "should call run on hydra" do
48
+ @hydra.should_receive(:run)
49
+ Itrigga::NetHelper::Typhoeus.get(@opts)
50
+ end
51
+
52
+ it "should return the response body" do
53
+ @request.stub!(:on_complete).and_yield(@response)
54
+ Itrigga::NetHelper::Typhoeus.get(@opts).should == "stuff"
55
+ end
56
+
57
+ context "a timeout" do
58
+ before do
59
+ @response = mock("Response", :success? => false, :timed_out? => true, :body => "stuff")
60
+ @request.stub!(:on_complete).and_yield(@response)
61
+ end
62
+
63
+ it "should raise a TimeoutError" do
64
+ lambda { Itrigga::NetHelper::Typhoeus.get(@opts) }.should raise_error(TimeoutError)
65
+ end
66
+ end
67
+
68
+ context "response code == 0" do
69
+ before do
70
+ @response = mock("Response", :success? => false, :timed_out? => false, :code => 0, :body => "stuff", :curl_error_message => "blart")
71
+ @request.stub!(:on_complete).and_yield(@response)
72
+ end
73
+
74
+ it "should raise a TimeoutError" do
75
+ lambda { Itrigga::NetHelper::Typhoeus.get(@opts) }.should raise_error(IOError)
76
+ end
77
+ end
78
+
79
+ context "any other http status code" do
80
+ before do
81
+ @response = mock("Response", :success? => false, :timed_out? => false, :code => 500, :body => "stuff")
82
+ @request.stub!(:on_complete).and_yield(@response)
83
+ end
84
+
85
+ it "should raise a TimeoutError" do
86
+ lambda { Itrigga::NetHelper::Typhoeus.get(@opts) }.should raise_error(IOError)
87
+ end
88
+ end
89
+
90
+ end