14. Simple way to connect your Rails application to ‘Google Places’

In post 12, we covered how to create custom API clients to fetch data. It may be a good pre-requisite for this post if you’re not familiar with generating api clients.

In this post we will look to explore the Google Places API and integrate it with our base client in Rails.

To get started, you will need to get an API key which you can do here.

Once you’ve created your project, you will be presented with a screen looking like this:

From this screen, we can select the ‘Places API’ as that’s what we will look to integrate in this project.

You can navigate to the credentials page in order to get your API keys.

Click ‘Create credentials’ and select an ‘API key’.

Copy the key, you could even export it to use for use later:

export GOOGLE_API_KEY=<your_key_here>

You could also apply it your heroku environment variables if using that, or add it to your rails credentials.

Before you can use the google API’s, you will also have to enable billing: https://console.cloud.google.com/project/_/billing/enable

Once this is done, you can test it using Postman as before. From the google documentation, there’s a ‘Find places’ example that we can test:

https://maps.googleapis.com/maps/api/place/findplacefromtext/json?input=Museum%20of%20Contemporary%20Art%20Australia&inputtype=textquery&fields=photos,formatted_address,name,rating,opening_hours,geometry&key=YOUR_API_KEY

Edit YOUR_API_KEY with your copied API key and enter it to Postman, you should have something like this:

If you get a 200 OK response, we can continue to implementing the client in Ruby, otherwise check what the errors are and fix them before continuing.

The next thing you will want to do is make sure you have these 2 gems in your Gemfile:

gem 'google_places'

Google places gem is a Ruby wrapper using HttParty. This gem will make the integration a lot simpler and easier for us.

Here I will quickly cover the 2 base classes that we need, which are also referenced and explained in post 12.

Create these 2 files:

app/clients/resty/client.rb
app/clients/resty/response.rb

And enter the following content:

client.rb:

# frozen_string_literal: true

module Resty
  module Client
    def initialize(options = {})
      @base_url = options[:base_url]
    end

    def get(path, options = {})
      Response.new(RestClient.get(build_url(path), get_options(options)))
    end

    def post(path, payload, options = {})
      Response.new(RestClient.post(build_url(path), payload, options))
    end

    def put(path, payload, options = {})
      Response.new(RestClient.put(build_url(path), payload, options))
    end

    def patch(path, payload, options = {})
      Response.new(RestClient.patch(build_url(path), payload, options))
    end

    def delete(path, options = {})
      Response.new(RestClient.post(build_url(path), options))
    end

    private

    attr_reader :base_url

    def build_url(path)
      [base_url, path].join
    end

    def get_options(options)
      default_get_options.deep_merge(options.deep_symbolize_keys)
    end

    def default_get_options
      {
        accept: :json
      }
    end
  end
end

response.rb:

# frozen_string_literal: true

module Resty
  class Response
    delegate :code, to: :response

    def initialize(response)
      @response = response
    end

    def content
      Oj.strict_load(response.body)
    end

    private

    attr_reader :response
  end
end

And now for the main part

create a file for your google client:

app/clients/google_place_client.rb

Add the following content:

# frozen_string_literal: true

class GooglePlaceClient
  def self.find(name, options = {})
    new(options).find(name)
  end

  def initialize(options = {})
    @options = options
  end

  def find(name)
    list = client.spots_by_query("#{name} office", options)
    place = list.find { |item| item.permanently_closed.nil? && item.types.include?('establishment') }
    return nil unless place

    client.spot(place.place_id)
  end

  private

  attr_reader :options

  def client
    @client ||= GooglePlaces::Client.new(api_key)
  end

  def api_key
    ENV['GOOGLE_API_KEY']
  end
end

Let’s go through what we have here

def client
  @client ||= GooglePlaces::Client.new(api_key)
end

We initialise the google client gem and make it available for use.

def api_key
  ENV['GOOGLE_API_KEY']
end

Make sure you have the google api key exported in your environment. You can do this with:

export GOOGLE_API_KEY=<YOUR_KEY_HERE>

You can also verify it using env | grep GOOGLE_API_KEY and just make sure there’s the environment variable returned.

The main function call is:

def find(name)
  list = client.spots_by_query("#{name} office", options)
  place = list.find { |item| item.permanently_closed.nil? && item.types.include?('establishment') }
  return nil unless place

  client.spot(place.place_id)
