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).
cd /u1/rails rails new micromounts cd micromounts vi GemfileAll 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 serverThis gives me the rails greeting page on http:localhost:3000, which is good and conforting.
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.
git init vi .gitignore git add . git commit -m "empty project"
And an amusing bit of trivia:
rails generate model Mount myid:string species:string associations:string location:string bundle exec rake db:migrate rails generate controller Mounts new --no-test-frameworkI 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:
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.
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".
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.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:
link_to "Edit", edit_mount_pathThe 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", @mountAnd 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.
vi Gemfile gem 'will_paginate', '3.0.3' bundle installThen 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.
#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.
<%= 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.
<%= 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).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.
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).
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 endThis 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.
Ruby on Rails notes / tom@mmto.org