sequel 0.1.9.6 → 0.1.9.7

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