sequel 0.4.1.3 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,4 +1,16 @@
1
- === SVN
1
+ === 0.4.2 (2007-12-07)
2
+
3
+ * Implemented Model#save_changes.
4
+
5
+ * Extended Model#save to accept specific columns to update.
6
+
7
+ * Implemented experimental JDBC adapter.
8
+
9
+ * Added adapter skeleton as starting point for new adapters.
10
+
11
+ * Cleaned-up adapters and moved automatic requiring of 'sequel' to adapter stubs.
12
+
13
+ === 0.4.1.3 (2007-12-05)
2
14
 
3
15
  * Better plugin conventions.
4
16
 
data/README CHANGED
@@ -37,7 +37,7 @@ Sequel currently supports:
37
37
  * PostgreSQL
38
38
  * SQLite 3
39
39
 
40
- There are also experimental adapters for DB2 and OpenBase.
40
+ There are also experimental adapters for DB2, OpenBase and JDBC (on JRuby).
41
41
 
42
42
  == The Sequel Console
43
43
 
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.4.1.3"
9
+ VERS = "0.4.2"
10
10
  CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
11
11
  RDOC_OPTS = ['--quiet', '--title', "Sequel: Concise ORM for Ruby",
12
12
  "--opname", "index.html",
@@ -0,0 +1,67 @@
1
+ # require 'adapter_lib'
2
+
3
+ module Sequel
4
+ module Adapter
5
+ class Database < Sequel::Database
6
+ set_adapter_scheme :adapter
7
+
8
+ def connect
9
+ AdapterDB.new(@opts[:database], @opts[:user], @opts[:password])
10
+ end
11
+
12
+ def disconnect
13
+ @pool.disconnect {|c| c.disconnect}
14
+ end
15
+
16
+ def dataset(opts = nil)
17
+ Adapter::Dataset.new(self, opts)
18
+ end
19
+
20
+ def execute(sql)
21
+ @logger.info(sql) if @logger
22
+ @pool.hold {|conn| conn.exec(sql)}
23
+ end
24
+
25
+ alias_method :do, :execute
26
+ end
27
+
28
+ class Dataset < Sequel::Dataset
29
+ def literal(v)
30
+ case v
31
+ when Time: literal(v.iso8601)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def fetch_rows(sql, &block)
38
+ @db.synchronize do
39
+ cursor = @db.execute sql
40
+ begin
41
+ @columns = cursor.get_col_names.map {|c| c.to_sym}
42
+ while r = cursor.fetch
43
+ row = {}
44
+ r.each_with_index {|v, i| row[@columns[i]] = v}
45
+ yield row
46
+ end
47
+ ensure
48
+ cursor.close
49
+ end
50
+ end
51
+ self
52
+ end
53
+
54
+ def insert(*values)
55
+ @db.do insert_sql(*values)
56
+ end
57
+
58
+ def update(values, opts = nil)
59
+ @db.do update_sql(values, opts)
60
+ end
61
+
62
+ def delete(opts = nil)
63
+ @db.do delete_sql(opts)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'win32ole'
6
2
 
7
3
  module Sequel
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'db2/db2cli'
6
2
 
7
3
  module Sequel
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'dbi'
6
2
 
7
3
  module Sequel
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'informix'
6
2
 
7
3
  module Sequel
@@ -0,0 +1,107 @@
1
+ require 'java'
2
+
3
+ module Sequel
4
+ module JDBC
5
+ module JavaLang; include_package 'java.lang'; end
6
+ module JavaSQL; include_package 'java.sql'; end
7
+
8
+ def self.load_driver(driver)
9
+ JavaLang::Class.forName(driver)
10
+ # "com.mysql.jdbc.Driver"
11
+ end
12
+
13
+ class Database < Sequel::Database
14
+ set_adapter_scheme :jdbc
15
+
16
+ def connect
17
+ unless conn_string = @opts[:uri] || @opts[:url] || @opts[:database]
18
+ raise SequelError, "No connection string specified"
19
+ end
20
+ unless conn_string =~ /^jdbc:/
21
+ conn_string = "jdbc:#{conn_string}"
22
+ end
23
+ JavaSQL::DriverManager.getConnection(
24
+ conn_string,
25
+ @opts[:user],
26
+ @opts[:password]
27
+ )
28
+ # "jdbc:mysql://127.0.0.1:3306/ruby?user=root"
29
+ # "mysql://127.0.0.1:3306/ruby?user=root"
30
+ end
31
+
32
+ def disconnect
33
+ @pool.disconnect {|c| c.close}
34
+ end
35
+
36
+ def dataset(opts = nil)
37
+ JDBC::Dataset.new(self, opts)
38
+ end
39
+
40
+ def execute_and_forget(sql)
41
+ @logger.info(sql) if @logger
42
+ @pool.hold do |conn|
43
+ stmt = conn.createStatement
44
+ begin
45
+ stmt.executeQuery(sql)
46
+ ensure
47
+ stmt.close
48
+ end
49
+ end
50
+ end
51
+
52
+ def execute(sql)
53
+ @logger.info(sql) if @logger
54
+ @pool.hold do |conn|
55
+ stmt = conn.createStatement
56
+ begin
57
+ yield stmt.executeQuery(sql)
58
+ ensure
59
+ stmt.close
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ class Dataset < Sequel::Dataset
66
+ def literal(v)
67
+ case v
68
+ when Time: literal(v.iso8601)
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ def fetch_rows(sql, &block)
75
+ @db.synchronize do
76
+ @db.execute(sql) do |result|
77
+ # get column names
78
+ meta = result.getMetaData
79
+ column_count = meta.getColumnCount
80
+ @columns = []
81
+ column_count.times {|i| @columns << meta.getColumnName(i).to_sym}
82
+
83
+ # get rows
84
+ while result.next
85
+ row = {}
86
+ @columns.each_with_index {|v, i| row[v] = result.getObject(i)}
87
+ yield row
88
+ end
89
+ end
90
+ end
91
+ self
92
+ end
93
+
94
+ def insert(*values)
95
+ @db.execute_and_forget insert_sql(*values)
96
+ end
97
+
98
+ def update(values, opts = nil)
99
+ @db.execute_and_forget update_sql(values, opts)
100
+ end
101
+
102
+ def delete(opts = nil)
103
+ @db.execute_and_forget delete_sql(opts)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'mysql'
6
2
 
7
3
  # Monkey patch Mysql::Result to yield hashes with symbol keys
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'odbc'
6
2
 
7
3
  module Sequel
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  if !Sequel.const_defined?('ODBC')
6
2
  require File.join(File.dirname(__FILE__), 'odbc')
7
3
  end
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'openbase'
6
2
 
7
3
  module Sequel
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'oci8'
6
2
 
7
3
  module Sequel
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'postgres'
6
2
 
7
3
  class PGconn
@@ -1,7 +1,3 @@
1
- if !Object.const_defined?('Sequel')
2
- require File.join(File.dirname(__FILE__), '../../sequel')
3
- end
4
-
5
1
  require 'sqlite3'
6
2
 
7
3
  module Sequel
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/ado' is deprecated. Please modify your code to only require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/ado')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/db2' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/db2')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/dbi' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/dbi')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/informix' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/informix')
@@ -1,14 +1,16 @@
1
1
  module Sequel
