« Subversion Primer | Main | The Intangible Doppleganger »

RailRoad

Here are some notes I've collected as I've worked through developing my application on Rails:

PostgreSQL

I generally prefer using PostgreSQL for my personal projects. PostgreSQL's pedigree and consistent level of technical superiority means that I'll never have to second guess my decision regarding the database as an application's use begins to scale. Given Monty Widenius' rant against MySQL 5.1, I don't foresee it approaching the capabilities of PostgreSQL anytime soon.

Rails Database Adapters

That being said, there were a few caveats to keep in mind for Rails to use PostgreSQL as a back end store. The first is clarification on which gem adapter module is required for rails configuration. There are two - one which is a pure ruby driver, the other a compiled native driver. The postgres-pr (the pure ruby PostgreSQL driver) worked right out of the box. The pros of this driver is it is implemented in ruby; the con however, is that it is relatively slow compared to the native driver.

With the latest version of rails (2.2.2), I found that the postgresql gem is no longer recognized. It has been superceded by the postgres native adapter. Attempting to compile the postgres gem against my Fink managed PostgreSQL 8.3 installation resulted in the following errors:

Building native extensions.  This could take a while...
ERROR:  Error installing postgres:
	ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb install postgres

===========   WARNING   ===========

You are building this extension on OS X without setting the
ARCHFLAGS environment variable, and PostgreSQL does not appear
to have been built as a universal binary. If you are seeing this
message, that means that the build will probably fail.

Try setting the environment variable ARCHFLAGS
to '-arch i386' before building.

For example:
(in bash) $ export ARCHFLAGS='-arch i386'
(in tcsh) $ setenv ARCHFLAGS '-arch i386'

Then try building again.

===================================
checking for main() in -lpq... yes
checking for libpq-fe.h... yes
checking for libpq/libpq-fs.h... yes
checking for PQsetClientEncoding()... no
checking for pg_encoding_to_char()... no
checking for PQfreemem()... no
checking for PQserverVersion()... no
checking for PQescapeString()... no
creating Makefile

make
gcc -I. -I. -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 -I. -DHAVE_LIBPQ_FE_H -DHAVE_LIBPQ_LIBPQ_FS_H -I/sw/include/postgresql  -fno-common -arch ppc -arch i386 -Os -pipe -fno-common  -c libpq-compat.c
libpq-compat.c: In function 'PQescapeBytea':
libpq-compat.c:104: warning: incompatible implicit declaration of built-in function 'sprintf'
libpq-compat.c:104: warning: pointer targets in passing argument 1 of 'sprintf' differ in signedness
libpq-compat.c: In function 'PQunescapeBytea':
libpq-compat.c:158: warning: incompatible implicit declaration of built-in function 'strlen'
libpq-compat.c:158: warning: pointer targets in passing argument 1 of 'strlen' differ in signedness
libpq-compat.c: In function 'PQescapeBytea':
libpq-compat.c:104: warning: incompatible implicit declaration of built-in function 'sprintf'
libpq-compat.c:104: warning: pointer targets in passing argument 1 of 'sprintf' differ in signedness
libpq-compat.c: In function 'PQunescapeBytea':
libpq-compat.c:158: warning: incompatible implicit declaration of built-in function 'strlen'
libpq-compat.c:158: warning: pointer targets in passing argument 1 of 'strlen' differ in signedness
gcc -I. -I. -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0 -I. -DHAVE_LIBPQ_FE_H -DHAVE_LIBPQ_LIBPQ_FS_H -I/sw/include/postgresql  -fno-common -arch ppc -arch i386 -Os -pipe -fno-common  -c postgres.c
postgres.c:41: error: static declaration of 'PQserverVersion' follows non-static declaration
/sw/include/postgresql/libpq-fe.h:262: error: previous declaration of 'PQserverVersion' was here
postgres.c:41: error: static declaration of 'PQserverVersion' follows non-static declaration
/sw/include/postgresql/libpq-fe.h:262: error: previous declaration of 'PQserverVersion' was here
postgres.c: In function 'Init_postgres':
postgres.c:2676: error: 'pgconn_protocol_version' undeclared (first use in this function)
postgres.c:2676: error: (Each undeclared identifier is reported only once
postgres.c:2676: error: for each function it appears in.)
postgres.c:2677: error: 'pgconn_server_version' undeclared (first use in this function)
postgres.c: In function 'Init_postgres':
postgres.c:2676: error: 'pgconn_protocol_version' undeclared (first use in this function)
postgres.c:2676: error: (Each undeclared identifier is reported only once
postgres.c:2676: error: for each function it appears in.)
postgres.c:2677: error: 'pgconn_server_version' undeclared (first use in this function)
lipo: can't open input file: /var/tmp//ccZF2c6t.out (No such file or directory)
make: *** [postgres.o] Error 1

