Monday, November 11, 2013

The Beauty and Simplistic Complexity of jQuery: A Love Story.

It was week 7 at WDI and JavaScript was still on my mind. How could I have been so naive? So foolish? To think that just two weeks before I was riding the Rails, my first full app dinr was deployed and going strong, no cares in the world...the Internet, my playground. The CRUDiest MVCs could be found rendering my heart's desires for the world to see. RESTful days, led to RESTful nights. It seemed like I could conquer anything, the Internet was mine. 

Then I met her...

Her close friends called her ECMA, but she told me to call her JS. After that day everything changed. Implicit returns? Gone. My dreams turned to nightmares filled with unexpected token errors, NaN violations, schizophrenic variables changing value with reckless abandon.



The solid world that was Ruby had become a metaphysical canvas of fleeting elements, coming and going, passing with each failed callback. I couldn't understand a single word she was saying. We met on the DOM everyday, hoping we might share a moment of understanding. Global variables, prototypes, Objects, nothing made sense. I was losing confidence, doubting myself. It seemed like an insurmountable feat. I knew our potential, but concepts that were a breeze with Ruby seemed completely foreign to me. The libraries of gems that I once played in had become toxic ground overnight. It was better to write out a for loop than try to use Underscore, for loops were comfortable, something we both understood. I was ready to walk away from JS forever, and retreat to the back-end for the rest of my days. But one day JS met me with a puzzling look on her face. Whenever she went to speak I heard faint rumblings of Jerry Maguire..."SHOW ME THE MONEY! SHOW ME THE MONEY!" We were having a breakthrough. My words started to make sense, we started to understand each other. Our most complicated of thoughts seemed to link together and appear in front of my eyes almost magically.

jQuery, or just $ for short, changed everything. She told me that it was all a test. A test of patience, a test of persistence, a test of grit. I had to suffer, before I could truly understand and appreciate the complex beauty. Would jQuery mean as much to me if we hadn't spent all those hours, struggling with our words, wanting so hard to understand each other, but always falling just a little bit short. Now I feel whole again, stronger than ever. Working on my first group project this weekend, jQuery by my side, Rails beneath us. The future looks bright...and full of gifs. 

Monday, November 4, 2013

Give a hoot! Don't pollute the global namespace!

Today my partners and I were where working on a snazzy little javascript list maker called List.io.


Basically, you add a task to the form click on the New Task button and it will create to do items in a list. Each item in the list will have a 'completed' button and a 'delete' button.

Everything worked great when we where testing one item. But when we made an actual list of more than three items and began to click them as 'completed', we got an error:

The error says "Uncaught NotFoundError: An attempt was made to reference a Node in a context where it does not exist." It occurs on line 19:  actionDiv.removeChild(completeButton);

var listItems =[];

function makeElem(list){ 
   li = document.createElement("li"); //creating li

  itemDiv = document.createElement("div"); //creating div
  itemDiv.setAttribute("class", "items"); //creating the class name
  itemDiv.innerHTML = list[list.length -1]; //put inside div the last thing in array

  var actionDiv = document.createElement("div");
  actionDiv.setAttribute("class", "actions");

  completeButton = document.createElement("button"); //create button
  completeButton.setAttribute("id", "complete"); //give button the complete attribute
  completeButton.innerHTML = "complete";  //label the button 'complete'
  completeButton.addEventListener("click", function(){
    console.log(actionDiv);
    console.log(completeButton);
    actionDiv.removeChild(completeButton);
    completeTasks.appendChild(li);
  }); etc...

Can you guess what's wrong with this picture?

Exactly, the global namespace is crazy polluted, yo!
And because it was polluted, our "actionDiv" which among other things,  removed the 'completed' button, stopped working. 

Lets add some vars to clean it up like so:

var listItems =[];

function makeElem(list){
-->  var li = document.createElement("li"); //creating li

--> var itemDiv = document.createElement("div"); //creating div
  itemDiv.setAttribute("class", "items"); //creating the class name
  itemDiv.innerHTML = list[list.length -1]; //put inside div the last thing in array

-->  var actionDiv = document.createElement("div");
  actionDiv.setAttribute("class", "actions");

-->  var completeButton = document.createElement("button"); //create button
  completeButton.setAttribute("id", "complete"); //give button the complete attribute
  completeButton.innerHTML = "complete"; //label the button 'complete'
  completeButton.addEventListener("click", function(){
    console.log(actionDiv);
    console.log(completeButton);
    actionDiv.removeChild(completeButton);
    completeTasks.appendChild(li);
  });  etc...

By NOT declaring the variables with "var" INSIDE your function, you are in effect creating a variable outside of the function which makes it a global variable which can be overwritten, disappear, muck things up...in essence, POLLUTE the global namespace, dude. So if you ever get a funky error, like we did, check to see if you need to give hoot and mind your vars.


Saturday, November 2, 2013

data and object modeling using the real world

Recently we were working on creating object and data models for a various number of scenarios, including Auction, Magic Eight Ball, and Taxi Meters.  It was an interesting exercise, all using pseudo code, that got us thinking about how to set up models, attributes, actions, and how the models interacted with each other.  I'd like to take a second and review the Taxi Meter example because I found it particularly exciting.

Having walked through the various functions with my partner and come up with a very nice simple model and methods, I was looking at what we had, which looked something like this...


class Taxi Meter

  attributes: fare, extras, dollar_increment, time_increment

  #initialize(base, dollar_increment, time_increment)
  #start()
  #stop()
  #reset() 

... and what what I would describe as a little mini-epiphany. What I was looking at was basically a translation for the actual, real world object!


The attributes were what you could see (mostly) and the methods were the buttons.  Simple.  It was pretty cool seeing the direct connection between the model and the object and I will try to use this as a strategy of mine going forward.

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:
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 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