Ruby on Rails - My Micromount database

If the thermometer had been an inch longer, we would have frozen to death"
Mark Twain

June 15, 2013

Some time ago (November, 2012), I worked my way through the Ruby on Rails Tutorial by Michael Hartl. I liked it in many ways, but swore that I would like to do it again, leaving out all the test driven development and maybe even the git aspects of it so I could just learn rails.

I am doing this on a Fedora 18 system, that shows (via rails -v) that I have rails 4.0.0.rc1. Forging boldly ahead (we could get sidetracked figuring out what rails 4.0 is all about and worrying about locking ourselves onto version 3.2.8 as per the tutorial, but we won't).

Initial setup

cd /u1/rails
rails new micromounts
cd micromounts
vi Gemfile
All this goes smoothly (in part perhaps because I had run through the tutorial less than a year ago and sorted out all kinds of package issues then). I am going to go ahead and run with 4.0.0.rc1, but am locking the sqlite3 gem down to 1.3.7 (I have found that letting my rails apps use the "latest" of anything leads to unhappiness and suffering). I also lock down jquery-rails to 3.0.1.
bundle update
bundle install
rails server
This gives me the rails greeting page on http:localhost:3000, which is good and conforting.

Random tips

It really pays to watch the messages that spew out on whatever console you type "rails server" (or "rails s" as you may as well do). Among other unexpected and interesting things you can learn, you will get rails deprecation warnings and various tips and bits of advice that will enable you to improve your code.

Commenting out code in views can be a pain in the rear. You mix up ruby comments inside <% ... %> blocks with <!-- style html comments. And I found that I could disable a <%= block by jabbing a pound sign in the middle like so: <%#=.

There is a cool new ruby syntax that replaces :fish => 'trout' with fish: 'trout'. You can use this as you wish and feel cool and/or mix it with the old syntax (as I do by accident), which will confuse the poor innocents that follow.

Use git after all

It only takes a few seconds to put the project under git so I am going ahead with it. I may thank myself later. Even setting up the remote repository takes only seconds.
git init
vi .gitignore
git add .
git commit -m "empty project"

And an amusing bit of trivia:

A first cut at the database

I set up a first pass at the database. I am careful to name the table in the singular and capitalize it.
rails generate model Mount myid:string species:string associations:string location:string
bundle exec rake db:migrate
rails generate controller Mounts new --no-test-framework
I should magically get the rails "id" field along with the timestamps "created_at" and "updated_at". Later I end up adding several other fields in several interations, including: Someday I will get busy and read all about migrations in:

I have some dates I want to load into the database, but these will go into the created_at and updated_at fields.

The database itself is in "db/development.sqlite3" Indeed I can run the sqlite3 command line utility on it and see the "mounts" table using ".table" and I can use ".schema mounts" to see the schema. But there is a better way (see my notes on sqlite manager below).

Later I decide to add some more fields. I could set up another migration to alter the database (and I would if I had data I wanted to preserve). However, I can just drop and regenerate the database without any trouble at this stage, so I edit my existing migration file to add the fields and then do:

bundle exec rake db:rollback
rm db/development.sqlite3
bundle exec rake db:migrate
minerals/minload

I actually do this several times over the course of a week during development. If I had data I needed to preserve, I would use layers of migrations, but this is clean and simple given that can reload the database at will, and am resisting the urge to add to and fiddle with the database.

Note that I am being manly and avoiding using rails scaffolds. Instead I am explicitly creating a model and a controller and making more work for myself, hoping that it will build character and add to my knowledge.

Loading the database -- and sqlite manager

I wrote a little ruby program outside of rails to load the database. I let the rails migration scheme detailed above set up the schema. My program has to parse and format an ascii file that has been accumulating my micromount information (849 entries) for several years, then inject it row by row into the empty database. Surprisingly this goes nicely (albeit quite slowly) the first time! I set up my timestamp initializers to yyyy-mm-dd format (like 2010-12-25), and set the field for "id" to NULL so it will autoincrement and generate id numbers for me. Amazingly I got these things right the first time, I guess I am learning some things about rails after all.

I next find myself scrounging about for some kind of GUI database inspector in the flavor of "phpmyadmin" for the dreaded MySQL database. I come across a firefox plugin called sqlite manager. It is effortless to install. I restart firefox and ask "where the heck is it?" I might have used stronger words, I don't remember, but I looked in likely places, and fairly quickly found it under "Tools". I launch it, use database->connect to attach to /u1/rails/micromounts/db/development.sqlite3 and there I am looking at my data. Pretty doggone cool. It defaults to looking for extension ".sqlite", so to find my database (with extension .sqlite3), I had to select "browse for all files".

Getting ready for actual rails hacking

Someday I am going to look into vim plugins that make the business of thrashing around in the rails file hierarchy more sensible. There is a specific rails plugin along with "nerd tree" that people claim can work out nicely. Even though I am an old "vi" veteran, I am belatedly climbing the learning curve on lots of amazing "vim" stuff.

You can't climb a dozen learning curves all at once (unless maybe you are young and smart like Dallan), so I make do with the current state of my vim knowledge and swear that when this is done, I will learn to do better.

For now, I use my "trails" shell script to give me a cluster of terminal windows, each in a sensible place in the rails directory tree for this project.

I will note that the tutorial just suggests doing "vim ." in the rails root for the project.

Eruby

Once upon a time, long long ago, files to render views ended with ".rhtml". Now they end with ".erb" and have names like gizmo.html.erb These files are just html with 3 flavors of markup to inject dynamic content via ruby into them: The first form just lets you insert ruby code without adding anything to the view. The second form injects whatever output comes from the ruby code into the view. The last form injects the output from the ruby code into the view, but suppresses any final newline. Amazingly you can do ruby branching and other control structures wrapped around html sections.

Routes

I note that in the new rails 4.0.0 I am using, the file public/index.html that it used to be a tradition to delete has vanished, which is OK. As soon as I add a route for root this no longer appears at the root of my site (which instead now goes to the index action).

I glance at this:

Actually I should probably read this several times, along with other things among the rails guides. The game though for now, me being impatient and lazy, is to hack around in config/routes.rb. I first add a bunch of Get entries, then decide that just having a root entry and declaring a resource for "mounts" gives me what I want for now. Then I edit the controller to add corresponding actions and I add files to the view directory to provide views for the various actions. Pretty soon I have:

This is a decent start. And with a bit more work, both the index and show action display something useful.

Redirect, Render, or ...

Every action must culminate in sending something to the browser. Routing decides what controller will handle the request, and in particular what method in what controller will handle the request. Once this is done there are 3 ways things can end up (in my simplified view of things). I am not sure why nobody ever explained things like this in any of the documentation I have read, but some of the guys who write these documents are so close to all the details that they can't see the trees for the forest.

link_to

I have found this "helper" quite difficult to tame and among other things poorly documented. You would like to use it to put a link in one page that would take you to another. Simple enough. Also when you set up rails routes, rails sets up some convenience variables for you that you can use to specify the routes. All well and good, as long as you know the conventions for setting these up. This is where things get inconvenient. Perhaps the conventions are documented somewhere, but (ah, grasshopper!) in general this is the whole trick to using rails -- having firmly in mind what all the conventions are. The first tip is that you can use "rake routes" to get a little table that provides part of what you would like to know. Better than nothing, but we would like more. In my project I have a route to mounts#edit and would like to set up a link to this. Now "rake routes" tells me the prefix (or some such nonsense) for this route is "edit_mount". The magic is to append "_path to this, ultimately what I want is:
link_to "Edit", edit_mount_path
The string "Edit" is what gets displayed on the page in blue for the user to click on. Deeper magic is available though. Suppose I have a variable that holds the data for an item in my model (say @mount or just mount in certain cases). I can use this:
link_to "Show", @mount
And this takes me to the path mounts#show. Notice that I never need to make any effort to explicitly pass :id or anything.

Good practices encourage me to persist in using this (rather than wiring in explicit URL's) because it will make the code easier to maintain and robust in cases where I rearrange the site or its location.

Pagination

The index action is in trouble with 849 entries, so we need to use our old friend "Will Paginate" (good old Will!). This requires a new gem, so we edit the gemfile
vi Gemfile
  gem 'will_paginate', '3.0.3'
bundle install
Then you hack some stuff into the index view and the index controller. This is all simple when I just want to paginate through all 849 entries in the database. However, when I set up a form to do a limited search I encounter two things.

Layouts

This is the way to install some site global boilerplate for all of your views. You go to views/layouts and edit the layout file that corresponds to the controller in question. This is an ideal place to put navigations links. There is a "yield" statement in here that pulls in the details for each view. This is good.

Stylesheets

I wish I could ignore these, but alas stylesheets are a sensible way to do things. Look in app/assets. To specify style for an "id" use:
#dingus {
    color: red;
    font-size: large:
    font-weight: bold;
}

To specify style for an "class" use .dingus instead of "#dingus"

Using this and "div" give you lots of control.

Remember use "id" for some one of a kind thing in your layout and use "class" for things that there are many of.

Partials

Partials (more fully known as "partial templates") are a chunk o code that gets injected into a view. The injection is done by a render command that might look like this:
<%= render :partial => 'mount' %>
The above render will look for the file _mount.html.erb in the same directory as the view executing the render request. Note the prepended underscore. The easy default here is to name the partial the same as the object it is being used to display. In this case let us say we already have the variable @mount set for the controller action and we name the partial the same as the variable it displays, then the variable 'mount' gets defined for use inside the partial automatically. Otherwise do this:
<%= render :partial => 'mount' :object => @this_mount %>
Suppose you want to set a variable in the partial:
<%= render :partial => 'mount' :object => @this_mount, :locals => { :var => true } %>
Inside the partial, this will set up a variable "var" set to (in this case) true.

Helpers

Helpers are collections of methods that are available for use in all views. You put them into app/helpers. You name the file containing helpers for all views invoked by controller "dog" app/helpers/dog_helper.rb The helper should be a module named ApplicationHelper.

Forms

It has been said that real coders write their HTML tags (for forms) by hand. However rails provides several helpers that aid this kind of thing, I guess to support users of rails who are not "real coders". Or maybe it is because these helpers are such a mess, are forever changing, and the documentation is so confusing. I just got bitten by some old API change (that makes most of the books and documentation obsolete), namely you must do the first of the two following lines:
<%= form_for @mount, :url => { :action => 'update' } do |form| %>
<% form_for @mount, :url => { :action => 'update' } do |form| %>
Ah, this is the kind of thing that teaches you patience and builds character. These kind of unexpected API changes are why you absolutely want to lock down your rails versions in the Gemfile. This ensures that the code your wrote for the rails API at one point in time will continue to have the same collection of rails components. Beware if you copy code from old working rails projects though (like I just did).

You can choose between "FormHelper" and "FormTagHelper". FormHelper is meant to work with ActiveRecord objects, while the methods provided by FormTagHelper are not (so can be used to generate arbitrary forms I guess).

FormTagHelper has methods that end with "_tag". I am using FormHelper, which begins with the line "form_for" and is certainly more convenient if you are setting up a form to create or edit a model object (which is exactly what I am doing).

Rails 4.0 Strong Parameters

This is a new thing that you will trip over in Rails 4.0. Apparently there has long been a known vulnerability in rails called the "mass assignment vulnernability", and it was used to pull off a high profile compromise of the GitHub site. This led to the Strong Parameters business which is a default in Rails 4, and an optional gem in Rails 3.

It raised its head for me when I did this:

@mount.update_attributes(params[:mount])
and I got a ForbiddenAttributesError.

This site gave a cogent and understandable explanation of what this is all about. To get things going, I change the line above to:

@mount.update_attributes(mount_params)
and then add a private message to my controller to "white list" whatever set of model fields I want to expose.

AJAX and Javascript in Rails

What I have is a nice index action, and I want to put a checkbox next to each item and allow the user to select items by clicking. It would be really nice if the only thing the user saw was the checkbox getting set (or unset), without bouncing around different forms. Ajax would seem like just the answer here, and of course it is promised that Rails will make this easy and painless.

This is a significant topic of its own, which I a going to discuss in detail in a separate document:

It turned out that I didn't need to write any Javascript for what I was doing (although it might serve me well in the long run). What I wanted to do was to have a column of checkboxes along a big paginated index that let me select this and that. When I click the checkbox (or unclick it), I want to see an Ajax event call some action in my controller. I stumbled around with lots of examples that use non-coffeescript javascript and wrapped the checkbox in a form (which I wanted to avoid, though it could have advantages for bundling a hidden field that would set a parameter for when the box gets unchecked, but we made do without all of that.

The line in the view that produced my checkbox looked like this:

<%= check_box_tag( 'label', 1, mount.label != 0,
            data: { remote: true, url: url_for( action: :label_create, id: mount.id ) } ) %>
The "remote: true" turns on whatever rails ajax magic makes things happen and it just worked (after an hour of two of total frustration and chasing bad information).
The controller action that handles this looks like this:
def label_create
        # When the box gets checked, we get the label param set,
        # When it gets unchecked, it is not set at all.
        # also ... mighty fast and loose with error checking.
        if params[:label]
            add_label params[:id]
        else
            label = Label.delete_all mount_id: params[:id]
        end
        # This is Ajax, so we don't render anything.
        render :nothing => true
    end
This could certainly be improved, but gets the job done. Note that I am not returning any kind of response to this ajax request, just adding or removing records silently from a table.
Feedback? Questions? Drop me a line!

Ruby on Rails notes / tom@mmto.org