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.
- data/LICENSE +19 -0
- data/README +74 -0
- data/aspect.rb +80 -0
- data/behaviours/lockable.rb +41 -0
- data/behaviours/movable.rb +54 -0
- data/behaviours/versioned.rb +24 -0
- data/benchmark.rb +193 -0
- data/bits.rb +52 -0
- data/cache/abstract_entity_cache.rb +82 -0
- data/cache/bits.rb +22 -0
- data/cache/cacheable.rb +202 -0
- data/cache/cached_entities.rb +116 -0
- data/cache/file_index.rb +35 -0
- data/cache/mmap_entity_cache.rb +67 -0
- data/clause.rb +528 -0
- data/connection.rb +155 -0
- data/custom_functions.sql +14 -0
- data/exception/ambiguous_attribute.rb +14 -0
- data/exception/cache_exception.rb +30 -0
- data/exception/invalid_klass_parameters.rb +63 -0
- data/exception/invalid_parameter.rb +42 -0
- data/exception/unknown_typecode.rb +19 -0
- data/file_index.sql +56 -0
- data/gui/erb_template.rb +79 -0
- data/gui/erb_template_helpers.rhtml +19 -0
- data/gui/form.rb +314 -0
- data/gui/form_element.rb +676 -0
- data/gui/form_generator.rb +151 -0
- data/gui/templates/button.rhtml +2 -0
- data/gui/templates/checkbox.rhtml +3 -0
- data/gui/templates/checkbox_row.rhtml +1 -0
- data/gui/templates/file.rhtml +2 -0
- data/gui/templates/file_readonly.rhtml +3 -0
- data/gui/templates/form_element.rhtml +5 -0
- data/gui/templates/form_element_horizontal.rhtml +3 -0
- data/gui/templates/form_element_listed.rhtml +8 -0
- data/gui/templates/form_table.rhtml +3 -0
- data/gui/templates/form_table_blank.rhtml +3 -0
- data/gui/templates/form_table_horizontal.rhtml +8 -0
- data/gui/templates/password.rhtml +2 -0
- data/gui/templates/password_readonly.rhtml +3 -0
- data/gui/templates/radio.rhtml +1 -0
- data/gui/templates/radio_row.rhtml +1 -0
- data/gui/templates/select.rhtml +23 -0
- data/gui/templates/text.rhtml +2 -0
- data/gui/templates/text_readonly.rhtml +3 -0
- data/gui/templates/textarea.rhtml +3 -0
- data/gui/templates/textarea_readonly.rhtml +4 -0
- data/lore.gemspec +40 -0
- data/lore.rb +94 -0
- data/migration.rb +48 -0
- data/model.rb +139 -0
- data/model_factory.rb +202 -0
- data/model_shortcuts.rb +16 -0
- data/query_shortcuts.rb +367 -0
- data/reserved_methods.txt +3 -0
- data/result.rb +100 -0
- data/symbol.rb +58 -0
- data/table_accessor.rb +1926 -0
- data/table_deleter.rb +115 -0
- data/table_inserter.rb +168 -0
- data/table_instance.rb +384 -0
- data/table_selector.rb +314 -0
- data/table_updater.rb +155 -0
- data/test/README +31 -0
- data/test/env.rb +5 -0
- data/test/lore_test.log +8218 -0
- data/test/model.rb +142 -0
- data/test/prepare.rb +37 -0
- data/test/tc_aspect.rb +58 -0
- data/test/tc_cache.rb +80 -0
- data/test/tc_clause.rb +104 -0
- data/test/tc_deep_inheritance.rb +49 -0
- data/test/tc_factory.rb +57 -0
- data/test/tc_filter.rb +37 -0
- data/test/tc_form.rb +32 -0
- data/test/tc_model.rb +86 -0
- data/test/tc_prepare.rb +45 -0
- data/test/tc_refined_query.rb +88 -0
- data/test/tc_table_accessor.rb +265 -0
- data/test/test.log +181 -0
- data/test/test_db.sql +400 -0
- data/test/ts_lore.rb +49 -0
- data/types.rb +55 -0
- data/validation/message.rb +60 -0
- data/validation/parameter_validator.rb +104 -0
- data/validation/reason.rb +54 -0
- data/validation/type_validator.rb +91 -0
- data/validation.rb +65 -0
- metadata +170 -0
data/table_selector.rb
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
|
2
|
+
require('lore/cache/cached_entities')
|
3
|
+
require('lore/connection')
|
4
|
+
require('lore/clause')
|
5
|
+
|
6
|
+
module Lore
|
7
|
+
|
8
|
+
module Table_Selector # :nodoc:
|
9
|
+
|
10
|
+
@@logger = Lore.logger
|
11
|
+
|
12
|
+
public
|
13
|
+
|
14
|
+
# Extracted, recursive method for building the JOIN-part of
|
15
|
+
# a SELECT query.
|
16
|
+
def self.build_joined_query(accessor, query_string='', joined_tables=[])
|
17
|
+
|
18
|
+
top_table = accessor.table_name
|
19
|
+
is_a_hierarchy = accessor.get_joins()
|
20
|
+
own_primary_keys = accessor.get_primary_keys()
|
21
|
+
own_foreign_keys = accessor.get_foreign_keys()
|
22
|
+
|
23
|
+
joined_accessors = (accessor.get_is_a_klasses).dup
|
24
|
+
joined_accessors.update(accessor.get_aggregate_klasses)
|
25
|
+
|
26
|
+
# predefine
|
27
|
+
own_p_keys = Hash.new
|
28
|
+
foreign_p_keys = Hash.new
|
29
|
+
on_string = String.new
|
30
|
+
field_counter = 0
|
31
|
+
|
32
|
+
is_a_hierarchy.each_pair { |foreign_table, foreign_base_tables|
|
33
|
+
# JOIN base.table ON (
|
34
|
+
if !(joined_tables.include?(foreign_table)) then
|
35
|
+
|
36
|
+
foreign_p_keys = own_primary_keys[foreign_table]
|
37
|
+
|
38
|
+
own_p_keys = own_foreign_keys[foreign_table]
|
39
|
+
|
40
|
+
if !own_p_keys.nil? then
|
41
|
+
|
42
|
+
joined_tables << foreign_table
|
43
|
+
query_string << "\n JOIN #{foreign_table} on ("
|
44
|
+
|
45
|
+
field_counter = 0
|
46
|
+
on_string = ''
|
47
|
+
foreign_p_keys.uniq.each { |foreign_field|
|
48
|
+
|
49
|
+
# base.table.foreign_field =
|
50
|
+
on_string << "#{foreign_table}.#{foreign_field} = #{top_table}.#{own_p_keys[field_counter]}"
|
51
|
+
# this.table.own_field
|
52
|
+
|
53
|
+
if field_counter > 0 then query_string << ", #{on_string}"
|
54
|
+
else query_string << on_string
|
55
|
+
end
|
56
|
+
|
57
|
+
field_counter += 1
|
58
|
+
}
|
59
|
+
|
60
|
+
query_string << ')'
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# sub-joins of joined table:
|
65
|
+
query_string = build_joined_query(joined_accessors["#{top_table}.#{own_p_keys.first}"],
|
66
|
+
query_string,
|
67
|
+
joined_tables)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
return query_string
|
71
|
+
end # def
|
72
|
+
|
73
|
+
public
|
74
|
+
|
75
|
+
# Extracted, recursive method for building the AS-part of
|
76
|
+
# a SELECT query.
|
77
|
+
def self.build_as_query(table_fields,
|
78
|
+
query_string='')
|
79
|
+
|
80
|
+
return '*'
|
81
|
+
|
82
|
+
first = query_string == ''
|
83
|
+
|
84
|
+
table_fields.each_pair { |table_name, fields|
|
85
|
+
|
86
|
+
fields.each { |foreign_field|
|
87
|
+
|
88
|
+
# base.table.foreign_field =
|
89
|
+
query_string << ', ' unless first
|
90
|
+
query_string << "\n #{table_name}.#{foreign_field} AS "
|
91
|
+
query_string << "\"#{table_name}.#{foreign_field}\""
|
92
|
+
|
93
|
+
first = false
|
94
|
+
}
|
95
|
+
|
96
|
+
}
|
97
|
+
|
98
|
+
query_string
|
99
|
+
|
100
|
+
end #def
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def self.build_select_query(accessor,
|
105
|
+
value_keys)
|
106
|
+
# this is to be wrapped in an adapter to provide DB abstraction:
|
107
|
+
|
108
|
+
table_name = accessor.get_table_name
|
109
|
+
query_string = 'SELECT '
|
110
|
+
query_string << build_as_query(accessor.get_attributes)
|
111
|
+
query_string << ' FROM ' << table_name + ' '
|
112
|
+
query_string << build_joined_query(accessor)
|
113
|
+
query_string << "\n WHERE "
|
114
|
+
|
115
|
+
operator = '='
|
116
|
+
field = String.new
|
117
|
+
value_keys[0].each_pair { |field, value|
|
118
|
+
# Filtering values is not necessary when feeded with
|
119
|
+
# Lore::Attributes instance
|
120
|
+
field = field.to_s
|
121
|
+
if value.instance_of? Hash then
|
122
|
+
value.each_pair { |attrib_name, attrib_value|
|
123
|
+
query_string << "#{field}.#{attrib_name} #{operator} '#{attrib_value.to_s.lore_escape}' AND "
|
124
|
+
}
|
125
|
+
else
|
126
|
+
query_string << "#{table_name}." if field.split('.')[2].nil?
|
127
|
+
query_string << "#{field} #{operator} '#{value.to_s.lore_escape}' AND "
|
128
|
+
end
|
129
|
+
}
|
130
|
+
# remove trailing AND:
|
131
|
+
query_string.chomp!(' AND ')
|
132
|
+
|
133
|
+
return query_string
|
134
|
+
|
135
|
+
end # def
|
136
|
+
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
140
|
+
def self.select_query(what,
|
141
|
+
accessor,
|
142
|
+
clause = nil,
|
143
|
+
&block)
|
144
|
+
|
145
|
+
query_string = 'SELECT '
|
146
|
+
|
147
|
+
# Example:
|
148
|
+
# select(Car.name) -> SELECT public.car.name AS "value"
|
149
|
+
if what.instance_of? Lore::Clause then
|
150
|
+
what = what.to_s + ' AS "value" '
|
151
|
+
end
|
152
|
+
|
153
|
+
if(what.nil? || what == '*' || what == '') then
|
154
|
+
query_as_part = build_as_query(accessor.get_attributes)
|
155
|
+
else
|
156
|
+
query_as_part = what.to_s
|
157
|
+
end
|
158
|
+
query_from_part = " FROM #{accessor.get_table_name} "
|
159
|
+
# Add JOIN part for system defined type (user defined
|
160
|
+
# joins will be set in Clause_Parser object in later
|
161
|
+
# yield):
|
162
|
+
query_join_part = build_joined_query(accessor)
|
163
|
+
|
164
|
+
clause_string = ''
|
165
|
+
if block_given? then
|
166
|
+
yield_obj = Lore::Clause_Parser.new(accessor.table_name, *(accessor.get_attributes))
|
167
|
+
clause = yield *yield_obj
|
168
|
+
end
|
169
|
+
# Extend AS part by attributes that have been added in clause
|
170
|
+
query_parts = clause.parts
|
171
|
+
query_as_part << ' ' << query_parts[:as].to_s
|
172
|
+
# Extend JOIN part by joins that have been added in clause
|
173
|
+
query_join_part << ' ' << query_parts[:join].to_s
|
174
|
+
# Set WHERE part to conditions defined in clause
|
175
|
+
query_where_part = ' ' << query_parts[:where].to_s
|
176
|
+
# Set GROUP BY part
|
177
|
+
query_group_by_part = ' ' << query_parts[:group_by].to_s
|
178
|
+
# Set HAVING part
|
179
|
+
query_having_part = ' ' << query_parts[:having].to_s
|
180
|
+
# Set LIMIT, OFFSET
|
181
|
+
query_filter_part = ' ' << query_parts[:filter].to_s
|
182
|
+
query_order_by_part = ' ' << query_parts[:order_by].to_s
|
183
|
+
query_limit_part = ' ' << query_parts[:limit].to_s
|
184
|
+
query_limit_part << ' ' << query_parts[:offset].to_s
|
185
|
+
|
186
|
+
query_string << query_as_part << query_from_part << query_join_part << query_where_part << query_group_by_part << query_having_part << query_filter_part << query_order_by_part << query_limit_part
|
187
|
+
|
188
|
+
# TODO:
|
189
|
+
# Implement class Plan_Clause, offering exactly the same methods as Clause,
|
190
|
+
# but generating a Plan instead the query.
|
191
|
+
# Pass block& to Plan_Clause, too, only in case a plan is needed.
|
192
|
+
|
193
|
+
return query_string
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.plan_select_query(what, accessor, &block)
|
198
|
+
|
199
|
+
auto_plan = Lore::Plan.new(clause, select_query(what, accessor, &block), accessor)
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
protected
|
204
|
+
|
205
|
+
def self.select(what,
|
206
|
+
accessor,
|
207
|
+
&block)
|
208
|
+
|
209
|
+
query_string = select_query(what,
|
210
|
+
accessor,
|
211
|
+
&block)
|
212
|
+
|
213
|
+
return perform_select(accessor, query_string)
|
214
|
+
|
215
|
+
end # def
|
216
|
+
|
217
|
+
def self.select_cached(what,
|
218
|
+
accessor,
|
219
|
+
&block)
|
220
|
+
|
221
|
+
query_string = select_query(what,
|
222
|
+
accessor,
|
223
|
+
&block)
|
224
|
+
|
225
|
+
@@logger.debug { 'Select on ' << accessor.to_s }
|
226
|
+
|
227
|
+
result = Array.new
|
228
|
+
if Lore.cache_enabled? && accessor.entity_cache && accessor.entity_cache.include?(accessor, query_string) then
|
229
|
+
result = accessor.entity_cache.read(accessor, query_string)
|
230
|
+
result = nil if result == ''
|
231
|
+
@@logger.debug { "cache contents for #{accessor.table_name} found: #{result.to_s != ''}" }
|
232
|
+
else
|
233
|
+
db_result = perform_select(accessor, query_string)
|
234
|
+
db_result.get_rows[:values].each { |row|
|
235
|
+
result.push(accessor.new(row))
|
236
|
+
}
|
237
|
+
if Lore.cache_enabled? && accessor.entity_cache then
|
238
|
+
accessor.entity_cache.create(accessor, query_string, result)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
return result
|
242
|
+
end # def
|
243
|
+
|
244
|
+
def self.select_on_keys(accessor,
|
245
|
+
value_keys)
|
246
|
+
|
247
|
+
query_string = build_select_query(accessor,
|
248
|
+
value_keys)
|
249
|
+
|
250
|
+
return perform_select(accessor, query_string)
|
251
|
+
|
252
|
+
end #def
|
253
|
+
|
254
|
+
def self.prepare(plan_name, accessor, args, &block)
|
255
|
+
args_string = ''
|
256
|
+
args.map { |a| a = Lore::TYPE_NAMES[a] }
|
257
|
+
if args.to_s != '' && args.length > 0 then args_string = "(#{args.join(',')})" end
|
258
|
+
query_string = "PREPARE #{accessor.table_name.gsub('.','_')}__#{plan_name.to_s}#{args_string} AS " << select_query(nil, accessor, &block)
|
259
|
+
begin
|
260
|
+
result = Lore::Connection.perform(query_string)
|
261
|
+
rescue ::Exception => excep
|
262
|
+
@@logger.debug("Exception when preparing #{plan_name.to_s}: #{excep.message}. Already prepared before?")
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def self.select_prepared(plan_name, accessor, *args)
|
267
|
+
args_string = ''
|
268
|
+
if args.to_s != '' && args.length > 0 then args_string = "(#{args.join(',')})" end
|
269
|
+
query_string = "EXECUTE #{plan_name.to_s} #{args_string}; "
|
270
|
+
result = Array.new
|
271
|
+
if Lore.cache_enabled? && accessor.entity_cache && accessor.entity_cache.include?(accessor, query_string) then
|
272
|
+
result = accessor.read_entity_cache(query_string)
|
273
|
+
result = nil if result == ''
|
274
|
+
@@logger.debug { "cache contents for prepared #{accessor.table_name}: #{result.to_s.inspect}" }
|
275
|
+
else
|
276
|
+
db_result = perform_select(accessor, query_string)
|
277
|
+
db_result.get_rows[:values].each { |row|
|
278
|
+
result.push(accessor.new(row))
|
279
|
+
}
|
280
|
+
if Lore.cache_enabled? && accessor.entity_cache then
|
281
|
+
accessor.create_entity_cache(query_string, result)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
return result
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
def self.deallocate(plan_name, accessor)
|
289
|
+
begin
|
290
|
+
query_string = "DEALLOCATE #{accessor.table_name.gsub('.','_')}__#{plan_name.to_s}; "
|
291
|
+
result = Lore::Connection.perform(query_string)
|
292
|
+
rescue ::Exception => excep
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
private
|
297
|
+
|
298
|
+
def self.perform_select(accessor, query_string)
|
299
|
+
|
300
|
+
Context.enter(accessor.get_context) unless accessor.get_context.nil?
|
301
|
+
begin
|
302
|
+
result = Lore::Connection.perform(query_string)
|
303
|
+
return result
|
304
|
+
rescue PGError => pge
|
305
|
+
raise pge
|
306
|
+
ensure
|
307
|
+
Context.leave unless accessor.get_context.nil?
|
308
|
+
GC.start
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
end # module
|
314
|
+
end # module
|
data/table_updater.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
|
2
|
+
require('lore/connection')
|
3
|
+
require('lore/bits')
|
4
|
+
|
5
|
+
module Lore
|
6
|
+
|
7
|
+
module Table_Updater # :nodoc:
|
8
|
+
|
9
|
+
@logger = Logger.new(Lore.logfile)
|
10
|
+
|
11
|
+
def self.atomic_update_query(table_name,
|
12
|
+
attributes,
|
13
|
+
primary_key_values,
|
14
|
+
value_keys,
|
15
|
+
explicit_fields)
|
16
|
+
|
17
|
+
@logger.debug('EXPLICIT FIELDS: ' << explicit_fields.inspect)
|
18
|
+
|
19
|
+
query_string = "\n"
|
20
|
+
query_string += 'UPDATE '+table_name+' SET '
|
21
|
+
|
22
|
+
set_string = String.new
|
23
|
+
|
24
|
+
key_counter = 0
|
25
|
+
attributes.each { |attribute_name|
|
26
|
+
|
27
|
+
internal_attribute_name = attribute_name[0..24]
|
28
|
+
value = value_keys[internal_attribute_name].to_s
|
29
|
+
if value == '' then
|
30
|
+
value = value_keys[table_name+'.'+internal_attribute_name].to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
# attrib = Lore.resolve_passed_value(value_keys, table_name, attribute_name)
|
34
|
+
# value = attrib[:value]
|
35
|
+
# internal_attribute_name = attrib[:field]
|
36
|
+
|
37
|
+
# only include attribute to update query if
|
38
|
+
# this attribute is not marked for explicit updating or
|
39
|
+
# marked as explicit but non-empty:
|
40
|
+
if(
|
41
|
+
!(explicit_fields && explicit_fields.include?(internal_attribute_name) && value.empty?) &&
|
42
|
+
!(primary_key_values[attribute_name] && value.empty?)
|
43
|
+
)
|
44
|
+
|
45
|
+
if key_counter > 0
|
46
|
+
set_string += ', '
|
47
|
+
end # if
|
48
|
+
set_string += attribute_name + '=\'' + value.to_s + '\' '
|
49
|
+
|
50
|
+
key_counter = 1
|
51
|
+
|
52
|
+
end # if
|
53
|
+
}
|
54
|
+
query_string += set_string
|
55
|
+
|
56
|
+
query_string += 'WHERE '
|
57
|
+
|
58
|
+
field_counter=0
|
59
|
+
primary_key_values.each_pair { |field, value|
|
60
|
+
query_string += field + '=\'' + value.to_s + '\' '
|
61
|
+
if field_counter < primary_key_values.keys.length-1
|
62
|
+
query_string += 'AND '
|
63
|
+
end
|
64
|
+
field_counter += 1
|
65
|
+
}
|
66
|
+
query_string += ';'
|
67
|
+
|
68
|
+
query_string
|
69
|
+
|
70
|
+
end #def
|
71
|
+
|
72
|
+
def self.block_update(accessor,
|
73
|
+
&block)
|
74
|
+
|
75
|
+
query_string = 'UPDATE '+accessor.get_table_name
|
76
|
+
|
77
|
+
if block_given? then
|
78
|
+
yield_obj = Lore::Clause_Parser.new(accessor.table_name)
|
79
|
+
clause = yield *yield_obj
|
80
|
+
end
|
81
|
+
|
82
|
+
query_string += clause.set_part
|
83
|
+
query_string += clause.where_part
|
84
|
+
|
85
|
+
Lore::Context.enter(accessor.get_context) unless accessor.get_context.nil?
|
86
|
+
|
87
|
+
begin
|
88
|
+
Lore::Connection.perform(query_string)
|
89
|
+
ensure
|
90
|
+
Lore::Context.leave unless accessor.get_context.nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
# :nodoc
|
96
|
+
def self.update_query(table_name,
|
97
|
+
is_a_hierarchy,
|
98
|
+
|
99
|
+
attributes,
|
100
|
+
primary_key_values,
|
101
|
+
value_keys,
|
102
|
+
explicit_fields,
|
103
|
+
|
104
|
+
query_string='')
|
105
|
+
|
106
|
+
is_a_hierarchy.each_pair { |table, base_tables|
|
107
|
+
|
108
|
+
# pass base tables first, recursively, as IS_A-based creation has
|
109
|
+
# to be done bottom-up:
|
110
|
+
query_string += update_query(table,
|
111
|
+
base_tables,
|
112
|
+
|
113
|
+
attributes,
|
114
|
+
primary_key_values,
|
115
|
+
value_keys,
|
116
|
+
explicit_fields
|
117
|
+
).to_s
|
118
|
+
}
|
119
|
+
# finally, add query string for this table:
|
120
|
+
query_string += atomic_update_query(table_name,
|
121
|
+
attributes[table_name],
|
122
|
+
primary_key_values[table_name],
|
123
|
+
value_keys[table_name],
|
124
|
+
# value_keys[table_name],
|
125
|
+
explicit_fields[table_name]
|
126
|
+
).to_s
|
127
|
+
|
128
|
+
query_string
|
129
|
+
|
130
|
+
end #def
|
131
|
+
|
132
|
+
# :nodoc
|
133
|
+
def self.perform_update(accessor, accessor_instance)
|
134
|
+
|
135
|
+
query_string = update_query(accessor.get_table_name,
|
136
|
+
accessor.get_is_a,
|
137
|
+
accessor.get_attributes,
|
138
|
+
accessor_instance.get_primary_key_values,
|
139
|
+
accessor_instance.get_attribute_values,
|
140
|
+
accessor.get_explicit)
|
141
|
+
|
142
|
+
Context.enter(accessor.get_context) unless accessor.get_context.nil?
|
143
|
+
begin
|
144
|
+
Lore::Connection.perform("BEGIN;\n#{query_string}\nCOMMIT;")
|
145
|
+
ensure
|
146
|
+
Context.leave unless accessor.get_context.nil?
|
147
|
+
end
|
148
|
+
|
149
|
+
accessor.flush_entity_cache()
|
150
|
+
|
151
|
+
end #def
|
152
|
+
|
153
|
+
end # module
|
154
|
+
|
155
|
+
end # module
|
data/test/README
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
How to run tests
|
3
|
+
|
4
|
+
- Create (empty) database test:
|
5
|
+
|
6
|
+
$ createdb test
|
7
|
+
|
8
|
+
- Create user 'lore' with password 'lore23'
|
9
|
+
(change this in trunk/lore.rb, if you want to)
|
10
|
+
|
11
|
+
$shell: createuser lore
|
12
|
+
$shell: ...
|
13
|
+
$shell: psql test
|
14
|
+
$psql/test: ALTER USER lore WITH PASSWORD 'lore23';
|
15
|
+
$psql/test: \q
|
16
|
+
|
17
|
+
- Insert test schema into database:
|
18
|
+
$shell: psql test < test/test_db.sql
|
19
|
+
|
20
|
+
- Run tests. No test should fail except those
|
21
|
+
saying they are supposed to (printing something like
|
22
|
+
'This will fail')
|
23
|
+
|
24
|
+
It's best to redirect STDERR to a file.
|
25
|
+
|
26
|
+
$shell: ruby test/ts_lore.rb 2>test.log
|
27
|
+
|
28
|
+
- Check the logfile in case you are interested in
|
29
|
+
the kinky details.
|
30
|
+
|
31
|
+
|