Friday, February 20, 2009

How to use Ruby on Rails to integrate with a legacy database while leveraging scaffolding.

I am trying to be as lazy as possible...

rails_app $ rails independentreps -d mysql
Edit config/database.yml with specific database name, host, username, password. Add additional environment records if required. For example:

development:
adapter: mysql
encoding: utf8
database: indrep_development
pool: 5
username: root
password: pw
host: localhost

test:
adapter: mysql
encoding: utf8
database: indrep_test
pool: 5
username: ...
...
production:
...
database: indrep_production
...
## Additional Environment Records
legacy_production:
adapter: mysql
encoding: utf8
database: reps_db
pool: 5
username: admin
password: letmein
host: 192.168.0.10
port: 13540

From within the application folder, use the generation features of the Ruby on Rails framework
rails_app $ ruby script/generate model Client
rails_app $ ruby script/generate scaffold Client
So scaffolding is generated for the first time.


Next, edit app/models/client.rb with set_table_name, set_primary_key with actual identifiers from legacy database
rails_app $ script/console
>> Client
=> will spit out Ruby-friendly style of describing table schema/model, i.e. Client model. Copy this result to clipboard.
>> exit
rails_app $ rm app/helpers/clients_helper.rb


So we generate scaffolding again, but this time with value-added features of our schema/model attributes.
rails_app $ ruby script/generate scaffold Client <Paste model attributes from clipboard here.> However take out the commas between attributes, and the spaces between the attribute and datatype.
replace files if asked.
rails_app $ mongrel_rails start -d
visit http://localhost:3000/Clients

Why do we generate scaffold twice instead of just once? There is no logical reason for it when the first run creates the controller.rb and view templates (index, new, show, edit), while the second run would do the same, only with the attributes all spelled out. Why not just edit the model client.rb with the legacy table information before running script/generate scaffold? Well for some reason if scaffolding is not generated twice, you will end up with the following error:

ActionController::RoutingError in Clients#index

instead of nice table rows with Show Edit Destroy working links.

I know that generating scaffold twice will add the line map.resoures :clients twice to the config/routes.rb file. But manually duplicating that line in routes.rb does not solve the problem.
One day, when I have the time and am able to understand Ruby better I'll take a look at this article about route generation. I figure it will explain this strange behaviour.
The following shows a part of the Full Trace response generated by this strange problem.

client_url failed to generate from {:action=>"show", :controller=>"clients", :id=>#<Client pkId: 2, Name: "Make It Work Enterprises"...

Extracted source (around line #57):
56: <td><%=h client.IDamandar %></td>
57: <td><%= link_to 'Show', client %></td>
58: <td><%= link_to 'Edit', edit_client_path(client) %></td>

/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/routing/route_set.rb:377:in `raise_named_route_error'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/routing/route_set.rb:341:in `generate'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb:208:in `rewrite_path'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb:187:in `rewrite_url'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/url_rewriter.rb:165:in `rewrite'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/base.rb:626:in `url_for'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb:85:in `send'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb:85:in `url_for'
(eval):16:in `client_path'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb:112:in `__send__'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb:112:in `polymorphic_url'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/polymorphic_routes.rb:119:in `polymorphic_path'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb:91:in `url_for'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/helpers/url_helper.rb:228:in `link_to'
app/views/clients/index.html.erb:107
app/views/clients/index.html.erb:56:in `each'
app/views/clients/index.html.erb:56
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/renderable.rb:39:in `send'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/renderable.rb:39:in `render'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/template.rb:73:in `render_template'
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_view/base.rb:256:in `render'

blog comments powered by Disqus