2
2
  class Model
3
3
  attr_reader :values
4
+ attr_reader :changed_columns
4
5
 
5
6
  # Returns value of attribute.
6
7
  def [](column)
7
8
  @values[column]
8
9
  end
9
- # Sets value of attribute.
10
+ # Sets value of attribute and marks the column as changed.
10
11
  def []=(column, value)
11
12
  @values[column] = value
13
+ @changed_columns << column unless @changed_columns.include?(column)
12
14
  end
13
15
 
14
16
  # Enumerates through all attributes.
@@ -171,6 +173,7 @@ module Sequel
171
173
  # <tt>new_record</tt> is set to false.
172
174
  def initialize(values = {}, new_record = false, &block)
173
175
  @values = values
176
+ @changed_columns = []
174
177
 
175
178
  @new = new_record
176
179
  unless @new # determine if it's a new record
@@ -194,8 +197,9 @@ module Sequel
194
197
  this.count > 0
195
198
  end
196
199
 
197
- # Creates or updates dataset for Model and runs hooks.
198
- def save
200
+ # Creates or updates the associated record. This method can also
201
+ # accept a list of specific columns to update.
202
+ def save(*columns)
199
203
  run_hooks(:before_save)
200
204
  if @new
201
205
  run_hooks(:before_create)