The error appeared despite setting the ARCHFLAGS environment variable and supplying the POSTGRES_INCLUDE argument to the gem install command. I finally got it to compile correctly after a bit of digging:

# sudo env ARCHFLAGS="-arch i386" gem install postgres

Note how the ARCHFLAGS needed to be passed into sudo. Good to know someone out there already figured it out.

To use either of these database adapters, you will need to ensure that the config/database.yml file specifies postgresql for the adapter parameter. The default is sqlite3. It's probably a good idea to have only either the ruby or native driver installed at any given time.

In addition, you should remove the database parameter from the database.yml file. These are only required for SQLite databases.

PostgreSQL Application Accounts

The last bit of configuration requires each of the different releases (development, test, and production) have their own database users. It is generally good practice to completely keep these environments isolated from one another to avoid accidental loss of data. The config/database.yml file requires additional information to configure these separate connections. Each entry in the database.yml should have the following parameters:

host : machine_name
username : database_user
password : database_password

Due to the sensitive nature of the information contained in the database.yml file, it is highly recommended that this file be NOT world readable.

All of this configuration is fruitless without actual PostgreSQL users. Use the following commands to create a database and user for each of these environments (these commands assume that you are the local PostgreSQL user):

# createuser -SdRlEP development
# createuser -SdRlEP test
# createuser -SdRlEP production

The options will result in PostgreSQL users which will not have superuser privileges or the ability to create new roles, but have privileges to create databases (for scaffold creation/teardown) and log in. As well, the account will have an encrypted password.

Once these accounts have been created, you will need databases to associate with each of them:

# createdb -E UTF8 -O development dev_database_name
# createdb -E UTF8 -O test test_database_name
# createdb -E UTF8 -O production prod_database_name

The last step requires enabling psql local machine connections so that you can inspect the structure of the database for each of these releases are the evolve over the development process. This requires modifying the data/pg_hba.conf file for PostgreSQL. There should be entries for each user and database for each local address group (localhost / local IPV4 / local IP6).

# "local" is for Unix domain socket connections only
local        dev_database_name              development	md5
local 	test_database_name            	test   		md5
local 	prod_database_name        	production	md5

# IPv4 local connections:
host 	dev_database_name	development	127.0.0.1/32	md5
host 	test_database_name	test   		127.0.0.1/32	md5
host 	prod_database_name	production	127.0.0.1/32	md5

# IPv6 local connections:
host 	dev_database_name	development	::1/128		md5
host 	test_database_name	test	        	::1/128		md5
host 	prod_database_name	production	::1/128		md5

By specifying the encrypt password option when creating the database roles, you ensure that passwords are never transmitted in clear text. As a consequence, you must use the MD5 authentication method in the pg_hba.conf file.

As with any configuration changes, you will have to restart the server for the changes to be applied. Test the accounts by making local psql connections by specifying both the user and database as options:

# psql -U development -d dev_database_name

If you plan on making the PostgreSQL account accessible over the network it is highly recommended that you secure your connections using SSL. Please refer to the PostgreSQL documentation to configure the server with OpenSSL certificates.

Here are some useful PostgreSQL/Ruby links:

Rails

Gem Management

Here are some useful commands for rails:

Get the a remote list of gems available.

# gem list --remote

Find a gem either in the local or remote repository.

# gem search --both name_of_gem

Display a remote gem's specifications.

# gem specification --remote name_of_gem

Search for remote generator gems which can be installed.

