Class: Lore::Table_Accessor

Inherits:
Object
  • Object
show all
Extended by:
Aspect, Model_Shortcuts, Query_Shortcuts
Includes:
Table_Instance
Defined in:
lib/lore/table_accessor.rb

Direct Known Subclasses

Model

Constant Summary collapse

@@logger =
Lore.logger
@@prepared_statements =
Hash.new
@@cache_impl =
false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Aspect

after_commit, after_instance_delete, before_commit, before_instance_delete

Methods included from Query_Shortcuts

all, all_with, delete, delete_all, each, find, set, value_of

Methods included from Model_Shortcuts

html_escape_values_of

Methods included from Table_Instance

#<=>, #==, #===, #[], #[]=, #abs_attr, #attr, #commit, #delete, #get_attribute_values, #get_label_string, #get_primary_key_values, #id, #inspect, #is_cached_entity?, #key, #marshal_dump, #marshal_load, #method_missing, #move, #set_attribute_value, #setup_instance, #table_accessor, #touched?

Constructor Details

#initialize(instance_attrib_values, joined_models = [], cache = nil) ⇒ Table_Accessor

Constructor is usually wrapped by e.g. self.load or self.marshal_load. Constructor just accepts a value hash, and returns a Table_Accessor instance holding it. Note that this method is operating on a Table_Accessor instance, not on class Table_Accessor itself.



797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
# File 'lib/lore/table_accessor.rb', line 797

def initialize(instance_attrib_values, joined_models=[], cache=nil) 
# {{{
  @loaded_from_cache = (cache == :cached)
  # set instance variables. 
  # class instance variables are made accessible 
  # in Table_Instance.setup_instance
  @joined_models = joined_models

  if @loaded_from_cache then
    @attribute_values = instance_attrib_values
  else
    @attribute_values = Hash.new
    values      = instance_attrib_values
    field_index = 0
    models = [ self.class ] 
    models += joined_models 
    models.each { |model|
      model.get_all_table_names.each { |table| 
        @attribute_values[table] = Hash.new
        field_names = model.get_attributes[table]
        for attr_index in 0...field_names.length do
          @attribute_values[table][field_names[attr_index]] = values[field_index]
          field_index += 1
        end
      }
    }
  end

  setup_instance()
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Lore::Table_Instance

Instance Attribute Details

#attribute_typesObject (readonly)

Returns the value of attribute attribute_types.



22
23
24
# File 'lib/lore/table_accessor.rb', line 22

def attribute_types
  @attribute_types
end

#attributesObject (readonly)

Returns the value of attribute attributes.



22
23
24
# File 'lib/lore/table_accessor.rb', line 22

def attributes
  @attributes
end

#constraintsObject (readonly)

Returns the value of attribute constraints.



22
23
24
# File 'lib/lore/table_accessor.rb', line 22

def constraints
  @constraints
end

#foreign_keysObject (readonly)

Returns the value of attribute foreign_keys.



22
23
24
# File 'lib/lore/table_accessor.rb', line 22

def foreign_keys
  @foreign_keys
end

#primary_keysObject (readonly)

Returns the value of attribute primary_keys.



22
23
24
# File 'lib/lore/table_accessor.rb', line 22

def primary_keys
  @primary_keys
end

Class Method Details

.[](attribute_name) ⇒ Object

Returns full attribute name of given attribute



780
781
782
783
# File 'lib/lore/table_accessor.rb', line 780

def self.[](attribute_name)
# {{{
  return get_table_name+'.'+attribute_name.to_s
end

.context(context_name) ⇒ Object

If this model is not to be located in a projects default context, you can tell Cuba which context to use via

context :other_context_name


836
837
838
839
# File 'lib/lore/table_accessor.rb', line 836

def self.context(context_name) 
# {{{
  @context = context_name
end

.create(attrib_values = {}) ⇒ Object

Returns a new Table_Accessor instance by inserting given attribute values into db and returning an instance for further operations.



1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
# File 'lib/lore/table_accessor.rb', line 1604

