Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

My all new Ruby Database Script Runner - now with Objects!

Back in December I blogged about how to access ActiveRecord & Migrations outside of Ruby on Rails. It was a little to scriptish for my liking so I've refactored it in a couple of classes instead.

Before you had to copy the code to a new file and add your database migration inside the Script.run method. I've now broken this up into a DatabaseScript class that extends ActiveRecord's Migration and has the guts of how and where to run any database commands and then any number of script classes that do the actual work.

The classes that you write on a day to day basis are now similar in style to the standard Rails Migrations:

require 'database_script'

class UniqueScriptName < DatabaseScript
def self.run
# your migration code goes here
end
end
For example I want to update my_table and set all the updated column for all rows to be true:
require 'database_script'

class UpdateMyTable < DatabaseScript
def self.run
execute('update my_table set updated = true')
end
end
If you want to use a specific database connection (rather than one of the standard Rails ones development, testing, production) then add the following method in the above class:
  def self.database_connection_details
<<-YAML
custom:
adapter: ????
database: ????
username: ????
password: ????
YAML
end
This is what the DatabaseScript class looks like:
require 'active_record'

class DatabaseScript < ActiveRecord::Migration; end

def self.script_name(script_file)
return *script_file.scan(/(.*).rb/).first
end

def script_class(script_name)
script_name.camelize.constantize
end

script_name = script_name($0)
require script_name
script_class = script_class(script_name)

# to run this within the context of a Rails app then pass your environment on the command line:
# ruby <script name>.rb development
database_type = ARGV[0]

if database_type.nil?
database_yaml = script_class.database_connection_details
else
# if you have a Rails project then place your scripts in db/script
database_yaml = IO.read('../../config/database.yml')
end

databases = YAML::load(database_yaml)

database_type = databases.keys[0] if database_type.nil?

ActiveRecord::Base.establish_connection(databases[database_type])
script_class.run
Technorati Tags: , , , ,

A hint when working with Rails Migrations and legacy databases (ID columns)

Had an interesting problem recently when writing some Rail migrations scripts for an old non-Rails MySQL database. After writing the migration and running it via Rake we found that it was erroring with a complaint about the primary key.

We checked that the table had an id column, it did but instead of id it was ID. ActiveRecord is very specific in what it looks for and so was not viewing this ID column as the primary key that it required when adding new rows to the table.

A quick lookup in the ActiveRecord documentation pointed out the solution: use set_primary_key in your ActiveRecord class:

class SomeLegacyTable < ActiveRecord::Base
set_primary_key :ID
end
Technorati Tags: , , , ,

How to access ActiveRecord & Migrations outside of Ruby on Rails

Rails migrations are an extremely handy way of capturing changes to your database and rolling them back if they are incorrect.

Manipulating or querying a database is a pretty useful tool and ActiveRecord turns your tables into objects so I looked into how to access these features outside of a Rails environment.

I wanted to be able to either specify the database connection details directly in the script if I was running it outside of a Rails application, or to be able to bind into the Rails application's database.yml database configuration file.

To run it as a standalone script with the database details described inside the script run:

ruby play_with_the_database.rb
To run it within the context of a Rails application place the script inside a new directory called db/script and run:
ruby play_with_the_database.rb development
If you want to run the above script against the production database run:
ruby play_with_the_database.rb production
I'll show you the ruby code that I came up with and then explain the important bits below.
require 'active_record'
require 'pp'

database_type = ARGV[0]

if database_type.nil?
database_type = 'custom'
database_yaml = <<-YAML
#{database_type}:
adapter: oracle
database: database.andrewbeacock.com/beacock01
username: my_schema
password: my_schema
YAML
else
# if you have a Rails project then place your scripts in db/script
database_yaml = IO.read('../../config/database.yml')
end

class MyTable < ActiveRecord::Base; set_table_name :my_table; end

class Script < ActiveRecord::Migration
def self.run
execute('update my_table set updated = true')
pp MyTable.find(:all)
end
end

databases = YAML::load(database_yaml)
ActiveRecord::Base.establish_connection(databases[database_type])
Script.run
  • database_type - this is the RAILS_ENV that you want to run the database script against if you are running within a Rails application, it's empty at the start of the script and set to the value that you pass as the first argument on the command line. If an argument is present it looks up that value in the database.yml file otherwise it uses the inlined database connection details within the script.

  • class MyTable - any ActiveRecord object definitions go here allowing you to map objects to tables.

  • def self.run - Place the guts of your database script within this method so that it has access to all the ActiveRecord objects as well as any methods that migrations have access to (such as execute, create_table, add_column, remove_index, etc.).
This script was spawned from two things, a requirement to change the values in a database table without it being a fully tracked migration and a post on Dave Thomas's blog regarding Migrations Outside Rails.