# gem search -r generator

Here are a list of useful gems:

  • ruby-debug

Rails Scaffolding

Rails excels as a tool for rapid prototyping with a focus on web applications which adhere to the Model-View-Controller (MVC) design paradigm. Using a relatively succinct number of ruby scripts, project scaffolding can be generated quickly and efficiently. Before projects can be automatically run in Xcode, the project must first be configured for use. This requires the following steps:

  1. Generate the basic project framework using the following command:

    # rails project_name
    

    This will generate a core project scaffold which contains all the required files and default configuration for a basic rails application.

  2. Open Xcode and drag the generated application directory into the Project & Sources category in the Organizer window.
  3. Click the Run icon and select Edit Actions ...
  4. Create a new item named.
    • Create a new item named start server.
    • In the Directory drop down, select the Top Level Organizer Item option.
    • In the Command text box, enter the following command : script/server. This corresponds to a ruby script located in the script directory generated by the initial rails scaffolding command.
    • Ensure that there are no Arguments.
    • Click OK to save the Run Action.
  5. At this point, clicking the Run icon should start the ruby application on your local machine. Pointing a browser to the http://localhost:3000 address should display the default web page for the application.

By default, Rails uses the Active Record pattern to persist application objects to a relational database.

To generate scaffolding for application objects which create default entries for each of the MVC components issue the following command from the top of the application directory:

# ./script/generate scaffold object_name property:data_type property:data_type

where:

  • the object_name is the class of application object you wish to create scaffolding for.
  • the property is a property or attribute of the application object.
  • the data_type is the storage data type of property.

In addition to the MVC files, the scaffold generate script also creates a migration file which allows database schema to upgraded/downgraded as the application is developed. In addition, by specifying different development, test, and production databases in the configuration, these separate environments remain isolated.

Caveats

2.2.2 Release Notes

There are number of significant changes introduced to Rails 2.2.2. These result in older applications being unable to run in the new Rails environment. It is highly recommended that you read the Release Notes for 2.2.2 to be sure that your application is not broken by this upgrade. The following is a list of a number of issues that I encountered.


The following command is deprecated in Rails 2.2.2 in config/environments/development.rb:

config.action_view.cache_template_extensions = false

Using Inflectors requires a change to the default inflections.rb in Rails 2.2.2 :

 ActiveSupport::Inflector.inflections do |inflect|
   inflect.plural /^(ox)$/i, '\1en'
   inflect.singular /^(ox)en/i, '\1'
 end

Failing to add the ActiveSupport namespace qualifier will result in the following initialization error :

/Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:445:in `load_missing_constant': uninitialized constant Inflector (NameError)
	from /Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:77:in `const_missing'
	from /Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:89:in `const_missing'
	from /Users/mel/Documents/Workspace/vuespace/src/config/initializers/inflections.rb:5
	from /Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:142:in `load_without_new_constant_marking'
	from /Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:142:in `load'
	from /Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:521:in `new_constants_in'
	from /Library/Ruby/Gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:142:in `load'
	from /Library/Ruby/Gems/1.8/gems/rails-2.2.2/lib/initializer.rb:550:in `load_application_initializers'
	 ... 32 levels...
	from /Library/Ruby/Gems/1.8/gems/rails-2.2.2/lib/commands/server.rb:49
	from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `gem_original_require'
	from /Library/Ruby/Site/1.8/rubygems/custom_require.rb:31:in `require'
	from /Users/mel/Documents/Workspace/vuespace/src/script/server:3

Rails now supports database connection pooling to support larger numbers of concurrent users. This feature is enabled by default in the supplied database.yml file where the pool parameter (which specifies the maximum number of connections to use) is specified with a default. In addition, configuration now also supports the wait_timeout (the amount of time to wait before trying another connection; the default is 5 seconds) parameter.

TrackBack

TrackBack URL for this entry:
http://www.z1r0.com/mt/mt-tb.cgi/66

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About

This page contains a single entry from the blog posted on December 8, 2008 7:36 PM.

Many more can be found on the main index page or by looking through the archives.

Colophon

Creative Commons License
This weblog is licensed under a Creative Commons License.