LoreClause

From Cubawiki

Jump to: navigation, search

Lore::Clause

Database queries are sent by composing a clause and passing it to a Lore TableAccessor, for Example:

 clause = (Car.owner_id == 3) & (Car.type == 'Convertible')
 Car.select { |c|
   c.where(clause)
 }

A clause is a regular ruby object and thus can be passed between methods and used in any design pattern, like Decorator (*hinthinthint*).

In this example, Car is a model class .

A model provides class methods for every attribute it has, which return a clause instance pointing to it:

 p Car.owner_id   # ->  Clause(public.car.owner_id)

In most cases, you won't have to think about this, as Lore provides query shortcuts for most common queries:

 # All cars: 
 Car.all.entities
 
 # Five cars someone owns: 
 Car.find(5).with(Car.owner_id == 3).entities
 
 # Somebody not owning a car at the moment: 
 Owner.find(1).with( Owner.owner_id.not_in ( Car.all :owner_id ) ).entity
 
 # All owner names: 
 Owner.all(:name).values

Note the .entities at the end. Before calling this method, you are dealing with a RefinedSelect . No query is sent and no DB result returned until calling one of it's instance methods entities, entity, values or value.

If there is no query shortcut for your needs, you can fine-grain your query using clauses like:

 # All cars manufactured by a companies with 'Bob' in their name: 
 Car.manufacturer_id.in ( 
      Manufacturer.select(:manufacturer_id) { |mid| 
            mid.where(mid.name.like '%Bob%') 
      } 
 )

You can store such a clause in a variable for passing it to methods like

 def list_cars(clause)
   Car.select { |c|
     c.where(clause)
     c.order_by(:name, :asc)
   }.each { |car 
     puts car.name << "\t" << car.manufacturer_entity.name
   }
 end

... likewise define filter methods ...

 def filter_car_name(clause)
   return clause & (Car.name == 'BMW318i')
 end

... and use plain ruby code in your query

 owner_inst = Owner.load(:owner_id => 23)
 type_inst = Type.load(:type_id => 42)
 by_owner = true
 
 Car.select(:name) { |c|
 
   if(by_owner) then
     c.where(Car.owner == owner_inst)
   else 
     c.where(Car.type == type_inst)
   end
 
   c.limit(5)
 }


Requesting values

Sometimes we don't want to load complete model instances but just a concrete value, like sums or maxima. Or perhaps we just want a list of attributes. Lore provides easy access to those.


Examples:

  # Like before, but with an attribute (Clause instance) passed to Model.all: 
  all_car_names = Car.all(Car.name).with(Car.name.like '%BMW%').entities 
  # -> [ 'BMW318i', 'BMW316 Convertible', 'BMW Mini' ]

  five_car_names = Car.find(5).values_of(Car.name).with(Car.name.like '%BMW%').entities
  # -> [ 'BMW318i', 'BMW316 Convertible']
  
  # Limits and everything else work just like before: 
  one_car_name = Car.find(1,1).value_of(Car.name).with(Car.name.like '%BMW%').entity
  # -> 'BMW316i Convertible'

  # Alternatively to .entity, .to_s and to_i can be used to get request values. 
  # .entity always returns value as string. 
  amount_of_cars = Car.value_of.sum(Car.car_id).to_s
  # -> '3'
  highest_vehicle_id => Vehicle.value_of.max(:vehicle_id).to_i
  # -> 2342
Personal tools