diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d87d4be --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..5f16476 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--format progress diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..734ddf5 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in geocodio.gemspec +gemspec diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..eced072 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2014 David Celis + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fad49e9 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# Geocodio + +Geocodio is a lightweight Ruby wrapper around [geocod.io][geocod.io]'s API. + +## Installation + +In your Gemfile: + +```ruby +gem 'geocodio' +``` + +## Usage + +The point of entry to geocod.io's API is the `Geocodio::Client` class. Initialize +one by passing your API key or allowing the initializer to automatically use +the `GEOCODIO_API_KEY` environment variable: + +```ruby +geocodio = Geocodio::Client.new('0123456789abcdef') + +# Or, if you've set GEOCODIO_API_KEY in your environment: +geocodio = Geocodio::Client.new +``` + +### Geocoding + +```ruby +results = geocodio.geocode('1 Infinite Loop, Cupertino, CA 95014') +# => # +address = results.first +# => # +puts address +# => 1 Infinite Loop, Cupertino CA, 95014 +puts address.latitude # or address.lat +# => 37.331669 +puts address.longitude # or address.lng +# => -122.03074 +puts address.accuracy +# => 1 +``` + +You can pass multiple addresses to `Geocodio::Client#geocode`: + +```ruby +result_sets = geocodio.geocode('1 Infinite Loop, Cupertino, CA 95014', '54 West Colorado Boulevard, Pasadena, CA 91105') +# => [#, #] +cupertino = result_sets.first.best +# => # +``` + +Geocoding will return one or more instances of `Geocodio::AddressSet` which represent a collection of addresses returned from geocod.io. Each address in the result set has an associated accuracy. If you just want whichever result was the most accurate, a `#best` convenience method is provided: + +```ruby +results = geocodio.geocode('1 Infinite Loop, Cupertino, CA 95014') +# => # +results.size +# => 2 +results.best +# => # +``` + +### Parsing + +```ruby +address = geocodio.parse('1 Infinite Loop, Cupertino, CA 95014') +# => # +``` + +Note that this endpoint performs no geocoding; it merely formats a single provided address according to geocod.io's standards. + +## Contributing + +1. Fork it ( http://github.com/[my-github-username]/geocodio/fork ) +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request + +[geocod.io]: http://geocod.io/ diff --git a/geocodio.gemspec b/geocodio.gemspec new file mode 100644 index 0000000..612c064 --- /dev/null +++ b/geocodio.gemspec @@ -0,0 +1,26 @@ +# encoding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'geocodio/version' + +Gem::Specification.new do |s| + s.name = 'geocodio' + s.version = Geocodio::VERSION + s.authors = ['David Celis'] + s.email = ['me@davidcel.is'] + + s.summary = %q{An unofficial Ruby client library for geocod.io} + s.description = %q{Geocodio is a geocoding service that aims to fill a void in the community by allowing developers to geocode large amounts of addresses without worrying about daily limits and high costs.} + s.homepage = 'https://github.com/davidcelis/geocodio' + s.license = 'MIT' + + s.files = Dir['lib/**/*.rb'] + s.test_files = Dir['spec/**/*'] + s.require_paths = ['lib'] + + s.add_dependency 'json' + + s.add_development_dependency 'rspec' + s.add_development_dependency 'webmock' + s.add_development_dependency 'vcr' +end diff --git a/lib/geocodio.rb b/lib/geocodio.rb new file mode 100644 index 0000000..b972ea7 --- /dev/null +++ b/lib/geocodio.rb @@ -0,0 +1,8 @@ +require 'geocodio/address' +require 'geocodio/address_set' +require 'geocodio/client' + +require 'geocodio/version' + +module Geocodio +end diff --git a/lib/geocodio/address.rb b/lib/geocodio/address.rb new file mode 100644 index 0000000..11433e6 --- /dev/null +++ b/lib/geocodio/address.rb @@ -0,0 +1,47 @@ +module Geocodio + class Address + attr_accessor :number + attr_accessor :street + attr_accessor :suffix + attr_accessor :city + attr_accessor :state + attr_accessor :zip + + attr_accessor :latitude + attr_accessor :longitude + alias :lat :latitude + alias :lng :longitude + + # How accurage geocod.io deemed this result to be given the original query. + # + # @return [Float] a number between 0 and 1 + attr_accessor :accuracy + + def initialize(payload = {}) + if payload['address_components'] + @number = payload['address_components']['number'] + @street = payload['address_components']['street'] + @suffix = payload['address_components']['suffix'] + @city = payload['address_components']['city'] + @state = payload['address_components']['state'] + @zip = payload['address_components']['zip'] + end + + if payload['location'] + @latitude = payload['location']['lat'] + @longitude = payload['location']['lng'] + end + + @accuracy = payload['accuracy'] + + @formatted_address = payload['formatted_address'] + end + + # Formats the address in the standard way. + # + # @return [String] a formatted address + def to_s + @formatted_address + end + end +end diff --git a/lib/geocodio/address_set.rb b/lib/geocodio/address_set.rb new file mode 100644 index 0000000..6d5d589 --- /dev/null +++ b/lib/geocodio/address_set.rb @@ -0,0 +1,37 @@ +# AddressSet is a collection of Geocodio::Address objects that get returned from +# a query to geocod.io. Because one query can return multiple results, each with +# an accuracy, a collection to manage them is needed. Most of the time, the user +# should only need to use the #best method. +module Geocodio + class AddressSet + include Enumerable + + # Returns the query that retrieved this result set. + # + # @return [String] the original query + attr_reader :query + + def initialize(query, *addresses) + @query = query + @addresses = addresses + end + + def each(&block) + @addresses.each(&block) + end + + # Returns the result that geocod.io deemed the most accurate for the query. + # + # @return [Geocodio::Address] the most accurate address + def best + max_by(&:accuracy) + end + + # Returns the number of addresses contained in this result set. + # + # @return [Integer] the number of addresses + def size + @addresses.size + end + end +end diff --git a/lib/geocodio/client.rb b/lib/geocodio/client.rb new file mode 100644 index 0000000..ce0012d --- /dev/null +++ b/lib/geocodio/client.rb @@ -0,0 +1,110 @@ +require 'net/http' +require 'json' +require 'cgi' + +require 'geocodio/client/error' +require 'geocodio/client/response' + +module Geocodio + class Client + CONTENT_TYPE = 'application/json' + METHODS = { + :get => Net::HTTP::Get, + :post => Net::HTTP::Post, + :put => Net::HTTP::Put, + :delete => Net::HTTP::Delete + } + HOST = 'api.geocod.io' + BASE_PATH = '/v1' + PORT = 80 + + def initialize(api_key = ENV['GEOCODIO_API_KEY']) + @api_key = api_key + end + + # Geocodes one or more addresses. If one address is specified, a GET request + # is submitted to http://api.geocod.io/v1/geocode. Multiple addresses will + # instead submit a POST request. + # + # @param addresses [Array] one or more String addresses + # @return [Geocodio::Address, Array] One or more Address Sets + def geocode(*addresses) + addresses = addresses.first if addresses.first.is_a?(Array) + + if addresses.size < 1 + raise ArgumentError, 'You must provide at least one address to geocode.' + elsif addresses.size == 1 + geocode_single(addresses.first) + else + geocode_batch(addresses) + end + end + + # Sends a GET request to http://api.geocod.io/v1/parse to correctly dissect + # an address into individual parts. As this endpoint does not do any + # geocoding, parts missing from the passed address will be missing from the + # result. + # + # @param address [String] the full or partial address to parse + # @return [Geocodio::Address] a parsed and formatted Address + def parse(address) + Address.new get('/parse', q: address).body + end + + private + + METHODS.each do |method, _| + define_method(method) do |path, params = {}, options = {}| + request method, path, options.merge(params: params) + end + end + + def geocode_single(address) + response = get '/geocode', q: address + results = response.body['results'] + query = response.body['input']['formatted_address'] + addresses = results.map { |result| Address.new(result) } + + AddressSet.new(query, *addresses) + end + + def geocode_batch(addresses) + response = post '/geocode', {}, body: addresses + result_sets = response.body['results'] + + result_sets.map do |result_set| + query = result_set['response']['input']['formatted_address'] + results = result_set['response']['results'] + addresses = results.map { |result| Address.new(result) } + + AddressSet.new(query, *addresses) + end + end + + def request(method, path, options) + path += "?api_key=#{@api_key}" + + if params = options[:params] and !params.empty? + q = params.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" } + path += "&#{q.join('&')}" + end + + req = METHODS[method].new(BASE_PATH + path, 'Accept' => CONTENT_TYPE) + + if options.key?(:body) + req['Content-Type'] = CONTENT_TYPE + req.body = options[:body] ? JSON.dump(options[:body]) : '' + end + + http = Net::HTTP.new HOST, PORT + res = http.start { http.request(req) } + + case res + when Net::HTTPSuccess + return Response.new(res) + else + raise Error, res + end + end + end +end diff --git a/lib/geocodio/client/error.rb b/lib/geocodio/client/error.rb new file mode 100644 index 0000000..a27618c --- /dev/null +++ b/lib/geocodio/client/error.rb @@ -0,0 +1,15 @@ +module Geocodio + class Client + class Error < RuntimeError + attr_reader :response + + def initialize(response) + @response, @json = response, JSON.parse(response.body) + end + + def body() @json end + def message() body['error'] end + alias :error :message + end + end +end diff --git a/lib/geocodio/client/response.rb b/lib/geocodio/client/response.rb new file mode 100644 index 0000000..53aa682 --- /dev/null +++ b/lib/geocodio/client/response.rb @@ -0,0 +1,13 @@ +require 'json' + +module Geocodio + class Client + class Response + attr_reader :body + + def initialize(response) + @body = JSON.parse(response.body) + end + end + end +end diff --git a/lib/geocodio/version.rb b/lib/geocodio/version.rb new file mode 100644 index 0000000..39778b7 --- /dev/null +++ b/lib/geocodio/version.rb @@ -0,0 +1,13 @@ +module Geocodio + class Version + MAJOR = 1 + MINOR = 0 + PATCH = 0 + + def self.to_s + [MAJOR, MINOR, PATCH].join('.') + end + end + + VERSION = Version.to_s +end diff --git a/spec/address_set_spec.rb b/spec/address_set_spec.rb new file mode 100644 index 0000000..4b41606 --- /dev/null +++ b/spec/address_set_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Geocodio::AddressSet do + let(:geocodio) { Geocodio::Client.new } + let(:addresses) do + [ + '1 Infinite Loop Cupertino CA 95014', + '54 West Colorado Boulevard Pasadena CA 91105', + '826 Howard Street San Francisco CA 94103' + ] + end + + subject(:address_set) do + VCR.use_cassette('batch_geocode') do + geocodio.geocode(*addresses).last + end + end + + it 'has a size' do + expect(address_set.size).to eq(2) + end + + it 'has a best' do + expect(address_set.best).to be_a(Geocodio::Address) + expect(address_set.best.accuracy).to eq(1) + end + + it 'references the original query' do + expect(address_set.query).to eq('826 Howard St, San Francisco CA, 94103') + end +end diff --git a/spec/address_spec.rb b/spec/address_spec.rb new file mode 100644 index 0000000..52646bc --- /dev/null +++ b/spec/address_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +describe Geocodio::Address do + let(:geocodio) { Geocodio::Client.new } + + context 'when parsed' do + subject(:address) do + VCR.use_cassette('parse') do + geocodio.parse('1 Infinite Loop Cupertino CA 95014') + end + end + + it 'has a number' do + expect(address.number).to eq('1') + end + + it 'has a street' do + expect(address.street).to eq('Infinite') + end + + it 'has a suffix' do + expect(address.suffix).to eq('Loop') + end + + it 'has a city' do + expect(address.city).to eq('Cupertino') + end + + it 'has a state' do + expect(address.state).to eq('CA') + end + + it 'has a zip' do + expect(address.zip).to eq('95014') + end + + it 'does not have a latitude' do + expect(address.latitude).to be_nil + expect(address.lat).to be_nil + end + + it 'does not have a longitude' do + expect(address.longitude).to be_nil + expect(address.lng).to be_nil + end + + it 'does not have an accuracy' do + expect(address.accuracy).to be_nil + end + end + + context 'when geocoded' do + subject(:address) do + VCR.use_cassette('geocode') do + geocodio.geocode('1 Infinite Loop Cupertino CA 95014').best + end + end + + it 'has a number' do + expect(address.number).to eq('1') + end + + it 'has a street' do + expect(address.street).to eq('Infinite') + end + + it 'has a suffix' do + expect(address.suffix).to eq('Loop') + end + + it 'has a city' do + expect(address.city).to eq('Monta Vista') + end + + it 'has a state' do + expect(address.state).to eq('CA') + end + + it 'has a zip' do + expect(address.zip).to eq('95014') + end + + it 'has a latitude' do + expect(address.latitude).to eq(37.331669) + expect(address.lat).to eq(37.331669) + end + + it 'has a longitude' do + expect(address.longitude).to eq(-122.03074) + expect(address.lng).to eq(-122.03074) + end + + it 'has an accuracy' do + expect(address.accuracy).to eq(1) + end + end +end diff --git a/spec/client_spec.rb b/spec/client_spec.rb new file mode 100644 index 0000000..49839ea --- /dev/null +++ b/spec/client_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe Geocodio::Client do + let(:geocodio) { Geocodio::Client.new } + let(:address) { '1 Infinite Loop Cupertino CA 95014' } + + it 'requires an API key' do + VCR.use_cassette('invalid_key') do + expect { geocodio.geocode(address) }.to raise_error(Geocodio::Client::Error) + end + end + + it 'parses an address into components' do + VCR.use_cassette('parse') do + result = geocodio.parse(address) + + expect(result).to be_a(Geocodio::Address) + end + end + + it 'geocodes a single address' do + VCR.use_cassette('geocode') do + addresses = geocodio.geocode(address) + + expect(addresses.size).to eq(1) + expect(addresses).to be_a(Geocodio::AddressSet) + end + end + + it 'geocodes multiple addresses' do + VCR.use_cassette('batch_geocode') do + addresses = [ + '1 Infinite Loop Cupertino CA 95014', + '54 West Colorado Boulevard Pasadena CA 91105', + '826 Howard Street San Francisco CA 94103' + ] + + addresses = geocodio.geocode(*addresses) + + expect(addresses.size).to eq(3) + addresses.each { |address| expect(address).to be_a(Geocodio::AddressSet) } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..e112abb --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,19 @@ +require 'geocodio' +require 'webmock/rspec' +require 'vcr' + +ENV['GEOCODIO_API_KEY'] ||= 'secret_api_key' + +VCR.configure do |c| + c.cassette_library_dir = 'spec/vcr_cassettes' + c.hook_into :webmock + c.filter_sensitive_data('secret_api_key') { ENV['GEOCODIO_API_KEY'] } +end + +RSpec.configure do |config| + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = 'random' +end diff --git a/spec/vcr_cassettes/batch_geocode.yml b/spec/vcr_cassettes/batch_geocode.yml new file mode 100644 index 0000000..683339f --- /dev/null +++ b/spec/vcr_cassettes/batch_geocode.yml @@ -0,0 +1,57 @@ +--- +http_interactions: +- request: + method: post + uri: http://api.geocod.io/v1/geocode?api_key=secret_api_key + body: + encoding: UTF-8 + string: "[\"1 Infinite Loop Cupertino CA 95014\",\"54 West Colorado Boulevard + Pasadena CA 91105\",\"826 Howard Street San Francisco CA 94103\"]" + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + Content-Type: + - application/json + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 21 Jan 2014 20:08:06 GMT + Server: + - Apache + X-Powered-By: + - PHP/5.4.24 + Cache-Control: + - no-cache + Access-Control-Allow-Origin: + - "*" + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: "{\"results\":[{\"query\":\"1 Infinite Loop Cupertino CA 95014\",\"response\":{\"input\":{\"address_components\":{\"number\":\"1\",\"street\":\"Infinite\",\"suffix\":\"Loop\",\"city\":\"Cupertino\",\"state\":\"CA\",\"zip\":\"95014\"},\"formatted_address\":\"1 + Infinite Loop, Cupertino CA, 95014\"},\"results\":[{\"address_components\":{\"number\":\"1\",\"street\":\"Infinite\",\"suffix\":\"Loop\",\"city\":\"Monta + Vista\",\"state\":\"CA\",\"zip\":\"95014\"},\"formatted_address\":\"1 Infinite + Loop, Monta Vista CA, 95014\",\"location\":{\"lat\":37.331669,\"lng\":-122.03074},\"accuracy\":1}]}},{\"query\":\"54 + West Colorado Boulevard Pasadena CA 91105\",\"response\":{\"input\":{\"address_components\":{\"number\":\"54\",\"predirectional\":\"W\",\"street\":\"Colorado\",\"suffix\":\"Blvd\",\"city\":\"Pasadena\",\"state\":\"CA\",\"zip\":\"91105\"},\"formatted_address\":\"54 + W Colorado Blvd, Pasadena CA, 91105\"},\"results\":[{\"address_components\":{\"number\":\"54\",\"predirectional\":\"W\",\"street\":\"Colorado\",\"suffix\":\"Blvd\",\"city\":\"Pasadena\",\"state\":\"CA\",\"zip\":\"91105\"},\"formatted_address\":\"54 + W Colorado Blvd, Pasadena CA, 91105\",\"location\":{\"lat\":34.145760590909,\"lng\":-118.15204363636},\"accuracy\":1},{\"address_components\":{\"number\":\"54\",\"predirectional\":\"W\",\"street\":\"Colorado\",\"suffix\":\"Blvd\",\"city\":\"Pasadena\",\"state\":\"CA\",\"zip\":\"91105\"},\"formatted_address\":\"54 + W Colorado Blvd, Pasadena CA, 91105\",\"location\":{\"lat\":34.1457634625,\"lng\":-118.15170725},\"accuracy\":0.8}]}},{\"query\":\"826 + Howard Street San Francisco CA 94103\",\"response\":{\"input\":{\"address_components\":{\"number\":\"826\",\"street\":\"Howard\",\"suffix\":\"St\",\"city\":\"San + Francisco\",\"state\":\"CA\",\"zip\":\"94103\"},\"formatted_address\":\"826 + Howard St, San Francisco CA, 94103\"},\"results\":[{\"address_components\":{\"number\":\"826\",\"street\":\"Howard\",\"suffix\":\"St\",\"city\":\"San + Francisco\",\"state\":\"CA\",\"zip\":\"94103\"},\"formatted_address\":\"826 + Howard St, San Francisco CA, 94103\",\"location\":{\"lat\":37.7815,\"lng\":-122.404933},\"accuracy\":1},{\"address_components\":{\"number\":\"826\",\"street\":\"Howard\",\"suffix\":\"St\",\"city\":\"San + Francisco\",\"state\":\"CA\",\"zip\":\"94103\"},\"formatted_address\":\"826 + Howard St, San Francisco CA, 94103\",\"location\":{\"lat\":37.781552539474,\"lng\":-122.40486656579},\"accuracy\":0.8}]}}]}" + http_version: + recorded_at: Tue, 21 Jan 2014 20:08:08 GMT +recorded_with: VCR 2.8.0 diff --git a/spec/vcr_cassettes/geocode.yml b/spec/vcr_cassettes/geocode.yml new file mode 100644 index 0000000..81d4f55 --- /dev/null +++ b/spec/vcr_cassettes/geocode.yml @@ -0,0 +1,43 @@ +--- +http_interactions: +- request: + method: get + uri: http://api.geocod.io/v1/geocode?api_key=secret_api_key&q=1%20Infinite%20Loop%20Cupertino%20CA%2095014 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 21 Jan 2014 19:55:27 GMT + Server: + - Apache + X-Powered-By: + - PHP/5.4.24 + Cache-Control: + - no-cache + Access-Control-Allow-Origin: + - "*" + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: "{\"input\":{\"address_components\":{\"number\":\"1\",\"street\":\"Infinite\",\"suffix\":\"Loop\",\"city\":\"Cupertino\",\"state\":\"CA\",\"zip\":\"95014\"},\"formatted_address\":\"1 + Infinite Loop, Cupertino CA, 95014\"},\"results\":[{\"address_components\":{\"number\":\"1\",\"street\":\"Infinite\",\"suffix\":\"Loop\",\"city\":\"Monta + Vista\",\"state\":\"CA\",\"zip\":\"95014\"},\"formatted_address\":\"1 Infinite + Loop, Monta Vista CA, 95014\",\"location\":{\"lat\":37.331669,\"lng\":-122.03074},\"accuracy\":1}]}" + http_version: + recorded_at: Tue, 21 Jan 2014 19:55:28 GMT +recorded_with: VCR 2.8.0 diff --git a/spec/vcr_cassettes/invalid_key.yml b/spec/vcr_cassettes/invalid_key.yml new file mode 100644 index 0000000..7c3e85f --- /dev/null +++ b/spec/vcr_cassettes/invalid_key.yml @@ -0,0 +1,40 @@ +--- +http_interactions: +- request: + method: get + uri: http://api.geocod.io/v1/geocode?api_key=secret_api_key&q=1%20Infinite%20Loop%20Cupertino%20CA%2095014 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + response: + status: + code: 403 + message: Forbidden + headers: + Date: + - Tue, 21 Jan 2014 20:47:20 GMT + Server: + - Apache + X-Powered-By: + - PHP/5.4.24 + Cache-Control: + - no-cache + Access-Control-Allow-Origin: + - "*" + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: "{\"error\":\"Invalid API key\"}" + http_version: + recorded_at: Tue, 21 Jan 2014 20:47:21 GMT +recorded_with: VCR 2.8.0 diff --git a/spec/vcr_cassettes/parse.yml b/spec/vcr_cassettes/parse.yml new file mode 100644 index 0000000..177f85c --- /dev/null +++ b/spec/vcr_cassettes/parse.yml @@ -0,0 +1,41 @@ +--- +http_interactions: +- request: + method: get + uri: http://api.geocod.io/v1/parse?api_key=secret_api_key&q=1%20Infinite%20Loop%20Cupertino%20CA%2095014 + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 21 Jan 2014 19:39:24 GMT + Server: + - Apache + X-Powered-By: + - PHP/5.4.24 + Cache-Control: + - no-cache + Access-Control-Allow-Origin: + - "*" + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: "{\"address_components\":{\"number\":\"1\",\"street\":\"Infinite\",\"suffix\":\"Loop\",\"city\":\"Cupertino\",\"state\":\"CA\",\"zip\":\"95014\"},\"formatted_address\":\"1 + Infinite Loop, Cupertino CA, 95014\"}" + http_version: + recorded_at: Tue, 21 Jan 2014 19:39:25 GMT +recorded_with: VCR 2.8.0