thor/rails generators, missing docs/source

Sorry catalogers, a programming post.

Thor is a great ruby library for automating tasks. It’s really slick.

It’s the engine on top of which Rails3 Generators are built. That’s where I’m working with them.

However, a bunch of things are pretty poorly documented. Made even more confusing by the fact that Rails changes thor, in the context of Rails generators,  in some not entirely documented ways.  So I figure, okay, I’ll look at the source — but there are some things I’m not successful at finding even in the source, wondering if I’ve succesfully found the latest version of the source as used in Rails3 generators or what.

So I’m going to document a few useful things I’ve discovered here, so maybe someone else can find it on google and save some time. This is also a cry for help, if anyone can explain what’s going on and where the right source is.

The Actions You Didn’t Know About

Some Thor actions are documented are documented on the rdoc.info for wycats/thor. And can be looked at in the source for wycats/thor too.

In the context of Rails3 generators, Rails3 adds some more actions, which are mentioned in the Rails3 Generators Guide. Okay, I get it, Rails is subclassing or otherwise adding more stuff, and great job in actually documenting it in the Guide, no prob.

But there are a bunch more thor actions available too. Not sure how I found this, but I found them listed on this other automatically generated thor rdoc, at http://textmate.rubyforge.org/thor/Thor/Actions.html.

The actions listed there actually do work in a Rails3 generator. I found “get” to fetch a remote http file particularly useful, and there were a few others there I had started to write myself before discovering there were built in actions for em.

You’ve got to look in all three documentation places to get all of em; each has their own, with no overlap.

But where the heck are these actions coming from? Anyone know? I can’t find em in the source at github wycats/thor.  Is that not actually the version of thor used in Rails?  textmate.rubyforge.org….   is the most recent version of thor actually on rubyforge not github? That’d be odd. But textmate.rubyforge.org, that’s a project for textmate, huh?  Anyone have any idea what’s up there?  There are a few questions I have about some of those actions which could be answered by the source, and a few places I wouldn’t mind submitting a patch to improve things if I knew where the source was and who to submit it to. Mysteries.

Intentionally fatal erroring

Let’s say you are writing a task/generator, and something happens and you want to end the task early. You know, an “exceptional” condition. Say you are downloading something from a remote location but it can’t be downloaded. You want to let the user know and cancel the rest of the task.

If you just raise an exception, thor will catch it and stop running the rest of the task. But it’ll also print out an ugly complete stack trace to the user, assuming this is a developer error that the developer will want a stack trace for.

But in this case it’s not a developer error, we intentionally want to end the task early for reason of exceptional conditions.

Turns out if you raise a Thor::Error instead, thor will catch it and print out the #message without a stack trace. I did find that one by looking through the Thor source on github wycats/thor.  It was documented kind of suggesting it was only meant to be used internally by Thor for argument-parsing type errors, but it works, and Dan Funk noted it’s used by Rails3 generators too (thanks Dan), so if it’s good enough for Rails source it’s good enough for me.

Except my example above was kind of a lie. “If you try to download something from a remote location…”  If you use the hidden thor get action for that, if you pass it a LOCAL file path (not documented to work but it does) that does not exist, thor will indeed raise a Thor::Error and print out an error without a backtrace.  But if you ask for a remote http: address, which returns a 404 or is otherwise unreachable…. the exception raised by open-uri is just raised all the way up, and thor will print out the stack trace for it. Annoying. This is something I’d submit a patch enhancement for if I could figure out where the source was and who (if anyone) maintained it. (I don’t have the energy to muss my code up trying to catch it myself and re-raise as Thor::Action).

Rails Generator difference via Thor: arguments/options

A Rails3 generator is mostly just an ordinary Thor task, with some extra built-in actions provided by Rails3.

But it’s different in one key way. An ordinary Thor task — at least according to the documentation, I haven’t tried it myself — has you put individual actions which take their own arguments and are run individually, in individual methods in the task class.

A Rails3 generator, on the other hand, will run every task method in the class when executed.  This fact is mentioned in the Rails Guide.

But this has an implication for arguments/options that isn’t covered in the Rails Guide.  And when you go looking at the Thor documentation (at least the documentation I’ve found!) for more details, this might confuse you. Ordinarily, in the Thor documentation, it talks about using #method_option and #argument to add options to a task. But in the Rails3 generator world, you use #class_option (a class method that takes the same arguments as #method_option), and a class-level #argument method (taking the same arguments as the instance level #argument method) to do the same thing.

Neither of these methods you actually use in Rails Generators are documented in Thor, neither are they mentioned in the Rails Guide. I found em by looking at existing Rails generators source on the web. Perhaps they’re added by Rails extensions to thor.  I haven’t hunted down the source code for Rails extensions yet, or any available rdoc if there are any. (If such exist, it might be nice if the rails Guide linked to em).

I haven’t figured out if it’s possible to ask Rails to run just one method from your Rails Generator, classic thor style.  Sometimes it would be useful.

outputting messages

#say and #say_status are standard in Thor and documented (sort of), but not mentioned on the Generators Guide. But you can use em in generators. #say_status is what will get you those nice padded label/sentance outputs you see in Rails generators frequently.

It’s hard to find the rdoc or source becuase of the way it’s implemented with delegation to use different versions depending on whether a color terminal is present. (But I found the source/rdoc eventually, not too bad).

You can pass a color option for the text in #say or the label in #say_status that will use color if there’s a color interactive terminal (and I _think_ avoid the color if it’s not an interactive TTY but I’m not sure).

say("A message", :green)
say_status("something", "did it") # will be green
say_status("error", "this is bad", :red)

The colors available correspond to constants in Thor::Shell::Color

This entry was posted in General. Bookmark the permalink.

4 Responses to thor/rails generators, missing docs/source

  1. michael says:

    Thanks for the links!

    That last arguments/options difference you mention sounds like the difference between inheriting from Thor (each methods becomes a thor task, and you can enhance them with method_options) vs. inheriting from Thor::Group (the class becomes a thor task and all the methods within it are run in order, and you resort to class_options instead of method_options for parameters). I don’t know the reason for the seemingly split personality when it comes to defining tasks, but I do know that even though it’s possible you really really don’t want to mix and match the two styles in the same project if you can avoid it.

  2. jrochkind says:

    Ah, thank you Michael! With that tip, it’s easier to understand what’s going on and consult the neccesary documentation — I hadn’t managed to figure that out on my own. (Why is this stuff SO under-documented? The Thor documentation doesn’t seem to mention that inheriting from Thor::Group is possible, nor does the Rails documentation mention that this is what Rails tasks do. Just one sentence in each place would be so helpful!)

  3. Matt says:

    You can run just one task by placing the rest in a no_tasks block. E.g.
    no_tasks do
    def not_automatically_called
    puts ‘I am not_automatically_called!’
    end
    end
    You can still call not_automatically_called by any methods in the generator class.

    Now I have a question:
    When I use the Thor action “copy_file” in a class defined in a separate file, included via “require”, it in fact successfully copies the file, but it doesn’t output the green text informing the user that the file is copied. It only seems to output this message when placed directly in the generator file. How do you get it to output the green text when included in a separate file?

  4. Andrew Hao says:

    Thank you! This helped me a lot in transitioning a Thor task to a Generator.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s