Enjoy!

Technorati Tags: , , , , , ,

How to rollback Rails database migrations

When you perform database migrations in Rails you use the following command:

rake db:migrate
If you want to roll back your change to need need to find out what version your database is currently at and then roll back to a previous version:
rake db:migrate VERSION=<version to roll back to>
I've never liked this way to roll back, seems to much like hard work to me so I found a migration rollback script on the programmingishard site and simplified so that it only rollback by one version:
    namespace :db do
namespace :migrate do
desc "Rollback the database schema to the previous version"
task :rollback => :environment do
previous_version = ActiveRecord::Migrator.current_version.to_i - 1
ActiveRecord::Migrator.migrate("db/migrate/", previous_version)
puts "Schema rolled back to previous verison (#{previous_version})."
end
end
To use this scriptlet, copy and paste the code into a file called db_rollback.rake and place it the lib/tasks directory within your Rails application.

To use it to roll back your most recent migration simply run:
rake db:migrate:rollback
Technorati Tags: , , , , , , ,

How to get the schema version number out of your Rails migrations

When you want to revert your Rails migration you need the version number so that you can perform the rollback:

rake db:migration VERSION=?
One way is to access the database and take a look in the schema_info table to find the version number.

An easier way is to add an additional task to Rake (found via the Quoted-Printable):
namespace :db do
desc 'Print the current database migration version number'
task :version => :environment do
puts ActiveRecord::Migrator.current_version
end
end
Copy this code into a file called db_version.rake and place it in the lib/tasks directory within your Rails application.

To find out the current version number, simply run:
rake db:version
Technorati Tags: , , , , ,

A Capistrano recipe for restarting Apache 2 (on Linux)

Capistrano is the rather excellent tool for automating Rails application deployment. But it can do a lot more than just uploading Rails applications to live webservers. It's scripts are basically Rake scripts and so have the full power of Ruby behind then so you can pretty much write code to do anything that you want.

I wanted a way to reload the configuration for my Apache2 web server running on my home Ubuntu server. I found a post on Mongrel and Capistrano 2.0 by John Ward which showed a very nifty way to create Capistrano tasks for variants of the same base command.

My adapted version for controlling Apache is:

namespace :apache do
[:stop, :start, :restart, :reload].each do |action|
desc "#{action.to_s.capitalize} Apache"
task action, :roles => :web do
invoke_command "/etc/init.d/apache2 #{action.to_s}", :via => run_method
end
end
end
Add this code to your config/deploy.rb. This will add four new tasks to Capistrano which you can use to restart or reload Apache:
cap apache:stop
cap apache:start
cap apache:restart
cap apache:reload
Technorati Tags: , , , , , , ,

How to stop schema.rb from being generated when running Rails Migrations

A couple of days ago I blogged about why rails migrations caused the database schema to be dumped into schema.rb. I've now found a way to stop it from happening without changing the schema_type or modifying any core Rails or Rake classes.

What I planned to do was override the db:migrate rake task to do the same as the default one bar the schema dump. My first attempt failed as it appears that rake will allow more than one task to have the same name, it runs then one after the other. The result of this was some strange migrations plus the delay due to dumping the schema!

After a little googling I found an excellent post on Matthew Bass's blog entitled Overriding existing Rake tasks. This described a similar situation and I've based my code on his advice.

I needed to delete the existing db:migrate task before adding my new one. You can't just call delete task on the Rake::Application class (there's no such method) so Matthew's suggestion was to add a delete_task method to the Rake::TaskManager module (that has access to the tasks) and then call this method before we define our new db:migrate task. The finished script is given below:

# add a delete_task method to the TaskManager and delete db:migrate
Rake::TaskManager.class_eval do
def delete_task(task_name)
@tasks.delete(task_name.to_s)
end

Rake.application.delete_task("db:migrate")
end

# define a new db:migrate that did the same as the old one bar the schema dump
namespace :db do
desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x, don't run db:schema:dump at the end."

task :migrate => :environment do
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
end
end
Technorati Tags: , , , , ,

Why do Rails Migrations cause the database schema to be dumped into schema.rb?

Have you ever wondered why rake db:migrate took so long to complete when you ran a simple migration script? The reason is that it's dumping your database's entire schema into db/schema.rb just like rake db:schema:dump does.

I had to get to the bottom of why this was happening and searching the internet was not providing any answers, so I took to searching the Rails codebase to see if I could find any clues.

databases.rake in /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/ showed the root cause:

namespace :db do
desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x"
task :migrate => :environment do
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
If the 'schema_format' is set to':ruby' then run the rake task 'db:schema:dump' after we have finished running the migrations. But where is schema_format set? A look in config/environment.rb gave a clue:
  # Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