def self.create(attrib_values={})
# {{{
  before_create(attrib_values)

  input_filters = get_input_filters
  attrib_key  = ''
  attrib_name = ''

  attrib_values.each_pair { |attrib_name, attrib_value|
    if attrib_name.instance_of? Symbol then 
      attrib_key = attrib_name
    else 
      attrib_key = attrib_name.split('.')[-1].intern
    end
    
    if (input_filters && input_filters[attrib_key]) then
      attrib_values[attrib_name] = input_filters[attrib_key].call(attrib_value) 
    end
  }
  after_filters(attrib_values)
  
  values = distribute_attrib_values(attrib_values)
  
  begin
    before_validation(values)
    Lore::Validation::Parameter_Validator.invalid_params(self, 
                                                         values)
  rescue Lore::Exception::Invalid_Klass_Parameters => ikp
    # log'n'throw
    ikp.log
    raise ikp
  end

  before_insert(attrib_values)

  # retreive all final attrib values after insert: (this way, also 
  # sequence values are resolved): 
  #
  attrib_values = insert(attrib_values)
  
  # This would be a double check, as self.load already filters 
  # non-primary key attributes
  select_keys = Hash.new
  if @primary_keys then
    @primary_keys[table_name].each { |key|
      select_keys[table_name + '.' << key] = attrib_values[table_name][key]
    }
  end
  
  obj = self.load(select_keys)
  after_create(obj)
  
  return obj
  
end

.create_entity_cache(query_string, result) ⇒ Object



124
125
126
# File 'lib/lore/table_accessor.rb', line 124

def self.create_entity_cache(query_string, result) 
  @@cache_impl.create(self, query_string, result) if @@cache_impl
end

.create_shallow(attrib_values) ⇒ Object

Create a shallow instance, that is: An instance with no reference to DB model. Attribute values passed to Table_Accessor.create_shallow are not processed through hooks, filters, and validation. Values are, however, processed through output filters. Usage and result is the same as for Table_Accessor.create, but it only returns an accessor instance, without storing it in the database. To commit a shallow copy to database (and thus process given attribute values through stages mentioned before), call #commit.



1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
# File 'lib/lore/table_accessor.rb', line 1587

def self.create_shallow(attrib_values)
  before_create(attrib_values)
  values = distribute_attrib_values(attrib_values)
  flat_attribs = []
  get_all_table_names.each { |table|
    get_attributes[table].each { |attrib|
      flat_attribs << (values[table][attrib])
    }
  }
  instance = self.new(flat_attribs)
end

.delete(value_keys = nil, &block) ⇒ Object

Delete this object and use Table_Deleter to delete its entity tuple from database.



1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
# File 'lib/lore/table_accessor.rb', line 1710

def self.delete(value_keys=nil, &block)
# {{{

  if value_keys then
  before_delete(value_keys)
    Lore::Table_Deleter.perform_delete(self, value_keys)
  after_delete(value_keys)
  else 
    Lore::Table_Deleter.block_delete(self, &block)
  end

end

.entity_cacheObject



111
112
113
# File 'lib/lore/table_accessor.rb', line 111

def self.entity_cache
  @@cache_impl 
end

.execute_prepared(plan_name, *args) ⇒ Object

}}}



1447
1448
1449
1450
# File 'lib/lore/table_accessor.rb', line 1447

def self.execute_prepared(plan_name, *args)
  plan_name = "#{table_name.gsub('.','_')}__#{plan_name.to_s}"
  Table_Selector.select_prepared(plan_name, self, args)
end

.explicit_insert(keys) ⇒ Object

Wrap explicit select. Example:

SomeModule::SomeAccessor.explicit_insert({
                      table_name_A =>
                        {'some_field'=>'2', 
                        'other_field'=>'3'}, 
                      table_name_A =>
                        {'another_field'=>'5'}
                     })

Note that field in ‘field’=>‘value’ is exactly the field name in the table (e.g. table_name_A) it holds.



1486
1487
1488
1489
# File 'lib/lore/table_accessor.rb', line 1486

def self.explicit_insert(keys)
# {{{
  Lore::Table_Inserter.perform_insert(self, keys)
end

.flush_entity_cacheObject



120
121
122
# File 'lib/lore/table_accessor.rb', line 120

def self.flush_entity_cache
  @@cache_impl.flush(self) if @@cache_impl
end

.get_all_table_namesObject



308
309
310
311
312
313
# File 'lib/lore/table_accessor.rb', line 308

def self.get_all_table_names
  return @table_names if @table_names
  @table_names = [@table_name]
  @table_names += get_joins.keys_flat
  return @table_names
end

.get_constraintsObject

}}}



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/lore/table_accessor.rb', line 276

