Connecting Rails to a Database

Throughout decades of client-server computing, the architecture of database-driven applications has been designed by database administrators working in the "enterprise". This architecture places the responsibility for managing data squarely in the database itself. Applications are considered "dumb", not to be trusted with vital tasks like data validation and formatting, or managing relationships between different tables. Using this traditional architecture, we might manage our Intranet application's data structures (see Chapter 2) like this:

  • Stored procedures could be used to manage complex operations on the database. For example, inserting a new person, company, and associated addresses might be done using a single call to a stored procedure. This would insert new records into the appropriate tables, manage foreign key relationships between the new records, and perform validation of data.
  • Cascade operations could be used to manage dependencies between records in related tables. For example, if the id field of a record in the companies table changed, any references to that in the people table would be updated.
  • Views could be used to retrieve, sort, and format data from one or more tables using a single query. In our case, a view might be used to retrieve the names, phone numbers, and website addresses from the companies table, ordered by company name.

By contrast, in the Rails view of the world, the application is king, and the database is its servant. This is due to Rails being the product of web developers: in the web development environment, databases are typically unsophisticated buckets for holding "stuff" (because they are often designed by people who aren't full-time database administrators).

However, beyond this, performing database operations through model classes simplifies database access considerably: making a change to an application can be done in the high-level application code, rather than by modifying low-level SQL code in the database layer. Therefore, operations, which would be delegated to the database in the traditional client-server architecture (such as the ones in the bullet points above) are instead handled by ActiveRecord in a Rails application.

ActiveRecord is a so-called object-relational mapping (ORM) layer (also known as a relational persistence layer), similar in scope to Hibernate (Java) or ADODB (PHP). Instead of dealing with the database through SQL statements, an ORM layer presents records in a database table as a series of objects; it also provides class-level methods for creating database connections, querying tables, and so on. The fields in a record are not read or written directly, but accessed through instance methods on an object. This reduces the complexity and tedium of working with a relational database, as the onus for creating database connections, correctly formatting SQL statements, escaping quote marks in strings, etc. is moved into the ORM layer, away from the developer.

Note

Bruce Tate and Curt Hibbs call ActiveRecord an Object Relational Wrapping layer, rather than a Mapping layer: their view is that as there is no explicit configuration of the relationship between the model classes and the database tables, it doesn't count as a "mapping". They have a point, but the exact semantics aren't too important. Here, I'm treating "mapping" in the sense that objects can be mapped onto records in the database tables.

An ORM layer is one way to implement the model part of the MVC architecture, and this is the role ActiveRecord fulfils in a Rails application: it acts as the intermediary between the controller and the database, retrieving, validating, updating, and deleting records on behalf of the controller.

Before we can see how this works, we need a database to use as the back-end for our application. Setting this up is covered in the following sections.

Creating a Database and System Account

As described in Chapter 2, we're going to use MySQL as the database back-end for the Intranet application. If you followed the installation instructions, you should have MySQL available on your machine.

Note

These instructions cover how to setup a database and user in MySQL. However, if you have an alternative database back-end (Oracle, SQL Server, PostgreSQL, etc.), create a database and a user with whichever tools that are appropriate for your database instead.

The first step is to initialise the MySQL database. You could use one of the MySQL GUI clients to do this, but we'll use the command line as this works the same regardless of your platform. Use this command to login to the database server on your local machine:

$ mysql -u root -p

Note that -u root specifies we are logging in as the root user: you need to use an account with sufficient privileges to create a new database. -p indicates that we intend to use a password to login. If your login is successful, you should see:

Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 1 to server version: 5.0.24a
Type 'help;' or 'h' for help. Type 'c' to clear the buffer.
mysql>

Once you've logged in successfully, create a database called intranet_development with:

mysql> CREATE DATABASE intranet_development;
Query OK, 1 row affected (0.05 sec)

This is the database we'll be using during the development of our application. The next step is to create a user account for our Rails application. This is a good practice: instead of using a real person's account (or even worse, the root account) to connect our Rails application to MySQL, we will use a dedicated system account. This gives us a handful of security advantages:

  • The system account can be given minimal permissions: just the ones the application needs to work with the database. If our application never needs to create temporary tables, we don't need to give the system account the CREATE TEMPORARY TABLES permission, for example. MySQL allows us to refine permissions further, down to the individual table and row level if required.
  • We can restrict the system account's access to the Rails application's database. If the account's were compromised, the cracker's vandalism would be limited to just that database.
  • We can restrict the load that the system account can put on the database service, such as the number of queries, updates, and connections the system account can make per hour. If the Rails application were hit by a denial of service attack, these settings could help limit the damage that the application can cause.
  • The system account can be limited to accessing the database from a single IP domain name, IP address, or IP range. Any attempt to access the database from a machine outside the range automatically fails, further tightening our setup.

Again, you can use either a GUI tool or the command line to create the system account. Here's how to do it on the command line (once you are logged in to MySQL):

mysql> GRANT ALL ON intranet_development.* TO intranet@localhost IDENTIFIED BY 'police73';
Query OK, 0 rows affected (0.01 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

This simultaneously creates the intranet user account and gives that user all permissions (known as privileges in MySQL) on the intranet database. The IDENTIFIED BY clause specifies the account password ('police73'). @localhost specifies the host name from which this user can access the database. If the database server is on a separate physical box from the application server, the application server's IP address or domain name should be used here. FLUSH PRIVILEGES applies the permissions to the server.

Note

A note on permissions

In the example above, the system account is given all permissions on the database. You may not want to give your application this level of access to the database, once it is in production. MySQL makes it trivial to restrict permissions on individual tables or columns within tables and/or by SQL operation (e.g. the production system account could just be given SELECT permissions if the application just reads from the database tables).

Setting Up a Database Connection

The database connection settings for a Rails application are stored in a single file called database.yml, inside the config directory (which is in turn inside the RAILS_ROOT). The freshly-minted database.yml file (produced by the rails command) can seem a bit confusing, mainly because it contains a lot of examples and hints. However, after removing comments, it boils down to this:

development:
adapter: mysql
database: Intranet_development
username: root
password:
host: localhost
test:
adapter: mysql
database: Intranet_test
username: root
password:
host: localhost
production:
adapter: mysql
database: Intranet_production
username: root
password:
host: localhost

This is a YAML (rhymes with "camel") file, which configures the database settings for each of the three Rails' environments: development, test, and production—which is explained in the next section).

YAML, ( "YAML Ain't a Markup Language"), is officially described as a "data serialization language" (see, http://yaml.org/). Its purpose is comparable to that of XML: both YAML and XML are used to define configuration settings, log file formats, or other kinds of structured data. However, YAML has the following benefits over XML:

  • Concise
  • Easy to parse
  • Easy to implement
  • Human-readable

The YAML syntax used in the database.yml file defines a YAML mapping from keys to values. (If you've ever worked with a configuration file, the key-value concept should be familiar.) When the file is read by Rails, it is converted into a Ruby hash (c.f. a dictionary or associative array in other languages). The words which are flush with the left of the file ('development', 'test', 'production') are the keys of this top-level hash; in Ruby syntax, the hash looks like this:

{
'development' => {...},
'test' => {...},
'production' => {...}
}

Underneath each of these top-level keys is a series of name-value pairs; each series is also converted into a hash. The parent key has this hash as a value. In Ruby syntax, the resulting "hash of hashes" looks like this:

{
'development' => {
'adapter' => 'mysql',
'database' => 'Intranet_development',
'username' => 'root',
'password' => nil,
'host' => 'localhost'
},
'test' => {...},
'production' => {...}
}

(The 'test' and 'production' sections are truncated for brevity.)

Each sub-hash specifies a database configuration for one of the Rails environments, as described in the next section.

Configuring the Rails Environments

Rails can run an application in different modes; each mode is known as an environment. Each environment is kept isolated from the others, uses a different database, and handles logging and error checking differently. This makes it possible to develop your code without breaking the live application, run tests without wiping records from the live database, and run your application more efficiently in production (amongst other things).

The three environments have different purposes and characteristics:

  1. Development

    This is the environment you use when building your application. Logging and error reporting are at their most verbose, making it simpler to track down bugs. This environment uses its own database which is kept isolated from the production database.

  2. Test

    This environment is only normally used when running automated tests. The database for this environment is regenerated from scratch each time tests are run, by cloning the structure (not the data) of the development environment.

  3. Production

    Rails does a lot of work behind the scenes creating classes and their methods on the fly (which is how it does the clever database reflection discussed later in this chapter). In the production environment, this work is done once when the application starts and cached to save work later (by contrast, in development, it is done on each request). Debugging and logging are also set to a minimum in this environment.

You can configure database settings by editing the section of the database.yml file named after the environment. For now, we'll just edit the settings for the development environment:

development:
adapter: mysql
database: 'intranet_development'
username: intranet
password: police73
socket: /var/run/mysqld/mysqld.sock

Note

You must use spaces, not tabs, in a YAML file: attempting to use tabs in a YAML file will break it completely. For reference, so that you can recognize it in case it happens to you, here's a YAML error caused by an errant tab:

/usr/lib/ruby/1.8/yaml.rb:133:in 'load': syntax error on line 13, col 11: ' adapter: mysql' (ArgumentError)

There is no need to configure the test and production databases at this point: we can leave the default settings as they are until we need these environments.

Each line of the configuration is described in more detail in the table below:

Configuration option

Example setting

Description

adapter

mysql

The type of database to which the application is connected.

database

'intranet_development'

The name of the database to use in this environment. The Rails convention is to use the suffix _development for the development database, and _test for the test database. (Note: quote marks are used around the database name as it contains an underscore: MySQL is sometimes a bit funny about underscores...)

username

intranet

The username for the system account.

password

police73

The password for the system account.

host

localhost

If the application needs to connect to the database server over a network, this is the hostname or IP address of the database server. Where the Rails application and the database server are running on the same machine, localhost can be used.

socket

/var/run/mysqld/mysqd.sock

This is an interesting setting, and one which is often overlooked. It is mainly used on *nix machines, where the database connection is made using a Unix-style socket, rather than a network (TCP) connection. In some cases, it may not be possible to create a network connection at all (TCP can be switched off on MySQL on *nix); in this case, specifying a socket location is the only option. The example setting shown here points at the default socket location on Ubuntu. See the section Troubleshooting a MySQL Connection for more details about sockets.

Although we are using MySQL as our database server, ActiveRecord supports a range of other back-ends. You can specify which by changing the adapter configuration option to one of these values:

  • db2
  • firebird
  • frontbase
  • oci (Oracle)
  • openbase
  • postgresql
  • sqlite
  • sqlserver
  • sybase

Each adapter has its own set of configuration options: see the Rails documentation for adapter-specific details. (Chapter 3 explains how to locate the Rails documentation on your machine.)

Note

Some adapters have specific requirements, such as the installation of additional Ruby or native libraries. This may restrict your choice of server operating system for running your Rails application. For example, the sqlserver adapter requires the use of ODBC, which may in turn require you to install a variety of ODBC software on your application server (see http://wiki.rubyonrails.org/rails/pages/HowtoConnectToMicrosoftSQLServerFromRailsOnLinux). Make sure you are aware of all the pre-requisite software you need for some of the Rails database adapters.

Testing the Database Connection

It is useful to check that the database connection is working before going any further. There's an easy way to do this. Start up a Rails console session using another of the built-in scripts Rails adds to your application called the console. Do this using the command line, by connecting into RAILS_ROOT and running:

ruby script/console

You should see:

Loading development environment
>>

If you do, Rails has loaded your application's components correctly. The command prompt can now be used to enter commands, which directly manipulate your Rails application, without having to use a browser: very useful for general testing and tinkering. (We'll come back to the console throughout this chapter.) For now, enter this command to test your database connection:

>> ActiveRecord::Base.connection

If the database connection is working correctly, you should see:

=> #<ActiveRecord::ConnectionAdapters::MysqlAdapter:0xb74dbe34 ...>

This has been truncated, as you get a lot of detail back from the console. But providing an ActiveRecord::ConnectionAdapters::MySQLAdapter instance has been successfully created, you can be happy that the database connection is working.

If the database connection fails, you might see this instead:

Mysql::Error: Can't connect to local MySQL server through socket '/var/run/mysql/mysqld.sock' (2)

...which means the socket location is wrong.

Or:

Mysql::Error: Access denied for user 'intranet'@'localhost' (using password: YES)

...which means the system account does not have access to the server and/or the database.

The next section covers what to do if one of these errors occurs.

Troubleshooting a MySQL Connection

If you discover that your MySQL connection isn't working (see the previous section), check the following:

  1. Test whether you can login to MySQL using the mysql command line client and the system account, as specified in your database.yml file. If you can login, it's likely that your database.yml file contains a typo in the username and/or password.
  2. If you can login with the mysql command line client using the system account, try the command:

    USE intranet_development;

    If you get an error at this point, it means the system account doesn't have the correct level of access to the database. Check the permissions granted to the system account.

  3. Check that you are using the correct MySQL socket location. If you are unsure where your socket is located, login using the MySQL command line client and run this query:

    | mysql> SHOW VARIABLES LIKE 'socket';

    You should get back something like this:

    +--------------------+----------------------------------------+

    | Variable_name | Value |

    +--------------------+----------------------------------------+

    | socket | /var/run/mysqld/mysqld.sock |

    +--------------------+----------------------------------------+

    Now, make sure that the socket setting in database.yml matches the value of the socket variable reported by MySQL.

  4. If you are connecting using a host option, ensure that you can connect from the application server to the database server using the mysql command line client. If you can connect successfully, but your Rails application can't, it may be that the host setting is wrong in database.yml.

Hopefully, you should be able to fix pretty much any MySQL connection error by following these steps.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.2.225