Monday, March 10, 2008

I am a leet rails haxor...

Been playing with Rails a bit, to see how easy it is for me to learn (since I have some nodding familiarity with Ruby). It's generally fun, but I really disliked the new (in v2) style scaffolding. Apparently, the scaffold generator no longer inspects any existing tables in generating the views. You've got to list everything out on the command line. Very disappointing. I whined about it here.

Encouraged by the kind folks on the Rails Talk group, I tried writing my own scaffold generator--generally a complete ripoff of the original, except that if you don't specify any attributes on the command line, it sniffs the db for a table w/the proper name & then packs the arguments with whatever it finds. It's not fully tested, but seems to work for me. Here is the relevant code:


def initialize(runtime_args, runtime_options = {})

# TODO: test this proper (adapt rails' scaffold_test.rb)
# This is hokey, but it's within my powers.
# Rescue the noob who generates scaffold
# on an existing table w/out specifying attributes
# on the command line.
si_uncounted_arguments = %w(--skip-timestamps --skip-migration)
if (runtime_args - si_uncounted_arguments).length == 1 then
# Called in "static introspective" mode--pack
# runtime_args with name:type pairs if we find
# an existing table.
si_table_name = runtime_args[0].pluralize.underscore
puts("Called without any attribute specs on the command
line--looking for a table called '#{si_table_name}'
from which to infer attributes...")
begin
si_cols = ActiveRecord::Base.connection.columns(si_table_name, "#{name} Columns")
rescue ActiveRecord::StatementInvalid => err
# Table not found.
puts("Sorry--table '#{si_table_name}' not found--you're
going to have a lame-ass scaffold. Did you forget
to run 'rake db:migrate' maybe?")
else
si_attributes = []
si_ignorable_fields = %w(id:integer created_at:datetime updated_at:datetime)
si_cols.each do |c|
si_attributes << "#{c.name}:#{c.type}"
end
si_attributes -= si_ignorable_fields
runtime_args += si_attributes
# No need for a migration in this case--we're using the existing table.
runtime_args += %w(--skip-migration) unless
runtime_args.include?('--skip-migration')
end
else
puts("User specified attributes on the command
line--no introspection done.")
end

super

#etc.

end



I think this will work well enough for my purposes. I'll be using it (gotta get back to trying to learn Rails) for my toy app & will see if it serves me. If it does, maybe I'll try to gem it up (or package it up however such things get packaged up) and put it someplace accessible. In the meantime feel free to steal it, suggest improvements, make fun of it, etc.

3 comments:

monde said...

awesome!

John Congdon said...

Do you have any advice for someone just starting in Rails that already has an established DB?

1) Do I need to create the migrations?
2) Should I start from scratch and then port all of my old data over?
3) Ignore migrations all together?

Roy said...

Hey John,

Tough to say. I guess my inclination would be to go with your option 2.

Rails is, as they say "opinionated", and you're probably better off just learning how to go along w/its opinions than learning the framework and learning the extra bits you need to override its conventions.

But don't take my word for it--I'm really no expert. Surf around & ask on the rails-talk google group if you don't get a good sense of your best path.

Cheers!