def self.get_constraints

  @constraints = Hash.new if @constraints.nil?
  if !@is_a_klasses.nil? then
    @is_a_klasses.each_pair { |foreign_key, klass|
      @constraints.update(klass.get_constraints)
    }
  end
  @constraints

end

.get_contextObject



840
841
842
# File 'lib/lore/table_accessor.rb', line 840

def self.get_context
  @context
end

.get_explicit_attributesObject

def



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/lore/table_accessor.rb', line 253

def self.get_explicit_attributes # :nodoc:
  
  if @explicit_attributes.nil? then
    return Hash.new
  else
  # filter entries that are present in @implicit_attributes: 
    if !@implicit_attributes.nil? then
      
      @explicit_attributes.each_pair { |table, attribs|
        attribs.delete_if { |attrib|
          !@implicit_attributes[table].nil? &&
          @implicit_attributes[table].include?(attrib.to_s)
        }
      }
      
    end

    return @explicit_attributes
    
  end
end

.get_foreign_keysObject

Recursively gets foreign keys from parent, if own foreign keys don’t have been set, yet:



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/lore/table_accessor.rb', line 136

def self.get_foreign_keys 
# {{{
  
  if @foreign_keys.nil? then
    if @foreign_keys_klasses.nil? then
      @foreign_keys = Hash.new
    else
      @is_a_klasses.each { |foreign_key, k|
        @foreign_keys[k.table_name] << k.get_foreign_keys
      }
    end
  else
    return @foreign_keys
  end

end

.get_implicit_attributesObject

def



238
239
240
241
242
243
244
# File 'lib/lore/table_accessor.rb', line 238

def self.get_implicit_attributes # :nodoc:
  if @implicit_attributes.nil? then
    return Hash.new
  else 
    return @implicit_attributes
  end
end

.get_primary_keysObject

Recursively gets primary keys from parent, if own primary keys don’t have been set, yet: Returns all derived primary keys WITHOUT OWN PKEYS.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/lore/table_accessor.rb', line 161

def self.get_primary_keys 
# {{{
  
  if @primary_keys.nil? then
    if @is_a_klasses.nil? then
      return Hash.new
    else
      plain_base_pkeys     = Hash.new
      formatted_base_pkeys = Hash.new
      base_schema          = ''

      get_is_a_klasses.each { |k|
        plain_base_pkeys = k.get_primary_keys
        base_schema = k.get_schema
        if base_schema.nil? then 
          formatted_base_pkeys.update(plain_base_pkeys)
        else
          plain_base_pkeys.each_pair { |key, value|
            formatted_base_pkeys[base_schema+'.'+key] = value
          }
          return formatted_base_pkeys
          @primary_keys = formatted_base_pkeys
        end
      }
      return formatted_base_pkeys
      @primary_keys = formatted_base_pkeys
    end
  else
    return @primary_keys
  end

end

.get_sequencesObject

Recursively gets sequences from parent, if own sequences don’t have been set, yet:



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/lore/table_accessor.rb', line 208

def self.get_sequences 
# {{{
  
  if @sequences.nil? then
    if @is_a_klasses.nil? then
      return Hash.new
    else
      seq_map = Hash.new
      @is_a_klasses.each_pair { |foreign_key,k|
        seq_map[k.table_name] = k.get_sequences
      }
      return seq_map
    end
  else
    return @sequences
  end

end

.get_table_nameObject



291
292
293
294
# File 'lib/lore/table_accessor.rb', line 291

def self.get_table_name # :nodoc:
  return @table_name unless @table_name.nil? 
  return ''
end

.get_table_namesObject



302
303
304
305
306
307
# File 'lib/lore/table_accessor.rb', line 302

def self.get_table_names
  return @table_names if @table_names
  @table_names = [@table_name]
  @table_names += get_is_a.keys_flat
  return @table_names
end

.inspectObject

Inspect method



1782
1783
1784
1785
# File 'lib/lore/table_accessor.rb', line 1782

def self.inspect
# {{{
  'Lore::Table_Accessor: ' << self.to_s
end

.key_arrayObject

}}}



194
195
196
197
198
199
200
201
202
203
# File 'lib/lore/table_accessor.rb', line 194

def self.key_array()
# {{{
  keys = Array.new
  get_primary_keys.each_pair { |table, attribs|
    attribs.each { |attrib|
      keys.push attrib
    }
  }
  return keys
end

.load(keys) ⇒ Object

