og 0.17.0 → 0.18.0
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/CHANGELOG +81 -0
- data/README +1 -0
- data/doc/AUTHORS +9 -6
- data/doc/RELEASES +24 -1
- data/lib/og.rb +8 -2
- data/lib/og/collection.rb +38 -22
- data/lib/og/entity.rb +15 -0
- data/lib/og/manager.rb +52 -10
- data/lib/og/mixin/hierarchical.rb +3 -4
- data/lib/og/mixin/orderable.rb +3 -2
- data/lib/og/mixin/timestamped.rb +2 -0
- data/lib/og/relation.rb +6 -1
- data/lib/og/relation/has_many.rb +6 -0
- data/lib/og/relation/joins_many.rb +5 -0
- data/lib/og/store/kirby.rb +280 -0
- data/lib/og/store/kirby/README +6 -0
- data/lib/og/store/kirby/kirbybase.rb +1601 -0
- data/lib/og/store/kirby/readme.txt +63 -0
- data/lib/og/store/madeleine.rb +0 -2
- data/lib/og/store/mysql.rb +5 -5
- data/lib/og/store/psql.rb +15 -7
- data/lib/og/store/sql.rb +50 -23
- data/lib/og/store/sqlite.rb +16 -12
- data/lib/og/store/sqlserver.rb +7 -3
- data/test/og/tc_store.rb +19 -6
- metadata +8 -3
@@ -0,0 +1,63 @@
|
|
1
|
+
KirbyBase 2.2
|
2
|
+
|
3
|
+
A small, plain-text, dbms written in Ruby. It can be used either embedded
|
4
|
+
or client/server. Version 2 is a complete re-write with a completely new
|
5
|
+
interface. It is NOT backwards compatible. If you need backwards
|
6
|
+
compatibility, please use version 1.6.
|
7
|
+
|
8
|
+
|
9
|
+
*Installation:
|
10
|
+
|
11
|
+
Unpack the file you downloaded. Execute "ruby install.rb" or simply make
|
12
|
+
sure kirbybase.rb is somewhere in your Ruby library path.
|
13
|
+
|
14
|
+
|
15
|
+
*Documentation:
|
16
|
+
|
17
|
+
Documentation is in manual.html. Also, RDoc generated documentation is in
|
18
|
+
the doc directory.
|
19
|
+
|
20
|
+
See kbtest.rb for examples of how to use KirbyBase.
|
21
|
+
|
22
|
+
|
23
|
+
*Manifest:
|
24
|
+
|
25
|
+
readme.txt - this file
|
26
|
+
install.rb - install script
|
27
|
+
changes.txt - history of changes.
|
28
|
+
manual.html - documentation
|
29
|
+
kirbybase.rb - dbms library
|
30
|
+
kbserver.rb - multi-threaded database server script.
|
31
|
+
kbtest.rb - test script with examples.
|
32
|
+
record_class_test.rb - script showing how to have KB return class instances.
|
33
|
+
csv_import_test.rb - script showing how to have KB import a csv file.
|
34
|
+
plane.csv - sample csv file used in csv_import_test.rb
|
35
|
+
doc directory - RDoc generated documentation in html format.
|
36
|
+
|
37
|
+
|
38
|
+
*License:
|
39
|
+
|
40
|
+
KirbyBase is distributed under the same license as Ruby.
|
41
|
+
|
42
|
+
|
43
|
+
*Warranty:
|
44
|
+
|
45
|
+
I should probably put something more legalese here but let me just say:
|
46
|
+
|
47
|
+
KirbyBase carries no warranty! Use at your own risk. If it eats your
|
48
|
+
data, please don't come after me. :)
|
49
|
+
|
50
|
+
|
51
|
+
That being said, please send any bug reports, suggestions, ideas,
|
52
|
+
improvements, to:
|
53
|
+
|
54
|
+
jcribbs@twmi.rr.com
|
55
|
+
|
56
|
+
You can find more info about KirbyBase at:
|
57
|
+
|
58
|
+
http://www.netpromi.com/kirbybase.html
|
59
|
+
|
60
|
+
|
61
|
+
Thanks for trying KirbyBase.
|
62
|
+
|
63
|
+
Jamey Cribbs
|
data/lib/og/store/madeleine.rb
CHANGED
data/lib/og/store/mysql.rb
CHANGED
@@ -152,9 +152,9 @@ class MysqlStore < SqlStore
|
|
152
152
|
private
|
153
153
|
|
154
154
|
def create_table(klass)
|
155
|
-
|
155
|
+
fields = fields_for_class(klass)
|
156
156
|
|
157
|
-
sql = "CREATE TABLE #{klass::OGTABLE} (#{
|
157
|
+
sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
|
158
158
|
|
159
159
|
# Create table constrains.
|
160
160
|
|
@@ -210,7 +210,7 @@ private
|
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
213
|
-
def
|
213
|
+
def create_field_map(klass)
|
214
214
|
conn.query_with_result = true
|
215
215
|
res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
|
216
216
|
map = {}
|
@@ -245,9 +245,9 @@ private
|
|
245
245
|
|
246
246
|
def read_prop(p, col)
|
247
247
|
if p.klass.ancestors.include?(Integer)
|
248
|
-
return "res[#{col} + offset]
|
248
|
+
return "#{self.class}.parse_int(res[#{col} + offset])"
|
249
249
|
elsif p.klass.ancestors.include?(Float)
|
250
|
-
return "res[#{col} + offset]
|
250
|
+
return "#{self.class}.parse_float(res[#{col} + offset])"
|
251
251
|
elsif p.klass.ancestors.include?(String)
|
252
252
|
return "res[#{col} + offset]"
|
253
253
|
elsif p.klass.ancestors.include?(Time)
|
data/lib/og/store/psql.rb
CHANGED
@@ -79,6 +79,14 @@ class PsqlStore < SqlStore
|
|
79
79
|
options[:user].to_s,
|
80
80
|
options[:password].to_s
|
81
81
|
)
|
82
|
+
|
83
|
+
schema_order = options[:schema_order]
|
84
|
+
encoding = options[:encoding]
|
85
|
+
min_messages = options[:min_messages]
|
86
|
+
|
87
|
+
@conn.exec("SET search_path TO #{schema_order}") if schema_order
|
88
|
+
@conn.exec("SET client_encoding TO '#{encoding}'") if encoding
|
89
|
+
@conn.exec("SET client_min_messages TO '#{min_messages}'") if min_messages
|
82
90
|
rescue => ex
|
83
91
|
# gmosx: any idea how to better test this?
|
84
92
|
if ex.to_s =~ /database .* does not exist/i
|
@@ -117,9 +125,9 @@ class PsqlStore < SqlStore
|
|
117
125
|
private
|
118
126
|
|
119
127
|
def create_table(klass)
|
120
|
-
|
128
|
+
fields = fields_for_class(klass)
|
121
129
|
|
122
|
-
sql = "CREATE TABLE #{klass::OGTABLE} (#{
|
130
|
+
sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
|
123
131
|
|
124
132
|
# Create table constrains.
|
125
133
|
|
@@ -180,12 +188,12 @@ private
|
|
180
188
|
exec "DROP SEQUENCE #{klass::OGSEQ}"
|
181
189
|
end
|
182
190
|
|
183
|
-
def
|
191
|
+
def create_field_map(klass)
|
184
192
|
res = @conn.exec "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
|
185
193
|
map = {}
|
186
194
|
|
187
|
-
for
|
188
|
-
map[
|
195
|
+
for field in res.fields
|
196
|
+
map[field.intern] = res.fieldnum(field)
|
189
197
|
end
|
190
198
|
|
191
199
|
return map
|
@@ -195,9 +203,9 @@ private
|
|
195
203
|
|
196
204
|
def read_prop(p, col)
|
197
205
|
if p.klass.ancestors.include?(Integer)
|
198
|
-
return "res.getvalue(row, #{col} + offset)
|
206
|
+
return "#{self.class}.parse_int(res.getvalue(row, #{col} + offset))"
|
199
207
|
elsif p.klass.ancestors.include?(Float)
|
200
|
-
return "res.getvalue(row, #{col} + offset)
|
208
|
+
return "#{self.class}.parse_float(res.getvalue(row, #{col} + offset))"
|
201
209
|
elsif p.klass.ancestors.include?(String)
|
202
210
|
return "res.getvalue(row, #{col} + offset)"
|
203
211
|
elsif p.klass.ancestors.include?(Time)
|
data/lib/og/store/sql.rb
CHANGED
@@ -30,6 +30,20 @@ module SqlUtils
|
|
30
30
|
return nil unless date
|
31
31
|
return "#{date.year}-#{date.month}-#{date.mday}"
|
32
32
|
end
|
33
|
+
|
34
|
+
# Parse an integer.
|
35
|
+
|
36
|
+
def parse_int(int)
|
37
|
+
int = int.to_i if int
|
38
|
+
int
|
39
|
+
end
|
40
|
+
|
41
|
+
# Parse a float.
|
42
|
+
|
43
|
+
def parse_float(fl)
|
44
|
+
fl = fl.to_f if fl
|
45
|
+
fl
|
46
|
+
end
|
33
47
|
|
34
48
|
# Parse sql datetime
|
35
49
|
#--
|
@@ -246,7 +260,18 @@ class SqlStore < Store
|
|
246
260
|
|
247
261
|
exec "INSERT INTO #{table} (key1, key2) VALUES (#{obj1.pk}, #{obj2.pk})"
|
248
262
|
end
|
249
|
-
|
263
|
+
|
264
|
+
# Unrelate two objects be removing their relation from the
|
265
|
+
# join table.
|
266
|
+
|
267
|
+
def unjoin(obj1, obj2, table)
|
268
|
+
if obj1.class.to_s > obj2.class.to_s
|
269
|
+
obj1, obj2 = obj2, obj1
|
270
|
+
end
|
271
|
+
|
272
|
+
exec "DELETE FROM #{table} WHERE key1=#{obj1.pk} AND key2=#{obj2.pk}"
|
273
|
+
end
|
274
|
+
|
250
275
|
# :section: Transaction methods.
|
251
276
|
|
252
277
|
# Start a new transaction.
|
@@ -280,42 +305,42 @@ private
|
|
280
305
|
exec "DROP TABLE #{klass.table}"
|
281
306
|
end
|
282
307
|
|
283
|
-
# Create the
|
284
|
-
# The generated
|
308
|
+
# Create the fields that correpsond to the klass properties.
|
309
|
+
# The generated fields array is used in create_table.
|
285
310
|
# If the property has an :sql metadata this overrides the
|
286
311
|
# default mapping. If the property has an :extra_sql metadata
|
287
312
|
# the extra sql is appended after the default mapping.
|
288
313
|
|
289
|
-
def
|
290
|
-
|
314
|
+
def fields_for_class(klass)
|
315
|
+
fields = []
|
291
316
|
|
292
|
-
klass.
|
317
|
+
klass.properties.each do |p|
|
293
318
|
klass.index(p.symbol) if p.meta[:index]
|
294
319
|
|
295
|
-
|
320
|
+
field = p.symbol.to_s
|
296
321
|
|
297
322
|
if p.meta and p.meta[:sql]
|
298
|
-
|
323
|
+
field << " #{p.meta[:sql]}"
|
299
324
|
else
|
300
|
-
|
325
|
+
field << " #{type_for_class(p.klass)}"
|
301
326
|
|
302
327
|
if p.meta
|
303
328
|
if default = p.meta[:default]
|
304
|
-
|
329
|
+
field << " DEFAULT #{default.inspect} NOT NULL"
|
305
330
|
end
|
306
331
|
|
307
|
-
|
332
|
+
field << " UNIQUE" if p.meta[:unique]
|
308
333
|
|
309
334
|
if extra_sql = p.meta[:extra_sql]
|
310
|
-
|
335
|
+
field << " #{extra_sql}"
|
311
336
|
end
|
312
337
|
end
|
313
338
|
end
|
314
339
|
|
315
|
-
|
340
|
+
fields << field
|
316
341
|
end
|
317
342
|
|
318
|
-
return
|
343
|
+
return fields
|
319
344
|
end
|
320
345
|
|
321
346
|
def type_for_class(klass)
|
@@ -354,9 +379,9 @@ private
|
|
354
379
|
|
355
380
|
def read_prop(p, col)
|
356
381
|
if p.klass.ancestors.include?(Integer)
|
357
|
-
return "res[#{col} + offset]
|
382
|
+
return "#{self.class}.parse_int(res[#{col} + offset])"
|
358
383
|
elsif p.klass.ancestors.include?(Float)
|
359
|
-
return "res[#{col} + offset]
|
384
|
+
return "#{self.class}.parse_float(res[#{col} + offset])"
|
360
385
|
elsif p.klass.ancestors.include?(String)
|
361
386
|
return "res[#{col} + offset]"
|
362
387
|
elsif p.klass.ancestors.include?(Time)
|
@@ -413,16 +438,16 @@ private
|
|
413
438
|
|
414
439
|
# Compile the og_read method for the class. This method is
|
415
440
|
# used to read (deserialize) the given class from the store.
|
416
|
-
# In order to allow for changing
|
417
|
-
#
|
441
|
+
# In order to allow for changing field/attribute orders a
|
442
|
+
# field mapping hash is used.
|
418
443
|
|
419
444
|
def eval_og_read(klass)
|
420
445
|
code = []
|
421
446
|
props = klass.properties
|
422
|
-
|
447
|
+
field_map = create_field_map(klass)
|
423
448
|
|
424
449
|
props.each do |p|
|
425
|
-
if col =
|
450
|
+
if col = field_map[p.symbol]
|
426
451
|
code << "@#{p.symbol} = #{read_prop(p, col)}"
|
427
452
|
end
|
428
453
|
end
|
@@ -487,7 +512,7 @@ private
|
|
487
512
|
end
|
488
513
|
end
|
489
514
|
|
490
|
-
|
515
|
+
fields = tables.collect { |t| "#{t}.*" }.join(',')
|
491
516
|
|
492
517
|
if options[:condition]
|
493
518
|
options[:condition] += " AND #{join_conditions.join(' AND ')}"
|
@@ -495,7 +520,7 @@ private
|
|
495
520
|
options[:condition] = join_conditions.join(' AND ')
|
496
521
|
end
|
497
522
|
else
|
498
|
-
|
523
|
+
fields = '*'
|
499
524
|
end
|
500
525
|
|
501
526
|
if join_table = options[:join_table]
|
@@ -507,7 +532,7 @@ private
|
|
507
532
|
end
|
508
533
|
end
|
509
534
|
|
510
|
-
sql = "SELECT #{
|
535
|
+
sql = "SELECT #{fields} FROM #{tables.join(',')}"
|
511
536
|
|
512
537
|
if condition = options[:condition] || options[:where]
|
513
538
|
sql << " WHERE #{condition}"
|
@@ -597,3 +622,5 @@ private
|
|
597
622
|
end
|
598
623
|
|
599
624
|
end
|
625
|
+
|
626
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/og/store/sqlite.rb
CHANGED
@@ -9,13 +9,11 @@ require 'fileutils'
|
|
9
9
|
|
10
10
|
require 'og/store/sql'
|
11
11
|
|
12
|
-
# Customize the standard
|
12
|
+
# Customize the standard Sqlite3 resultset to make
|
13
13
|
# more compatible with Og.
|
14
14
|
|
15
15
|
class SQLite3::ResultSet
|
16
|
-
|
17
|
-
false
|
18
|
-
end
|
16
|
+
alias_method :blank?, :eof?
|
19
17
|
|
20
18
|
def each_row
|
21
19
|
each do |row|
|
@@ -38,9 +36,15 @@ module Og
|
|
38
36
|
|
39
37
|
class SqliteStore < SqlStore
|
40
38
|
|
39
|
+
# Override if needed.
|
40
|
+
|
41
|
+
def self.db_filename(options)
|
42
|
+
"#{options[:name]}.db"
|
43
|
+
end
|
44
|
+
|
41
45
|
def self.destroy(options)
|
42
46
|
begin
|
43
|
-
FileUtils.rm(
|
47
|
+
FileUtils.rm(db_filename(options))
|
44
48
|
super
|
45
49
|
rescue Object
|
46
50
|
Logger.info "Cannot drop '#{options[:name]}'!"
|
@@ -49,7 +53,7 @@ class SqliteStore < SqlStore
|
|
49
53
|
|
50
54
|
def initialize(options)
|
51
55
|
super
|
52
|
-
@conn = SQLite3::Database.new(
|
56
|
+
@conn = SQLite3::Database.new(self.class.db_filename(options))
|
53
57
|
end
|
54
58
|
|
55
59
|
def close
|
@@ -95,9 +99,9 @@ class SqliteStore < SqlStore
|
|
95
99
|
private
|
96
100
|
|
97
101
|
def create_table(klass)
|
98
|
-
|
102
|
+
fields = fields_for_class(klass)
|
99
103
|
|
100
|
-
sql = "CREATE TABLE #{klass::OGTABLE} (#{
|
104
|
+
sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
|
101
105
|
|
102
106
|
# Create table constrains.
|
103
107
|
|
@@ -153,14 +157,14 @@ private
|
|
153
157
|
end
|
154
158
|
end
|
155
159
|
|
156
|
-
def
|
160
|
+
def create_field_map(klass)
|
157
161
|
res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
|
158
162
|
map = {}
|
159
163
|
|
160
|
-
|
164
|
+
fields = res.fields
|
161
165
|
|
162
|
-
|
163
|
-
map[
|
166
|
+
fields.size.times do |i|
|
167
|
+
map[fields[i].intern] = i
|
164
168
|
end
|
165
169
|
|
166
170
|
return map
|
data/lib/og/store/sqlserver.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# WARNING:
|
2
|
+
# This store is not converted to the latest Og codebase.
|
3
|
+
# DO NOT USE YET!
|
4
|
+
|
1
5
|
begin
|
2
6
|
require 'dbi'
|
3
7
|
rescue Object => ex
|
@@ -121,9 +125,9 @@ class SqlserverStore < SqlStore
|
|
121
125
|
private
|
122
126
|
|
123
127
|
def create_table(klass)
|
124
|
-
|
128
|
+
fields = fields_for_class(klass)
|
125
129
|
|
126
|
-
sql = "CREATE TABLE #{klass::OGTABLE} (#{
|
130
|
+
sql = "CREATE TABLE #{klass::OGTABLE} (#{fields.join(', ')}"
|
127
131
|
|
128
132
|
# Create table constrains.
|
129
133
|
|
@@ -189,7 +193,7 @@ private
|
|
189
193
|
=end
|
190
194
|
end
|
191
195
|
|
192
|
-
def
|
196
|
+
def create_field_map(klass)
|
193
197
|
conn.query_with_result = true
|
194
198
|
res = @conn.query "SELECT * FROM #{klass::OGTABLE} LIMIT 1"
|
195
199
|
map = {}
|
data/test/og/tc_store.rb
CHANGED
@@ -64,20 +64,33 @@ class TCOgStore < Test::Unit::TestCase # :nodoc: all
|
|
64
64
|
@og.store.class.destroy(@og.options)
|
65
65
|
@og = nil
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
=begin
|
69
|
+
def test_kirby
|
70
|
+
@og = Og.setup(
|
71
|
+
:destroy => true,
|
72
|
+
:store => :kirby,
|
73
|
+
:name => 'test',
|
74
|
+
:embedded => true
|
75
|
+
)
|
76
|
+
features_test
|
77
|
+
# conversions_test
|
78
|
+
end
|
79
|
+
=end
|
80
|
+
#=begin
|
69
81
|
def test_psql
|
70
82
|
@og = Og.setup(
|
71
83
|
:destroy => true,
|
72
84
|
:store => :psql,
|
73
85
|
:name => 'test',
|
86
|
+
:min_messages => 'ERROR',
|
74
87
|
:user => 'postgres',
|
75
88
|
:password => 'navelrulez'
|
76
89
|
)
|
77
90
|
features_test
|
78
91
|
conversions_test
|
79
92
|
end
|
80
|
-
|
93
|
+
#=end
|
81
94
|
=begin
|
82
95
|
def test_mysql
|
83
96
|
@og = Og.setup(
|
@@ -91,7 +104,7 @@ class TCOgStore < Test::Unit::TestCase # :nodoc: all
|
|
91
104
|
# conversions_test
|
92
105
|
end
|
93
106
|
=end
|
94
|
-
|
107
|
+
=begin
|
95
108
|
def test_sqlite
|
96
109
|
@og = Og.setup(
|
97
110
|
:destroy => true,
|
@@ -101,7 +114,7 @@ class TCOgStore < Test::Unit::TestCase # :nodoc: all
|
|
101
114
|
features_test
|
102
115
|
conversions_test
|
103
116
|
end
|
104
|
-
|
117
|
+
=end
|
105
118
|
=begin
|
106
119
|
def test_memory
|
107
120
|
@og = Og.setup(
|
@@ -134,7 +147,7 @@ class TCOgStore < Test::Unit::TestCase # :nodoc: all
|
|
134
147
|
@og.store.delete(a3)
|
135
148
|
|
136
149
|
assert @og.store.load(1, Article)
|
137
|
-
|
150
|
+
assert !Article[2]
|
138
151
|
assert Article.load(1)
|
139
152
|
|
140
153
|
a2.delete
|
@@ -220,7 +233,7 @@ class TCOgStore < Test::Unit::TestCase # :nodoc: all
|
|
220
233
|
|
221
234
|
assert_equal 3, @og.store.count(:class => Comment)
|
222
235
|
|
223
|
-
a4.comments.
|
236
|
+
a4.comments.delete_all
|
224
237
|
assert_equal 0, a4.comments.size
|
225
238
|
|
226
239
|
# has_one
|