shb 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e59ea891242f1ed837c35ba9be734526226019f1
4
+ data.tar.gz: ff0757e7ed3e2e7a556122804181ab9215cc733c
5
+ SHA512:
6
+ metadata.gz: c0ca8bb25eda6b02228174552d1c23d90199f6c35490899f28ababf6675c7efb161f9c3f84622903dfd1f4f3e73669e8de7c51b998c6948f43c7b645c71580dc
7
+ data.tar.gz: c16c2157ea4be0f51df260c250911e02df0d52d097fcaaae53422fe6c18cc65d0fe0d3741c20a299e5b328108c7c70bf9759d8848504ff8cfb8d82d694cae106
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg
2
+ Gemfile.lock
3
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in slackistrano.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Philip Hallstrom
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Shb
2
+
3
+ ## Requirements
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'shb'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ ## Configuration
16
+
17
+ ## Usage
18
+
19
+ ## TODO
20
+
21
+ ## Contributing
22
+
23
+ 1. Fork it
24
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
25
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
26
+ 4. Push to the branch (`git push origin my-new-feature`)
27
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :spec
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.0.0-p451
@@ -0,0 +1,138 @@
1
+ module Shb
2
+ class AbstractClient
3
+
4
+ include ActiveSupport::Configurable
5
+ include HTTParty
6
+
7
+ AGENT_ALIASES = [
8
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.71 (KHTML, like Gecko) Version/6.1 Safari/537.71', # Safari on OSX Lion
9
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9) AppleWebKit/537.71 (KHTML, like Gecko) Version/7.0 Safari/537.71', # Safari on OSX Mavericks
10
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:17.0) Gecko/20100101 Firefox/17.0', # Firefox on Mac
11
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36', # Chrome on Mac
12
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36', # Chrome on Windows 7
13
+ 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)', # IE10 on Windows 7
14
+ 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0', # Firefox on Windows 7
15
+ ]
16
+
17
+ config.cache = !!(ENV['SHB_CACHE'] =~ /^[T1Y]/i) # TRUE, 1, YES
18
+ config.cache_class = ::Shb::Cache
19
+ config.cycle_user_agent = false
20
+ config.use_cookies = false
21
+ config.logger = nil
22
+
23
+ parser ::Shb::Parser
24
+ follow_redirects false
25
+ headers 'User-Agent' => AGENT_ALIASES.first
26
+
27
+ def initialize(base_uri: 'http://supremegolf.com')
28
+ self.class.base_uri base_uri
29
+ @root_uri = URI.parse(self.class.base_uri.to_s)
30
+ end
31
+
32
+ def get(path, options = {}, &block)
33
+ make_request!(:get, path, options, &block)
34
+ end
35
+
36
+ def post(path, options = {}, &block)
37
+ make_request!(:post, path, options, &block)
38
+ end
39
+
40
+ def put(path, options = {}, &block)
41
+ make_request!(:put, path, options, &block)
42
+ end
43
+
44
+ ################################################################################
45
+ private
46
+
47
+ #
48
+ def make_request!(method, path, options = {}, &block)
49
+ uri = path_to_uri(path)
50
+ if (response = cache_read(method, uri, options)).nil?
51
+ log_request!(method, uri, options)
52
+ cycle_user_agent!
53
+ set_cookies!
54
+ response = self.class.send(method, uri.to_s, options, &block)
55
+ save_cookies!(response)
56
+ cache_write(response, uri, options)
57
+ end
58
+ response
59
+ rescue SocketError, Net::ReadTimeout => e
60
+ logger.error "ERROR #{e.inspect} : uri=#{uri}"
61
+ sleep 60
62
+ retry
63
+ end
64
+
65
+ #
66
+ def path_to_uri(path)
67
+ @root_uri.merge(path.to_s.gsub(' ', '%20'))
68
+ end
69
+
70
+ #
71
+ def log_request!(method, uri, options)
72
+ logger.info "#{method.to_s.upcase} #{uri.to_s}#{options[:query].nil? ? nil : "?#{HashConversions.to_params(options[:query])}"}"
73
+ end
74
+
75
+ #
76
+ def cycle_user_agent!
77
+ return unless config.cycle_user_agent
78
+ @user_agent_alias_idx ||= 0
79
+ self.class.headers('User-Agent' => AGENT_ALIASES[@user_agent_alias_idx])
80
+ @user_agent_alias_idx += 1
81
+ @user_agent_alias_idx %= AGENT_ALIASES.size
82
+ end
83
+
84
+ #
85
+ def set_cookies!
86
+ return unless config.use_cookies && !@cookies.nil?
87
+ self.class.headers('Cookie' => @cookies)
88
+ end
89
+
90
+ #
91
+ def save_cookies!(response)
92
+ return unless config.use_cookies
93
+ @cookies = response.headers['set-cookie']
94
+ end
95
+
96
+ #
97
+ def logger
98
+ return @logger unless @logger.nil?
99
+
100
+ @logger = if config.logger
101
+ ::Logger.new(config.logger)
102
+ elsif defined?(::Rails)
103
+ ::Logger.new( File.join(::Rails.root, 'log', 'shb.log') )
104
+ else
105
+ ::Logger.new(STDERR)
106
+ end
107
+
108
+ @logger.formatter = proc do |severity, datetime, progname, msg|
109
+ "%-7s [%s] -- %s\n" % [severity, datetime, msg]
110
+ end
111
+
112
+ @logger
113
+ end
114
+
115
+ #
116
+ def cache_write(response, uri, options = {})
117
+ return true unless config.cache
118
+ return true if response.code >= 400 # Don't cache bad responses
119
+ config.cache_class.write(response, uri, options)
120
+ end
121
+
122
+ def cache_read(method, uri, options = {})
123
+ return nil unless config.cache
124
+
125
+ logger.info "#{method.to_s.upcase} CACHE #{uri.to_s}#{method == :get && !options[:query].to_s.empty? ? "?#{HashConversions.to_params(options[:query])}" : nil}"
126
+
127
+ response = config.cache_class.read(method, uri, options)
128
+
129
+ return nil if response.nil?
130
+
131
+ HTTParty::Response.new(OpenStruct.new(options:options), response,
132
+ ->{ self.class.parser.call(response.body, options[:format] || self.class.parser.format_from_mimetype(response.content_type)) },
133
+ body: response.body)
134
+
135
+ end
136
+
137
+ end # of class AbstractClient
138
+ end
data/lib/shb/cache.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'fileutils'
2
+
3
+ module Shb
4
+ class Cache
5
+ class << self
6
+
7
+ def write(response, uri, options = {})
8
+ file = cache_file(uri, options)
9
+ FileUtils.mkdir_p(File.dirname(file))
10
+ File.open(file, 'w') do |f|
11
+ f.puts YAML::dump(response.response)
12
+ end
13
+ end
14
+
15
+ def read(method, uri, options = {})
16
+ file = cache_file(uri, options)
17
+ return nil unless File.exist?(file)
18
+ r = YAML::load_file(file)
19
+ r.content_type = 'text/plain' if r.content_type.nil?
20
+ r
21
+ end
22
+
23
+ def cache_file(uri, options = {})
24
+ bits = []
25
+ bits << Rails.root if defined?(::Rails)
26
+ bits << 'tmp'
27
+ bits << uri.host
28
+ path = uri.path == '/' ? 'ROOT' : uri.path.parameterize
29
+ query = options.empty? ? nil : "?#{HashConversions.to_params(options)}"
30
+ bits << Digest::MD5.hexdigest([path,uri.fragment,uri.query,query].join)
31
+ File.join(bits)
32
+ end
33
+
34
+ end
35
+ end
36
+ end
data/lib/shb/client.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Shb
2
+ class Client
3
+ # http://rubyscale.com/blog/2012/09/24/being-classy-with-httparty/
4
+ class << self
5
+ def new(*args)
6
+ Class.new(AbstractClient).new(*args)
7
+ end
8
+ end
9
+ end
10
+ end
data/lib/shb/parser.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'nokogiri'
2
+ require 'json'
3
+
4
+ module Shb
5
+ class Parser < HTTParty::Parser
6
+ SupportedFormats.merge!(
7
+ 'text/html' => :html,
8
+ 'text/xml' => :xml,
9
+ 'application/jsonnet' => :json,
10
+ 'application/json' => :json
11
+ )
12
+
13
+ def html
14
+ Nokogiri::HTML(body)
15
+ end
16
+
17
+ def xml
18
+ Nokogiri::XML(body)
19
+ end
20
+
21
+ def json
22
+ JSON.parse(body)
23
+ rescue JSON::ParserError
24
+ nil
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Shb
2
+ VERSION = '0.0.2'
3
+ end
data/lib/shb.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'active_support/configurable'
2
+ require 'httparty'
3
+ require 'logger'
4
+ require 'shb/version'
5
+ require 'shb/parser'
6
+ require 'shb/cache'
7
+ require 'shb/abstract_client'
8
+ require 'shb/client'
data/shb.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shb/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "shb"
8
+ gem.version = Shb::VERSION
9
+ gem.authors = ["Philip Hallstrom"]
10
+ gem.email = ["philip@supremegolf.com"]
11
+ gem.description = %q{}
12
+ gem.summary = %q{}
13
+ gem.homepage = "https://github.com/supremegolf/shb"
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency 'activesupport', '>= 4.0'
22
+ gem.add_dependency 'httparty', '>= 0.13.1'
23
+ gem.add_dependency 'nokogiri', '>= 1.6.3.1'
24
+ gem.add_dependency 'json'
25
+
26
+ gem.add_development_dependency 'rake'
27
+ gem.add_development_dependency 'rspec'
28
+ gem.add_development_dependency 'webmock'
29
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+ before do
5
+ stub_request(:any, 'supremegolf.com')
6
+ end
7
+
8
+ it "should test more than this" do
9
+ shb = Shb::Client.new
10
+ shb.config.cache = true
11
+ shb.get('/')
12
+ shb.get('/')
13
+ end
14
+
15
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+
5
+ it "does not cycle the user agent" do
6
+ stub_request(:any, 'supremegolf.com')
7
+ shb = Shb::Client.new
8
+ shb.config.cycle_user_agent = false
9
+
10
+ expect(shb.get('/').request.options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES[0]
11
+ expect(shb.get('/').request.options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES[0]
12
+ expect(shb.get('/').request.options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES[0]
13
+ end
14
+
15
+ it "cycles the user agent" do
16
+ stub_request(:any, 'supremegolf.com')
17
+ shb = Shb::Client.new
18
+ shb.config.cycle_user_agent = true
19
+
20
+ expect(shb.get('/').request.options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES[0]
21
+ expect(shb.get('/').request.options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES[1]
22
+ expect(shb.get('/').request.options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES[2]
23
+ end
24
+
25
+ end
data/spec/html_spec.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+ before do
5
+ stub_request(:any, 'supremegolf.com').to_return {|r|
6
+ {
7
+ body: '<html><title>Test</title></html>',
8
+ headers: {'Content-Type' => 'text/html'}
9
+ }
10
+ }
11
+ end
12
+ let(:shb) { Shb::Client.new }
13
+
14
+ specify { expect(shb.get('/').parsed_response).to be_a Nokogiri::HTML::Document }
15
+ specify { expect(shb.get('/').parsed_response.at('title').text).to eq 'Test' }
16
+ end
data/spec/json_spec.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+ before do
5
+ stub_request(:any, 'supremegolf.com').to_return {|r|
6
+ {
7
+ body: '{"one":1}',
8
+ headers: {'Content-Type' => 'application/json'}
9
+ }
10
+ }
11
+ stub_request(:any, 'supremegolf.com/bad-json').to_return {|r|
12
+ {
13
+ body: '{missing_first_double_quote":1}',
14
+ headers: {'Content-Type' => 'application/json'}
15
+ }
16
+ }
17
+ end
18
+ let(:shb) { Shb::Client.new }
19
+
20
+ specify { expect(shb.get('/').parsed_response).to be_a Hash }
21
+ specify { expect(shb.get('/').parsed_response['one']).to eq 1 }
22
+ specify { expect(shb.get('/bad-json').parsed_response).to eq nil }
23
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+ before do
5
+ stub_request(:any, 'supremegolf.com')
6
+ end
7
+
8
+ it "logs to STDERR by default" do
9
+ expect(::Logger).to receive(:new).with(STDERR).and_call_original
10
+ shb = Shb::Client.new
11
+ shb.get('/')
12
+ end
13
+
14
+ it "logs to custom location if specified" do
15
+ expect(::Logger).to receive(:new).with(STDOUT).and_call_original
16
+ shb = Shb::Client.new
17
+ shb.config.logger = STDOUT
18
+ shb.get('/')
19
+ end
20
+
21
+ it "logs to Rails.root/tmp/shb.log if using Rails" do
22
+ skip "How to double ::Rails and ::Rails.root?"
23
+ shb = Shb::Client.new
24
+ expect(::Logger).to receive(:new).with('rails-root/log/shb.log').and_call_original
25
+ shb.get('/')
26
+ end
27
+
28
+
29
+ end
data/spec/shb_spec.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+
5
+ context "defaults" do
6
+ specify { expect(Shb::Client.new.config.cache).to be_falsey }
7
+ specify { expect(Shb::Client.new.config.cycle_user_agent).to be_falsey }
8
+ specify { expect(Shb::Client.new.config.use_cookies).to be_falsey }
9
+ specify { expect(Shb::Client.new.class.default_options[:parser]).to eq Shb::Parser }
10
+ specify { expect(Shb::Client.new.class.default_options[:follow_redirects]).to be_falsey }
11
+ specify { expect(Shb::Client.new.class.default_options[:headers]['User-Agent']).to eq Shb::AbstractClient::AGENT_ALIASES.first }
12
+ end
13
+
14
+ context "simple requests" do
15
+ before do
16
+ stub_request(:any, 'supremegolf.com').to_return {|r|
17
+ {
18
+ status:200,
19
+ body: r.method.to_s.downcase
20
+ }
21
+ }
22
+ end
23
+ let(:shb) { Shb::Client.new }
24
+
25
+ specify { expect(shb.get('/').code).to eq 200 }
26
+ specify { expect(shb.get('/').body).to eq "get" }
27
+ specify { expect(shb.post('/').code).to eq 200 }
28
+ specify { expect(shb.post('/').body).to eq "post" }
29
+ specify { expect(shb.put('/').code).to eq 200 }
30
+ specify { expect(shb.put('/').body).to eq "put" }
31
+ end
32
+
33
+ context "requests with data" do
34
+ before do
35
+ stub_request(:any, 'supremegolf.com?q=1').to_return {|r|
36
+ { status:200, body: [r.uri.query, r.body].join('&') }
37
+ }
38
+ end
39
+ let(:shb) { Shb::Client.new }
40
+
41
+ specify { expect(shb.get('/', query: {q: 1}).body).to eq "q=1&" }
42
+ specify { expect(shb.post('/', query: {q: 1}, body: {body: 1}).body).to eq "q=1&body=1" }
43
+ specify { expect(shb.put('/', query: {q: 1}, body: {body: 1}).body).to eq "q=1&body=1" }
44
+ end
45
+
46
+
47
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'shb'
4
+ require 'rspec'
5
+ require 'webmock/rspec'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir['#{File.dirname(__FILE__)}/support/**/*.rb'].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+ config.order = 'random'
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+
5
+ it "does not use cookies" do
6
+ stub_request(:any, 'supremegolf.com').to_return {|r|
7
+ { headers: {'Set-Cookie' => 'cookie123'} }
8
+ }
9
+ shb = Shb::Client.new
10
+ shb.config.use_cookies = false
11
+
12
+ expect(shb.get('/').request.options[:headers]['Cookie']).to be_nil
13
+ expect(shb.get('/').request.options[:headers]['Cookie']).to be_nil
14
+ end
15
+
16
+ it "uses cookies" do
17
+ stub_request(:any, 'supremegolf.com').to_return {|r|
18
+ { headers: {'Set-Cookie' => 'cookie123'} }
19
+ }
20
+ shb = Shb::Client.new
21
+ shb.config.use_cookies = true
22
+
23
+ expect(shb.get('/').request.options[:headers]['Cookie']).to be_nil
24
+ expect(shb.get('/').request.options[:headers]['Cookie']).to eq 'cookie123'
25
+ end
26
+
27
+ end
data/spec/xml_spec.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Shb::Client do
4
+ before do
5
+ stub_request(:any, 'supremegolf.com').to_return {|r|
6
+ {
7
+ body: '<xml><title>Test</title></xml>',
8
+ headers: {'Content-Type' => 'text/xml'}
9
+ }
10
+ }
11
+ end
12
+ let(:shb) { Shb::Client.new }
13
+
14
+ specify { expect(shb.get('/').parsed_response).to be_a Nokogiri::XML::Document }
15
+ specify { expect(shb.get('/').parsed_response.at('title').text).to eq 'Test' }
16
+ end
17
+
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Philip Hallstrom
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.13.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.13.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.6.3.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.3.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: ''
112
+ email:
113
+ - philip@supremegolf.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - circle.yml
124
+ - lib/shb.rb
125
+ - lib/shb/abstract_client.rb
126
+ - lib/shb/cache.rb
127
+ - lib/shb/client.rb
128
+ - lib/shb/parser.rb
129
+ - lib/shb/version.rb
130
+ - shb.gemspec
131
+ - spec/cache_spec.rb
132
+ - spec/cycle_user_agent_spec.rb
133
+ - spec/html_spec.rb
134
+ - spec/json_spec.rb
135
+ - spec/logger_spec.rb
136
+ - spec/shb_spec.rb
137
+ - spec/spec_helper.rb
138
+ - spec/user_cookies_spec.rb
139
+ - spec/xml_spec.rb
140
+ homepage: https://github.com/supremegolf/shb
141
+ licenses:
142
+ - MIT
143
+ metadata: {}
144
+ post_install_message:
145
+ rdoc_options: []
146
+ require_paths:
147
+ - lib
148
+ required_ruby_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ required_rubygems_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 2.2.2
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: ''
164
+ test_files:
165
+ - spec/cache_spec.rb
166
+ - spec/cycle_user_agent_spec.rb
167
+ - spec/html_spec.rb
168
+ - spec/json_spec.rb
169
+ - spec/logger_spec.rb
170
+ - spec/shb_spec.rb
171
+ - spec/spec_helper.rb
172
+ - spec/user_cookies_spec.rb
173
+ - spec/xml_spec.rb