baza 0.0.0 → 0.0.1
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/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.rdoc +106 -1
- data/VERSION +1 -1
- data/baza.gemspec +103 -0
- data/include/db.rb +108 -29
- data/include/drivers/mysql/mysql.rb +52 -47
- data/include/drivers/mysql/mysql_columns.rb +48 -41
- data/include/drivers/mysql/mysql_indexes.rb +2 -2
- data/include/drivers/mysql/mysql_tables.rb +91 -59
- data/include/drivers/sqlite3/sqlite3.rb +66 -45
- data/include/drivers/sqlite3/sqlite3_columns.rb +41 -39
- data/include/drivers/sqlite3/sqlite3_indexes.rb +2 -2
- data/include/drivers/sqlite3/sqlite3_tables.rb +86 -54
- data/include/dump.rb +5 -2
- data/include/revision.rb +130 -90
- data/spec/baza_spec.rb +312 -263
- data/spec/info_mysql_example.rb +6 -0
- data/spec/info_sqlite3.rb +20 -0
- metadata +22 -3
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,111 @@
|
|
1
1
|
= baza
|
2
2
|
|
3
|
-
|
3
|
+
A database abstraction layer for Ruby.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
Is fairly painless.
|
8
|
+
gem install baza
|
9
|
+
|
10
|
+
== Connection to a database.
|
11
|
+
|
12
|
+
=== MySQL
|
13
|
+
db = Baza::Db.new(:type => :mysql, :subtype => :mysql2, :host => "localhost", :user => "my_user", :pass => "my_password", :port => 3306, :db => "my_database")
|
14
|
+
|
15
|
+
=== SQLite3
|
16
|
+
db = Baza::Db.new(:type => :sqlite3, :path => "/path/to/file.sqlite3")
|
17
|
+
|
18
|
+
== Queries
|
19
|
+
|
20
|
+
=== Select
|
21
|
+
db.select(:users, {:name => "Kasper"}, {:orderby => "age"}) do |row|
|
22
|
+
puts "Row: #{row}"
|
23
|
+
end
|
24
|
+
|
25
|
+
name = "Kasper"
|
26
|
+
db.q("SELECT * FROM users WHERE name = '#{db.esc(name)}' ORDER BY age") do |row|
|
27
|
+
puts "Row: #{row}"
|
28
|
+
end
|
29
|
+
|
30
|
+
=== Inserting
|
31
|
+
db.insert(:users, {:name => "Kasper", :age => 27})
|
32
|
+
id = db.last_id
|
33
|
+
|
34
|
+
It can also return the ID at the same time
|
35
|
+
id = db.insert(:users, {:name => "Kasper", :age => 27}, :return_id => true)
|
36
|
+
|
37
|
+
Inserting multiple rows in one query is also fairly painless:
|
38
|
+
db.insert_multi(:users, [
|
39
|
+
{:name => "Kasper", :age => 27},
|
40
|
+
{:name => "Christina", :age => 25},
|
41
|
+
{:name => "Charlotte", :age => 23}
|
42
|
+
])
|
43
|
+
|
44
|
+
=== Update
|
45
|
+
db.update(:users, {:name => "Kasper Johansen"}, {:name => "Kasper"})
|
46
|
+
|
47
|
+
=== Delete
|
48
|
+
db.delete(:users, {:name => "Kasper"})
|
49
|
+
|
50
|
+
=== Upsert
|
51
|
+
The following example handels a row that will be inserted with {:name => "Kasper", :age => 27} if it doesnt exist or rows with {:name => "Kasper"} will have their their age updated to 27.
|
52
|
+
db.upsert(:users, {:name => "Kasper"}, {:age => 27})
|
53
|
+
|
54
|
+
== Structure
|
55
|
+
|
56
|
+
=== Table creation
|
57
|
+
db.tables.create(:users, {
|
58
|
+
:columns => [
|
59
|
+
{:name => :id, :type => :int, :autoincr => true, :primarykey => true},
|
60
|
+
{:name => :name, :type => :varchar}
|
61
|
+
],
|
62
|
+
:indexes => [
|
63
|
+
:name
|
64
|
+
]
|
65
|
+
})
|
66
|
+
|
67
|
+
=== Table dropping
|
68
|
+
table = db.tables[:users]
|
69
|
+
table.drop
|
70
|
+
|
71
|
+
=== Table listing
|
72
|
+
array_of_tables = db.tables.list
|
73
|
+
|
74
|
+
Or you can use blocks:
|
75
|
+
db.tables.list do |table|
|
76
|
+
puts "Table-name: #{table.name}"
|
77
|
+
end
|
78
|
+
|
79
|
+
=== Table renaming
|
80
|
+
table = db.tables[:users]
|
81
|
+
table.rename(:new_table_name)
|
82
|
+
|
83
|
+
=== Column listing
|
84
|
+
table = db.tables[:users]
|
85
|
+
cols = table.columns
|
86
|
+
|
87
|
+
Or a specific column:
|
88
|
+
col = table.column(:id)
|
89
|
+
puts "Column: #{col.name} #{col.type}(#{col.maxlength})"
|
90
|
+
|
91
|
+
== Copying databases
|
92
|
+
db_mysql = Baza::Db.new(:type => :mysql, :subtype => :mysql2, ...)
|
93
|
+
db_sqlite = Baza::Db.new(:type => :sqlite3, :path => ...)
|
94
|
+
|
95
|
+
db_mysql.copy_to(db_sqlite)
|
96
|
+
|
97
|
+
== Dumping SQL to an IO
|
98
|
+
db = Baza::Db.new(...)
|
99
|
+
dump = Baza::Dump.new(:db => db)
|
100
|
+
str_io = StringIO.new
|
101
|
+
dump.dump(str_io)
|
102
|
+
|
103
|
+
== Transactions
|
104
|
+
db.transaction do
|
105
|
+
1000.times do
|
106
|
+
db.insert(:users, {:name => "Kasper"})
|
107
|
+
end
|
108
|
+
end
|
4
109
|
|
5
110
|
== Contributing to baza
|
6
111
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
data/baza.gemspec
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "baza"
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kasper Johansen"]
|
12
|
+
s.date = "2013-04-16"
|
13
|
+
s.description = "A database abstraction layer, model framework and database framework."
|
14
|
+
s.email = "kj@gfish.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"baza.gemspec",
|
29
|
+
"include/db.rb",
|
30
|
+
"include/dbtime.rb",
|
31
|
+
"include/drivers/.DS_Store",
|
32
|
+
"include/drivers/mysql/mysql.rb",
|
33
|
+
"include/drivers/mysql/mysql_columns.rb",
|
34
|
+
"include/drivers/mysql/mysql_indexes.rb",
|
35
|
+
"include/drivers/mysql/mysql_sqlspecs.rb",
|
36
|
+
"include/drivers/mysql/mysql_tables.rb",
|
37
|
+
"include/drivers/sqlite3/libknjdb_java_sqlite3.rb",
|
38
|
+
"include/drivers/sqlite3/libknjdb_sqlite3_ironruby.rb",
|
39
|
+
"include/drivers/sqlite3/sqlite3.rb",
|
40
|
+
"include/drivers/sqlite3/sqlite3_columns.rb",
|
41
|
+
"include/drivers/sqlite3/sqlite3_indexes.rb",
|
42
|
+
"include/drivers/sqlite3/sqlite3_sqlspecs.rb",
|
43
|
+
"include/drivers/sqlite3/sqlite3_tables.rb",
|
44
|
+
"include/dump.rb",
|
45
|
+
"include/idquery.rb",
|
46
|
+
"include/model.rb",
|
47
|
+
"include/model_custom.rb",
|
48
|
+
"include/model_handler.rb",
|
49
|
+
"include/model_handler_sqlhelper.rb",
|
50
|
+
"include/query_buffer.rb",
|
51
|
+
"include/revision.rb",
|
52
|
+
"include/row.rb",
|
53
|
+
"include/sqlspecs.rb",
|
54
|
+
"lib/baza.rb",
|
55
|
+
"spec/baza_spec.rb",
|
56
|
+
"spec/db_spec_encoding_test_file.txt",
|
57
|
+
"spec/info_mysql_example.rb",
|
58
|
+
"spec/info_sqlite3.rb",
|
59
|
+
"spec/spec_helper.rb"
|
60
|
+
]
|
61
|
+
s.homepage = "http://github.com/kaspernj/baza"
|
62
|
+
s.licenses = ["MIT"]
|
63
|
+
s.require_paths = ["lib"]
|
64
|
+
s.rubygems_version = "1.8.25"
|
65
|
+
s.summary = "A database abstraction layer, model framework and database framework."
|
66
|
+
|
67
|
+
if s.respond_to? :specification_version then
|
68
|
+
s.specification_version = 3
|
69
|
+
|
70
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
71
|
+
s.add_runtime_dependency(%q<datet>, [">= 0"])
|
72
|
+
s.add_runtime_dependency(%q<wref>, [">= 0"])
|
73
|
+
s.add_runtime_dependency(%q<knjrbfw>, [">= 0"])
|
74
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
75
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
76
|
+
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
77
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
78
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
79
|
+
s.add_development_dependency(%q<mysql2>, [">= 0"])
|
80
|
+
else
|
81
|
+
s.add_dependency(%q<datet>, [">= 0"])
|
82
|
+
s.add_dependency(%q<wref>, [">= 0"])
|
83
|
+
s.add_dependency(%q<knjrbfw>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
85
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
86
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
87
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
88
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
89
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
90
|
+
end
|
91
|
+
else
|
92
|
+
s.add_dependency(%q<datet>, [">= 0"])
|
93
|
+
s.add_dependency(%q<wref>, [">= 0"])
|
94
|
+
s.add_dependency(%q<knjrbfw>, [">= 0"])
|
95
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
96
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
97
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
98
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
99
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
100
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
data/include/db.rb
CHANGED
@@ -17,10 +17,57 @@ Knj.gem_require([:wref, :datet])
|
|
17
17
|
class Baza::Db
|
18
18
|
attr_reader :sep_col, :sep_table, :sep_val, :opts, :conn, :conns, :int_types
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
#Returns an array containing hashes of information about each registered driver.
|
21
|
+
def self.drivers
|
22
|
+
path = "#{File.dirname(__FILE__)}/drivers"
|
23
|
+
drivers = []
|
24
|
+
|
25
|
+
Dir.foreach(path) do |file|
|
26
|
+
next if file.to_s.slice(0, 1) == "."
|
27
|
+
fp = "#{path}/#{file}"
|
28
|
+
next unless File.directory?(fp)
|
29
|
+
|
30
|
+
driver_file = "#{fp}/#{file}.rb"
|
31
|
+
class_name = "#{file.slice(0, 1).to_s.upcase}#{file.slice(1, file.length)}".to_sym
|
32
|
+
|
33
|
+
drivers << {
|
34
|
+
:name => file,
|
35
|
+
:driver_path => driver_file,
|
36
|
+
:class_name => class_name
|
37
|
+
}
|
38
|
+
end
|
22
39
|
|
23
|
-
|
40
|
+
return drivers
|
41
|
+
end
|
42
|
+
|
43
|
+
#Tries to create a database-object based on the given object which could be a SQLite3 object or a MySQL 2 object (or other supported).
|
44
|
+
def self.from_object(args)
|
45
|
+
args = {:object => args} if !args.is_a?(Hash)
|
46
|
+
raise "No :object was given." if !args[:object]
|
47
|
+
|
48
|
+
Baza::Db.drivers.each do |driver|
|
49
|
+
require driver[:driver_path]
|
50
|
+
|
51
|
+
const = Baza::Driver.const_get(driver[:class_name])
|
52
|
+
next unless const.respond_to?(:from_object)
|
53
|
+
|
54
|
+
obj = const.from_object(args)
|
55
|
+
if obj.is_a?(Hash) and obj[:type] == :success
|
56
|
+
if obj[:args]
|
57
|
+
return Baza::Db.new(obj[:args])
|
58
|
+
else
|
59
|
+
raise "Didnt know what to do."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
raise "Could not figure out what to do what object of type: '#{args[:object].class.name}'."
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(opts)
|
68
|
+
@conn = opts.delete(:driver) if opts[:driver]
|
69
|
+
self.opts = opts if opts != nil
|
70
|
+
@int_types = [:int, :bigint, :tinyint, :smallint, :mediumint]
|
24
71
|
|
25
72
|
if !@opts[:threadsafe]
|
26
73
|
require "monitor"
|
@@ -41,7 +88,7 @@ class Baza::Db
|
|
41
88
|
return @opts
|
42
89
|
end
|
43
90
|
|
44
|
-
def
|
91
|
+
def opts=(arr_opts)
|
45
92
|
@opts = {}
|
46
93
|
arr_opts.each do |key, val|
|
47
94
|
@opts[key.to_sym] = val
|
@@ -163,28 +210,36 @@ class Baza::Db
|
|
163
210
|
end
|
164
211
|
end
|
165
212
|
|
213
|
+
COPY_TO_ALLOWED_ARGS = [:tables, :debug]
|
166
214
|
#Copies the content of the current database to another instance of Baza::Db.
|
167
215
|
def copy_to(db, args = {})
|
168
|
-
|
216
|
+
debug = args[:debug]
|
217
|
+
raise "No tables given." if !data[:tables]
|
218
|
+
|
219
|
+
data[:tables].each do |table|
|
169
220
|
table_args = nil
|
170
|
-
table_args = args[
|
171
|
-
next if table_args and table_args[
|
172
|
-
table.delete(
|
173
|
-
|
221
|
+
table_args = args[:tables][table[:name.to_sym]] if args and args[:tables] and args[:tables][table[:name].to_sym]
|
222
|
+
next if table_args and table_args[:skip]
|
223
|
+
table.delete(:indexes) if table.key?(:indexes) and args[:skip_indexes]
|
224
|
+
|
225
|
+
table_name = table.delete(:name)
|
226
|
+
puts "Creating table: '#{table_name}'." if debug
|
227
|
+
db.tables.create(table_name, table)
|
174
228
|
|
175
229
|
limit_from = 0
|
176
230
|
limit_incr = 1000
|
177
231
|
|
178
232
|
loop do
|
233
|
+
puts "Copying rows (#{limit_from}, #{limit_incr})." if debug
|
179
234
|
ins_arr = []
|
180
|
-
q_rows = self.select(
|
235
|
+
q_rows = self.select(table_name, {}, {:limit_from => limit_from, :limit_to => limit_incr})
|
181
236
|
while d_rows = q_rows.fetch
|
182
237
|
col_args = nil
|
183
238
|
|
184
|
-
if table_args and table_args[
|
239
|
+
if table_args and table_args[:columns]
|
185
240
|
d_rows.each do |col_name, col_data|
|
186
|
-
col_args = table_args[
|
187
|
-
d_rows[col_name] = "" if col_args and col_args[
|
241
|
+
col_args = table_args[:columns][col_name.to_sym] if table_args and table_args[:columns]
|
242
|
+
d_rows[col_name] = "" if col_args and col_args[:empty]
|
188
243
|
end
|
189
244
|
end
|
190
245
|
|
@@ -193,7 +248,8 @@ class Baza::Db
|
|
193
248
|
|
194
249
|
break if ins_arr.empty?
|
195
250
|
|
196
|
-
|
251
|
+
puts "Insertering #{ins_arr.length} rows." if debug
|
252
|
+
db.insert_multi(table_name, ins_arr)
|
197
253
|
limit_from += limit_incr
|
198
254
|
end
|
199
255
|
end
|
@@ -210,7 +266,7 @@ class Baza::Db
|
|
210
266
|
end
|
211
267
|
|
212
268
|
return {
|
213
|
-
|
269
|
+
:tables => tables_ret
|
214
270
|
}
|
215
271
|
end
|
216
272
|
|
@@ -265,12 +321,22 @@ class Baza::Db
|
|
265
321
|
return sql if args and args[:return_sql]
|
266
322
|
|
267
323
|
self.conn_exec do |driver|
|
268
|
-
|
324
|
+
begin
|
325
|
+
driver.query(sql)
|
326
|
+
rescue => e
|
327
|
+
self.add_sql_to_error(e, sql) if @opts[:sql_to_error]
|
328
|
+
raise e
|
329
|
+
end
|
330
|
+
|
269
331
|
return driver.lastID if args and args[:return_id]
|
270
332
|
return nil
|
271
333
|
end
|
272
334
|
end
|
273
335
|
|
336
|
+
def add_sql_to_error(error, sql)
|
337
|
+
error.message << " (SQL: #{sql})"
|
338
|
+
end
|
339
|
+
|
274
340
|
#Returns the correct SQL-value for the given value. If it is a number, then just the raw number as a string will be returned. nil's will be NULL and strings will have quotes and will be escaped.
|
275
341
|
def sqlval(val)
|
276
342
|
return @conn.sqlval(val) if @conn.respond_to?(:sqlval)
|
@@ -300,14 +366,21 @@ class Baza::Db
|
|
300
366
|
|
301
367
|
if @esc_driver.respond_to?(:insert_multi)
|
302
368
|
if args and args[:return_sql]
|
303
|
-
|
369
|
+
res = @esc_driver.insert_multi(tablename, arr_hashes, args)
|
370
|
+
if res.is_a?(String)
|
371
|
+
return [res]
|
372
|
+
elsif res.is_a?(Array)
|
373
|
+
return res
|
374
|
+
else
|
375
|
+
raise "Unknown result: '#{res.class.name}'."
|
376
|
+
end
|
304
377
|
end
|
305
378
|
|
306
379
|
self.conn_exec do |driver|
|
307
380
|
return driver.insert_multi(tablename, arr_hashes, args)
|
308
381
|
end
|
309
382
|
else
|
310
|
-
ret = []
|
383
|
+
ret = []
|
311
384
|
arr_hashes.each do |hash|
|
312
385
|
if ret
|
313
386
|
ret << self.insert(tablename, hash, args)
|
@@ -372,6 +445,7 @@ class Baza::Db
|
|
372
445
|
end
|
373
446
|
end
|
374
447
|
|
448
|
+
SELECT_ARGS_ALLOWED_KEYS = [:limit, :limit_from, :limit_to]
|
375
449
|
#Makes a select from the given arguments: table-name, where-terms and other arguments as limits and orders. Also takes a block to avoid raping of memory.
|
376
450
|
def select(tablename, arr_terms = nil, args = nil, &block)
|
377
451
|
#Set up vars.
|
@@ -402,18 +476,18 @@ class Baza::Db
|
|
402
476
|
end
|
403
477
|
|
404
478
|
if args != nil
|
405
|
-
if args[
|
406
|
-
sql << " ORDER BY #{args[
|
479
|
+
if args[:orderby]
|
480
|
+
sql << " ORDER BY #{args[:orderby]}"
|
407
481
|
end
|
408
482
|
|
409
|
-
if args[
|
410
|
-
sql << " LIMIT #{args[
|
483
|
+
if args[:limit]
|
484
|
+
sql << " LIMIT #{args[:limit]}"
|
411
485
|
end
|
412
486
|
|
413
|
-
if args[
|
414
|
-
raise "'limit_from' was not numeric: '#{args[
|
415
|
-
raise "'limit_to' was not numeric: '#{args[
|
416
|
-
sql << " LIMIT #{args[
|
487
|
+
if args[:limit_from] and args[:limit_to]
|
488
|
+
raise "'limit_from' was not numeric: '#{args[:limit_from]}'." if !(Float(args[:limit_from]) rescue false)
|
489
|
+
raise "'limit_to' was not numeric: '#{args[:limit_to]}'." if !(Float(args[:limit_to]) rescue false)
|
490
|
+
sql << " LIMIT #{args[:limit_from]}, #{args[:limit_to]}"
|
417
491
|
end
|
418
492
|
end
|
419
493
|
|
@@ -437,7 +511,7 @@ class Baza::Db
|
|
437
511
|
#===Examples
|
438
512
|
# row = db.single(:users, {:lastname => "Doe"})
|
439
513
|
def single(tablename, arr_terms = nil, args = {})
|
440
|
-
args[
|
514
|
+
args[:limit] = 1
|
441
515
|
|
442
516
|
#Experienced very weird memory leak if this was not done by block. Maybe bug in Ruby 1.9.2? - knj
|
443
517
|
self.select(tablename, arr_terms, args) do |data|
|
@@ -549,8 +623,13 @@ class Baza::Db
|
|
549
623
|
end
|
550
624
|
end
|
551
625
|
|
552
|
-
|
553
|
-
|
626
|
+
begin
|
627
|
+
self.conn_exec do |driver|
|
628
|
+
return driver.query(string)
|
629
|
+
end
|
630
|
+
rescue => e
|
631
|
+
e.message << " (SQL: #{string})" if @opts[:sql_to_error]
|
632
|
+
raise e
|
554
633
|
end
|
555
634
|
end
|
556
635
|
|