Return new Table_Accessor instance by loading an existing entry from table if present, or false if no entry has been found. Accepts any combination of :primary_key => ‘value’ Also allows inherited primary keys.



1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
# File 'lib/lore/table_accessor.rb', line 1667

def self.load(keys)
# {{{

  before_load(keys)

  select_keys = {}
  value = false
  get_primary_keys.each_pair { |table, pkeys| 
    pkeys.each { |attrib_name|
      value = keys[table+'.'+attrib_name.to_s]  # The more explicit, the better. 
      value = keys[attrib_name.intern] if value.nil? # Symbols are supposed to be more frequent than strings
      value = keys[attrib_name.to_s] if value.nil?
      select_keys[table+'.'+attrib_name.to_s] = value unless value.nil?
    }
  }

  return false if select_keys.empty? 
  
  # We have to perform a select here instead of returning 
  # the instance with given attribute values, as this is the 
  # only way to retreive attribute values set in the DB via
  # default values, triggers, etc.
  cp = Clause.for(self)
  c = Clause.new
  select_keys.each_pair { |k,v|
    c & (Clause.new(k.to_s.dup) == v.to_s)
  }

  instance = self.select { |inst|
    inst.where(c)
    inst.limit(1)
  }.first

  return false unless instance
  return instance

end

.log(message, level = :debug) ⇒ Object



26
27
28
# File 'lib/lore/table_accessor.rb', line 26

def self.log(message, level=:debug)
  @@logger.debug(message)
end

.method_missing(meth) ⇒ Object

Simulates inheritance: Delegate missing methods to parent Table_Accessor.

Raises:

  • (::Exception)


1769
1770
1771
1772
1773
1774
1775
1776
# File 'lib/lore/table_accessor.rb', line 1769

def self.method_missing(meth)
  if @is_a_klasses then
    @is_a_klasses.each_pair { |foreign_key, k|
      return (k.__send__(meth.to_s)) if k.respond_to? meth
    }
  end
  raise ::Exception.new('Undefined method '<< meth.to_s << ' for ' << self.to_s)
end

.prepare(plan_name, *args, &block) ⇒ Object

Prepares a query for execution. This offers four advantages:

  • The query doesn’t have to be interpreted by the DB every time

  • The query call is available via direct method call.

  • DB validates against types, thus preventing SQL injection

  • It doesn’t Lore require to compose the query string again. This effects the most significant performance gain (Up to 60% execution time in some benchmarks)

Usage:

