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 CHANGED
@@ -15,4 +15,5 @@ group :development do
15
15
  gem "bundler", ">= 1.0.0"
16
16
  gem "jeweler", "~> 1.8.4"
17
17
  gem "sqlite3"
18
+ gem "mysql2"
18
19
  end
data/Gemfile.lock CHANGED
@@ -18,6 +18,7 @@ GEM
18
18
  ruby_process
19
19
  tsafe
20
20
  wref
21
+ mysql2 (0.3.11)
21
22
  php4r (0.0.4)
22
23
  datet
23
24
  http2
@@ -49,6 +50,7 @@ DEPENDENCIES
49
50
  datet
50
51
  jeweler (~> 1.8.4)
51
52
  knjrbfw
53
+ mysql2
52
54
  rdoc (~> 3.12)
53
55
  rspec (~> 2.8.0)
54
56
  sqlite3
data/README.rdoc CHANGED
@@ -1,6 +1,111 @@
1
1
  = baza
2
2
 
3
- Description goes here.
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.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
- def initialize(opts)
21
- self.setOpts(opts) if opts != nil
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
- @int_types = ["int", "bigint", "tinyint", "smallint", "mediumint"]
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 setOpts(arr_opts)
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
- data["tables"].each do |table|
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["tables"][table["name"].to_s] if args and args["tables"] and args["tables"][table["name"].to_s]
171
- next if table_args and table_args["skip"]
172
- table.delete("indexes") if table.key?("indexes") and args["skip_indexes"]
173
- db.tables.create(table["name"], table)
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(table["name"], {}, {"limit_from" => limit_from, "limit_to" => limit_incr})
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["columns"]
239
+ if table_args and table_args[:columns]
185
240
  d_rows.each do |col_name, col_data|
186
- col_args = table_args["columns"][col_name.to_s] if table_args and table_args["columns"]
187
- d_rows[col_name] = "" if col_args and col_args["empty"]
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
- db.insert_multi(table["name"], ins_arr)
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
- "tables" => tables_ret
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
- driver.query(sql)
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
- return [@esc_driver.insert_multi(tablename, arr_hashes, args)]
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 = [] if args and (args[:return_id] or args[:return_sql])
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["orderby"]
406
- sql << " ORDER BY #{args["orderby"]}"
479
+ if args[:orderby]
480
+ sql << " ORDER BY #{args[:orderby]}"
407
481
  end
408
482
 
409
- if args["limit"]
410
- sql << " LIMIT #{args["limit"]}"
483
+ if args[:limit]
484
+ sql << " LIMIT #{args[:limit]}"
411
485
  end
412
486
 
413
- if args["limit_from"] and args["limit_to"]
414
- raise "'limit_from' was not numeric: '#{args["limit_from"]}'." if !(Float(args["limit_from"]) rescue false)
415
- raise "'limit_to' was not numeric: '#{args["limit_to"]}'." if !(Float(args["limit_to"]) rescue false)
416
- sql << " LIMIT #{args["limit_from"]}, #{args["limit_to"]}"
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["limit"] = 1
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
- self.conn_exec do |driver|
553
- return driver.query(string)
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