baza 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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