Article.prepare(:by_name_and_date, Lore::Type::Integer, Lore::Type::Date) { |a,fields|
  a.where((Article.article_id == fields[0] & 
          (Article.date == fields[1]))
}
Article.by_name_and_date('Merry Christmas', '20081224')

From the PostgreSQL 7.4 Manual:

"In some situations, the query plan produced by for a prepared statement may be 
 inferior to the plan produced if the statement were submitted and executed normally. 
 This is because when the statement is planned and the planner attempts to determine 
 the optimal query plan, the actual values of any parameters specified in the 
 statement are unavailable. PostgreSQL collects statistics on the distribution of 
 data in the table, and can use constant values in a statement to make guesses about 
 the likely result of executing the statement. Since this data is unavailable when 
 planning prepared statements with parameters, the chosen plan may be suboptimal. To 
 examine the query plan PostgreSQL has chosen for a prepared statement, use 
 EXPLAIN EXECUTE. "


1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
# File 'lib/lore/table_accessor.rb', line 1431

def self.prepare(plan_name, *args, &block)
# {{{
  log('PREPARE: TRYING CLASS METHOD ' << plan_name.to_s)
  if !@@prepared_statements[plan_name] then
    Table_Selector.prepare(plan_name, self, args, &block)

    log('PREPARE: CREATE CLASS METHOD ' << plan_name.to_s)
    instance_eval("
    def #{plan_name.to_s}(*args) 
      execute_prepared(:#{plan_name}, args)
    end")
    @@prepared_statements[plan_name] = true
    log('PREPARE: CREATED CLASS METHOD ' << plan_name.to_s)
  end
end

.read_entity_cache(query_string) ⇒ Object



128
129
130
# File 'lib/lore/table_accessor.rb', line 128

def self.read_entity_cache(query_string)
  @@cache_impl.read(self, query_string) if @@cache_impl
end

.select(clause = nil, &block) ⇒ Object



1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
# File 'lib/lore/table_accessor.rb', line 1378

def self.select(clause=nil, &block)
# {{{
  GC.disable
  if(!clause.nil? && !clause.to_s.include?('*,')) then
    query_string = Lore::Table_Selector.select_query(clause.to_s, self, &block)
    return Clause.new(query_string[:query])
  else
    what = clause.to_s
    result = Lore::Table_Selector.select_cached(what, self, &block)
  # result = Array.new
  # db_result.get_rows.each { |row|
  #   result.push(self.new(self, row))
  # }
    GC.enable
    return result
  end

end

.select_by_key(*keys) ⇒ Object

DELETE ME



1363
1364
1365
1366
1367
# File 'lib/lore/table_accessor.rb', line 1363

def self.select_by_key(*keys)
# {{{
  result = Lore::Table_Selector.select_on_keys(self, keys)
  field_array = result.get_rows()
end

.select_query(clause = nil, &block) ⇒ Object



1399
1400
1401
# File 'lib/lore/table_accessor.rb', line 1399

def self.select_query(clause=nil, &block)
  query_string = Lore::Table_Selector.select_query(clause.to_s, self, &block)
end

.select_value(what, &block) ⇒ Object

Same as select, but returns



1455
1456
1457
1458
1459
1460
1461
# File 'lib/lore/table_accessor.rb', line 1455

def self.select_value(what, &block)
# {{{
  db_result = Lore::Table_Selector.select(what, self, &block)
  row = db_result.get_row
  return row[''] if row
  return {}
end

.select_values(what, &block) ⇒ Object



1465
1466
1467
1468
1469
1470
1471
# File 'lib/lore/table_accessor.rb', line 1465

def self.select_values(what, &block) 
# {{{
  db_result = Lore::Table_Selector.select(what, self, &block)
  return db_result.get_rows[:values].map { |e| 
    e.first 
  }
end

.set_explicit_attributes(arg) ⇒ Object



250
251
252
# File 'lib/lore/table_accessor.rb', line 250

def self.set_explicit_attributes(arg) # :nodoc:
  @explicit_attributes = arg
end

.set_foreign_keys(arg) ⇒ Object

}}}



152
153
154
155
# File 'lib/lore/table_accessor.rb', line 152

def self.set_foreign_keys(arg) 
# {{{
  @foreign_keys = arg
end

.set_implicit_attributes(arg) ⇒ Object



235
236
237
# File 'lib/lore/table_accessor.rb', line 235

def self.set_implicit_attributes(arg) # :nodoc:
  @implicit_attributes = arg
end

.set_sequences(arg) ⇒ Object

}}}



227
228
229
230
# File 'lib/lore/table_accessor.rb', line 227

def self.set_sequences(arg) # :nodoc:
# {{{
  @sequences = arg
end

.set_table_name(arg) ⇒ Object

:nodoc:



298
299
300
# File 'lib/lore/table_accessor.rb', line 298

def self.set_table_name(arg) # :nodoc:
  @table_name = arg
end

.table(_table, _schema = nil) ⇒ Object

use table :tablename if class != table only.



849
850
851
852
853
854
# File 'lib/lore/table_accessor.rb', line 849

def self.table(_table, _schema=nil)
# {{{
  set_table_name(_table.to_s)
  schema(_schema.to_s) unless _schema.nil?
  load_attribute_fields();
end

.table_nameObject

:nodoc:



295
296
297
# File 'lib/lore/table_accessor.rb', line 295

def self.table_name # :nodoc:
  get_table_name
end

.update(&block) ⇒ Object



1371
1372
1373
1374
# File 'lib/lore/table_accessor.rb', line 1371

def self.update(&block)
# {{{
  query_string = Lore::Table_Updater.block_update(self, &block)
end

.use_entity_cache(entity_cache_class) ⇒ Object



115
116
117
118
# File 'lib/lore/table_accessor.rb', line 115

def self.use_entity_cache(entity_cache_class)
  log('Setting cache implementation: ' << entity_cache_class.inspect)
  @@cache_impl = entity_cache_class 
end

.validate_params?Boolean

tell dispatchers to validate Table_Accessors

Returns:

  • (Boolean)


34
# File 'lib/lore/table_accessor.rb', line 34

def self.validate_params?() true end

Instance Method Details

#log(message, level = :debug) ⇒ Object



29
30
31
# File 'lib/lore/table_accessor.rb', line 29

def log(message, level=:debug)
  @@logger.debug(message)
end