This configuration value is commented out and indicates that if this is enabled then the schema dumps will be in SQL format rather than the more portable (and in my mind preferable) Ruby format.

base.rb in /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/ gave the final clue:
    # Specifies the format to use when dumping the database schema with Rails'
# Rakefile. If :sql, the schema is dumped as (potentially database-
# specific) SQL statements. If :ruby, the schema is dumped as an
# ActiveRecord::Schema file which can be loaded into any database that
# supports migrations. Use :ruby if you want to have different database
# adapters for, e.g., your development and test environments.
cattr_accessor :schema_format , :instance_writer => false
@@schema_format = :ruby
So base.rb sets the schema_format variable to be ruby and therefore enables the automatic dumping of the schema after every migration.

Now I just have to figure out how to disable it...

Update: I've figured out a way to disable schema.rb from being generated.

Technorati Tags: , , , , ,

Undefined 'define_a_column' method when using Rail's Oracle OCI adapter?

One of the first hurdles I ran into when I started with Rails migrations was:

undefined method define_a_column' for class OCI8::Cursor'

It means that your LD_LIBRARY_PATH environment variable does not include the Oracle client binaries.

(I got my clue on how to solve this issue from this old defect ticket on the Rails Trac).

Technorati Tags: , , , ,

Some useful Ruby & Rails firefox search plugins

I've found the following two Firefox search plugins useful and thought
I'd pass then on:

RubyInside

This plugin searches a massive list of sites (scroll to the comments for more details) maintained by Peter Cooper, a UK Rails developer who runs the excellent RailsInside website.

RailsHelp

This plugin was developed by Nick Cody to search the RailsHelp.com website.
Update - railshelp.com seems to be down at the moment...

How to change your Rails database adapter from OCI to Oracle

I've started to use Rails migrations recently connecting to an Oracle database.

