lore 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. data/LICENSE +19 -0
  2. data/README +74 -0
  3. data/aspect.rb +80 -0
  4. data/behaviours/lockable.rb +41 -0
  5. data/behaviours/movable.rb +54 -0
  6. data/behaviours/versioned.rb +24 -0
  7. data/benchmark.rb +193 -0
  8. data/bits.rb +52 -0
  9. data/cache/abstract_entity_cache.rb +82 -0
  10. data/cache/bits.rb +22 -0
  11. data/cache/cacheable.rb +202 -0
  12. data/cache/cached_entities.rb +116 -0
  13. data/cache/file_index.rb +35 -0
  14. data/cache/mmap_entity_cache.rb +67 -0
  15. data/clause.rb +528 -0
  16. data/connection.rb +155 -0
  17. data/custom_functions.sql +14 -0
  18. data/exception/ambiguous_attribute.rb +14 -0
  19. data/exception/cache_exception.rb +30 -0
  20. data/exception/invalid_klass_parameters.rb +63 -0
  21. data/exception/invalid_parameter.rb +42 -0
  22. data/exception/unknown_typecode.rb +19 -0
  23. data/file_index.sql +56 -0
  24. data/gui/erb_template.rb +79 -0
  25. data/gui/erb_template_helpers.rhtml +19 -0
  26. data/gui/form.rb +314 -0
  27. data/gui/form_element.rb +676 -0
  28. data/gui/form_generator.rb +151 -0
  29. data/gui/templates/button.rhtml +2 -0
  30. data/gui/templates/checkbox.rhtml +3 -0
  31. data/gui/templates/checkbox_row.rhtml +1 -0
  32. data/gui/templates/file.rhtml +2 -0
  33. data/gui/templates/file_readonly.rhtml +3 -0
  34. data/gui/templates/form_element.rhtml +5 -0
  35. data/gui/templates/form_element_horizontal.rhtml +3 -0
  36. data/gui/templates/form_element_listed.rhtml +8 -0
  37. data/gui/templates/form_table.rhtml +3 -0
  38. data/gui/templates/form_table_blank.rhtml +3 -0
  39. data/gui/templates/form_table_horizontal.rhtml +8 -0
  40. data/gui/templates/password.rhtml +2 -0
  41. data/gui/templates/password_readonly.rhtml +3 -0
  42. data/gui/templates/radio.rhtml +1 -0
  43. data/gui/templates/radio_row.rhtml +1 -0
  44. data/gui/templates/select.rhtml +23 -0
  45. data/gui/templates/text.rhtml +2 -0
  46. data/gui/templates/text_readonly.rhtml +3 -0
  47. data/gui/templates/textarea.rhtml +3 -0
  48. data/gui/templates/textarea_readonly.rhtml +4 -0
  49. data/lore.gemspec +40 -0
  50. data/lore.rb +94 -0
  51. data/migration.rb +48 -0
  52. data/model.rb +139 -0
  53. data/model_factory.rb +202 -0
  54. data/model_shortcuts.rb +16 -0
  55. data/query_shortcuts.rb +367 -0
  56. data/reserved_methods.txt +3 -0
  57. data/result.rb +100 -0
  58. data/symbol.rb +58 -0
  59. data/table_accessor.rb +1926 -0
  60. data/table_deleter.rb +115 -0
  61. data/table_inserter.rb +168 -0
  62. data/table_instance.rb +384 -0
  63. data/table_selector.rb +314 -0
  64. data/table_updater.rb +155 -0
  65. data/test/README +31 -0
  66. data/test/env.rb +5 -0
  67. data/test/lore_test.log +8218 -0
  68. data/test/model.rb +142 -0
  69. data/test/prepare.rb +37 -0
  70. data/test/tc_aspect.rb +58 -0
  71. data/test/tc_cache.rb +80 -0
  72. data/test/tc_clause.rb +104 -0
  73. data/test/tc_deep_inheritance.rb +49 -0
  74. data/test/tc_factory.rb +57 -0
  75. data/test/tc_filter.rb +37 -0
  76. data/test/tc_form.rb +32 -0
  77. data/test/tc_model.rb +86 -0
  78. data/test/tc_prepare.rb +45 -0
  79. data/test/tc_refined_query.rb +88 -0
  80. data/test/tc_table_accessor.rb +265 -0
  81. data/test/test.log +181 -0
  82. data/test/test_db.sql +400 -0
  83. data/test/ts_lore.rb +49 -0
  84. data/types.rb +55 -0
  85. data/validation/message.rb +60 -0
  86. data/validation/parameter_validator.rb +104 -0
  87. data/validation/reason.rb +54 -0
  88. data/validation/type_validator.rb +91 -0
  89. data/validation.rb +65 -0
  90. metadata +170 -0
