LoreTutorial

From Cubawiki

Jump to: navigation, search

Defining database models

A model is a class providing a database type consisting of one or more tables.

Let's use the following tables for our example:

public.vehicle Abstract base class for all vehicles
vehicle_id integer primary key
name character varying (100)
owner_id integer references public.owner.owner_id
manufacturer_id integer references public.manufacturer.manufacturer_id
public.car Specialisation of vehicle
car_id integer primary key
vehicle_id integer references public.vehicle
public.owner
owner_id integer primary key
name character varying (100)
public.manufacturer
manufacturer_id integer primary key
name character varying (100)
public.driver
driver_id integer primary key
name character varying (100)
public.vehicle_driver Maps n drivers to n vehicles
v_id integer references public.vehicle.vehicle_id
d_id integer references public.driver.driver_id


Our model would look like this:

 name_format = /a-ZA-Z0-9_-/
 
 class Manufacturer < Lore::Model
   table :manufacturer
   primary_key :manufacturer_id, manufacturer_id_seq
   
   validates :name, :maxlength => 100, :format => name_format
 end
 
 class Vehicle < Lore::Model
   table :vehicle, :public  # optional. When no schema is given, :public is used
   primary_key :vehicle_id, :vehicle_id_seq
   
   validates :name, :maxlength => 100, :format => name_format
 
   has_a Manufacturer, :manufacturer_id
   has_many Driver, :through => :vehicle_driver
 
   def owned_by(owner_instance)
   end
 end
 
 class Car < Vehicle # inheritance from Vehicle necessary for custom methods like 'owned_by' only    
   table :car
   primary_key :car_id   
   is_a Vehicle, :vehicle_id  
 end
 class Driver < Lore::Model
   table :driver, :public
   primary_key :driver_id
   has_many Vehicle, :through => :vehicle_driver
 end
 class Vehicle_Driver < Lore::Model
    table :vehicle_driver, :public
    primary_key :v_id, :d_id
    has_many Driver, :d_id
    has_many Vehicle, :v_id
 end
 

Learn more about Lore::Model

Creating and modifying records

Creating a new record is quite easy. Just pass attributes to klass method create of a model:

 m = Manufacturer.create( :name => 'BMW' )
 o = Owner.create( :name => 'Mathilda Bang' )
 c = Car.create( :name => 'BMW318i', 
                 :owner_id => o.owner_id, 
                 :manufacturer_id => m.manufacturer_id )
 
 # Let's validate that: 
 assert_equal m, c.manufacturer_entity
 assert_equal o, c.owner_entity


In this case, there are two has_a relations involved, so we also can use model instances as well:

 Car.create( :name => 'BMW318i', 
             :owner => o,                      # <---  here
             :manufacturer => m )              # <---  and here

Selecting records from the database

After reading this section, be sure to read about Lore's prepared statements , which increase performance and security when loading instances from DB.

Ok, now that our database is filled with values, we want to get our information back.

Selecting one model instance by its primary key values is provided by model klass method .load(pkey_values):

 car_obj = Car.load(:car_id => 4)

Attribute values of a model instance are accessed by automatically defined instance methods having the same name as the attribte:

 puts car_obj.car_id 
 puts car_obj.name

Related entities defined with has_many and has_a in the Model klass can be retreived by calling automatically defined model instance methods with suffix "_entities" for 1:n and "_entity" for 1:1 relations:

Driver.load(:driver_id => 4).vehicle_entities.each { |vehicle|

 puts vehicle.manufacturer_entity.name

To select more than one instance, let's use Lore's language directly for starters. This always works the same: Call a model's select method.

As you already know from SQL, a DB entity is returned if the given query statements evaluate to true for it. So the most simple select is: Passing true.

 all_cars = Car.select { |c|
   c.where(true)
 }

This returns all cars. But thanks to query shortcuts you also could state: all_cars = Cars.all.entities. Ok, that's a boring example, i know. Normally we only want entities matching more specific criteria.

The block variable c is a clause parser instance. It's most frequently used methods are:

  • #where( <clause> )
  • #limit( <integer|range> )
  • #order_by( <:attribute_name>, <:asc|:desc> )
  • #group_by( <:attribute_name> )

Not that frequent, but handy sometimes:

  • #join(<Other_Model>).on(<clause>)
  • #join(<Other_Model>).using(<:common_attribute>)

Let's see what we can do with it:

 my_cars = Car.select { |c|
   c.where c.name.like('%BMW%')
   c.order_by :name, :asc
   c.limit(10..20)
 }

Not bad. With query shortcuts, this could look like this:

 my_cars = Car.find(10..20).with(Car.name.like '%BMW%').ordered_by(:name, :asc).entities

There also are rather exotic things you can do with Lore's query language. Let's assume we want to join Car with a model klass that regularly has no relationship to it. Like ... umm ... pie. I like pie.

 my_pie_car = Car.select { |c|
   c.join(Pie).on(Car.car_id == Pie.pie_id) { |pc|
     pc.where(pc.name.like '�rry%')
   }
 }

To add some complications: What if Car and Pie both have an attribute name?

c.name just returns 'name', like in WHERE name LIKE '�rry%'. We want WHERE public.car.name LIKE '�rry%'. So we have to tell Lore explicitly which one to use:

 my_pie_car = Car.select { |c|
   c.join(Pie).on(Car.car_id == Pie.pie_id) { |pc|
     pc.where(Car.name.like '�rry%')                # <-- see?
   }
 }

How many cars are there at all? Of course there's Cars.all.entities.length, but this is a bad thing to do, as we know. We want something like

 SELECT count(car_id) FROM public.car

So let's use value_of. This returns a helper object providing methods like sum, max, count and whichever aggregate function Postgres provides. In our case we need:

 $(irb) Car.value_of.count(:car_id) 
 $(irb) -> 5

Be sure to read about Lore's prepared statements , which increase performance and security when loading instances from DB.

Learn more about Lore::Clause

Personal tools