After reading many examples of how to configure the database.yml file on the Rails wiki (as well as ones from Oracle's own website), I ended up with something like:

development:
adapter: oci
host: database.com/mySID
username: schema_name
password: schema_name
This worked fine, but after researching other migration-related topics I read that oci was the 'old' way to connect to Oracle, the 'new' adapter to use is oracle.

A tutorial from Oracle showing usage of the new 'oracle' adapter (scroll down to the section entitled "Part 1: Of configuration, Cursors, and Rows") suggested that your config should look like this:
development:
adapter: oracle
host: DEBIAN
username: rails
password: rails
Note: DEBIAN was the name of their TNS entry specified in their local tnsadmin.ora file.

I didn't want to use a reference to a local tnsadmin.ora file on my machine, I wanted to specify the host and SID explicitly. After many failed attempts at specifying this in different formats I happened across this Rails patch ticket regarding the change from 'oci' to 'oracle' - the winning clue was:

To use the adapter with it's new name, simply change your database.yml file to include:
adapter: oracle
database: <your db name here>

Note that database is now used instead of host.

So the problem was that I was entering the database server details in the host field (like all the examples said to) but for a non-TNS named server you need to use the database field.

My fully working Oracle database.yml config entry now looks like this:
development:
adapter: oracle
database: database.com/mySID
username: schema_name
password: schema_name
Technorati Tags: , , , , ,

Ruby on Rails at June's AgileNorth meetup

Tonight was June's AgileNorth meetup and the topic was Ruby on Rails. It was hosted by David Draper who did an excellent job of introducing Rails in an easy to digest way even though he has very little Rails experience himself. Just shows what a good trainer can do!

He demo'ed a shortened version of Curt Hibbs's Recipe Cookbook tutorial using the RadRails plugin for Eclipse as his IDE. David started by creating the database in mysql (note: just the database - no tables) and then used RadRails to create the initial 'cookbook' application environment. He used the 'migration' feature of rails to create the database tables which were auto-generated by ruby code - this was one feature of rails that I was not aware of and was particularly impressive (for me at least!).

A great Ruby on Rails bookHe used the scaffolding tools of rails to auto-generate the recipe & category models, the cookbook and category controllers and all the associated views. All of this was done in little steps so that he could show the changes that were made to the code at each stage. I lot of this I had seen before as I ran through the 'Four Days on Rails' last Christmas but it was a nice refresher on the power of Rails.

Following David's walk-through we had a thirty minute discussion around the benefits of Rails and how to introduce Rails to systems with an existing database structure - could it be used to map onto this schema rather than creating a Rails-friendly one from scratch?

I really enjoyed this evening's talk, David did an excellent job if giving a brief overview of how to get kick-started with Rails, and it has re-energised me to think about how I can introduce it in some aspect at work.

Note: I've tagged quite a few of the items discussed with the 'agilenorth' tag over on del.icio.us.

Technorati Tags: , , , ,

Ruby :symbols understood - "A symbol is an object with a name"


Back in August
, I blogged about Ruby's symbols as I tried to get my head around what they really were. Jim Weirich made a post yesterday entitled 'Symbols Are Not Immutable Strings' which really helped solidify my understanding, his main two points being:

* Symbols are not immutable strings
* A symbol is an object with a name

Thanks Jim!

Technorati Tags: , ,

Migration to Ruby for Java developers

As an experienced Java developer converting to a Ruby newbie I found the following posts from Sam Newman rather helpful in making the switch:

* Ruby For Java (and C#) Programmers, Part 1 - Conventions, methods, modules, and classes
* Ruby For Java (and C#) Programmers, Part 2 - Operators, methods, and more on classes
* Ruby For Java (and C#) Programmers, Part 3 - Introducing Arrays, Hashes and the typing system

If your anything like me having a good book by your side can really help when picking up a new language. There is one Ruby book that everyone seems to have, Programming Ruby: The Pragmatic Programmer's Guide. It really is an excellent book, I liked the first edition so much that I bought the second as well!

Let me know if you find any other resources that would be helpful to anyone else making the switch.

Another useful resource for people interested in see what Ruby is all about is Try Ruby! - an online interactive Ruby terminal that runs in a browser, created by the rather insane why the lucky stiff. Try it, you might like it!

Technorati Tags: , , , , ,

DevBoi - the Ruby on Rails & web development quick reference sidebar for Firefox

If you use Firefox and develop either websites in HTML/XHTML, CSS & JavaScript or web applications in Ruby on Rails (or PHP) then the DevBoi sidebar written by Martin Cohen is a pretty handy extension to have.
A great Ruby on Rails book
Rob Sanheim has a great post introducing the new features and the offline version comes highly recommended if you ever want to develop whilst not connected to the net.

Technorati Tags: , , , , , , , ,

Ruby to replace Java - or compliment it?

I've been singing Ruby's praises ever since I started researching the language a couple of years ago. I've not written much Ruby code, picked up the 1st pickaxe book, read it a couple of times, then the next year came round so I've now got the 2nd edition!

I've written a few Ruby 'scripts' (no objects or classes) and have been impressed with how quickly I have been able to get it to do my bidding compared to Java. One application running on Linux needed to read some data from a Postgresql database, generate a Windows Zip file then FTP it to a remote server. When I thought about coding this in Java I started to shiver, Java's strong point has never been it's ease to interact with the command line, and FTP libraries are not part of the java. or javax. packages.

Several people have asked "so are you saying that Java is rubbish and we should all move to Ruby?". My basic answer is "no, but I want to really get into a scripting language and Ruby seems OO enough for me to get into it fairly easily".

Dave Thomas (one of the Pragmatic Programmers) said it best recently when he said "not exclusively: you're likely to want to use Rails as well as Java." Dave has started a number of posts around this topic the first is titled "Is Ruby Better Than ...?".

Java and Ruby are similar in some respects and different in others, if I need to write some quick utility scripts or manipulate some text files then Ruby would be my choice. If I need to access some middleware message queues and fit into an existing application server infrastructure then Java would be the language of choice.

At the end of the day, it's all down to adding strings to your bow, sometimes the choice is just which strings to invest your time and effort in...

Technorati Tags: , , , ,

Trying to understand Ruby :symbols

After reading Kevin Clark's recent post on Ruby symbols made me want to learn more about this mysterious part of the Ruby language. After writing some unit tests to ensure that a symbol really is the same object as any other with the same name, I have come to the following conclusion: symbols are immutable string objects, same named symbols share the same object and therefore the same object_id.

Kevin's example points out that they make great keys for hashs, and other indicators of what action to take (:get, :post, etc.)

Coming from a Java background, symbols have made me hit my head a number of times, this rather helpful post from Rob Sanheim helped clear things up a lot for me.

Why the lucky stiff also has a good explaination, he suggests:

Symbols

Symbols are words that look just like variables. Again, they may contain letters, digits, or underscores. But they start with a colon.

:a, :b, or :ponce_de_leon are examples.

Symbols are lightweight strings. Usually, symbols are used in situations where you need a string but you won’t be printing it to the screen.

You could say a symbol is a bit easier on the computer. It’s like an antacid. The colon indicates the bubbles trickling up from your computer’s stomach as it digests the symbol. Ah. Sweet, sweet relief.

So I've got a number of different explainations of symbols, I've written 5 unit tests about them, but I'm still not 100% sure of all their uses. I suppost I just need to get Rails installed and start playing with that to really see how symbols should be used...

Technorati Tags: , , , , ,