<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://jamesbedont.com/</id>
    <title>James Bedont's Writing</title>
    <updated>2022-04-04T14:27:54.102Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>James Bedont</name>
        <email/>
        <uri>https://jamesbedont.com</uri>
    </author>
    <link rel="alternate" href="https://jamesbedont.com/"/>
    <subtitle>Feed of all the articles written on jamesbedont.com</subtitle>
    <logo>https://jamesbedont.com/headshot.webp</logo>
    <icon>https://jamesbedont.com/favicon.webp</icon>
    <rights>All rights reserved 2022, James Bedont</rights>
    <category term="Software Development"/>
    <entry>
        <title type="html"><![CDATA[Installing Ruby on Rails Leveraging Ubuntu on Windows 10]]></title>
        <id>https://jamesbedont.com/installing-ruby-on-rails-leveraging-ubuntu-on-windows-10</id>
        <link href="https://jamesbedont.com/installing-ruby-on-rails-leveraging-ubuntu-on-windows-10"/>
        <updated>2017-07-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Installing Ruby on Rails Leveraging Ubuntu on Windows 10]]></summary>
        <content type="html"><![CDATA[<h3 id="background">Background</h3>
<p>Its been my experience that getting Ruby and by extension Rails setup on a Windows machine can be difficult and headache inducing. It is also my experience that upon getting it all setup it runs just a bit slower than on Linux or OSX.</p>
<p>Because of the challenges surrounding Ruby on Windows the best solution often involves either <a href="https://docs.docker.com/compose/rails/">Docker</a> or Virtual Machines. Although there is nothing wrong with those solutions another one has arrived; <a href="https://blogs.windows.com/buildingapps/2016/03/30/run-bash-on-ubuntu-on-windows/#psSTUpZWO53T1Ly4.97">Ubuntu on Windows 10</a>.</p>
<p>Put simply Microsoft has developed &quot;Windows Subsystem for Linux (WSL)&quot; which Ubuntu is run on. It allows for files on a Windows machine to be interacted with using Linux command-line tools as well as providing a genuine Ubuntu Linux environment within Windows.</p>
<p>Ruby on Rails can now be installed and ran within a Ubuntu environment while accessing application code located on the Windows File System. This means we can use windows applications like Visual Studio Code to modify the application code but have it be served through the Ubuntu environment.</p>
<h3 id="enabling-ubuntu-on-windows-10">Enabling Ubuntu on Windows 10</h3>
<p>You must be running 64bit Windows 10 with the Anniversary Update or later. And follow the <a href="https://msdn.microsoft.com/en-us/commandline/wsl/install_guide">MSDN installation instructions</a></p>
<h3 id="installing-ruby-on-rails">Installing Ruby on Rails</h3>
<p>After the restart you should be able to press the start menu type in &quot;ubuntu&quot; and see the following new program. Clicking on it opens up a bash shell.</p>
<p><img src="/windowsrails/ubuntuapp.webp" alt="Ubuntu on Windows"></p>
<p>It is within Bash where we will be installing Ruby on Rails. Because this isn&#39;t a post about how to install Ruby on Rails on Ubuntu I will link to a <a href="https://gorails.com/setup/ubuntu/14.04">great guide on how to do just that</a>. Since we are now in a Ubuntu Bash Shell we can treat it as such and follow any Ubuntu resources.</p>
<h3 id="conceptual-overview">Conceptual Overview</h3>
<p>Now that we have enabled Ubuntu on Windows and installing Ruby on Rails in that environment we are ready to generate a new project.</p>
<p>It&#39;s now when I would like to point out that <a href="https://blogs.msdn.microsoft.com/commandline/2016/11/17/do-not-change-linux-files-using-windows-apps-and-tools">using Windows apps and tools to change Linux files is a bad idea and can lead to data loss</a>. Instead we will be using Linux to modify Windows files which is completely fine and <a href="https://blogs.msdn.microsoft.com/commandline/2016/11/17/do-not-change-linux-files-using-windows-apps-and-tools/#comment-25677">within the intended use of WSL</a>.</p>
<p>Within our Bash shell the Windows File System can be found at <code>/mnt/</code>. Below is a comparison of <code>/mnt/c</code> and the C drive contents navigated to from the file explorer. Notice that they are identical; we are now navigating into the Windows File System from Linux.</p>
<p><img src="/windowsrails/mntcompare.webp" alt="mnt/c and C drive compare"></p>
<p>So long as we generate the Rails project within the Windows File System (<code>/mnt</code>) we are inline with intended use and at no risk of data loss. Not to mention we can then modify those files as we would for any other Windows files.</p>
<h3 id="generating-a-new-rails-app">Generating a New Rails App</h3>
<p>I have found that the best thing to do first is create a <a href="https://en.wikipedia.org/wiki/Symbolic_link">symlink</a> to our Windows development folder. For this post I have created the following directory within windows <code>E:\documents\ruby_projects</code> which from within the Bash shell is <code>/mnt/e/documents/ruby_projects</code>.</p>
<p><img src="/windowsrails/symlink.webp" alt="creating a symlink"></p>
<p><code>$ ln -s TARGET LINK_NAME</code></p>
<ol>
<li>change directories into <code>~/ruby_projects</code> and see files that are actually located at <code>/mnt/e/documents/ruby_projects</code></li>
<li><code>$ rails new blog</code> to generate a new Rails app named &quot;blog&quot;</li>
<li>CD into the new blog directory and do <code>$ rails server</code> which will start a server listening on port 3000</li>
<li>Navigate to <code>localhost:3000</code> within a Windows web browser.</li>
</ol>
<p><img src="/windowsrails/rails_setup_success.webp" alt="rails setup success image"></p>
<p>Success! Note how that message is saying Ruby is running within Linux.</p>
<p>Now you can open up your favorite text editor/IDE in Windows and edit the files at <code>E:\documents\ruby_projects</code> just like normal. The changes will be seen by the Rails server running on Ubuntu and reflected on localhost.</p>
]]></content>
        <author>
            <name>James Bedont</name>
            <email/>
            <uri>https://jamesbedont.com</uri>
        </author>
        <published>2017-07-23T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Export a Heroku Postgres table to a csv file]]></title>
        <id>https://jamesbedont.com/export-a-heroku-postgres-table-to-a-csv-file</id>
        <link href="https://jamesbedont.com/export-a-heroku-postgres-table-to-a-csv-file"/>
        <updated>2016-08-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Export a Heroku Postgres table to a csv file]]></summary>
        <content type="html"><![CDATA[<p>This post will be a walk through of how to export a Heroku PostgreSQL database, table, or query to a <code>.csv</code> file. Its worth noting that if the goal is to export Heroku&#39;s database and import it into another (like localhost) there is documentation for that in <a href="https://devcenter.heroku.com/articles/heroku-postgres-import-export">Heroku&#39;s dev center</a>.</p>
<p>Two methods of accessing a Heroku hosted Postgres database will be covered in this post.</p>
<ol>
<li>Utilizing the <a href="https://toolbelt.heroku.com/">Heroku CLI</a> to connect to the postgres server. Then issuing commands via the console which will require postgres be installed on local machine.</li>
<li>Utilizing <a href="https://www.pgadmin.org/">pgAdmin&#39;s GUI</a> which does not require that postgres be installed locally.</li>
</ol>
<p><strong>Database Credentials</strong></p>
<p>Heroku has a page that shows all postgres databases associated with an account located at <a href="https://postgres.heroku.com/databases">https://postgres.heroku.com/databases</a>. Going to this page and navigating to the appropriate app will yield connection information to access the database. Different parts of this connection page will be used for each method of exporting a CSV.</p>
<h2 id="heroku-cli">Heroku CLI</h2>
<p>After looking up the database information copy the value for the <code>Psql</code> connection setting and paste it into the terminal. Being logged into the Heroku CLI is required to execute the <code>Psql</code> command <code>$ heroku login</code>.</p>
<pre><code class="language-shell">heroku pg:psql --app amazing-wetland-1234 DATABASE
</code></pre>
<p>At this point it&#39;s now possible to run queries on the database. To start enter <code>$ \dt</code> which is the equivalent of a MySQL <code>$ show tables</code>.</p>
<pre><code class="language-none">List of relations
Schema | Name | Type | Owner
--------+-------------------+-------+----------------
public | schema_migrations | table | &lt;redacted&gt;
public | posts | table | &lt;redacted&gt;
public | users | table | &lt;redacted&gt;
(3 rows)
</code></pre>
<p>Now that its clear a connection has been made successfully the <a href="https://www.postgresql.org/docs/9.2/static/sql-copy.html">\COPY command</a> can be used.</p>
<p><strong>Export Entire Table</strong></p>
<pre><code class="language-sql">\COPY users TO &#39;~/user_export.csv&#39; WITH (FORMAT csv, DELIMITER &#39;,&#39;,  HEADER true);
</code></pre>
<p>The above command will export all the rows and metadata of the users table to <code>user_exports.csv</code> located at in home directory. the <code>HEADER</code> option means that the table columns are included at the first line of the csv file like below.</p>
<pre><code class="language-none">id,email,encrypted_password,etc,etc
1,example@domain.com,2a10iOnKWh6MGprDXxlS,etc,etc
</code></pre>
<p><strong>Export Specific Query</strong></p>
<pre><code class="language-sql">\COPY (SELECT * FROM shifts WHERE user_id = &#39;7&#39;) TO &#39;~/shifts_export.csv&#39; WITH (FORMAT csv, DELIMITER &#39;,&#39;,  HEADER true);
</code></pre>
<p>The above command will export a csv file in the same way as the previous method. The difference being only the query results are being exported; not an entire table.</p>
<h2 id="pgadmin-gui">pgAdmin GUI</h2>
<p>The first step is to add a new postgres server by going to <code>file &gt; add server...</code>. The <code>Properties</code> and <code>Advanced</code> tabs will be the only ones used on this window.</p>
<p>Under the <code>Properties</code> tab match the information with the Heroku connection settings page of the app discussed at the top of the post.</p>
<p><img src="/pgexport/properties_tab.webp" alt="properties tab settings"></p>
<p>Under the <code>Advanced</code> tab add a <code>DB restriction</code> of the database surronded by single quotes. Heroku hosts many postgres databases on a given server; without adding a restriction all databases will be shown and make it hard to connect to the right one.</p>
<p><img src="/pgexport/advanced_tab.webp" alt="advanced tab settings"></p>
<p>Upon connecting to the server select the database name from the Hierarchical menu on the left and side of the UI. to run a query pull up the <code>Query Tool</code> by going to <code>tools &gt; query tool</code>.</p>
<p><img src="/pgexport/query_tool.webp" alt="query tool"></p>
<p>The highlighted button will run the query then write the results to a file. Any query can be exported to a file and to export an entire table run the following query: <code>SELECT * FROM table</code>. Make sure to append the filename with the <code>.csv</code> extension.</p>
<p><img src="/pgexport/export_settings.webp" alt="export data settings"></p>
]]></content>
        <author>
            <name>James Bedont</name>
            <email/>
            <uri>https://jamesbedont.com</uri>
        </author>
        <published>2016-08-21T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Introduction to Google Maps API Featuring ES6 Promises]]></title>
        <id>https://jamesbedont.com/introduction-to-google-maps-api-featuring-es6-promises</id>
        <link href="https://jamesbedont.com/introduction-to-google-maps-api-featuring-es6-promises"/>
        <updated>2016-03-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction to Google Maps API Featuring ES6 Promises]]></summary>
        <content type="html"><![CDATA[<p>The Google Maps API has a multitude of services that need to be sown together to accomplish anything meaningful and can be initially intimidating. This post will walk through the creation of a location finder app powered by Google Maps and Google Places while shedding light on the most important services and their functionality. ES6 Promises will be used throughout this post; for those reading this without a complete understanding of promises refer to this fantastic blog post: <a href="http://www.html5rocks.com/en/tutorials/es6/promises/">JavaScript Promises There and back again</a> by Jake Archibald. Familiarity with Q or jQuery&#39;s promises will transfer over. For those who haven&#39;t fully grasped asynchronous JavaScript programming I would highly recommend <a href="https://www.youtube.com/watch?v=obaSQBBWZLk">Are you bad, good, better or best with Async JS? JS Tutorial: Callbacks, Promises, Generators</a> by LearnCode.academy on Youtube.</p>
<p><strong>outline</strong></p>
<ol>
<li>Terms explained.</li>
<li>Initial Setup.</li>
<li>Geocode a location.</li>
<li>RadarSearch for Places within a given radius surrounding a location.</li>
<li>GetDetails for each Place found within a radius.</li>
<li>Create map markers for each Place while extending the bounds of the map.</li>
<li>Full Code GIST</li>
</ol>
<h3 id="terms-explained">Terms Explained</h3>
<p><strong>Geocoding</strong> is the process of converting addresses into geographic coordinates. A user will enter a location string (Just like they would into google maps itself) and the geocoder will convert the string into a <code>LatLng</code> object. In future requests a LatLng object will be passed as the location parameter instead of a location string. <a href="https://developers.google.com/maps/documentation/javascript/geocoding#Geocoding">documentation</a></p>
<p><strong>Radar Search</strong> is a search under the Places service and through the <code>radarSearch()</code> method returns a large list of place within a specified search radius. This is the only search that can be done through the places services allowing specification of an exact radius. The downside is twofold: the search does not return rich data about each Place found essentially only returning the <code>place_id</code> and <code>LatLng</code> of each Place; meaning additional API calls are needed to get more data on each Place. Secondly the radar search has a maximum of 50,000 meters or about 30 miles. <a href="https://developers.google.com/maps/documentation/javascript/places#radar_search_requests">documentation</a></p>
<p><strong>Place Detail</strong> is also under the Places service and through the <code>getDetails()</code> method returns detailed information about a specific place such as: complete address, phone number, ratings, reviews, etc. Because the radar search doesn&#39;t return this kind of data a call will need to be made through the GetDetails() method for each Place found by the radar search. <a href="https://developers.google.com/maps/documentation/javascript/places#place_details">documentation</a></p>
<p><strong>Map Marker</strong> is the little red pointer icon used to draw attention to a specific point on the map. Markers should be placed on a map for each Place found during the radar search.</p>
<p><strong>Bounds</strong> represent the viewport of the map object. Users will only see what is inside of the bounds of the map and will have to scroll/zoom to see more. Its important for the Bounds of the map to encompass all the Places/Markers.</p>
<p><strong>InfoWindow</strong> is a bubble looking overlay often connected to a Marker that can display information about the Place represented by said Marker.</p>
<h3 id="initial-setup">Initial Setup</h3>
<p>Firstly visit the <a href="https://developers.google.com/maps/documentation/javascript/">Google Maps JavaScript API Product Page</a> and go through the process of getting an API key.</p>
<p>Next include the following script tag into your HTML file as the last element in the body</p>
<pre><code class="language-html">&lt;script
  src=&quot;https://maps.googleapis.com/maps/api/js?key=YOURKEYHERE&amp;libraries=places&amp;callback=initMap&quot;
  async
  defer
&gt;&lt;/script&gt;
</code></pre>
<p><code>async defer</code> lets the API load asynchronously; Once the API is loaded the function <code>initMap()</code> will be called. Also note that the places library is needed on top of the standard Google Maps API so <code>&amp;libraries=places</code> was included in the url. if more libraries are needed they can be added to the libraries url parameter separated by a comma.</p>
<p>Next add an empty <code>div</code> as a placeholder for where the Google Map will appear on the page.</p>
<pre><code class="language-html">&lt;div id=&quot;map&quot; style=&quot;height:500px;&quot;&gt;&lt;/div&gt;
</code></pre>
<p>Note that a height must be specified on the map div in order to appear correctly on the page. Inline styles may be used in code snippets throughout this post for ease or reading sake but should be moved to an external style sheet in a real environment.</p>
<p>Next create a <code>initMap()</code> function inside your javascript file that will be called when the API is loaded.</p>
<pre><code class="language-javascript">var map, geocoder, service, markers, bounds, infoWindow;
function initMap() {
  // create map inside the #map div
  map = new google.maps.Map(document.getElementById(&#39;map&#39;), {
    // LatLng object used as center property value
    center: { lat: -34.397, lng: 150.644 },
    zoom: 8
  });
  // initialize classes
  geocoder = new google.maps.Geocoder();
  service = new google.maps.places.PlacesService(map);
  infoWindow = new google.maps.InfoWindow();
  // initialize array to hold map markers
  markers = [];
}
</code></pre>
<p>The goal of the initMap function will be to load the Google Map centered upon an initial location and to initialize various classes for future use. Creating the map involves a center property which needs a <code>LatLng</code> object and zoom which needs a int value 1 - 20.</p>
<ul>
<li>1: world</li>
<li>5: Landmass/continent</li>
<li>10 City</li>
<li>15 Streets</li>
<li>20 Buildings</li>
<li>hint: each mouse scroll increments the zoom by 1. If a map is set to zoom 5 and the user scrolls in twice they will be at zoom 8.</li>
</ul>
<p>Navigating to <a href="https://www.google.com/maps">google.com/maps</a> typing in a city and looking at the URL is a good way to find Lat and Lng. For example upon typing in <code>chicago</code> the URL changed to:</p>
<pre><code class="language-none">/maps/place/Chicago,+IL/41.8339037,-87.8722387
</code></pre>
<p>at the end of the URL are the Lat and Lng values for chicago that can be placed into a <code>LatLng</code> object as seen above.</p>
<p>Finally create a user interface and a click event listener:</p>
<pre><code class="language-html">&lt;div class=&quot;container&quot;&gt;
  &lt;div class=&quot;col-md-10&quot;&gt;
    &lt;div class=&quot;form-inline inputs&quot;&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;location&quot;&gt;Starting Location:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;location&quot; value=&quot;UCF&quot; class=&quot;form-control&quot; /&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;searchTerm&quot;&gt;Store:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;searchTerm&quot; value=&quot;WAWA&quot; class=&quot;form-control&quot; /&gt;
      &lt;/div&gt;
      &lt;div class=&quot;form-group&quot;&gt;
        &lt;label for=&quot;miles&quot;&gt;Radius in Miles:&lt;/label&gt;
        &lt;input type=&quot;text&quot; id=&quot;miles&quot; value=&quot;5&quot; class=&quot;form-control&quot; /&gt;
      &lt;/div&gt;
      &lt;button type=&quot;button&quot; id=&quot;find&quot; class=&quot;btn btn-success&quot;&gt;Go!&lt;/button&gt;
    &lt;/div&gt;
    &lt;div id=&quot;map&quot;&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<pre><code class="language-javascript">document.getElementById(&#39;find&#39;).addEventListener(&#39;click&#39;, function() {
  clearLocations();
  // grab user input values
  var searchTerm = document.getElementById(&#39;searchTerm&#39;).value;
  var address = document.getElementById(&#39;location&#39;).value;
  var rad = document.getElementById(&#39;miles&#39;).value;

  //TODO geocode address
});
</code></pre>
<h3 id="geocoding-a-location">Geocoding a Location</h3>
<p>The first thing to do when the user presses the find button is to geocode whatever location they entered into the <code>#location</code> input.</p>
<p><strong>Call geoCodeAddress() within click event</strong></p>
<pre><code class="language-javascript">document.getElementById(&#39;find&#39;).addEventListener(&#39;click&#39;, function() {
  clearLocations();
  // grab user input values
  var searchTerm = document.getElementById(&#39;searchTerm&#39;).value;
  var address = document.getElementById(&#39;location&#39;).value;
  var rad = document.getElementById(&#39;miles&#39;).value;

  // call geocoder passing in address from user input #location
  geoCodeAddress(address)
    // geocoder returns a &quot;then-able&quot; promise with results
    // .then only runs after the promise resolves
    .then(function(results) {
      // when geocoder is done log the results in console
      console.log(results);
    })
    // .catch only runs when promise is rejected
    .catch(function(status) {
      alert(status);
    });
});
</code></pre>
<p><strong>Create geoCodeAddress()</strong></p>
<pre><code class="language-javascript">function geoCodeAddress(address) {
  // return a Promise
  return new Promise(function(resolve, reject) {
    geocoder.geocode({ address: address }, function(results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
        // resolve results upon a successful status
        resolve(results);
      } else {
        // reject status upon un-successful status
        reject(status);
      }
    });
  });
}
</code></pre>
<p>lets say a user inputs <code>UCF</code> within the <code>#location</code> input. the following would be in the console.</p>
<p><strong>Console Results</strong></p>
<pre><code class="language-javascript">address_components: Array[7]
formatted_address: &quot;University of Central Florida Arboretum, Wildflower Loop, Orlando, FL 32826, USA&quot;
geometry: Object
bounds: Object
location: Object
Lat: function()
Lng: function()
partial_match: true
place_id: &quot;ChIJDw-mn1Bo54gRQTEvgGNznb4&quot;
types: Array[1]
</code></pre>
<p>Recalling the outline the next step is to do a radar search based on the location the user gave us. So far we have taken <code>UCF</code> and geocoded it. We now need the <code>latLng</code> object of <code>UCF</code> to feed into the radar search. this object is located at: <code>results.geometry.location</code> found on line 5 of the response above.</p>
<h3 id="radar-search">Radar Search</h3>
<p>We now need to create a radarSearch function that uses the Places service <code>radarSearch()</code> method. the function will return a promise that resolves an array of Place objects within a given radius <code>#miles</code> of the <code>#searchTerm</code>.</p>
<p>On line <code>9</code> we are specifying to google maps we are looking for places with a <code>name</code> of what the user inputs. Note that the following fields could be used depending on the situation.</p>
<blockquote>
<p>keyword (optional) — A term to be matched against all available fields, including but not limited to name, type, and address, as well as customer reviews and other third-party content.</p>
<p>name (optional) — A term to be matched against the names of places. Results will be restricted to those containing the passed name value. Note that a place may have additional names associated with it, beyond its listed name. The API will try to match the passed name value against all of these names; as a result, places may be returned in the results whose listed names do not match the search term, but whose associated names do.</p>
<p>type — Restricts the results to places matching the specified type. Only one type may be specified (if more than one type is provided, all types following the first entry are ignored). See the list of supported types.</p>
</blockquote>
<p><strong>Create radarSearch() function</strong></p>
<pre><code class="language-javascript">// radarSearch() needs a LatLng location, radius, and user&#39;s search term.
function radarSearch(location, rad, searchTerm) {
  // setup request object
  var request = {
    // location is a LatLng object
    location: location,
    // user enters radius in miles be sure to convert to meters before sending request
    radius: rad * 1609.34,
    name: searchTerm
  };
  // return promise
  return new Promise(function(resolve, reject) {
    service.radarSearch(request, function(results, status) {
      if (status == google.maps.places.PlacesServiceStatus.OK) {
        // resolve promise with results on OK status
        resolve(results);
      } else {
        // reject promise otherwise
        reject(status);
      }
    });
  });
}
</code></pre>
<p>We need to call the <code>radarSearch()</code> function <em>after</em> the <code>geoCodeAddress()</code> is finished on line <code>14</code> below. The results of <code>radarSearch()</code> (a promise) needs to be returned allowing us to add another <code>.then</code> to our chain on line <code>17</code>. Anything within line 17&#39;s <code>.then</code> method will be ran after the <code>Geocoder</code> <strong>and</strong> the <code>radarSearch</code> have completed successfully.</p>
<p><strong>Call radarSearch() after geoCodeAddress() resolves</strong></p>
<pre><code class="language-javascript">document.getElementById(&#39;find&#39;).addEventListener(&#39;click&#39;, function() {
  clearLocations();
  // grab user input values
  var searchTerm = document.getElementById(&#39;searchTerm&#39;).value;
  var address = document.getElementById(&#39;location&#39;).value;
  var rad = document.getElementById(&#39;miles&#39;).value;

  // call geocoder passing in address from user input #location
  geoCodeAddress(address)
    // geocoder returns a &quot;then-able&quot; promise with results
    // .then only runs after the promise resolves
    .then(function(results) {
      // when geocoder is done call radarSearch()
      return radarSearch(results.geometry.location, rad, searchTerm);
    })
    // radarSearch returns a then-able promise
    .then(function(results) {
      // upon radarSearch completion log results
      console.log(results);
    })
    // .catch only runs when promise is rejected
    .catch(function(status) {
      alert(status);
    });
});
</code></pre>
<p>Lets say the user inputs <code>UCF</code> within <code>#location</code> input, <code>2</code> within <code>#miles</code> input, and <code>WAWA</code> within <code>#searchTerm</code> input. The following would be logged to the console.</p>
<p><strong>Console Results</strong></p>
<pre><code class="language-javascript">0: Object
geometry: Object
location: Object
id: &quot;16ccdc45848d5b1f8e74b75608687b93573d6dfe&quot;
place_id: &quot;ChIJpZKBVwdp54gRYDqmdK5r6Zg&quot;
1: Object
etc...
etc...
</code></pre>
<p>As you can see an array of objects containing summarized Place data is returned for every place within 2 miles of UCF with the name WAWA. Notice that we don&#39;t actually have much information about the Place itself; we will need to pass the <code>place_id</code> value of each Place into the <code>getDetails()</code> method on the Places Class next.</p>
<h3 id="get-place-details">Get Place Details</h3>
<p>The <code>getDetails</code> method of the Places service returns full details of a Place given a <code>place_id</code>. This service can only be called for one place at a time; meaning we must iterate through the results array sending an api request for each Place object. Lets start by simply defining a <code>findDetail()</code> function that will use the <code>getDetails</code> method.</p>
<p><strong>Create findDetail() function</strong></p>
<pre><code class="language-javascript">// findDetail() takes in a place object
function findDetail(place) {
  // return promise
  return new Promise(function(resolve, reject) {
    // use getDetails method to retrieve Place data via the Place&#39;s place_id property
    service.getDetails({ placeId: place.place_id }, function(place, status) {
      if (status == google.maps.places.PlacesServiceStatus.OK) {
        // upon successful request resolve place
        resolve(place);
      } else {
        // else reject with status
        reject(status);
      }
    });
  });
}
</code></pre>
<p>Now we must call <code>findDetail()</code> for every Place object in <code>results</code> array after the <code>radarSearch</code> is finished on line <code>17</code> below. There we will utilize <code>Promise.all()</code>.</p>
<blockquote>
<p>Promise.all takes an array of promises and creates a promise that fulfills when all of them successfully complete. You get an array of results (whatever the promises fulfilled to) in the same order as the promises you passed in. - Jake Archibald</p>
</blockquote>
<blockquote>
<p>The array.map() method creates a new array with the results of calling a provided function on every element in this array. - MDN</p>
</blockquote>
<p>In reference to line 19 <code>results</code> is an array of Place objects we want to get more detail on. Because <code>findDetail</code> returns a promise if we run the <code>findDetail()</code> function on each element in the array using <code>.map()</code> we will then have an array of promises. <code>Promise.all()</code> will fulfill when all the promises in our new array resolve. Meaning we can send one request to the API at a time but only move on when all of the requests complete!</p>
<p><strong>Call findDetail() after radarSearch is done</strong></p>
<pre><code class="language-javascript">document.getElementById(&#39;find&#39;).addEventListener(&#39;click&#39;, function() {
  clearLocations();
  // grab user input values
  var searchTerm = document.getElementById(&#39;searchTerm&#39;).value;
  var address = document.getElementById(&#39;location&#39;).value;
  var rad = document.getElementById(&#39;miles&#39;).value;

  // call geocoder passing in address from user input #location
  geoCodeAddress(address)
    // geocoder returns a &quot;then-able&quot; promise with results
    // .then only runs after the promise resolves
    .then(function(results) {
      // when geocoder is done call radarSearch()
      return radarSearch(results.geometry.location, rad, searchTerm);
    })
    // radarSearch returns a then-able promise
    .then(function(results) {
      //send each Place to findDetail() building an array of promises with .map()
      return Promise.all(results.map(findDetail));
    })
    .then(function(results) {
      // when .all() is fulfilled log results
      console.log(results);
    })
    // .catch only runs when promise is rejected
    .catch(function(status) {
      alert(status);
    });
});
</code></pre>
<p>Lets say the user inputs <code>UCF</code> within <code>#location</code> input, <code>2</code> within <code>#miles</code> input, and <code>WAWA</code> within <code>#searchTerm</code> input. The following would be logged to the console on line 25.</p>
<p><strong>Console Results</strong></p>
<pre><code class="language-javascript">0: Object
address_components: Array[6]
formatted_address: &quot;3000 Alafaya Trail, Oviedo, FL 32765, United States&quot;
formatted_phone_number: &quot;(407) 359-0144&quot;
geometry: Object
html_attributions: Array[0]
id: &quot;16ccdc45848d5b1f8e74b75608687b93573d6dfe&quot;
international_phone_number: &quot;+1 407-359-0144&quot;
name: &quot;Wawa&quot;
opening_hours: Object
etc...
1: Object
etc..
</code></pre>
<p><code>results</code> on line 25 contains <strong>full detail place objects</strong> for each WAWA Place. For a full understanding of everything that the detailed place object contains <a href="https://developers.google.com/maps/documentation/javascript/places#place_details_responses">refer to the documentation</a> at this point we have lots of great data.</p>
<h3 id="create-map-markers-that-open-infowindow-when-clicked">Create Map Markers that open infoWindow when clicked</h3>
<p>This is the home stretch. At last we have all the data we could possibly want to present the user; all that is left to be done is to add map markers that when clicked display an <code>infoWindow</code>.</p>
<p>Creating these functions is pretty self explanatory the only interaction that isn&#39;t super obvious has to do with <code>infoWindow</code>. Users will expect that clicking a marker opens an infoWindow and clicking another marker opens a new infoWindow while closing the old one. A good trick is to only define one <code>infoWindow</code> (like we did in the init() function) because the content and the position of the <code>infoWindow</code> will override itself on each time the click event fires.</p>
<p><strong>Create createMarker() and clearLoctions() functions</strong></p>
<pre><code class="language-javascript">function createMarker(element, index, array) {
  // setup HTML to be displayed in infoWindow
  var html = &#39;&lt;b&gt;&#39; + element.name + &#39;&lt;/b&gt; &lt;br/&gt;&#39; + element.formatted_address;
  // create map marker object
  var marker = new google.maps.Marker({
    map: map,
    position: element.geometry.location
  });
  // add listener for marker that opens an infoWindow with pre-defined HTML
  google.maps.event.addListener(marker, &#39;click&#39;, function() {
    infoWindow.setContent(html);
    infoWindow.open(map, marker);
  });
  // extend the bounds to accommodate each marker
  bounds.extend(element.geometry.location);
  // add each marker to the markers array
  markers.push(marker);
}

function clearLocations() {
  // set the map reference for each marker to null to erase from map
  markers.forEach(function(element, index, array) {
    element.setMap(null);
  });
  // empty markers array
  markers = [];
}
</code></pre>
<p>Now we can actually call our new functions. <code>clearLoctions()</code> should be the first thing called when the user clicks <code>#find</code> so we are starting with a clean slate. Remember that in <code>createMarker()</code> we are extending the bounds object for each marker position. Once all the markers are placed and the bounds are extended we simply make the <code>map</code> fit the new bounds object on line <code>30</code>.</p>
<p><strong>Add map markers and adjust map bounds</strong></p>
<pre><code class="language-javascript">document.getElementById(&#39;find&#39;).addEventListener(&#39;click&#39;, function() {
  clearLocations();
  // grab user input values
  var searchTerm = document.getElementById(&#39;searchTerm&#39;).value;
  var address = document.getElementById(&#39;location&#39;).value;
  var rad = document.getElementById(&#39;miles&#39;).value;

  // call geocoder passing in address from user input #location
  geoCodeAddress(address)
    // geocoder returns a &quot;then-able&quot; promise with results
    // .then only runs after the promise resolves
    .then(function(results) {
      // when geocoder is done call radarSearch()
      return radarSearch(results.geometry.location, rad, searchTerm);
    })
    // radarSearch returns a then-able promise
    .then(function(results) {
      return Promise.all(results.map(findDetail));
    })
    .then(function(results) {
      // At this point full place objects are contained within results array

      // create new bounds
      bounds = new google.maps.LatLngBounds();
      // call createMarker() for each Place in array
      results.forEach(createMarker);
      // Adjust map with final bounds
      map.fitBounds(bounds);
    })
    // .catch only runs when promise is rejected
    .catch(function(status) {
      alert(status);
    });
});
</code></pre>
<p>Done! An optional step would be to take all this great data and build a UI outside of the google map so the user has a nice results list to reference.</p>
<h3 id="full-code--html">Full Code + HTML</h3>
<p><a href="https://gist.github.com/JamesBedont/31fb39532a8b5b425aaf">Github Gist</a></p>
]]></content>
        <author>
            <name>James Bedont</name>
            <email/>
            <uri>https://jamesbedont.com</uri>
        </author>
        <published>2016-03-23T00:00:00.000Z</published>
    </entry>
</feed>