end

Let’s go through this in rails console to give you more insight with what this does. Open terminal and type:

rails c

client = GooglePlaces::Client.new(ENV['GOOGLE_API_KEY'])

spots = client.spots_by_query("facebook london")

spots
 => [#<GooglePlaces::Spot:0x00007f909ddedb88 @json_result_object={"business_status"=>"OPERATIONAL", "formatted_address"=>"1 Rathbone Square, Fitzrovia, London W1T 1FB, United Kingdom", "geometry"=>{"location"=>{"lat"=>51.5168049, "lng"=>-0.1340803}, "viewport"=>{"northeast"=>{"lat"=>51.51841112989272, "lng"=>-0.1328664201072778}, "southwest"=>{"lat"=>51.51571147010728, "lng"=>-0.1355660798927222}}}, "icon"=>"https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png", "name"=>"Facebook", "photos"=>[{"height"=>2592, "html_attributions"=>["<a href=\"https://maps.google.com/maps/contrib/112620674984930997750\">A Google User</a>"], "photo_reference"=>"CkQ0AAAAUGK72frVGTZz99VqIutWhfowCm2KVRrrmdrkFqA0TH-CH2f23PAfoMhO4qhGsPzdpZjejhvHAJWA0vRHhDqOwRIQYgOsb46QUjCFEY-XwHYUdhoUGKsscOnA0bVG2sRaM95v3Wi02QY", "width"=>4608}], "place_id"=>"ChIJP0tb3MwEdkgRpJ7l5ftXN2g", "plus_code"=>{"compound_code"=>"GV88+P9 London", "global_code"=>"9C3XGV88+P9"}, "rating"=>4.2, "reference"=>"ChIJP0tb3MwEdkgRpJ7l5ftXN2g", "types"=>["point_of_interest", "establishment"], "user_ratings_total"=>790}, @reference="ChIJP0tb3MwEdkgRpJ7l5ftXN2g", @place_id="ChIJP0tb3MwEdkgRpJ7l5ftXN2g", @vicinity=nil, @lat=51.5168049, @lng=-0.1340803, @viewport={"northeast"=>{"lat"=>51.51841112989272, "lng"=>-0.1328664201072778}, "southwest"=>{"lat"=>51.51571147010728, "lng"=>-0.1355660798927222}}, @name="Facebook", @icon="https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png", @types=["point_of_interest", "establishment"], @id=nil, @formatted_phone_number=nil, @international_phone_number=nil, @formatted_address="1 Rathbone Square, Fitzrovia, London W1T 1FB, United Kingdom", @address_components=nil, @street_number=nil, @street=nil, @city=nil, @region=nil, @postal_code=nil, @country=nil, @rating=4.2, @price_level=nil, @opening_hours=nil, @url=nil, @cid=0, @website=nil, @zagat_reviewed=nil, @zagat_selected=nil, @aspects=[], @review_summary=nil, @photos=[#<GooglePlaces::Photo:0x00007f909dded840 @width=4608, @height=2592, @photo_reference="CkQ0AAAAUGK72frVGTZz99VqIutWhfowCm2KVRrrmdrkFqA0TH-CH2f23PAfoMhO4qhGsPzdpZjejhvHAJWA0vRHhDqOwRIQYgOsb46QUjCFEY-XwHYUdhoUGKsscOnA0bVG2sRaM95v3Wi02QY", @html_attributions=["<a href=\"https://maps.google.com/maps/contrib/112620674984930997750\">A Google User</a>"], @api_key="AIzaSyBBBiKn4T7rFsWIGnyzmffpvuKuIIzb3d0">], @reviews=[], @nextpagetoken=nil, @events=[], @utc_offset=nil, @permanently_closed=nil>] 

client.spot(spots.first.place_id)                                                                                                                                      
 => #<GooglePlaces::Spot:0x00007f90a2457c90 @json_result_object={"address_components"=>[{"long_name"=>"1", "short_name"=>"1", "types"=>["street_number"]}, {"long_name"=>"Rathbone Sq
uare", "short_name"=>"Rathbone Square", "types"=>["route"]}, {"long_name"=>"Fitzrovia", "short_name"=>"Fitzrovia", "types"=>["neighborhood", "political"]}, {"long_name"=>"London", "
short_name"=>"London", "types"=>["postal_town"]}, {"long_name"=>"Greater London", "short_name"=>"Greater London", "types"=>["administrative_area_level_2", "political"]}, {"long_name
"=>"England", "short_name"=>"England", "types"=>["administrative_area_level_1", "political"]}, {"long_name"=>"United Kingdom", "short_name"=>"GB", "types"=>["country", "political"]}
, {"long_name"=>"W1T 1FB", "short_name"=>"W1T 1FB", "types"=>["postal_code"]}], "adr_address"=>"<span class=\"street-address\">1 Rathbone Square</span>, <span class=\"locality\">Lon
don</span> <span class=\"postal-code\">W1T 1FB</span>, <span class=\"country-name\">UK</span>", "business_status"=>"OPERATIONAL", "formatted_address"=>"1 Rathbone Square, Fitzrovia,
 London W1T 1FB, UK", "formatted_phone_number"=>"020 3386 6000", "geometry"=>{"location"=>{"lat"=>51.5168049, "lng"=>-0.1340803}, "viewport"=>{"northeast"=>{"lat"=>51.5184102802915,
 "lng"=>-0.132867269708498}, "southwest"=>{"lat"=>51.5157123197085, "lng"=>-0.1355652302915021}}}, "icon"=>"https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png