@@ -213,12 +217,24 @@ module Sequel
213
217
  run_hooks(:after_create)
214
218
  else
215
219
  run_hooks(:before_update)
216
- this.update(@values)
220
+ if columns.empty?
221
+ this.update(@values)
222
+ @changed_columns = []
223
+ else # update only the specified columns
224
+ this.update(@values.reject {|k, v| !columns.include?(k)})
225
+ @changed_columns.reject! {|c| columns.include?(c)}
226
+ end
217
227
  run_hooks(:after_update)
218
228
  end
219
229
  run_hooks(:after_save)
220
230
  self
221
231
  end
232
+
233
+ # Saves only changed columns or does nothing if no columns are marked as
234
+ # chanaged.
235
+ def save_changes
236
+ save(*@changed_columns) unless @changed_columns.empty?
237
+ end
222
238
 
223
239
  # Updates and saves values to database from the passed-in Hash.
224
240
  def set(values)
@@ -268,7 +284,10 @@ module Sequel
268
284
  # define the column accessor
269
285
  Thread.exclusive do
270
286
  if write
271
- model.class_def(m) {|v| @values[att] = v}
287
+ model.class_def(m) do |v|
288
+ @values[att] = v
289
+ @changed_columns << att unless @changed_columns.include?(att)
290
+ end
272
291
  else
273
292
  model.class_def(m) {@values[att]}
274
293
  end
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/mysql' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/mysql')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/odbc' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/odbc')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/oracle' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/oracle')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/postgres' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/postgres')
@@ -1,2 +1,6 @@
1
1
  warn "Requiring 'sequel/sqlite' is deprecated. Please modify your code to require 'sequel' instead."
2
+
3
+ if !Object.const_defined?('Sequel')
4
+ require File.join(File.dirname(__FILE__), '../sequel')
5
+ end
2
6
  require File.join(File.dirname(__FILE__), 'adapters/sqlite')
@@ -445,6 +445,118 @@ context "Model attribute accessors" do
445
445
  end
446
446
  end
447
447
 