@@ -0,0 +1,151 @@
1
+
2
+ require('lore/types')
3
+ require('lore/gui/form')
4
+
5
+ module Lore
6
+ module GUI
7
+
8
+ # Usage:
9
+ #
10
+ # generator = Form_Generator.new(Table_Accessor, Lang, :readonly | :mutable)
11
+ # form = generator.generate
12
+ #
13
+ # See documentation of Cuba::GUI::Form for usage of form instances.
14
+ #
15
+ class Form_Generator
16
+
17
+ def initialize(klass=nil, labels={}, mode=:mutable, custom_elements={}) # {{{
18
+
19
+ @logger = Lore.logger
20
+ @form = Form.new()
21
+ @labels = labels
22
+ @klass = klass
23
+ @custom_elements = custom_elements
24
+
25
+ Textarea.reset_counter
26
+
27
+ @logger.debug('CUSTOM ELEMENTS: ' << @custom_elements.inspect)
28
+
29
+ if @klass.nil? then return end
30
+ @logger.debug('GET_ATTRIBUTES: ' << @klass.inspect)
31
+ @logger.debug('GET_ATTRIBUTES: ' << @klass.get_attributes.inspect)
32
+ @klass.get_attributes.each_pair { |table, attributes|
33
+
34
+ # handle attributes passed via attrib_setup:
35
+
36
+ attributes.each { |attrib|
37
+
38
+ label_tag = table.gsub('.','--') << '--' << attrib
39
+ if(@labels[label_tag].to_s != '' && @labels[label_tag] != label_tag) then
40
+ label = @labels[label_tag]
41
+ else
42
+ label = attrib.gsub('_',' ').capitalize
43
+ end
44
+
45
+ # @custom_elements is a hash mapping attribute names to
46
+ # Custom_Element instances.
47
+ if ((@custom_elements[table]) and
48
+ (@custom_elements[table][attrib])) then
49
+
50
+ form_element = @custom_elements[table][attrib].new(table, attrib, label)
51
+ form_element.mode = mode
52
+ @form.add(form_element)
53
+
54
+ elsif (@klass.get_primary_keys[table].nil? or
55
+ !@klass.get_primary_keys[table].include? attrib) and
56
+ (@klass.get_implicit_attributes[table].nil? or
57
+ !@klass.get_implicit_attributes[table].include? attrib) and
58
+ (@klass.get_has_a_klasses.nil? or
59
+ @klass.get_has_a_klasses[table+'.'+attrib].nil?) and
60
+ (@klass.get_hidden_attributes[table].nil? or
61
+ !@klass.get_hidden_attributes[table].include? attrib)
62
+ then
63
+
64
+ case @klass.get_attribute_types[table][attrib]
65
+
66
+ when Lore::PG_BOOL
67
+ form_element = Radio.new(table, attrib, label, ['t','f'])
68
+
69
+ when Lore::PG_SMALLINT || Lore::PG_INT
70
+ form_element = Text.new(table, attrib, label, nil)
71
+
72
+ when Lore::PG_VARCHAR
73
+ form_element = Text.new(table, attrib, label, nil, nil)
74
+ if (!@klass.get_maxlength.nil? &&
75
+ !@klass.get_maxlength[table].nil? &&
76
+ !@klass.get_maxlength[table][attrib.intern].nil?) then
77
+ form_element.set_length(@klass.get_maxlength[table][attrib.intern])
78
+ end
79
+
80
+ when Lore::PG_TEXT
81
+ form_element = Textarea.new(table, attrib, label, nil, nil)
82
+ if (!@klass.get_maxlength.nil? &&
83
+ !@klass.get_maxlength[table].nil? &&
84
+ !@klass.get_maxlength[table][attrib.intern].nil?) then
85
+ form_element.set_length(@klass.get_maxlength[table][attrib.intern])
86
+ end
87
+
88
+ when Lore::PG_TIMESTAMP_TIMEZONE
89
+ form_element = Date.new(table, attrib, label, nil)
90
+
91
+ when Lore::PG_DATE
92
+ form_element = Date.new(table, attrib, label, nil)
93
+
94
+ else
95
+ form_element = Text.new(table, attrib, label, nil)
96
+ end
97
+
98
+ form_element.set_mode(mode)
99
+ @form.add(form_element)
100
+
101
+ elsif (!@klass.get_has_a_klasses.nil? and
102
+ !@klass.get_has_a_klasses[table+'.'+attrib].nil?)
103
+ then
104
+ foreign_klass = @klass.get_has_a_klasses[table+'.'+attrib]
105
+ foreign_table = foreign_klass.table_name
106
+
107
+ form_element = Klass_Select.new(foreign_klass, table, attrib, label)
108
+ form_element.set_mode(mode)
109
+ @form.add(form_element)
110
+
111
+ elsif (!@klass.get_aggregate_klasses.nil? and
112
+ !@klass.get_aggregate_klasses[table+'.'+attrib].nil?)
113
+ then
114
+ foreign_klass = @klass.get_aggregate_klasses[table+'.'+attrib]
115
+ foreign_table = foreign_klass.table_name
116
+
117
+ form_element = Klass_Select.new(foreign_klass, table, attrib, label)
118
+ form_element.set_mode(mode)
119
+ @form.add(form_element)
120
+
121
+ # Attribute is explixit (expected/required) but not
122
+ # catched before -> Add attribute as hidden field:
123
+ elsif (!@klass.get_explicit_attributes[table].nil? and
124
+ @klass.get_explicit_attributes[table].include? attrib)
125
+ then
126
+ form_element = Hidden.new(table, attrib)
127
+ @form.add(form_element)
128
+
129
+ elsif (!@klass.get_implicit_attributes[table].nil? and
130
+ @klass.get_implicit_attributes[table].include? attrib)
131
+ then
132
+ @logger.debug(attrib+' is implicit')
133
+
134
+ end
135
+
136
+ }
137
+ }
138
+ end # }}}
139
+
140
+ # Returns instance of Cuba::GUI::Form configured
141
+ # for klass passed in constructor:
142
+ def generate() # {{{
143
+ return @form
144
+ end # }}}
145
+
146
+ end # class
147
+
148
+
149
+ end # module
150
+ end # module
151
+
@@ -0,0 +1,2 @@
1
+
2
+ <input <%= tag_attributes(attributes) %> />
@@ -0,0 +1,3 @@
1
+
2
+ <%= label %>
3
+ <input type="checkbox" <%= tag_attributes(attributes) %> />
@@ -0,0 +1 @@
1
+ <div class="lore_checkbox_row"><%= checkboxes %></div>
@@ -0,0 +1,2 @@
1
+
2
+ <input class="lore_file" type="file" <%= tag_attributes(attributes) %> />
@@ -0,0 +1,3 @@
1
+ <div class="lore_readonly_wrap" <%= tag_attributes(attributes) %> >
2
+ <%= attributes[:value] %>
3
+ </div>
@@ -0,0 +1,5 @@
1
+ <li class="form_attribute_row" id="<%= element_obj.attribute_id %>_wrap">
2
+ <%= label %>
3
+ <%= element %>
4
+ <div id="<%= element_obj.attribute_id %>_message" style="display: none" class="lore_form_element_message" ></div>
5
+ </li>
@@ -0,0 +1,3 @@
1
+ <td height="20" valign="top" class="lore_form_element_horizontal">
2
+ <%= element %>
3
+ </td>
@@ -0,0 +1,8 @@
1
+ <tr align="left" valign="middle">
2
+ <td valign="top" class="lore_form_element_listed left">
3
+ <b><%= label %></b>
4
+ </td>
5
+ <td valign="top" class="lore_form_element_listed right">
6
+ <%= element %>
7
+ </td>
8
+ </tr>
@@ -0,0 +1,3 @@
1
+ <ul class="form_elements">
2
+ <%= form %>
3
+ </ul>
@@ -0,0 +1,3 @@
1
+
2
+ <%= form %>
3
+
@@ -0,0 +1,8 @@
1
+
2
+ <div class="form_table">
3
+ <table border="0" cellspacing="0" cellpadding="0">
4
+ <tr>
5
+ <%= form %>
6
+ </tr>
7
+ </table>
8
+ </div>
@@ -0,0 +1,2 @@
1
+
2
+ <input type="password" <%= tag_attributes(attributes) %> />
@@ -0,0 +1,3 @@
1
+ <div class="lore_readonly_wrap" <%= tag_attributes(attributes) %> >
2
+ *********
3
+ </div>
@@ -0,0 +1 @@
1
+ <nobr><font style="width: 120px; "><input type="radio" <%= tag_attributes(attributes) %> /> <%= attributes[:label] %> </font></nobr>
@@ -0,0 +1 @@
1
+ <div class="lore_radio_row"><%= radios %></div>
@@ -0,0 +1,23 @@
1
+ <div class="lore_select_wrap">
2
+ <select <%= tag_attributes(attributes) %> >
3
+ <%=
4
+ option_index = 0
5
+ options = "\n"
6
+
7
+ range.each { |attrib|
8
+ attrib = attrib.to_s
9
+
10
+ if value.to_s == attrib.to_s then
11
+ selected = 'selected'
12
+ else selected = '' end
13
+
14
+ options += '<option value="'+attrib.to_s+'" '+selected+' >'
15
+ options += labels[option_index].to_s
16
+ options += '</options>'
17
+
18
+ option_index += 1
19
+ }
20
+ options
21
+ %>
22
+ </select>
23
+ </div>
@@ -0,0 +1,2 @@
1
+
2
+ <input type="text" <%= tag_attributes(attributes) %> />
@@ -0,0 +1,3 @@
1
+ <div class="lore_readonly_wrap" <%= tag_attributes(attributes) %> >
2
+ <%= attributes[:value] %>
3
+ </div>
@@ -0,0 +1,3 @@
1
+ <div class="lore_textarea_wrap">
2
+ <textarea <%= tag_attributes(attributes) %> ><%= value %></textarea>
3
+ </div>
@@ -0,0 +1,4 @@
1
+
2
+ <div class="textarea_lore_readonly">
3
+ <%= value.gsub('</p>', "\n\n").gsub('<br />',"\n").gsub(/<[^>]+>/,'')[0..1000].gsub("\n", '<br />') %>
4
+ </div>
data/lore.gemspec ADDED
@@ -0,0 +1,40 @@
1
+
2
+ require 'rake'
3
+
4
+ spec = Gem::Specification.new { |s|
5
+
6
+ s.name = 'lore'
7
+ s.rubyforge_project = 'lore'
8
+ s.summary = 'A flexible ORM based on PostgreSQL'
9
+ s.description = <<-EOF
10
+ Lore is an object-relational mapping (ORM) implementation
11
+ providing many features like prepared statements,
12
+ (multiple) inheritance, a comfortable query syntax,
13
+ highly customizable automated form generation,
14
+ and result caching using memory mapping (MMap).
15
+ Lore is currently using PostgreSQL as database backend.
16
+ EOF
17
+ s.version = '0.4.2'
18
+ s.author = 'Tobias Fuchs'
19
+ s.email = 'fuchs@atomnode.net'
20
+ s.date = Time.now
21
+ s.files = '*.rb'
22
+ s.add_dependency('mmap', '>= 0.1')
23
+ s.add_dependency('postgres', '>= 0.1')
24
+ s.files = FileList['*',
25
+ 'behaviours/*',
26
+ 'test/*',
27
+ 'cache/*',
28
+ 'validation/*',
29
+ 'gui/*',
30
+ 'gui/templates/*',
31
+ 'exception/*'].to_a
32
+
33
+ s.has_rdoc = true
34
+ s.rdoc_options << '--title' << 'Lore ORM' <<
35
+ '--main' << 'README' <<
36
+ '--line-numbers'
37
+
38
+ s.homepage = 'http://lore.rubyforge.org'
39
+
40
+ }
data/lore.rb ADDED
@@ -0,0 +1,94 @@
1
+
2
+ require('logger')
3
+
4
+ module Lore
5
+
6
+ @logfile = '/var/log/lore/query.log'
7
+ def self.logfile
8
+ @logfile
9
+ end
10
+ def self.logfile=(file)
11
+ @logger = Logger.new(file)
12
+ end
13
+ def self.logger
14
+ @logger
15
+ end
16
+ @logger = Logger.new(Lore.logfile)
17
+
18
+ def self.log(&log_block)
19
+ return if Lore.logging_disabled?
20
+ Lore.logger.debug(&log_block)
21
+ end
22
+
23
+ def self.log_queries?
24
+ true
25
+ end
26
+ def self.logging_disabled?
27
+ false
28
+ end
29
+
30
+ @cache_entities = false
31
+
32
+ @logins = {
33
+ }
34
+
35
+ def self.set_login_data(login_hash)
36
+ @logins = login_hash
37
+ end
38
+ def self.add_login_data(login_hash)
39
+ @logins.update(login_hash)
40
+ end
41
+
42
+ def self.path
43
+ File.expand_path(File.dirname(__FILE__)) + '/'
44
+ end
45
+
46
+ @pg_server = 'localhost'
47
+ @pg_port = 5432
48
+
49
+ def self.pg_server
50
+ @pg_server
51
+ end
52
+ def self.pg_port
53
+ @pg_port
54
+ end
55
+ def self.pg_server=(s)
56
+ @pg_server = s
57
+ end
58
+ def self.pg_port=(p)
59
+ @pg_port = p
60
+ end
61
+
62
+ def self.user_for(dbname)
63
+ begin
64
+ @logins[dbname.to_s][0].to_s
65
+ rescue ::Exception => excep
66
+ raise ::Exception.new('Unable to resolve user for database ' << dbname.inspect)
67
+ end
68
+ end
69
+ def self.pass_for(dbname)
70
+ @logins[dbname.to_s][1].to_s
71
+ end
72
+
73
+ def self.disable_cache
74
+ @cache_entities = false
75
+ end
76
+
77
+ def self.enable_cache
78
+ @cache_entities = true
79
+ end
80
+
81
+ def self.cache_enabled?
82
+ @cache_entities
83
+ end
84
+
85
+ def self.on_connect_commands()
86
+ "set client_encoding to Unicode; set datestyle to 'European'"
87
+ end
88
+
89
+ end
90
+
91
+ require('lore/validation/parameter_validator')
92
+ require('lore/exception/invalid_parameter')
93
+ require('lore/exception/invalid_klass_parameters')
94
+ require('lore/connection')
data/migration.rb ADDED
@@ -0,0 +1,48 @@
1
+
2
+ module Lore
3
+
4
+
5
+ # Usage:
6
+ #
7
+ # Using auto-bootstrap of models:
8
+ #
9
+ # class My_Model < Lore::Model
10
+ # extend Lore::Migration
11
+ #
12
+ # table :my_model, :public
13
+ # primary_key :key_a, :key_a_seq
14
+ # primary_key :key_b
15
+ #
16
+ # has_attributes {
17
+ # :foo, Lore::Type.integer, { :unique => true, :default => 1 }
18
+ # :bar, Lore::Type.integer, { :not_null => true, :check => 'bar < 1000' }
19
+ # }
20
+ #
21
+ # def self.up
22
+ # bootstrap()
23
+ # My_Model.create(:foo => 23, :bar => 123)
24
+ # end
25
+ #
26
+ # end
27
+ #
28
+ module Migration
29
+
30
+ def has_attributes(attrib_hash)
31
+ @model_schema = attrib_hash
32
+ end
33
+
34
+ def update_schema
35
+ @factory = Model_Factory.new(self.class.to_s)
36
+ @model_schema.each_pair { |attrib_name, attrib_props|
37
+ @factory.add_attribute(attrib_name.to_s, attrib_props)
38
+ }
39
+ end
40
+
41
+ def bootstrap
42
+ @factory.build_table
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+
data/model.rb ADDED
@@ -0,0 +1,139 @@
1
+
2
+ require('logger')
3
+
4
+ require('lore/aspect');
5
+ require('lore/table_selector');
6
+ require('lore/table_inserter');
7
+ require('lore/table_updater');
8
+ require('lore/table_deleter');
9
+ require('lore/table_instance');
10
+ require('lore/query_shortcuts');
11
+ require('lore/validation');
12
+ require('lore/migration');
13
+ require('lore/validation/parameter_validator');
14
+ require('lore/exception/invalid_parameter');
15
+ require('lore/exception/invalid_klass_parameters');
16
+ require('lore/cache/cacheable');
17
+ require('lore/table_accessor.rb')
18
+
19
+ module Lore
20
+
21
+ # For API details see Lore::Table_Accessor
22
+ #
23
+ #
24
+ # =How to define models
25
+ #
26
+ # Example:
27
+ #
28
+ # require 'lore/model'
29
+ #
30
+ # class Vehicle < Lore::Model
31
+ # end
32
+ #
33
+ # class Car < Vehicle
34
+ # end
35
+ #
36
+ # Sets following defaults:
37
+ #
38
+ # class Vehicle
39
+ # table :vehicle, :vehicle_id
40
+ # primary_key :id # or :vehicle_id if :id is not present
41
+ # end
42
+ #
43
+ # class Car < Vehicle
44
+ # table :car, :public
45
+ # primary_key :id # or :car_id if :id is not present
46
+ # is_a Vehicle, :vehicle_id
47
+ # end
48
+ #
49
+ # = How to use models
50
+ #
51
+ # == Creating entities (INSERTs)
52
+ #
53
+ # manuf_1 = Manufacturer.create(:name => 'Audi')
54
+ # manuf_2 = Manufacturer.create(:name => 'BMW')
55
+ # car = Car.create(:manufacturer => manuf, :name => 'TT')
56
+ #
57
+ # == Changing entities (UPDATEs)
58
+ #
59
+ # car['name'] = 'BMW318i'
60
+ # car.manufacturer = manuf_2 # same as car[:manufacturer_id] = manuf_2.manufacturer_id
61
+ # car.commit
62
+ #
63
+ # == Deleting entities (DELETEs)
64
+ #
65
+ # car.delete!
66
+ # is the same as
67
+ # Car.delete.where(Car.car_id == car.car_id).perform
68
+ # is the same as
69
+ # Lore.perform Car.delete.where(Car.car_id == car.car_id)
70
+ # is the same as
71
+ # Car.delete { |c|
72
+ # c.where(c.car_id = car.car_id)
73
+ # }
74
+ #
75
+ # =How to disable/enable features
76
+ #
77
+ # Lore::Model extends
78
+ # * Lore::Cache::Cacheable
79
+ # * Lore::Query_Shortcuts
80
+ # * Lore::Validation
81
+ # * Lore::Aspect
82
+ #
83
+ # Each of them is optional. If you want, for example, a minimalistic
84
+ # version of Lore::Model with comfortable query interfaces you can define
85
+ # your own Model class that extends the Lore::Query_Shortcuts module only:
86
+ #
87
+ # class Slim_Model < Lore::Table_Accessor
88
+ # extend Lore::Query_Shortcuts
89
+ # end
90
+ #
91
+ # You can use it as base class for other Model classes, too:
92
+ #
93
+ # class Cache_Model < Slim_Model
94
+ # extend Lore::Query_Shortcuts
95
+ # extend Lore::Cacheable
96
+ # end
97
+ #
98
+ # You also can define this differently for every model by deriving them
99
+ # from Lore::Table_Accessor and extending every single one of them:
100
+ #
101
+ # class User < Lore::Table_Accessor
102
+ # extend Lore::Query_Shortcuts
103
+ # extend Lore::Aspect
104
+ # extend Lore::Validation
105
+ #
106
+ # table :user, :public
107
+ # primary_key :user_id
108
+ #
109
+ # end
110
+ #
111
+ # =How to use Lore::Model in a Rails app
112
+ #
113
+ # By default, Lore::Model doesn't extend Rails interface bridges. You
114
+ # either can define your own Model base class (see: How to disable/enable
115
+ # features) and extend it by Lore::Rails::Model
116
+ #
117
+ # class My_Model < Lore::Model
118
+ # extend Lore::Rails::Model
119
+ # end
120
+ #
121
+ # ... or extend Lore::Model directly:
122
+ #
123
+ # Lore::Model.extend Lore::Rails_Bridge
124
+ #
125
+ # In this case, every Model in your app will include Rails interfaces.
126
+ #
127
+ class Model < Table_Accessor
128
+ extend Lore::Cache::Cacheable
129
+ extend Lore::Query_Shortcuts
130
+ extend Lore::Validation
131
+ extend Lore::Aspect
132
+ extend Lore::Migration
133
+
134
+ def by_id(pkey_id)
135
+ _by_id(pkey_id).first # Auto-defined in Lore::Table_Accessor.primary_key
136
+ end
137
+ end
138
+
139
+ end