", "international_phone_number"=>"+44 20 3386 6000", "name"=>"Facebook", "photos"=>[{"height"=>2592, "html_attributions"=>["<a href=\"https://maps.google.com/maps/contrib/1126206749
84930997750\">Salvatore Parisi</a>"], "photo_reference"=>"CkQ0AAAALVlzdbt82Z6z4NJvRfH_2h2hBpI0QfcGhPPituk_dzYgg5eAuSeoM3ynKUk3K1DNTSAV0obD97KZgeN935Tp8BIQf7-d49vwa3FU2d4veLrjjxoUMT_
b4r4G-ad77eAqgG1vdK6rbAc", "width"=>4608}, ......

So we can initialise the google places gem with just our API key, and we can start using it out of the box.

You can check the documentation for these two functions:

  • spots_by_query
  • spot

Essentially though, spots_by_query will search google ‘spots’ with your query string and/or options. This returns a lot of data, but it’s not in depth. Then if you require further information about particular spot/place, you can query the spot and get detailed information about that place.

So let’s try this in practice in rails console:

rails c

GooglePlaceClient.find('Facebook')                                                                                                                                      
 => #<GooglePlaces::Spot:0x00007f90a15608b8 @json_result_object={"address_components"=>[{"long_name"=>"1", "short_name"=>"1", "types"=>["street_number"]}, {"long_name"=>"Rathbone S$
uare", "short_name"=>"Rathbone Square", "types"=>["route"]}, {"long_name"=>"Fitzrovia", "short_name"=>"Fitzrovia", "types"=>["neighborhood", "political"]}, {"long_name"=>"London", $
short_name"=>"London", "types"=>["postal_town"]}, {"long_name"=>"Greater London", "short_name"=>"Greater London", "types"=>["administrative_area_level_2", "political"]}, {"long_nam$
"=>"England", "short_name"=>"England", "types"=>["administrative_area_level_1", "political"]}, {"long_name"=>"United Kingdom", "short_name"=>"GB", "types"=>["country", "political"]$
, {"long_name"=>"W1T 1FB", "short_name"=>"W1T 1FB", "types"=>["postal_code"]}], "adr_address"=>"<span class=\"street-address\">1 Rathbone Square</span>, <span class=\"locality\">Lo$
don</span> <span class=\"postal-code\">W1T 1FB</span>, <span class=\"country-name\">UK</span>", "business_status"=>"OPERATIONAL", "formatted_address"=>"1 Rathbone Square, Fitzrovia$
 London W1T 1FB, UK", "formatted_phone_number"=>"020 3386 6000", "geometry"=>{"location"=>{"lat"=>51.5168049, "lng"=>-0.1340803}, "viewport"=>{"northeast"=>{"lat"=>51.5184102802915$
 "lng"=>-0.132867269708498}, "southwest"=>{"lat"=>51.5157123197085, "lng"=>-0.1355652302915021}}}, "icon"=>"https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png
", "international_phone_number"=>"+44 20 3386 6000", "name"=>"Facebook", .....

And that was it, it works!