sequel 0.4.1.3 → 0.4.2

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 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