Ruby Newbie

March 8th, 2008 by Jim Gagne

For me it’s been bloody confusing trying to figure out how the free, open-source Subversion version control system works. But now, after some study, I think I get it. Because I doubt I’m the only one whose head swam trying to understand this thing, I’ve been keeping notes on what I’ve discovered.

Like many things, Subversion is fundamentally pretty simple once you understand a few basic concepts. But the initial sources I turned to were confusing.

Subversion is an essential resource for developing code. There are versions for Unix, Mac OS (i.e., command-line Unix in Terminal), and Windows. Because I use a Mac, I won’t discuss the Windows version, but it’s similar to the Unix versions except for how file paths are handled.

You can use Subversion to manage any type of files you like, but most often it’s used to hold source code for computer software projects, saved as text files. Because Subversion allows you to compare text files to see precisely how one differs from another, it’s most useful dealing with text. For the purpose of this note, I’ll assume you’re using Subversion to manage text files. (Note that your project can be a mixture of many different kinds of files if that’s what works for you. Subversion doesn’t care.)

Why a version control system?

Subversion does the following:

  • Maintains a complete historical record of your project. If you do something that turns out to create problems, provided you’ve used Subversion properly, it’s easy to rewind to any point in the project. If you delete a file and then realize later you needed it, it’s apt to be there in a prior version of the project.
  • Automatically keeps track of the efforts of multiple people working on the same project. Almost always, it automatically and seamlessly combines everyone’s efforts. Moreover, the people working on the project can be dispersed in different physical locations and still work together effectively.
  • Makes it easy to keep track of a variety of different versions, releases, upgrades, and permutations. Usually this is murder to keep track of, but Subversion makes it easy–well, relatively easy.
  • Saves disk space. Though to the user, each different version, branch, and permutation of your project is a complete set of project files as of the date they were saved, internally Subversion uses a sophisticated database that is quite condensed.
  • Unless you’re an individual working alone, Subversion stores its archives on a separate server computer available to everyone working on the project. (Often it uses a special Apache module designed specifically for Subversion.) So long as the server is properly maintained and backed up, this ensures that vital project files cannot be lost. If well maintained, the server offers excellent security as well.
  • Subversion keeps track of all the changes to the files it manages, including author, date, version number, and stated reason for the change or update. You can add additional metainformation if you like.
  • Subversion handles errors gracefully. If a particular action causes a problem, it’s simply not done. If an error arises halfway through an action, the action is undone and the previous state is restored.
  • How does Subversion work?

    Subversion consists of repositories, where your project is stored and archived, and one or more working copies of whatever project or projects you’re working on.

    The repository is a database containing a complete historical record of whatever projects you assign to it. You can have as many repositories as you like. Each repository can have any number of projects. Internally, each repository links and condenses all of its files together. Some people combine all their projects into one big repository (probably the most common practice), but others use a different repository for each project.

    A repository is simply a place to store stuff in an organized fashion. You CANNOT access its information directly. You can’t even directly traverse its internal directory structure. Every interaction with a repository MUST be done using “svn”, the Subversion command line utility, or one of the GUI interfaces. Using this utility, you can check stuff in, check stuff out, display and organize its contents, and run diagnostics and comparisons of the information it stores.

    The working copy is a group of files in one directory tree, ready for you to work on the way you usually do. (For example, a Rails project would be contained in a single working copy.) These files have been “checked out” from the repository, and Subversion keeps track of them. Subversion adds a hidden “.svn” directory to every directory and subdirectory in a working copy. This hidden data tells Subversion where the assigned repository is, making interacting with the repository almost effortless.

    If you want to add or modify files or directories within the working copy, use “svn” commands to do so, just as you MUST to modify the repository.

    If you want a clean copy without any of the added Subversion metadata, you can export it at any time from either the working copy or the repository.

    Basic rules of working with Subversion

    This is the part that initially I had the most trouble with. If you don’t follow these rules, you get confusing and unhelpful results.

    1. Whenever you are referring to the repository, you MUST use URL notation. For the sake of brevity, let’s say I’ve created my repository in the directory “/Users/jim/svn” (in my user directory), containing two projects (subdirectories) “apples” and “pears.” Assuming I’m just working on a local machine, I refer to the first project’s repository as “file:///Users/jim/svn/apples” and to the second as “file:///Users/jim/svn/pears”. If I forget and refer to “/Users/jim/svn/apples”, Subversion will complain that “/Users/jim/svn/apples” is not a working copy. (Ditto “file://~/svn/apples”)

    2. Conversely, when referring to the working copy, you must use file path notation. So if my working copy is at “/Users/jim/Documents/apples”, that’s how I refer to it. (You can’t use “~/Documents/apples”.)

    3. The working copy keeps track of the repository it’s assigned to. (You can change it if you need to.) So in many commands in which you interact with the repository, which one you’re using is assumed. Similarly, if you omit the path, the present working directory (”.”) is assumed.

    4. Again, as noted above, the repository database encodes all its information. You can’t access the information directly; you must use the “svn” command-line command or a GUI interface.

    5. Though you may directly add or delete files or directories in your working copy, Subversion won’t recognize these changes unless you use the “svn add” and “svn delete” commands.

    6. If more than one person is working on a project at the same time, periodically during the day you need to update your working copy with any changes others have made. Subversion automatically combines changes you’ve saved to the working copy with updates have saved to the repository by others. (In the unlikely event you’ve both just changed the same line of code, Subversion points this out and gives you the opportunity to fix it.) Similarly, periodically during the workday you should save your work to the repository, usually when you’ve completed a particular task within the project.

    Though both getting others’ changes from the repository and saving your work to the repository are two kinds of updates, obviously you can’t refer to them the same way. So to update your working copy from the repository, you use the command “svn update”. To save your work, use “svn commit”.

    7. Anything that changes a repository must be accompanied by a “commitment” message. The purpose of this message is to remind you (and coworkers on the project) what you just did and why. You have two ways to enter the message. First, if the environment variables EDITOR, VISUAL, or SVN_EDITOR are set to the name of a text editing program, it will be started up for you to enter your message. Save and quit, and the message is stored with the update. More often, I use the “-m” option, which overrides the EDITOR if that’s set. Somewhere in the svn command, include your comments about what you just did as follows:

    -m “My comments contained within double quotes”

    If you forget the -m option and no EDITOR variable is set, you get the following message:

    svn: Could not use external editor to fetch log message; consider setting the $SVN_EDITOR environment variable or using the –message (-m) or –file (-F) options

    svn: None of the environment variables SVN_EDITOR, VISUAL or EDITOR is set, and no ‘editor-cmd’ run-time configuration option was found

    8. Some svn commands that affect the working copy require a commitment message and some don’t. Commands that don’t require the message aren’t carried out until you commit your changes to the repository, complete with message.

    How to set up a repository

    1. Figure out where you want the repository and how many directories and projects you want it to contain. Do you want a separate repository for each project? Be sure its name and path are short and easy to type.

    2. Use the regular command line command “mkdir” to create the repository directory, then use the command “svnadmin create PATH” (substitute the complete path to the repository for PATH) to convert it into a repository. Now, all subsequent interaction with the repository must be done with the svn command (or GUI interface). Here’s a command line example:

    cd ~
    mkdir svn
    svnadmin create /Users/jim/svn

    3. Create the repository directory structure using “svn mkdir” commands. If this is a programming project, most people use the “trunk, branches, tags” structure within each project to help manage various revisions, versions and iterations of the program. So, for example, let’s assume that in the repository “/Users/jim/svn” I’m creating two projects, “apples” and “pears”:

    svn mkdir file:///Users/jim/svn/apples -m "creating Apples project"
    svn mkdir file:///Users/jim/svn/apples/trunk -m "creating Apples trunk"
    svn mkdir file:///Users/jim/svn/apples/branches -m "creating Apples branches"
    svn mkdir file:///Users/jim/svn/apples/tags -m "creating Apples tags"
    svn mkdir file:///Users/jim/svn/pears -m "creating pears project"
    svn mkdir file:///Users/jim/svn/pears/trunk -m "creating pears trunk"
    svn mkdir file:///Users/jim/svn/pears/branches -m "creating pears branches"
    svn mkdir file:///Users/jim/svn/pears/tags -m "creating pears tags"

    You can shorten these commands, because “svn mkdir” allows you to create several new directories with one command. The first four of the above commands could be simplified to (the following must all go on one line):

    svn mkdir file:///Users/jim/svn/apples file:///Users/jim/svn/apples/trunk
    file:///Users/jim/svn/apples/branches file:///Users/jim/svn/apples/tags
    -m "creating Apples directories"

    Hidden gotchas in “svn mkdir”: First, you do not list the repository itself, only the complete URL for each directory you want to add (or, if adding directories to a working copy, the complete file path including the new directory). Second, any intermediate directories must exist or you must have just created them. So the following won’t work because you have to create the “apples” directory before the “trunk” subdirectory:

    svn mkdir file:///Users/jim/svn apples/trunk file:///Users/jim/svn/apples -m "Apples directories"

    4. Any time you want to see a listing of what your repository contains, use the “svn list” command. So, to see the contents of the Apples project, say:

    svn list file:///Users/jim/svn/apples

    You can shorten this to “svn ls”. Using the -R option recursively lists every file in every subdirectory:

    svn ls -R file:///Users/jim/svn/apples

    To remove a directory use “svn delete”, which can delete almost anything in a repository (using a URL) or working copy (using a file path).

    5. Let’s say I’ve already started the Apples project but haven’t yet saved it to Subversion or started a working copy. Here’s how to do that. Navigate to the original Apple directory and then import into the repository. Note the original directory isn’t linked to Subversion: it’s not a working copy, and it’s hard to convert it to one. Instead, change the name of the original directory to something else (to save for safekeeping) and then check out a working copy from the repository:

    cd ~/Rails/apples
    svn import . file:///Users/jim/svn apples/trunk -m "Initial commit of Apple project"
    cd ..
    mv apples apples.old
    svn checkout file:///Users/jim/svn apples/trunk apples

    Checkout syntax is as follows:

    svn checkout [repository URL] [path to new working copy]

    Note that you can leave out the path to the new working copy; Subversion defaults to creating a directory in the current location with the same name as your repository directory. So if you type “svn checkout file:///Users/jim/svn apples/trunk”, your working copy will be put in a new directory called “trunk” within the current working directory.

    Then, periodically I can submit changes from my working copy to the repository:

    cd ~/Rails/apples
    svn commit  -m "Hooked up the revinator to the zorch"

    Note that the pre-Subversion copy of apples (”apples.old”) can be tucked away for safekeeping. Or, once you’re sure everything is working properly, just delete it. You can always recreate it with “svn export”.

    Working with Subversion projects

    Once you have everything set up, the rest is easy.

    The following commands assume you’ve CD’d to your directory tree of the working copy. To ensure you’re in the right place, do a “svn info”. If you get an intelligent response, you’re set.

  • If others are working on the same project, periodically do a “svn update” to keep your files in sync with theirs.
  • Commit your project whenever you reach a point where it makes sense to do so (i.e., you can write a sensible commit message).
  • To add a new file in your working copy, create the file and then say “svn add <filename>”. To create a new directory, use “svn mkdir <relative directory path>”. To delete something, “svn delete <filename or directory path>”. You don’t need commit messages for these commands because they don’t take effect until your next “svn commit”.
  • It’s important to tell Subversion what not to archive. In Rails projects, this includes most everything in your log/, doc/, and tmp/ directories. Moreover, many people don’t want to archive config/database.yml, because a) it’s different for every user on a project, and b) it’s a security risk because it contains your SQL database password in cleartext. Finally, Rails automatically generates the file “schema.rb”, and it changes every time you run a test. It too should be ignored. So here are some commands that will do this:

    svn propset svn:ignore "database.yml" config
    svn propset svn:ignore "*" tmp
    svn propset svn:ignore "*" tmp/cache
    svn propset svn:ignore "*" tmp/pids
    svn propset svn:ignore "*" tmp/sessions
    svn propset svn:ignore "*" tmp/sockets
    svn propset svn:ignore "*doc" doc
    svn propset svn:ignore "schema.rb" db
    svn propset svn:ignore "*" log/

  • If you get the following error message, it means Subversion thinks you are trying to access a repository but you didn’t enter the correct URL. Usually when I get this message I either used a file path when I meant a URL, or I made a typo:

    svn: Unable to open an ra_local session to URL
    svn: Unable to open repository 'file:///Users/jim/svn/applzz'

  • Similarly, something like the following means you’re not referring properly to a working copy:

    svn: '.' is not a working copy
    svn: Can't open file '.svn/entries': No such file or directory

  • Resources and further reading

    The official Subversion web site has lots of information. It’s the place to find Subversion downloads. (Instead I installed mine with MacPorts: “sudo port install subversion”)

    Frankly I found the free Subversion Book not to be terribly useful. It’s vague about syntax, and I didn’t come away with an understanding of how to use the program. The official Subversion User Guide is a shorter clone of the Subversion Book and shares its deficiencies.

    A good quick reference is to just type “svn help” to get a list of commands, and then “svn help <command>” to see details about a particular command. Alas, the help files are also vague about syntax, and they don’t list nearly enough examples.

    The way I got up to speed was to get the Pragmatic Version Control using Subversion book from Amazon. It’s well written and a quick read.

    Here are some web sites that walk you through setting up  Rails projects in Subversion:

  • http://railscasts.com/episodes/36

  • http://blog.teksol.info/2006/03/09/subversion-primer-for-rails-projects

  • http://blog.mondragon.cc/articles/2006/11/05/subversion-rails-in-five-minutes

  • http://wiki.rubyonrails.org/rails/pages/HowtoUseRailsWithSubversion
  • Zigzig is a handy Macintosh GUI, free to noncommercial users. I didn’t find it that useful until I understood what was going on in the first place.

    Here’s an open-source Subversion repository browser written in Rails with plenty of AJAX. I haven’t tried it, but it looks slick.

    July 15th, 2007 by Jim Gagne

    I love playing with Ruby and Rails. Let’s figure out how much time it takes to do method calls. In the distant past we avoided procedure calls because they added overhead. I’m now reading Martin Fowler’s book Refactoring: Improving the design of existing code (Amazon link). Fowler says that with modern object-oriented languages, the time needed to call a method has shrunk dramatically. Hmmm… Let’s see.

    (Arrghh! Benchmarks!!! But this is play, not serious. I’m playing on a stock Mac Pro running irb. In the code that follows, I left out most of the command-line irb stuff while showing code.)

    1. Let’s start going a million times around a loop:
    def timeit
      t_start = Time.now
      1.upto(1000000) do         #1,000,000 times around an empty loop
      end
      puts "Elapsed time = #{Time.now - t_start}"
    end
    irb(main):040:0> timeit
    Elapsed time = 0.059517

    That’s insane: 60 nanoseconds per loop.

    2. Okay, now add a method call:
    def wastetime
    end
    def timeit
      t_start = Time.now
      1.upto(1000000) do         #1,000,000
        wastetime
      end
      puts "Elapsed time = #{Time.now - t_start}"
    end
    irb(main):048:0> timeit
    Elapsed time = 0.210933

    So that’s about 3-1/2 times slower, but still only 210 nanoseconds. Not bad.

    3. Let’s increase the pain:
    def waste1time
      waste2time
    end
    def waste2time
      waste3time
    end
    def waste3time
      waste4time
    end
    def waste4time
    end
    def wastetime
      waste1time
    end
    irb(main):067:0> timeit
    Elapsed time = 0.76093

    So five times up and down the method chain multiplied the time by five. Sounds about right.

    4. Time to add a parameter:
    def waste1time(t)
      waste2time(t)
    end
    def waste2time(t)
      waste3time(t)
    end
    def waste3time(t)
      waste4time(t)
    end
    def waste4time(t)
      t
    end
    def wastetime
      waste1time("xyz")
    end
    irb(main):088:0> timeit
    Elapsed time = 1.116462

    That slowed us to more than a millisecond a loop. Maybe passing parameters is inefficient.

    5. How does passing a parameter compare with returning “self”?
    def waste1time
      waste2time
    end
    def waste2time
      waste3time
    end
    def waste3time
      waste4time
    end
    def waste4time
      self
    end
    def wastetime
      waste1time
    end
    irb(main):104:0> timeit
    Elapsed time = 0.778056

    So sticking with “self” is quicker — not dramatically so but almost a third faster — even when “self” is returned down the method chain.

    Bottom line: Martin Fowler is right. Methods cause little delay in program execution.

    I’m at an early point in his book, in the section on how to tell when code “smells bad.” Great book. What Fowler says about writing good code is eerily reminiscent of the major points in the Ruby and Rails books I’ve read:

    1. Keep methods short, preferably a few lines or less. Use more smaller methods rather than fewer bigger ones. Name methods clearly according to what they do, so you can figure it out at a glance even years later. Break up big classes into small ones that each do a few things well. I love the quote on pg. 15: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”

    2. If you have to add a comment to code so you can understand it, that’s a sign you should pull out that section of code into a separate method, named clearly.

    3. If the same code appears more than once or twice, pull it into a separate method. It will not only be easier to understand, it’s much less effort to maintain. You only have to change it in one place. (AKA the Ruby/Rails maxim “DRY: Don’t repeat yourself.”)

    4. Other targets for “refactoring” (rewriting code so it’s better structured and easier to understand and maintain) into separate methods are long parameter lists, complex nests of conditionals, and “case” statements.

    5. Don’t try to guess which portions of your code take too much time. You’ll almost always guess wrong. Structure your code clearly, at which point you can test your code and find out objectively where the bottlenecks are.

    July 14th, 2007 by Jim Gagne

    One of the things it took me a while to figure out is how to get methods to work on objects without passing that object as a parameter. And how do you “shebang” the object (modify it in place)?

    Trivial example:
    irb(main):001:0> s = '123'
    => "123"
    irb(main):002:0> l = s.length
    => 3

    The whole point of Ruby is I don’t have to call length as length(s). And I can hook together multiple methods:

    irb(main):004:0> s.length+1
    => 4

    … so long as I do it carefully!

    rb(main):005:0> s.length+1.to_s
    TypeError: String can't be coerced into Fixnum
        from (irb):5:in `+'
        from (irb):5
        from :0
    irb(main):006:0> (s.length+1).to_s
    => "4"

    So how do you implement this? The answer is “self”

    The Pickaxe book defines “self” as “The receiver (object) of the current method” [pg. 337], noting it’s read-only. That doesn’t entirely make sense, but I guess it’s what I want. Earlier in the book [pg. 81], it describes “self” as “the current object”. Frankly, Pickaxe disappoints in covering the topic.

    Playing around, I discovered the following:

    irb(main):007:0> def plus1
    irb(main):008:1> self + 1
    irb(main):009:1> end
    irb(main):010:0> 3.plus1
    => 4

    Hey, that worked! And this doesn’t, because “self” is read-only:

    irb(main):011:0> def plus1
    irb(main):012:1> self += 1
    irb(main):013:1> end
    SyntaxError: compile error
    (irb):12: Can't change the value of self
    self += 1
          ^
          from (irb):13
          from :0

    But “self” isn’t completely read-only. Look at how Ruby itself implements String#chop!

    # File jcode.rb, line 192
    def chop!
      self.gsub!(/(?:.|\r?\n)\z/, '')
    end

    So you can manipulate “self” indirectly, in this case using gsub!. But it gets even trickier. Look at the source for String#succ!

    # File jcode.rb, line 78
    def succ!
      reg = end_regexp
      if $KCODE != 'NONE' && self =~ reg
        succ_table = SUCC[$KCODE[0,1].downcase]
        begin
          self[-1] += succ_table[self[-1]]
          self[-2] += 1 if self[-1] == 0
        end while self !~ reg
        self
      else
        original_succ!
      end
    end

    Here, modifying “self[-1]” works fine. Let’s try it:

    irb(main):014:0> def end_asterisk
    irb(main):015:1> self[-1] = '*'
    irb(main):016:1> end
    => nil
    irb(main):017:0> str = '123'
    => "123"
    irb(main):018:0> str.end_asterisk
    => "*"
    irb(main):019:0> str
    => "12*"

    The best discussion of “self” I’ve found so far is in David Black’s Ruby for Rails, which devotes an entire chapter to it. I read this in the first few days of learning Ruby and then lent the book to my son. Everyday Scripting with Ruby (Brian Marick) has a section on “self”, pg. 122-126, which reveals some interesting information.

    Lets’ review Marick’s example:

    irb(main):020:0> class Adder
    irb(main):021:1>   attr_reader :value
    irb(main):022:1>
    irb(main):023:1*   def initialize
    irb(main):024:2>     @value = 0
    irb(main):025:2>   end
    irb(main):026:1>
    irb(main):027:1*   def add1
    irb(main):028:2>    @value += 1
    irb(main):029:2>   end
    irb(main):030:1> end
    => nil
    irb(main):031:0> a = Adder.new
    => #
    irb(main):032:0> a.add1
    => 1
    irb(main):033:0> a.add1
    => 2
    irb(main):034:0> a.add1
    => 3

    By modifying the add1 method to return self, you get new behavior:

    irb(main):036:0> class Adder
    irb(main):037:1>   def add1
    irb(main):038:2>     @value += 1
    irb(main):039:2>     self
    irb(main):040:2>   end
    irb(main):041:1> end
    => nil
    irb(main):042:0> a = Adder.new.add1.add1.add1.add1.value
    => 4

    That’s pretty, but it doesn’t entirely describe what’s going on. Check this out:

    irb(main):045:0> a = Adder.new.add1.add1.add1.add1
    => #[Adder:0x84148 @value=4]
    irb(main):046:0> a.class
    => Adder

    In contrast, if you try it without the “self” in the add1 method, it returns an integer. Then tagging on more than one “add1″ generates an error:

    irb(main):043:0> b = Adder.new.add1
    => 1
    irb(main):044:0> b = Adder.new.add1.add1.add1.value
    NoMethodError: undefined method `add1' for 1:Fixnum
        from (irb):044
        from (null):0

    So returning “self” means the returning the instance object itself, allowing you to chain together multiple methods that work on that object. Neat!

    There’s a lot more to “self”. For instance, how do you tell what is the “current object”? This is precisely what Ruby for Rails covers in depth. For example, within a class, “self” refers to the class. But within a method, “self” refers to whatever called that method.

    July 12th, 2007 by Jim Gagne

    For the first month or so of using Ruby on Rails, I rarely used the Rails API documentation at http://api.rubyonrails.org. It’s simply too terse; explanations are incomplete. Now I can’t get along without it.

    I often get tired of constantly reloading that URL and thought I’d like to have a local copy of the Rails API on my hard drive. There’s a way to do this using rake:
    rake doc:rails

    But trying this on my machine always generated an error:
    rake aborted!
    Don't know how to build task 'vendor/rails/railties/CHANGELOG'

    Digging around in the rake source code for Rails (located in /opt/local/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/ on my machine, but on many installations the first directory is /usr rather than /opt) shows that Rake expects to find these files in the [project]/vendor/rails directory, which isn’t there in any of my projects! But turns out it’s not supposed to be, unless you freeze your version of Rails. You can do so thusly:

    rake rails:freeze:edge

    This loads the 9.2 MB Rails source into the vendor/rails directory. Then rake doc:rails works fine, producing a 5.8 MB api/ directory under [project]/doc. Use your browser to open doc/api/index.html. To dump the Rails source, run rake rails:unfreeze. Then you can move the doc/api directory anywhere that’s convenient.

    One final note: with rails:freeze:edge, there’s a way to specify which version of Rails you load into the vendor/ directory, but I haven’t figured that out yet.

    Also: there’s a longer discussion of this topic at the Null Is Love blog. Rather than “rake rails:freeze:edge“, they recommend “rake rails:freeze:gems”. This loads the actual version of Rails you’re using, always better than getting the wrong version. For reasons I haven’t figured out, this and similar gem commands generate a “no such file to load” error for “gem_original_require.rb”, which indeed is nowhere on my system.

    (Note added 7/21/07: It turns out my copy of gem wasn’t working properly. Uninstalling and reinstalling gem fixed that. Now I can generate local documentation for my current copy of Rails using “rake rails:freeze:gems“.)

    If you enter “gem_server” on your terminal command line and then open your browser to “http://localhost:8808/”, you’ll see the documentation for all your installed Ruby gems.

    Here’s how to get a local copy of the Ruby RDoc documentation. In your terminal, go to your Ruby installation and run “rdoc“. Use the -o option to specify the output directory. For example, let’s put them in a new folder called “ RubyDocs” on the Mac Desktop:
    cd /usr/local/lib/ruby/1.8/
    sudo rdoc -o ~/Desktop/RubyDocs

    The above location works on standard Ruby installations. If, like me, you installed Ruby with MacPorts, the first line of the above example should be: cd /opt/local/lib/ruby/1.8

    Finally, I learned about a nifty method to locate all the documentation MacPorts had so helpfully built on my system. Navigate to the directory that contains all your installed Ruby, Rails, gems, and MacPorts software. (When one uses MacPorts, this stuff is installed into /opt/local. Otherwise look in /usr/local.)

    Then run find . -name "doc"

    I got a nifty listing of all the documentation I wasn’t able to find before. Some of it was tucked in lib/ruby/gems and some in var/db/dports. For instance, here’s the path to the superb HTML documentation for the ExPat XML parser library:

    /opt/local/var/db/dports/software/expat/2.0.1_0/opt/local/share/doc/expat-2.0.1/html/reference.html

    July 8th, 2007 by Jim Gagne

    Ruby is a large universe; Ruby on Rails (RoR) even more so. Trying to start programming Ruby by reading a couple of web pages is like trying to learn French from a tourist brochure.

    But it’s easy to get started, and Ruby is so nice and inviting it pulls you in. Start with a couple of good books on learning Ruby and Rails. Though platform-independent, you’ll find most active Ruby and RoR developers use Macs.

    For Windows users, I’d recommend the Ruby on Rails for Dummies book (here’s the Amazon link). It shows you how to use RadRails, which is a FREE downloadable Integration Development Environment (IDE). In other words, you can do everything within RadRails. It has a lot of built-in help that’s useful once you develop a basic understanding of what’s going on.

    Other useful books:
    Mr. Neighborly’s Humble Little Ruby Book. You can download an earlier version for free. EXCELLENT place to get started with pure Ruby (no Rails).

    Beginning Ruby From Novice to Professional from Apress. You can get a downloadable PDF at half price, $20.

    FABULOUS introduction to the Ruby you need to understand to use Rails: Ruby for Rails: Ruby Techniques for Rails Developers, and the Amazon link.

    And the best introduction to RoR, once you have a little Ruby under your belt: Build Your Own Ruby on Rails Web Applications. Here’s the Amazon link.

    Finally, when you’ve become comfortable in this new idiom, get the two standards nobody can live without:

    • The “Pickaxe” book (named after the drawing on the cover) Programming Ruby by Thomas, Fowler, and Hunt (Amazon link)
    • Agile Web Development with Rails by Thomas, Hansson, and others (Amazon link)
    July 4th, 2007 by Jim Gagne

    One of the more intriguing resources in Ruby and Rails is the Rake facility. Short for “Ruby Make,” it’s the Ruby version of the Unix make facility for knitting together c programs.

    Yawn. As a Ruby newbie, that last thing I’m interested in is the c language. But there are multiple other uses for Rake, as any Rails user knows.

    Where does all that rake stuff come from in Rails, anyway?

    Here’s the best introduction I’ve found to rake, hands down: Ruby on Rails Rake Tutorial (aka. How rake turned me into an alcoholic) (by gregg on the RailsEnvy site). The “alcoholic” thing is actually the rake example. It chains together three tasks: buy vodka, mix drink, get loopy. Cute, and when I was done, I was clear on what Rake involved. Moreover, looking over the Rails rake libraries now made sense. These files (databases.rake, documentation.rake, framework.rake, log.rake, misc.rake, pre_namespace_aliases.rake, statistics.rake, testing.rake, and tmp.rake) hide in the directory rails-1.2.3/lib/tasks/

    By contrast, the rake tutorial at the main Rake site (docs.rubyrake.org) is tired and uninspired. The Documentation and Examples pages don’t add much either. I think they would work better if they were more up to date (written in 2005) or if I understood programming in c.

    The tutorial people refer to most often is Martin Fowler’s. I like it a lot, but read the “alcoholic” one first. Fowler refers to a delightful example of rakefile syntax that’s part of the Rake Rdoc in rubyforge.org. Here’s the full Rake api.

    Here’s another Rake tutorial based on building c applications. I didn’t find it as helpful.

    Check RubyInside and search for Rake. Lots of neat articles.

    July 3rd, 2007 by Jim Gagne

    I needed to load some data from a legacy database into an ActiveRecord-controlled MySQL model. I ran into some questions: Do I put the load-and-convert method into the model or the controller? After all, loading data into a model sounds like a model issue, not controller logic. But if it’s in the model, how do I call it?

    This led to further questions. What parts of the model and controller code run and when?

    To some extent, this should be easy to answer. The models and controllers are all separate classes. You should just use the standard Ruby methods to access them. But there’s a lot of magic going on behind the scenes in ActiveRecord and ActionController. Surely they’re linked together and know about each other somehow!

    I created a dummy Rails project called Futzit. I made three models: apple, pear, and peach. Each had a single-line data table; I called the field “variety” and made it a string. Each of the models contained a line saying: puts '*** in [...] model'an initialize method: puts '**in [...] model: initialize'and a “do_apple_model“ method that would have to be called directly. Again, all it did was: puts '+++in Apple do_apple_model'

    I put similar code in the controllers. Then I put in index and “do_apple“ (or “do_pear“, “do_peach“) views, which just contained links to everything else. In “application.rb“, I added: puts '***in main application controller' and another initialize method.

    (Note: any time you include a “puts” statement in a class or method, the result shows up in the terminal log, not on the browser screen. I find liberal use of these helps a lot during debugging. I always prefix the line with a string of asterisks or similar punctuation so I can find it in a long log printout.)

    Loading the Apple index page, here’s the result:
    *** in main application controller
    +++ in Apple controller
    ++ in Apple controller: initialize

    Clicking on the Pear index link:
    *** in main application controller
    +++ in Pear controller
    ++ in Pear controller: initialize

    Now the Pear do_pear link:
    +++ in main application controller
    +++ in Pear controller
    ++ in Pear controller: initialize
    +++ in Pear controller do_pear

    Adding a call in the do_apple (in apple_controller) to the do_apple_model method caused a NoMethodError. I confirmed this by going to script/console and listing the Apple controller instance methods:

    [~/Rails/futzit]% script/console
    Loading development environment.
    *** in main application controller
    ** in application controller: initialize
    >> t = AppleController.new
    +++ in Apple controller
    ++ in Apple controller: initialize
    => #<AppleController:0x34459c8>
    >> t.methods

    (generated a long list of methods)

    The only local method was “do_apple“. I tried the same thing with the Apple class itself:

    >> u = Apple.new
    *** in main Apple model
    ** in Apple model: initialize
    => #
    >> u.methods

    And again, the only local instance method was “do_apple_model“

    So I went back to the “do_apple” method and added the following code:
    def do_apple
      puts "+++ in Apple controller do_apple"
      t = Apple.new
      t.do_apple_model
    end

    This time it worked, producing the following output:
    *** in main application controller
    +++ in Apple controller
    ++ in Apple controller: initialize
    +++ in Apple controller do_apple
    *** in main Apple model
    ** in Apple model: initialize
    *** in Apple do_apple_model

    Removing the call to t.do_apple_model just omitted the last line of the printout; the model’s initialize method still ran. Replacing “t = Apple.new” and “t.do_apple_model” with “Apple.do_apple_model” didn’t work — in retrospect, this is obvious because do_apple_model is an instance method, not a class method.

    I tried before_filter :do_apple_model both in the model and the controller. The model gave me a NoMethodError for “before_filter”. The same before_filter in the apple_controller created another NoMethodError, this time for “do_apple_model”.

    Finally, I added the following class method to the three models (using the Apple model as an example):
    def self.do_apple_class
      puts "*** in Apple class model"
    end

    Calling that at the end of “do_apple” with “Apple.do_apple_class” worked just fine.

    Okay, we’re on a roll

    So what is the scope of all that stuff in the helper directories anyway? I’ve never exactly known. Time to find out!

    Looking again at Agile Web Development, p. 471, the authors talk about how important it is not to put too much code in your views. Fair enough. “Rails provides a nice compromise in the form of helpers.” Hmmm. Sure enough, in application_helper.rb, a helpful comment reminds us, “Methods added to this helper will be available to all templates in the application.”

    Not one to believe anybody, I put code like the following in each of the helpers:
    def say_hi_from_apple_helper
      puts "~~~ Hello from apple helper, called by #{self}"
    end

    As expected, none of the helpers was known to any of the models or the controllers. The views could call them in just the way you’d think — including “application.rhtml”, the overall view layout.

    Finally, is it really true that methods in “application.rb” can be accessed anywhere? I put a “do_ac” method in application.rb, which as always just
    def do_ac
      puts "*** in application controller do_ac, self = #{self}"
    end
    Rails could not find it from the body of the controller but did fine if it was called from within a controller method, including “initialize”. As you’d expect, “do_ac” was unknown to any of the models.

    Conclusions

    Controllers can access the models either by: 1) using an instance of the model to call an instance method or 2) by creating a class method in the model and using the Class.method means to call it.

    Frankly, this was a lot easier to figure out once I dropped my assumption that Rails somehow linked the model and controller, making them more than just standard Ruby classes.

    Initialization methods run immediately after any “naked” ruby code (code not wrapped in a method) contained in the same class. (I suspect “naked” code in a class is poor form and a bad idea. Use “initialize”.) But the only time the “initialize“ method ran in “application.rb“ was if there was an error, and when script/console first started up.

    Anything method in [model]_helper.rb in the “helpers/” directory is available only to your views. (Views can also use partial page templates, a different facility.) Put global controller methods in “application.rb”. There’s no facility for global methods for models. This sort of makes sense, since model code is specific to each model, but in my current project I sure would like to able to use some data translation methods across models.

    Bottom line: the data loading method can go wherever I want. It will probably work better in the model — after all, that’s the point of the model — as a class method, since I’m not working on a particular record.

    Download file: Partial Rails project (app/ and db/ directories) for this example

    A note about the enclosed file:

    This is the part of the Futzit project not autogenerated by the Rails command rails futzit Here’s how to use it. Download and unzip the file. Run “rails futzit” to create the default project. Then replace the app/ and db/ directories with those from the download.

    As always, configure “config/database.yml“ for your local setup and create the database “futzit_development“ or whatever you want to call it. Then run “rake db:migrate“ to get the database running, start “script/server“, and play. The file is what’s left after I put stuff in and moved it all around. Feel free to play with it.

    Note added 7/16/07

    I’ve been continuing to work on this issue and have learned some new information. In my current project, I’m loading data from a legacy Foxpro database into Rails so I can create new views and reports. I set up class methods in my model controllers to import the Foxpro data, using the excellent Ruby gem called “DBF”.

    Part of the task was fixing the data I was importing so it made sense in a Ruby on Rails environment. For example, the Foxpro system lists dates as one six-digit string (the date) and one two-digit string (the century). So it would list today’s date as “070716″ with a century of “20″. I wrote a number of methods to translate all this weirdness into true Ruby data.

    Try as I could, there was no way I could get the class methods that did the importing to recognize any of the data translation methods (which mostly work on strings). I made them local to the models. I moved them to a “DataTranslation” module in the lib/ directory. I put them in “application.rb”. Nothing — all I got was “no such method or variable.” In disgust, I moved all this stuff back to the “admin” controller that runs the view that allows you to do the importing. Works fine.

    I just ran some tests in the Futzit system described above. It turns out that class methods in the model can’t find any other method anywhere, other than the usual built-in Ruby methods. Even putting a method within the body of the class method didn’t work. In contrast, regular “instance” methods could call each other without difficulty. I need to learn some more Ruby before I can figure out what’s happening.

    There is a way to create methods common to all model instance methods: create a superclass. I tried the following:
    class ModelMommy < ActiveRecord::Base
      def do_mommy
        puts "*****In model mommy"
      end
    end

    Then I made each of the models subclasses of ModelMommy rather than ActiveRecord::Base. That way, every instance method in the models could call do_mommy. Alas, the class method “self.do_model” still couldn’t find do_mommy. Weird.

    —Jim Gagne—

    Vista Themes | Indoor Lighting | Dictionary