Ruby as Enterprise Glue
Listing 3. database.rb
require 'rubygems' require 'active_record' class Customer < ActiveRecord::Base set_table_name 'customer' has_one :address end class Address < ActiveRecord::Base set_table_name 'address' belongs_to :customer has_one :location end class Location < ActiveRecord::Base belongs_to :address end ActiveRecord::Base.establish_connection( :adapter => 'mysql', :database => 'customers' )
You do not have to do a lot. Derive every class from ActiveRecord::Base, and you get accessors for every column for free. These accessors have the same names as the corresponding columns. For example, the Customer class has accessors called id, forename, surname and created_on.
ActiveRecord maps a class to a table having the same name in plural form by default. A class named User is mapped to the users table, and a class named Location is mapped to the locations table. When you work with a legacy database, you cannot choose table names yourself. In such cases, specify the table name with the set_table_name method as we did for our two legacy tables.
Every table must have a numerical primary key called id that is filled by the database automatically. You can change the name of the primary key with the set_primary_key method, but if your legacy tables contain complicated primary keys spanning several columns, ActiveRecord might not be the right tool for your job. ActiveRecord really shines when you adhere to its conventions.
Use belongs_to, has_one, has_many and has_and_belongs_to_many to declare relationships between the different classes. Naming is important for specifying relationships too. Note the naming scheme we have used for the foreign keys. In the address table, for example, the foreign key is called customer_id. By loose convention, many developers built the name of a foreign key column by appending _id to the name of the table to which the key refers. If you do this too, there's nothing more to be done.
In the last lines of Listing 3, we establish the connection to the MySQL database. If you need to, you can pass :host, :username and :password options.
Listing 4 shows how to insert a new customer and address into the database. It's all very intuitive, and we have to clarify only a few details. In line 7, we store a customer in the database. The save method automatically creates a new customer ID. We use this ID in line 10 to associate the address with the customer. ActiveRecord creates accessors for depending tables automatically—that is, all instances of the Customer class have an address attribute that refers to the according entry in the address table. What could be easier?
Listing 4. create_customer.rb
require 'database' customer = Customer.new( :forename => 'Homer', :surname => 'Simpson' ) customer.save address = Address.new( :customer_id => customer.id, :street => 'Main Street', :house_number => '42', :postal_code => '75244', :city => 'Dallas', :state => 'TX' ) address.save
We can find our new customer with one of the following statements:
customer = Customer.find(1) customer = Customer.find_by_forename('Homer') customer = Customer.find_by_surname('Simpson)
ActiveRecord dynamically creates tons of useful find methods. For example, Address.find(:all) iterates over all entries in the address table. In addition, you can search for arbitrary combinations of column values—that is, there are methods such as find_by_forename_and_surname.
Gone are the days when you had to fiddle with LEFT OUTER JOIN clauses and the like. ActiveRecord hides all this nasty stuff, and it even has many more useful features, such as single table inheritance and validations. It has been ported to nearly all database systems available today and is constantly enhanced by a big community.
Now we know how to store the coordinates belonging to a customer's address in the database. The next thing to do is to calculate those coordinates. Normally, this would be a difficult problem and would call for a digital map. Luckily, we can delegate this job to a SOAP localization service.
SOAP is a Remote Procedure Call (RPC) protocol standardized by the W3C. It allows you to create and use objects on a remote host as if they were part of your local process. Method calls and their parameters are turned into XML documents and are sent over a network layer. In the receiving process, they are converted back into method calls again. Return values and exceptions are represented as XML documents also and are sent back to the calling process. Although SOAP is independent of the transport layer, most applications use HTTP or HTTPS.
Fortunately, you normally do not have to know all these nitty-gritty details to use a SOAP service. It's sufficient to know where you can find it on a network, what methods it supports and what transport layer it uses. For this purpose, you'll usually use the Web Service Description Language (WSDL). The localization services' interface is described in Listing 5. Even if you're not familiar with WSDL, you should have no problems finding the definition of the locate function of the LocalizationService service. It takes an address (street, house number, postal code, city and state) and returns a two-element array containing its longitude and latitude.