sequel 0.1.9.6 → 0.1.9.7

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,4 +1,20 @@
1
- === 0.2 (2007-08-13)
1
+ === 0.1.9.7 (2007-08-15)
2
+
3
+ * Added suppoort for executing batch statements in sqlite adapter.
4
+
5
+ * Changed #current_page_record_range to return 0..0 for an invalid page.
6
+
7
+ * Fixed joining of aliased tables.
8
+
9
+ * Improved Symbol#to_field_name to prevent false positives.
10
+
11
+ * Implemented Dataset#multi_insert with :commit_every option.
12
+
13
+ * More docs for Dataset#set_model.
14
+
15
+ * Implemented automatic creation of convenience methods for each adapter (e.g. Sequel.sqlite etc.)
16
+
17
+ === 0.1.9.6 (2007-08-13)
2
18
 
3
19
  * Refactored schema definition code. Gets rid of famous primary_key problem as well as other issues (e.g. issue #22).
4
20
 
@@ -6,7 +22,7 @@
6
22
 
7
23
  * Changed MySQL adapter to automatically reconnect (issue #26).
8
24
 
9
- * Changed Sequel() to acccept variable arity.
25
+ * Changed Sequel() to accept variable arity.
10
26
 
11
27
  * Added :elements option to column definition, in order to support ENUM and SET types.
12
28
 
@@ -30,7 +46,7 @@
30
46
 
31
47
  === 0.1.9.2 (2007-07-24)
32
48
 
33
- * Removed metaid dependency. Refactored requires in lib/sequel.rb.
49
+ * Removed metaid dependency. Re-factored requires in lib/sequel.rb.
34
50
 
35
51
  === 0.1.9.1 (2007-07-22)
36
52
 
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.1.9.6"
9
+ VERS = "0.1.9.7"
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",
@@ -24,7 +24,7 @@ Rake::RDocTask.new do |rdoc|
24
24
  rdoc.rdoc_dir = 'doc/rdoc'
25
25
  rdoc.options += RDOC_OPTS
26
26
  rdoc.main = "README"
27
- rdoc.title = "Sequel: Concise ORM for Ruby"
27
+ rdoc.title = "Sequel: Lightweight ORM library for Ruby"
28
28
  rdoc.rdoc_files.add ['README', 'COPYING', 'lib/sequel.rb', 'lib/sequel/**/*.rb']
29
29
  end
30
30
 
@@ -36,7 +36,7 @@ spec = Gem::Specification.new do |s|
36
36
  s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
37
37
  s.rdoc_options += RDOC_OPTS +
38
38
  ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/sequel.rb']
39
- s.summary = "Concise ORM for Ruby."
39
+ s.summary = "Lightweight ORM library for Ruby"
40
40
  s.description = s.summary
41
41
  s.author = "Sharon Rosner"
42
42
  s.email = 'ciconia@gmail.com'
data/bin/sequel CHANGED
@@ -3,17 +3,21 @@
3
3
  require 'rubygems'
4
4
  require 'sequel'
5
5
 
6
+ usage = <<END
7
+ Usage: sequel <connection URI>
8
+ Sequel: Lightweight ORM library for Ruby
9
+
10
+ Examples:
11
+ sequel sqlite:///blog.db
12
+ sequel postgres://localhost/my_blog
13
+
14
+ For more information see http://code.google.com/p/ruby-sequel
15
+ END
16
+
6
17
  db = ARGV.shift
7
18
 
8
19
  if db.nil? || db.empty?
9
- puts "Usage: sequel <connection string>"
10
- puts "Sequel: Concise ORM for Ruby."
11
- puts
12
- puts "Examples:"
13
- puts " sequel sqlite:///blog.db"
14
- puts " sequel postgres://localhost/blog"
15
- puts
16
- puts "For more information see http://sequel.rubyforge.org"
20
+ puts usage
17
21
  exit
18
22
  end
19
23
 
@@ -21,7 +25,7 @@ begin
21
25
  scheme = URI.parse(db).scheme
22
26
  require File.join('sequel', scheme)
23
27
  rescue LoadError
24
- puts "Invalid or unknown scheme: #{scheme}"
28
+ puts "Invalid adapter specified: #{scheme}"
25
29
  exit
26
30
  rescue => e
27
31
  puts e.message
@@ -54,17 +54,22 @@ class Symbol
54
54
  def AS(target)
55
55
  "#{to_field_name} AS #{target}"
56
56
  end
57
-
58
- AS_REGEXP = /(.*)___(.*)/.freeze
57
+
58
+ FIELD_REF_RE1 = /^([a-z_]+)__([a-z_]+)___([a-z_]+)/.freeze
59
+ FIELD_REF_RE2 = /^([a-z_]+)___([a-z_]+)$/.freeze
60
+ FIELD_REF_RE3 = /^([a-z_]+)__([a-z_]+)$/.freeze
59
61
  DOUBLE_UNDERSCORE = '__'.freeze
60
62
  PERIOD = '.'.freeze
61
63
 
62
64
  def to_field_name
63
65
  s = to_s
64
- if s =~ AS_REGEXP
65
- s = "#{$1} AS #{$2}"
66
+ case s
67
+ when FIELD_REF_RE1: "#{$1}.#{$2} AS #{$3}"
68
+ when FIELD_REF_RE2: "#{$1} AS #{$2}"
69
+ when FIELD_REF_RE3: "#{$1}.#{$2}"
70
+ else
71
+ s
66
72
  end
67
- s.split(DOUBLE_UNDERSCORE).join(PERIOD)
68
73
  end
69
74
 
70
75
  def ALL
@@ -215,6 +215,24 @@ module Sequel
215
215
  def self.set_adapter_scheme(scheme)
216
216
  @scheme = scheme
217
217
  @@adapters[scheme.to_sym] = self
218
+
219
+ # Define convenience method for this database class
220
+ db_class = self
221
+ Sequel.meta_def(scheme) do |*args|
222
+ begin
223
+ case args.size
224
+ when 1: # Sequel.dbi(db_name)
225
+ opts = {:database => args[0]}
226
+ when 0 # Sequel.dbi
227
+ opts = {}
228
+ else # Sequel.dbi(db_name, opts)
229
+ opts = args[1].merge(:database => args[0])
230
+ end
231
+ rescue
232
+ raise SequelError, "Invalid parameters specified"
233
+ end
234
+ db_class.new(opts)
235
+ end
218
236
  end
219
237
 
220
238
  # Returns the scheme for the Database class.
@@ -171,21 +171,44 @@ module Sequel
171
171
  d
172
172
  end
173
173
 
174
- # Associates the dataset with a model. If
174
+ # Associates or disassociates the dataset with a model. If no argument or
175
+ # nil is specified, the dataset is turned into a naked dataset and returns
176
+ # records as hashes. If a model class specified, the dataset is modified
177
+ # to return records as instances of the model class, e.g:
178
+ #
179
+ # class MyModel
180
+ # def initialize(values)
181
+ # @values = values
182
+ # end
183
+ # end
184
+ #
185
+ # dataset.set_model(MyModel)
186
+ #
187
+ # The dataset can be made polymorphic by specifying a column name as the
188
+ # polymorphic key and a hash mapping column values to model classes.
189
+ #
190
+ # dataset.set_model(:kind, {1 => Person, 2 => Business})
191
+ #
175
192
  def set_model(*args)
176
193
  if args.empty? || (args.first == nil)
194
+ # If no argument or nil is provided, the dataset is denuded
177
195
  @opts.merge!(:naked => true, :models => nil, :polymorphic_key => nil)
178
196
  extend_with_stock_each
179
197
  elsif args.size == 1
198
+ # If a single argument is provided, it is regarded the model class
180
199
  c = args.first
181
200
  @opts.merge!(:naked => nil, :models => {nil => c}, :polymorphic_key => nil)
182
201
  extend_with_model(c)
183
202
  extend_with_destroy
184
- else
203
+ elsif args.size == 2
204
+ # If two arguments are provided, the first is considered the
205
+ # polymorphic key, and the second a hash of classes.
185
206
  key, hash = args
186
207
  @opts.merge!(:naked => true, :models => hash, :polymorphic_key => key)
187
208
  extend_with_polymorphic_model(key, hash)
188
209
  extend_with_destroy
210
+ else
211
+ raise SequelError, "Invalid parameters specified"
189
212
  end
190
213
  self
191
214
  end
@@ -1,3 +1,5 @@
1
+ require 'enumerator'
2
+
1
3
  module Sequel
2
4
  class Dataset
3
5
  module Convenience
@@ -119,7 +121,7 @@ module Sequel
119
121
 
120
122
  # Returns the record range for the current page
121
123
  def current_page_record_range
122
- return nil if @current_page > @page_count
124
+ return (0..0) if @current_page > @page_count
123
125
 
124
126
  a = 1 + (@current_page - 1) * @page_size
125
127
  b = a + @page_size - 1
@@ -151,6 +153,29 @@ module Sequel
151
153
  def print(*cols)
152
154
  Sequel::PrettyTable.print(naked.all, cols.empty? ? columns : cols)
153
155
  end
156
+
157
+ # Inserts multiple records into the associated table. This method can be
158
+ # to efficiently insert a large amounts of records into a table. Inserts
159
+ # are automatically wrapped in a transaction. If the :commit_every
160
+ # option is specified, the method will generate a separate transaction
161
+ # for each batch of records, e.g.:
162
+ #
163
+ # dataset.multi_insert(list, :commit_every => 1000)
164
+ def multi_insert(list, opts = {})
165
+ if every = opts[:commit_every]
166
+ list.each_slice(every) do |s|
167
+ @db.transaction do
168
+ s.each {|r| @db.execute(insert_sql(r))}
169
+ # @db.execute(s.map {|r| insert_sql(r)}.join)
170
+ end
171
+ end
172
+ else
173
+ @db.transaction do
174
+ # @db.execute(list.map {|r| insert_sql(r)}.join)
175
+ list.each {|r| @db.execute(insert_sql(r))}
176
+ end
177
+ end
178
+ end
154
179
  end
155
180
  end
156
181
  end
@@ -10,13 +10,23 @@ module Sequel
10
10
  field.is_a?(Symbol) ? field.to_field_name : field
11
11
  end
12
12
 
13
- QUALIFIED_REGEXP = /(.*)\.(.*)/.freeze
13
+ ALIASED_REGEXP = /^(.*)\s(.*)$/.freeze
14
+ QUALIFIED_REGEXP = /^(.*)\.(.*)$/.freeze
14
15
 
15
16
  # Returns a qualified field name (including a table name) if the field
16
17
  # name isn't already qualified.
17
18
  def qualified_field_name(field, table)
18
- fn = field_name(field)
19
- fn =~ QUALIFIED_REGEXP ? fn : "#{table}.#{fn}"
19
+ field = field_name(field)
20
+ if field =~ QUALIFIED_REGEXP
21
+ # field is already qualified
22
+ field
23
+ else
24
+ # check if the table is aliased
25
+ if table =~ ALIASED_REGEXP
26
+ table = $2
27
+ end
28
+ "#{table}.#{field}"
29
+ end
20
30
  end
21
31
 
22
32
  WILDCARD = '*'.freeze
@@ -358,7 +368,7 @@ module Sequel
358
368
 
359
369
  join_expr = expr.map do |k, v|
360
370
  l = qualified_field_name(k, table)
361
- r = qualified_field_name(v, @opts[:last_joined_table] || @opts[:from])
371
+ r = qualified_field_name(v, @opts[:last_joined_table] || @opts[:from].first)
362
372
  "(#{l} = #{r})"
363
373
  end.join(AND_SEPARATOR)
364
374
 
@@ -463,7 +473,7 @@ module Sequel
463
473
  # 'INSERT INTO items (a, b) VALUES (1, 2)'
464
474
  def insert_sql(*values)
465
475
  if values.empty?
466
- "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
476
+ "INSERT INTO #{@opts[:from]} DEFAULT VALUES;"
467
477
  elsif (values.size == 1) && values[0].is_a?(Hash)
468
478
  field_list = []
469
479
  value_list = []
@@ -473,9 +483,9 @@ module Sequel
473
483
  end
474
484
  fl = field_list.join(COMMA_SEPARATOR)
475
485
  vl = value_list.join(COMMA_SEPARATOR)
476
- "INSERT INTO #{@opts[:from]} (#{fl}) VALUES (#{vl})"
486
+ "INSERT INTO #{@opts[:from]} (#{fl}) VALUES (#{vl});"
477
487
  else
478
- "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)})"
488
+ "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)});"
479
489
  end
480
490
  end
481
491
 
data/lib/sequel/dbi.rb CHANGED
@@ -2,15 +2,9 @@ if !Object.const_defined?('Sequel')
2
2
  require File.join(File.dirname(__FILE__), '../sequel')
3
3
  end
4
4
 
5
- require 'dbi'
5
+ # require 'dbi'
6
6
 
7
7
  module Sequel
8
- def self.dbi(conn_string, opts = nil)
9
- opts ||= {}
10
- opts.merge!(:database => conn_string)
11
- Sequel::DBI::Database.new(opts)
12
- end
13
-
14
8
  module DBI
15
9
  class Database < Sequel::Database
16
10
  set_adapter_scheme :dbi
data/lib/sequel/sqlite.rb CHANGED
@@ -14,7 +14,7 @@ module Sequel
14
14
  end
15
15
 
16
16
  def connect
17
- if @opts[:database].empty?
17
+ if @opts[:database].nil? || @opts[:database].empty?
18
18
  @opts[:database] = ':memory:'
19
19
  end
20
20
  db = ::SQLite3::Database.new(@opts[:database])
@@ -34,7 +34,7 @@ module Sequel
34
34
 
35
35
  def execute(sql)
36
36
  @logger.info(sql) if @logger
37
- @pool.hold {|conn| conn.execute(sql)}
37
+ @pool.hold {|conn| conn.execute_batch(sql)}
38
38
  end
39
39
 
40
40
  def execute_insert(sql)
@@ -60,6 +60,18 @@ context "An SQLite database" do
60
60
 
61
61
  proc {@db.temp_store = :invalid}.should raise_error(SequelError)
62
62
  end
63
+
64
+ specify "should be able to execute multiple statements at once" do
65
+ @db.create_table :t do
66
+ text :name
67
+ end
68
+
69
+ @db << "insert into t (name) values ('abc');insert into t (name) values ('def')"
70
+
71
+ @db[:t].count.should == 2
72
+
73
+ @db[:t].order(:name).map(:name).should == ['abc', 'def']
74
+ end
63
75
  end
64
76
 
65
77
  context "An SQLite dataset" do
@@ -339,6 +339,24 @@ context "A Database adapter with a scheme" do
339
339
  c.opts[:database].should == 'db'
340
340
  end
341
341
 
342
+ specify "should register a convenience method on Sequel" do
343
+ Sequel.should respond_to(:ccc)
344
+
345
+ # invalid parameters
346
+ proc {Sequel.ccc('abc', 'def')}.should raise_error(SequelError)
347
+
348
+ c = Sequel.ccc('mydb')
349
+ c.should be_a_kind_of(CCC)
350
+ c.opts.should == {:database => 'mydb'}
351
+
352
+ c = Sequel.ccc('mydb', :host => 'localhost')
353
+ c.should be_a_kind_of(CCC)
354
+ c.opts.should == {:database => 'mydb', :host => 'localhost'}
355
+
356
+ c = Sequel.ccc
357
+ c.should be_a_kind_of(CCC)
358
+ c.opts.should == {}
359
+ end
342
360
  end
343
361
 
344
362
  context "An unknown database scheme" do
data/spec/dataset_spec.rb CHANGED
@@ -99,11 +99,11 @@ context "A simple dataset" do
99
99
  end
100
100
 
101
101
  specify "should format an insert statement" do
102
- @dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES'
102
+ @dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES;'
103
103
  @dataset.insert_sql(:name => 'wxyz', :price => 342).
104
104
  should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
105
105
  @dataset.insert_sql('a', 2, 6.5).should ==
106
- "INSERT INTO test VALUES ('a', 2, 6.5)"
106
+ "INSERT INTO test VALUES ('a', 2, 6.5);"
107
107
  end
108
108
 
109
109
  specify "should format an update statement" do
@@ -830,6 +830,11 @@ context "Dataset#join_table" do
830
830
  specify "should raise if an invalid join type is specified" do
831
831
  proc {@d.join_table(:invalid, :a, :b)}.should raise_error(SequelError)
832
832
  end
833
+
834
+ specify "should treat aliased tables correctly" do
835
+ @d.from('stats s').join('players p', :id => :player_id).sql.should ==
836
+ 'SELECT * FROM stats s INNER JOIN players p ON (p.id = s.player_id)'
837
+ end
833
838
  end
834
839
 
835
840
  context "Dataset#[]=" do
@@ -1413,7 +1418,7 @@ context "A paginated dataset" do
1413
1418
  specify "should return the record range for the current page" do
1414
1419
  @paginated.current_page_record_range.should == (1..20)
1415
1420
  @d.paginate(4, 50).current_page_record_range.should == (151..153)
1416
- @d.paginate(5, 50).current_page_record_range.should == nil
1421
+ @d.paginate(5, 50).current_page_record_range.should == (0..0)
1417
1422
  end
1418
1423
  end
1419
1424
 
@@ -1457,4 +1462,53 @@ context "Dataset#print" do
1457
1462
  @output.read.should == \
1458
1463
  "+-+-+\n|a|b|\n+-+-+\n|1|2|\n|3|4|\n|5|6|\n+-+-+\n"
1459
1464
  end
1465
+ end
1466
+
1467
+ context "Dataset#multi_insert" do
1468
+ setup do
1469
+ @dbc = Class.new do
1470
+ attr_reader :sqls
1471
+
1472
+ def execute(sql)
1473
+ @sqls ||= []
1474
+ @sqls << sql
1475
+ end
1476
+
1477
+ def transaction
1478
+ @sqls ||= []
1479
+ @sqls << 'BEGIN;'
1480
+ yield
1481
+ @sqls << 'COMMIT;'
1482
+ end
1483
+ end
1484
+ @db = @dbc.new
1485
+
1486
+ @ds = Sequel::Dataset.new(@db).from(:items)
1487
+
1488
+ @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
1489
+ end
1490
+
1491
+ specify "should join all inserts into a single SQL string" do
1492
+ @ds.multi_insert(@list)
1493
+ @db.sqls.should == [
1494
+ 'BEGIN;',
1495
+ "INSERT INTO items (name) VALUES ('abc');",
1496
+ "INSERT INTO items (name) VALUES ('def');",
1497
+ "INSERT INTO items (name) VALUES ('ghi');",
1498
+ 'COMMIT;'
1499
+ ]
1500
+ end
1501
+
1502
+ specify "should accept the commit_every option for commiting every x records" do
1503
+ @ds.multi_insert(@list, :commit_every => 2)
1504
+ @db.sqls.should == [
1505
+ 'BEGIN;',
1506
+ "INSERT INTO items (name) VALUES ('abc');",
1507
+ "INSERT INTO items (name) VALUES ('def');",
1508
+ 'COMMIT;',
1509
+ 'BEGIN;',
1510
+ "INSERT INTO items (name) VALUES ('ghi');",
1511
+ 'COMMIT;'
1512
+ ]
1513
+ end
1460
1514
  end
metadata CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: sequel
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.9.6
7
- date: 2007-08-13 00:00:00 +03:00
8
- summary: Concise ORM for Ruby.
6
+ version: 0.1.9.7
7
+ date: 2007-08-15 00:00:00 +03:00
8
+ summary: Lightweight ORM library for Ruby
9
9
  require_paths:
10
10
  - lib
11
11
  email: ciconia@gmail.com
12
12
  homepage: http://sequel.rubyforge.org
13
13
  rubyforge_project:
14
- description: Concise ORM for Ruby.
14
+ description: Lightweight ORM library for Ruby
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
@@ -33,7 +33,6 @@ files:
33
33
  - README
34
34
  - Rakefile
35
35
  - bin/sequel
36
- - doc/rdoc
37
36
  - spec/adapters
38
37
  - spec/connection_pool_spec.rb
39
38
  - spec/core_ext_spec.rb