448
+ context "Model attribute setters" do
449
+ setup do
450
+ MODEL_DB.reset
451
+
452
+ @c = Class.new(Sequel::Model(:items)) do
453
+ def columns
454
+ [:id, :x, :y]
455
+ end
456
+ end
457
+ end
458
+
459
+ specify "should mark the column value as changed" do
460
+ o = @c.new
461
+ o.changed_columns.should == []
462
+
463
+ o.x = 2
464
+ o.changed_columns.should == [:x]
465
+
466
+ o.y = 3
467
+ o.changed_columns.should == [:x, :y]
468
+
469
+ o.changed_columns.clear
470
+
471
+ o[:x] = 2
472
+ o.changed_columns.should == [:x]
473
+
474
+ o[:y] = 3
475
+ o.changed_columns.should == [:x, :y]
476
+ end
477
+ end
478
+
479
+ context "Model#save" do
480
+ setup do
481
+ MODEL_DB.reset
482
+
483
+ @c = Class.new(Sequel::Model(:items)) do
484
+ def columns
485
+ [:id, :x, :y]
486
+ end
487
+ end
488
+ end
489
+
490
+ specify "should insert a record for a new model instance" do
491
+ o = @c.new(:x => 1)
492
+ o.save
493
+
494
+ MODEL_DB.sqls.first.should == "INSERT INTO items (x) VALUES (1)"
495
+ end
496
+
497
+ specify "should update a record for an existing model instance" do
498
+ o = @c.new(:id => 3, :x => 1)
499
+ o.save
500
+
501
+ MODEL_DB.sqls.first.should == "UPDATE items SET x = 1, id = 3 WHERE (id = 3)"
502
+ end
503
+
504
+ specify "should update only the given columns if given" do
505
+ o = @c.new(:id => 3, :x => 1, :y => nil)
506
+ o.save(:y)
507
+
508
+ MODEL_DB.sqls.first.should == "UPDATE items SET y = NULL WHERE (id = 3)"
509
+ end
510
+
511
+ specify "should mark saved columns as not changed" do
512
+ o = @c.new(:id => 3, :x => 1, :y => nil)
513
+ o[:y] = 4
514
+ o.changed_columns.should == [:y]
515
+ o.save(:x)
516
+ o.changed_columns.should == [:y]
517
+ o.save(:y)
518
+ o.changed_columns.should == []
519
+ end
520
+ end
521
+
522
+ context "Model#save_changes" do
523
+ setup do
524
+ MODEL_DB.reset
525
+
526
+ @c = Class.new(Sequel::Model(:items)) do
527
+ def columns
528
+ [:id, :x, :y]
529
+ end
530
+ end
531
+ end
532
+
533
+ specify "should do nothing if no changed columns" do
534
+ o = @c.new(:id => 3, :x => 1, :y => nil)
535
+ o.save_changes
536
+
537
+ MODEL_DB.sqls.should be_empty
538
+ end
539
+
540
+ specify "should update only changed columns" do
541
+ o = @c.new(:id => 3, :x => 1, :y => nil)
542
+ o.x = 2
543
+
544
+ o.save_changes
545
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
546
+ o.save_changes
547
+ o.save_changes
548
+ MODEL_DB.sqls.should == ["UPDATE items SET x = 2 WHERE (id = 3)"]
549
+ MODEL_DB.reset
550
+
551
+ o.y = 4
552
+ o.save_changes
553
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
554
+ o.save_changes
555
+ o.save_changes
556
+ MODEL_DB.sqls.should == ["UPDATE items SET y = 4 WHERE (id = 3)"]
557
+ end
558
+ end
559
+
448
560
  context "Model#new?" do
449
561
  setup do
450
562
  MODEL_DB.reset
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1.3
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2007-12-05 00:00:00 +02:00
12
+ date: 2007-12-07 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -54,6 +54,7 @@ files:
54
54
  - README
55
55
  - Rakefile
56
56
  - bin/sequel
57
+ - doc/rdoc
57
58
  - spec/adapters
58
59
  - spec/adapters/informix_spec.rb
59
60
  - spec/adapters/mysql_spec.rb
@@ -76,10 +77,12 @@ files:
76
77
  - spec/worker_spec.rb
77
78
  - lib/sequel
78
79
  - lib/sequel/adapters
80
+ - lib/sequel/adapters/adapter_skeleton.rb
79
81
  - lib/sequel/adapters/ado.rb
80
82
  - lib/sequel/adapters/db2.rb
81
83
  - lib/sequel/adapters/dbi.rb
82
84
  - lib/sequel/adapters/informix.rb
85
+ - lib/sequel/adapters/jdbc.rb
83
86
  - lib/sequel/adapters/mysql.rb
84
87
  - lib/sequel/adapters/odbc.rb
85
88
  - lib/sequel/adapters/odbc_mssql.rb