Tuesday, October 29, 2013
Understanding and Using `rake routes` or pry-rails's `show-routes` to Build and Debug your Application
It is hard to overestimate the usefulness of returning to your routes constantly while you are working on an application. An overview of how routing works can be found in the Rails Guide, but I hope here to offer a quick summation of using the rake routes tool. Knowing what to look for can ease both the development process AND debugging greatly. That's why the default Rails error page has a routes print out on it!
Routing in a Nutshell
Resources (Rails Default Routes) with resources and resource
Naming models as "resources" in the routes.rb file, as opposed to creating a series of "custom" routes, essentially allows the user to declare, all at once, a series of default routes for a model that follow a Rails default conception of how CRUD (create, read, update, delete) works in a RESTful manner. This also allows us to use built-in Rails helpers (form_for(), modelname_path(), etc.) to go to the most commonly used actions (index, show; new, create; edit, update; and delete).
If the model is inherently singular, either by itself or in terms of how it shall be accessed relative to any resources it belongs to (stay with me here), you can define it as a resource instead of as resources. The only difference, in terms of default actions, is that "index" is not created (since it lists multiples of a resource). It is worth noting that the path helpers also work differently, in that all of them other than "new" would normally need an instance of the model passed as a parameter (to know which of the resource's members we would be referring to, usually by its id) , but since there is only one resource now, this can be assumed.
Members and Collections
If we define a model as a resource / resources, and we get the whole suite of default routes and (controller#)actions built, we still may need to add more routes and their requisite actions to our routes.rb file. We can do this by declaring the routes as either members or collections, in order to plug in to the Rails-y conventions above, according to whether or not the resultant action needs to be aware of a specific "member" of the resource, or refers to the resource in its entirety (a "collection," as with search, etc.).
resources :projects do
member do
get 'status' # get and render a project's status in projects#status
end
collection do
get 'search' # search ALL the projects and render result projects#search
end
end
Note: if you have a similar route on a number of resources (such as "search", for example) you can declare it as a concern and
Nesting
In order to use the Rails-y conventions and their helpers, we should also make sure that we "nest" resources that have a has_one or has_many relationship on one end, and belongs_to on the other. That is, since each member of a collection that "belongs to" a member of another collection, ie each "project" belongs to a "student," whenever we reference a particular project, we are also referencing it in the context of a student. Thus we write the routes:
resources :students do
resources :projects do
resources :dependencies do
resources :links
end
end
end
Note: in order to keep the URLs short and sensible, we can request that the resource paths be "shallow" using shallow: true with our resource.
Reading rake routes from Left to Right
We can use rake routes to see all of the information defined above, and reference the correct helper-method-names, controllers and actions that we created in our routes.rb file (or see that we forgot to create)!
From top to bottom are listed our routes. They are all of your routes. Hurrah! For each route, from left to right, we can see:
Helpers
The prefix for our path- and url-helper-methods comes first. Appending "_path" to the prefix (ie ga_classes -> ga_classes_path) will give us a method that returns the internal path (ie the RESTful path from our application's root) for this route.
ga_classes_path() == "/classes"
Appending "_url" to the prefix (ie ga_classes -> ga_classes_url) will give us a method that returns the full URL for this route.
ga_classes_url() == "http://localhost:3000/classes"
Importantly! The helper methods need to be passed instances representing all of the "members" (ie singular instances of a model) of the referenced model, and any models it is nested beneath. A simple mnemonic is to look at the URL to the right of the prefix and find all of the "id" (or "..._id") references in the path. Eg: dependency_link_path() gives us the path to a single link (necessitating a link object), which belongs to a single dependency (necessitating another object). This looks like:
# if @dependency.id = 5 and @link.id = 12
dependency_link_path( @dependency, @link ) == "/dependencies/5/links/12"
Verbs & URL Formats
In the center you can find the full URL and HTTP verb that are expected by routes.rb. Remember that the same URL can route to multiple actions based on the verb that the request was sent with. Also remember that Rails helpers like link_to(), button_to(), form_for(), and form_tag(), etc. have default verbs associated with them that may need to be overwritten.
Controllers and Actions
Finally, on the right, is the name of the controller followed by the name of the action inside that controller that is called when the verb/path combo we just saw receives an HTTP request. Thus, a GET request to "/students/2/projects/13/new" runs the action described here:
# controller#action
# projects#new --> file: projects_controller.rb, which contains, at least:
class ProjectsController < ApplicationController
def new
# do this!
end
end
Bringing it All Together
You can use rake routes as a concordance: consult it whenever you only have part of the story! If you have created routes in routes.rb, and now need to build a controller, then get the action list from rake routes. If you are trying to link to an action and can't make it work, find the correct path or helper based on that action. Check pluralizations, and make sure that the actions are sensible for what you want to do! If they are incorrect, then you know you need to make changes to your routes.rb file.
All in all, it's a great resource to look to as you build and debug your app!
Sunday, October 27, 2013
Mechanical Bird - Twilio-Rails and Figaro Gems
For my first project at GA I wanted to create a web app that would simply send a random poem by phone to whatever phone number was inputted by the user. I called it Mechanical Bird because my poet friend Denver Butson has used it in his poetry and to me it evoked a mix of Old Tyme message sending (which if you have ever watched an episode of Game of Thrones, you know involved birds as the high tech messenger device). I felt the Twilio API was a mechanical messenger bird of sorts and I wanted to be able to tap into that feel and collaborate with Denver on a mechanical poetry project.
The Twilio Docs can be difficult to parse but they are super helpful over there and answered my questions right away. My first step was to get an .rb file to send me a poem. Denver has recordings of himself reading his poetry up on Soundcloud so I wanted to use Twilio API to connect to the recording directly. No Go. Soundcloud uses .m4a format and Twilio cannot send that format so I simply downloaded the file, then reformatted it to .mp3 using the free services of Media.io.
Once I had the mp3, I uploaded it to my server and the Twilio API was able to find it there and send it by phone. Seriously, I almost cried the first time I heard the poem being recited over my phone. It was so exciting. I used the Twilio-Rails gem to facilitate and integrate the API into my Rails app. The main brain to the app lives in my numbers_controller.rb file in a method I created called send_poem:
The other crucial gem I used was Figaro because I needed to hide my Twilio authorization keys. Once you have Figaro in your Rails app run :
rails generate figaro:install
and it creates a special file where you put your keys. This file is ignored when you push to Github. It was very easy to use, just don't forget to run :
rake figaro:heroku
when you push to Heroku.
As of this writing, the user:
1. Signs In.
2. Is taken to a page where they can input number and send a poem.
3. Can see a list of all the numbers they have sent a poem to.
I would like to:
1. Have the app send a random poem from many in my server.
2. Have a name associated with the number so the user can have a telephone book of sorts and see who she/he has sent poems to and re-send as well.
3. I have many, many more ideas that will be added when I have time...and oh yeah, style it decently.
Now that the app works, Denver will be writing/re-recording poems specifically for the app. I want it to be more of a collaboration and have the poetry speak to the intimacy of being read into your ear as well as the practical constraints of it, in terms of a poem arriving to you as a phone call (how long should the poem be and how much will it cost my Twilio account :) .
The Twilio Docs can be difficult to parse but they are super helpful over there and answered my questions right away. My first step was to get an .rb file to send me a poem. Denver has recordings of himself reading his poetry up on Soundcloud so I wanted to use Twilio API to connect to the recording directly. No Go. Soundcloud uses .m4a format and Twilio cannot send that format so I simply downloaded the file, then reformatted it to .mp3 using the free services of Media.io.
Once I had the mp3, I uploaded it to my server and the Twilio API was able to find it there and send it by phone. Seriously, I almost cried the first time I heard the poem being recited over my phone. It was so exciting. I used the Twilio-Rails gem to facilitate and integrate the API into my Rails app. The main brain to the app lives in my numbers_controller.rb file in a method I created called send_poem:
def send_poem(number)
# super-secret Twilio authorization keys
account_sid = ENV["account_sid"]
auth_token = ENV["auth_token"]
# set up a client to talk to the Twilio REST API
client = Twilio::REST::Client.new account_sid, auth_token
client.account.calls.create(
:from => '+13479605724', # From your Twilio number
:to => number, # To any number
# Fetch instructions from this URL when the call connects
:url => 'http://myserver.com/recording1.mp3'
)
end
The other crucial gem I used was Figaro because I needed to hide my Twilio authorization keys. Once you have Figaro in your Rails app run :
rails generate figaro:install
and it creates a special file where you put your keys. This file is ignored when you push to Github. It was very easy to use, just don't forget to run :
rake figaro:heroku
when you push to Heroku.
As of this writing, the user:
1. Signs In.
2. Is taken to a page where they can input number and send a poem.
3. Can see a list of all the numbers they have sent a poem to.
I would like to:
1. Have the app send a random poem from many in my server.
2. Have a name associated with the number so the user can have a telephone book of sorts and see who she/he has sent poems to and re-send as well.
3. I have many, many more ideas that will be added when I have time...and oh yeah, style it decently.
Now that the app works, Denver will be writing/re-recording poems specifically for the app. I want it to be more of a collaboration and have the poetry speak to the intimacy of being read into your ear as well as the practical constraints of it, in terms of a poem arriving to you as a phone call (how long should the poem be and how much will it cost my Twilio account :) .
Saturday, October 26, 2013
Custom Map Markers - gmaps4rails Gem
Another goal of my first project at GA was to use the location data I acquired from the image metadata, to plot thumbnails of the image on Google Map. I was able to accomplish this using the Google Maps API documents, but my first attempt was accomplished using pure JavaScript and iterating through a nested array of images paths (using the Rails
Due to my lack of knowledge in JavaScript I implemented the Ruby Gem gmaps4rails. In my Posts Controller I was able to utilize this Gem to create a nested array of hashes containing latitude, longitude, and image path data.
Now in the index view of my posts controller I call the JavaScript that targets a div with the ID of "map" and then builds the map with custom map image markers:
asset_path) and locations. From this I could see that images as map markers were possible, but the challenge was to get my ruby "post" object into the JavaScript and iteratively retrieve each image's path and latitude/longitude combination. Due to my lack of knowledge in JavaScript I implemented the Ruby Gem gmaps4rails. In my Posts Controller I was able to utilize this Gem to create a nested array of hashes containing latitude, longitude, and image path data.
def index
@posts = Post.all
@hash = Gmaps4rails.build_markers(@posts) do |post, marker|
marker.lat post.latitude.to_f
marker.lng post.longitude.to_f
marker.picture({
:url => post.image.url(:thumb),
:width => 60,
:height => 60
})
end
end
Now in the index view of my posts controller I call the JavaScript that targets a div with the ID of "map" and then builds the map with custom map image markers:
<script>
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers(<%=raw @hash.to_json %>);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
});
google.maps.event.addListener(marker, 'mouseout', function(){
infowindow.open(marker.get('map'), marker);
});
</script>
View the project on Github: https://github.com/dtothefp/dylanthedog
Scraping Geotag GPS Metadata from Photos - EXIFR Gem
One of the main goals of my first project for WDI_Array was to scrape GPS metadata from uploaded photos. In order to do this I used the Paperclip Gem for photo uploads and I discovered the EXIFR Gem for acquisition of metadata from photos: https://github.com/remvee/exifr. EXIFR is an amazing Gem put just as many Gems it is poorly documented. Questions/answers about it in StackOverflow are also minimal so it took us a little while to figure it out.
In this case the path to the image (in this case "image" is the name of the database column we defined using Paperclip) is passed to the EXIFR Gem, stored in the the exif_img variable and then the GPS data is passed to the database using Active Record methods. The method load_exif is called after the image is processed:
def load_exif
exif_img = EXIFR::JPEG.new(image.queued_for_write[:custom].path)
if !exif_img.gps.nil?
self.latitude = exif_img.gps.latitude
self.longitude = exif_img.gps.longitude
end
end
For my project I have a Post model correlating to the table "posts" in the database. In my Post model I defined a method that uses the EXIFR Gem to access the image metadata.In this case the path to the image (in this case "image" is the name of the database column we defined using Paperclip) is passed to the EXIFR Gem, stored in the the exif_img variable and then the GPS data is passed to the database using Active Record methods. The method load_exif is called after the image is processed:
after_image_post_process :load_exif
Using binding.pry to inspect the exif_img variable containing nested arrays and hashes is quite impressive:
View the project on Github: https://github.com/dtothefp/dylanthedog
Subscribe to:
Comments (Atom)

