sequel_core 1.2.1 → 1.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,29 @@
1
+ === 1.3 (2008-03-08)
2
+
3
+ * Added configuration file for running specs (#186).
4
+
5
+ * Changed Database#drop_index to accept fixed arity (#173).
6
+
7
+ * Changed column definition sql to put UNSIGNED constraint before unique in order to satisfy MySQL (#171).
8
+
9
+ * Enhanced MySQL adapter to support load data local infile_, added compress option for mysql connection by default (#172).
10
+
11
+ * Fixed bug when inserting hashes in array tuples mode.
12
+
13
+ * Changed SQLite adapter to catch RuntimeError raised when executing a statement and raise an Error::InvalidStatement with the offending SQL and error message (#188).
14
+
15
+ * Added Error::InvalidStatement class.
16
+
17
+ * Fixed Dataset#reverse to not raise for unordered dataset (#189).
18
+
19
+ * Added Dataset#unordered method and changed #order to remove order if nil is specified (#190).
20
+
21
+ * Fixed reversing order of ASC expression (#164).
22
+
23
+ * Added support for :null => true option when defining table columns (#192).
24
+
25
+ * Fixed Symbol#method_missing to accept variable arity (#185).
26
+
1
27
  === 1.2.1 (2008-02-29)
2
28
 
3
29
  * Added add_constraint and drop_constraint functionality to Database#alter_table (#182).
data/README CHANGED
@@ -1,6 +1,6 @@
1
- == Sequel: Concise ORM for Ruby
1
+ == Sequel: The Database Toolkit for Ruby
2
2
 
3
- Sequel is an ORM framework for Ruby. Sequel provides thread safety, connection pooling, and a concise DSL for constructing queries and table schemas.
3
+ Sequel is a database access toolkit for Ruby. Sequel provides thread safety, connection pooling, and a concise DSL for constructing queries and table schemas.
4
4
 
5
5
  Sequel makes it easy to deal with multiple records without having to break your teeth on SQL.
6
6
 
@@ -24,10 +24,6 @@ If you have any comments or suggestions please send an email to ciconia at gmail
24
24
 
25
25
  sudo gem install sequel
26
26
 
27
- Note: as of version 0.5, Sequel models are distributed in a separate gem. In order to use sequel models, you should install the sequel_model gem:
28
-
29
- sudo gem install sequel_model
30
-
31
27
  == Supported Databases
32
28
 
33
29
  Sequel currently supports:
@@ -53,7 +49,7 @@ You get an IRB session with the database object stored in DB.
53
49
 
54
50
  == An Introduction
55
51
 
56
- Sequel was designed to take the hassle away from connecting to databases and manipulating them. Sequel deals with all the boring stuff like maintaining connections, formatting SQL correctly and fetching records so you can concentrate on your application.
52
+ Sequel is designed to take the hassle away from connecting to databases and manipulating them. Sequel deals with all the boring stuff like maintaining connections, formatting SQL correctly and fetching records so you can concentrate on your application.
57
53
 
58
54
  Sequel uses the concept of datasets to retrieve data. A Dataset object encapsulates an SQL query and supports chainability, letting you fetch data using a convenient Ruby DSL that is both concise and infinitely flexible.
59
55
 
@@ -78,4 +74,178 @@ Or getting results as a transposed hash, with one column as key and another as v
78
74
 
79
75
  middle_east.to_hash(:name, :area) #=> {'Israel' => 20000, 'Greece' => 120000, ...}
80
76
 
81
- You can find more information on getting started with Sequel {here}[http://code.google.com/p/ruby-sequel/wiki/GettingStarted]
77
+ Much of Sequel is still undocumented (especially the part relating to model classes). The following section provides examples of common usage. Feel free to explore...
78
+
79
+ == Getting Started
80
+
81
+ === Connecting to a database
82
+
83
+ To connect to a database you simply provide Sequel with a URL:
84
+
85
+ require 'sequel'
86
+ DB = Sequel.open 'sqlite:///blog.db'
87
+
88
+ The connection URL can also include such stuff as the user name and password:
89
+
90
+ DB = Sequel.open 'postgres://cico:12345@localhost:5432/mydb'
91
+
92
+ You can also specify optional parameters, such as the connection pool size, or a logger for logging SQL queries:
93
+
94
+ DB = Sequel.open("postgres://postgres:postgres@localhost/my_db",
95
+ :max_connections => 10, :logger => Logger.new('log/db.log'))
96
+
97
+ === Arbitrary SQL queries
98
+
99
+ DB.execute("create table t (a text, b text)")
100
+ DB.execute("insert into t values ('a', 'b')")
101
+
102
+ Or more succinctly:
103
+
104
+ DB << "create table t (a text, b text)"
105
+ DB << "insert into t values ('a', 'b')"
106
+
107
+ === Getting Dataset Instances
108
+
109
+ Dataset is the primary means through which records are retrieved and manipulated. You can create an blank dataset by using the dataset method:
110
+
111
+ dataset = DB.dataset
112
+
113
+ Or by using the from methods:
114
+
115
+ posts = DB.from(:posts)
116
+
117
+ You can also use the equivalent shorthand:
118
+
119
+ posts = DB[:posts]
120
+
121
+ Note: the dataset will only fetch records when you explicitly ask for them, as will be shown below. Datasets can be manipulated to filter through records, change record order and even join tables, as will also be shown below.
122
+
123
+ === Retrieving Records
124
+
125
+ You can retrieve records by using the all method:
126
+
127
+ posts.all
128
+
129
+ The all method returns an array of hashes, where each hash corresponds to a record.
130
+
131
+ You can also iterate through records one at a time:
132
+
133
+ posts.each {|row| p row}
134
+
135
+ Or perform more advanced stuff:
136
+
137
+ posts.map(:id)
138
+ posts.inject({}) {|h, r| h[r[:id]] = r[:name]}
139
+
140
+ You can also retrieve the first record in a dataset:
141
+
142
+ posts.first
143
+
144
+ Or retrieve a single record with a specific value:
145
+
146
+ posts[:id => 1]
147
+
148
+ If the dataset is ordered, you can also ask for the last record:
149
+
150
+ posts.order(:stamp).last
151
+
152
+ === Filtering Records
153
+
154
+ The simplest way to filter records is to provide a hash of values to match:
155
+
156
+ my_posts = posts.filter(:category => 'ruby', :author => 'david')
157
+
158
+ You can also specify ranges:
159
+
160
+ my_posts = posts.filter(:stamp => (2.weeks.ago)..(1.week.ago))
161
+
162
+ Or lists of values:
163
+
164
+ my_posts = posts.filter(:category => ['ruby', 'postgres', 'linux'])
165
+
166
+ Sequel now also accepts expressions as closures, AKA block filters:
167
+
168
+ my_posts = posts.filter {:category == ['ruby', 'postgres', 'linux']}
169
+
170
+ Which also lets you do stuff like:
171
+
172
+ my_posts = posts.filter {:stamp > 1.month.ago}
173
+
174
+ Some adapters (like postgresql) will also let you specify Regexps:
175
+
176
+ my_posts = posts.filter(:category => /ruby/i)
177
+
178
+ You can also use an inverse filter:
179
+
180
+ my_posts = posts.exclude(:category => /ruby/i)
181
+
182
+ You can then retrieve the records by using any of the retrieval methods:
183
+
184
+ my_posts.each {|row| p row}
185
+
186
+ You can also specify a custom WHERE clause:
187
+
188
+ posts.filter('(stamp < ?) AND (author <> ?)', 3.days.ago, author_name)
189
+
190
+ Datasets can also be used as subqueries:
191
+
192
+ DB[:items].filter('price > ?', DB[:items].select('AVG(price) + 100'))
193
+
194
+ === Summarizing Records
195
+
196
+ Counting records is easy:
197
+ posts.filter(:category => /ruby/i).count
198
+
199
+ And you can also query maximum/minimum values:
200
+ max_value = DB[:history].max(:value)
201
+
202
+ Or calculate a sum:
203
+ total = DB[:items].sum(:price)
204
+
205
+ === Ordering Records
206
+
207
+ posts.order(:stamp)
208
+
209
+ You can also specify descending order
210
+
211
+ posts.order(:stamp.DESC)
212
+
213
+ === Deleting Records
214
+
215
+ posts.filter('stamp < ?', 3.days.ago).delete
216
+
217
+ === Inserting Records
218
+
219
+ posts.insert(:category => 'ruby', :author => 'david')
220
+
221
+ Or alternatively:
222
+
223
+ posts << {:category => 'ruby', :author => 'david'}
224
+
225
+ === Updating Records
226
+
227
+ posts.filter('stamp < ?', 3.days.ago).update(:state => 'archived')
228
+
229
+ === Joining Tables
230
+
231
+ Joining is very useful in a variety of scenarios, for example many-to-many relationships. With Sequel it's really easy:
232
+
233
+ order_items = DB[:items].join(:order_items, :item_id => :id).
234
+ filter(:order_items__order_id => 1234)
235
+
236
+ This is equivalent to the SQL:
237
+
238
+ SELECT * FROM items LEFT OUTER JOIN order_items
239
+ ON order_items.item_id = items.id
240
+ WHERE order_items.order_id = 1234
241
+
242
+ You can then do anything you like with the dataset:
243
+
244
+ order_total = order_items.sum(:price)
245
+
246
+ Which is equivalent to the SQL:
247
+
248
+ SELECT sum(price) FROM items LEFT OUTER JOIN order_items
249
+ ON order_items.item_id = items.id
250
+ WHERE order_items.order_id = 1234
251
+
data/Rakefile CHANGED
@@ -9,11 +9,11 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "sequel_core"
12
- VERS = "1.2.1"
12
+ VERS = "1.3"
13
13
  CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
14
14
  RDOC_OPTS = [
15
15
  "--quiet",
16
- "--title", "Sequel: Database access for Ruby",
16
+ "--title", "Sequel: The Database Toolkit for Ruby",
17
17
  "--opname", "index.html",
18
18
  "--line-numbers",
19
19
  "--main", "README",
@@ -29,7 +29,7 @@ Rake::RDocTask.new do |rdoc|
29
29
  rdoc.rdoc_dir = "doc/rdoc"
30
30
  rdoc.options += RDOC_OPTS
31
31
  rdoc.main = "README"
32
- rdoc.title = "Sequel: Database access for Ruby"
32
+ rdoc.title = "Sequel: The Database Toolkit for Ruby"
33
33
  rdoc.rdoc_files.add ["README", "COPYING", "lib/sequel_core.rb", "lib/**/*.rb"]
34
34
  end
35
35
 
@@ -49,7 +49,7 @@ spec = Gem::Specification.new do |s|
49
49
  s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
50
50
  s.rdoc_options += RDOC_OPTS +
51
51
  ["--exclude", "^(examples|extras)\/", "--exclude", "lib/sequel_core.rb"]
52
- s.summary = "Database access for Ruby"
52
+ s.summary = "The Database Toolkit for Ruby"
53
53
  s.description = s.summary
54
54
  s.author = "Sharon Rosner"
55
55
  s.email = "ciconia@gmail.com"
data/bin/sequel CHANGED
@@ -12,7 +12,7 @@ $logfile = nil
12
12
  $echo = nil
13
13
 
14
14
  opts = OptionParser.new do |opts|
15
- opts.banner = "Sequel: Lightweight ORM for Ruby"
15
+ opts.banner = "Sequel: The Database Toolkit for Ruby"
16
16
  opts.define_head "Usage: sequel <uri> [options]"
17
17
  opts.separator ""
18
18
  opts.separator "Examples:"
@@ -86,14 +86,18 @@ module Sequel
86
86
  end
87
87
 
88
88
  def connect
89
- conn = Mysql.real_connect(
89
+ conn = Mysql.init
90
+ conn.options(Mysql::OPT_LOCAL_INFILE, "client")
91
+ conn.real_connect(
90
92
  @opts[:host] || 'localhost',
91
93
  @opts[:user],
92
94
  @opts[:password],
93
95
  @opts[:database],
94
96
  @opts[:port],
95
97
  @opts[:socket],
96
- Mysql::CLIENT_MULTI_RESULTS
98
+ Mysql::CLIENT_MULTI_RESULTS +
99
+ Mysql::CLIENT_MULTI_STATEMENTS +
100
+ Mysql::CLIENT_COMPRESS
97
101
  )
98
102
  conn.query_with_result = false
99
103
  if encoding = @opts[:encoding] || @opts[:charset]
@@ -159,9 +163,10 @@ module Sequel
159
163
  column[:size] ||= 255 if column[:type] == :varchar
160
164
  elements = column[:size] || column[:elements]
161
165
  sql << "(#{literal(elements)})" if elements
166
+ sql << UNSIGNED if column[:unsigned]
162
167
  sql << UNIQUE if column[:unique]
163
168
  sql << NOT_NULL if column[:null] == false
164
- sql << UNSIGNED if column[:unsigned]
169
+ sql << NULL if column[:null] == true
165
170
  sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
166
171
  sql << PRIMARY_KEY if column[:primary_key]
167
172
  sql << " #{auto_increment_sql}" if column[:auto_increment]
@@ -341,6 +346,12 @@ module Sequel
341
346
  "REPLACE INTO #{@opts[:from]} DEFAULT VALUES"
342
347
  else
343
348
  values = values[0] if values.size == 1
349
+
350
+ # if hash or array with keys we need to transform the values
351
+ if @transform && (values.is_a?(Hash) || (values.is_a?(Array) && values.keys))
352
+ values = transform_save(values)
353
+ end
354
+
344
355
  case values
345
356
  when Sequel::Model
346
357
  insert_sql(values.values)
@@ -349,14 +360,12 @@ module Sequel
349
360
  "REPLACE INTO #{@opts[:from]} DEFAULT VALUES"
350
361
  elsif values.keys
351
362
  fl = values.keys.map {|f| literal(f.is_a?(String) ? f.to_sym : f)}
352
- vl = @transform ? transform_save(values.values) : values.values
353
- vl.map! {|v| literal(v)}
363
+ vl = values.values.map {|v| literal(v)}
354
364
  "REPLACE INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
355
365
  else
356
366
  "REPLACE INTO #{@opts[:from]} VALUES (#{literal(values)})"
357
367
  end
358
368
  when Hash
359
- values = transform_save(values) if @transform
360
369
  if values.empty?
361
370
  "REPLACE INTO #{@opts[:from]} DEFAULT VALUES"
362
371
  else
@@ -419,6 +428,23 @@ module Sequel
419
428
  self
420
429
  end
421
430
 
431
+ def array_tuples_transform_load(r)
432
+ a = []; a.keys = []
433
+ r.each_pair do |k, v|
434
+ a[k] = (tt = @transform[k]) ? tt[0][v] : v
435
+ end
436
+ a
437
+ end
438
+
439
+ # Applies the value transform for data saved to the database.
440
+ def array_tuples_transform_save(r)
441
+ a = []; a.keys = []
442
+ r.each_pair do |k, v|
443
+ a[k] = (tt = @transform[k]) ? tt[1][v] : v
444
+ end
445
+ a
446
+ end
447
+
422
448
  def multi_insert_sql(columns, values)
423
449
  columns = literal(columns)
424
450
  values = values.map {|r| "(#{literal(r)})"}.join(COMMA_SEPARATOR)
@@ -39,21 +39,29 @@ module Sequel
39
39
  def execute(sql)
40
40
  @logger.info(sql) if @logger
41
41
  @pool.hold {|conn| conn.execute_batch(sql); conn.changes}
42
+ rescue RuntimeError => e
43
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
42
44
  end
43
45
 
44
46
  def execute_insert(sql)
45
47
  @logger.info(sql) if @logger
46
48
  @pool.hold {|conn| conn.execute(sql); conn.last_insert_row_id}
49
+ rescue RuntimeError => e
50
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
47
51
  end
48
52
 
49
53
  def single_value(sql)
50
54
  @logger.info(sql) if @logger
51
55
  @pool.hold {|conn| conn.get_first_value(sql)}
56
+ rescue RuntimeError => e
57
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
52
58
  end
53
59
 
54
60
  def execute_select(sql, &block)
55
61
  @logger.info(sql) if @logger
56
62
  @pool.hold {|conn| conn.query(sql, &block)}
63
+ rescue RuntimeError => e
64
+ raise Error::InvalidStatement, "#{sql}\r\n#{e.message}"
57
65
  end
58
66
 
59
67
  def pragma_get(name)
@@ -190,8 +190,7 @@ module ArrayKeys
190
190
  # The DatasetExtensions module provides extensions that modify
191
191
  # a dataset to return Array tuples instead of Hash tuples.
192
192
  module DatasetExtensions
193
- # Fetches a dataset's records, converting each tuple into an array with
194
- # keys.
193
+ # Fetches a dataset's records, converting each tuple into an array with keys.
195
194
  def array_tuples_each(opts = nil, &block)
196
195
  fetch_rows(select_sql(opts)) {|h| block[Array.from_hash(h)]}
197
196
  end
@@ -240,6 +239,8 @@ module ArrayKeys
240
239
  end
241
240
 
242
241
  def array_tuples_transform_load(r)
242
+ puts "transform_load"
243
+
243
244
  a = []; a.keys = []
244
245
  r.each_pair do |k, v|
245
246
  a[k] = (tt = @transform[k]) ? tt[0][v] : v
@@ -287,8 +288,12 @@ module Sequel
287
288
  include ArrayKeys::DatasetExtensions
288
289
  alias_method :each, :array_tuples_each
289
290
  alias_method :update_each_method, :array_tuples_update_each_method
290
-
291
+ end
292
+
293
+ if method_defined?(:array_tuples_transform_load)
291
294
  alias_method :transform_load, :array_tuples_transform_load
295
+ end
296
+ if method_defined?(:array_tuples_transform_save)
292
297
  alias_method :transform_save, :array_tuples_transform_save
293
298
  end
294
299
  end
@@ -189,7 +189,7 @@ class Symbol
189
189
 
190
190
  # Converts missing method calls into functions on columns, if the
191
191
  # method name is made of all upper case letters.
192
- def method_missing(sym)
192
+ def method_missing(sym, *args)
193
193
  if ((s = sym.to_s) =~ /^([A-Z]+)$/)
194
194
  Sequel::SQL::Function.new(s.downcase, self)
195
195
  else
@@ -279,8 +279,8 @@ module Sequel
279
279
  #
280
280
  # DB.drop_index :posts, :title
281
281
  # DB.drop_index :posts, [:author, :title]
282
- def drop_index(table, *args)
283
- alter_table(table) {drop_index(*args)}
282
+ def drop_index(table, columns)
283
+ alter_table(table) {drop_index(columns)}
284
284
  end
285
285
 
286
286
  # Returns true if the given table exists.
@@ -194,9 +194,19 @@ module Sequel
194
194
  end
195
195
  alias_method :distinct, :uniq
196
196
 
197
- # Returns a copy of the dataset with the order changed.
197
+ # Returns a copy of the dataset with the order changed. If a nil is given
198
+ # the returned dataset has no order. This can accept multiple arguments
199
+ # of varying kinds, and even SQL functions.
200
+ #
201
+ # ds.order(:name).sql #=> 'SELECT * FROM items ORDER BY name'
202
+ # ds.order(:a, :b).sql #=> 'SELECT * FROM items ORDER BY a, b'
203
+ # ds.order('a + b'.lit).sql #=> 'SELECT * FROM items ORDER BY a + b'
204
+ # ds.order(:name.desc).sql #=> 'SELECT * FROM items ORDER BY name DESC'
205
+ # ds.order(:name.asc).sql #=> 'SELECT * FROM items ORDER BY name ASC'
206
+ # ds.order(:arr|1).sql #=> 'SELECT * FROM items ORDER BY arr[1]'
207
+ # ds.order(nil).sql #=> 'SELECT * FROM items'
198
208
  def order(*order)
199
- clone(:order => order)
209
+ clone(:order => (order == [nil]) ? nil : order)
200
210
  end
201
211
  alias_method :order_by, :order
202
212
 
@@ -223,15 +233,23 @@ module Sequel
223
233
  # dataset.invert_order(:category, :price.desc]) #=>
224
234
  # [:category.desc, :price]
225
235
  def invert_order(order)
236
+ return nil unless order
226
237
  new_order = []
227
238
  order.map do |f|
228
239
  if f.is_a?(Sequel::SQL::ColumnExpr) && (f.op == Sequel::SQL::ColumnMethods::DESC)
229
240
  f.l
241
+ elsif f.is_a?(Sequel::SQL::ColumnExpr) && (f.op == Sequel::SQL::ColumnMethods::ASC)
242
+ f.l.desc
230
243
  else
231
244
  f.desc
232
245
  end
233
246
  end
234
247
  end
248
+
249
+ # Returns a copy of the dataset with no order.
250
+ def unordered
251
+ clone(:order => nil)
252
+ end
235
253
 
236
254
  # Returns a copy of the dataset with the results grouped by the value of
237
255
  # the given columns
@@ -508,6 +526,12 @@ module Sequel
508
526
  "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
509
527
  else
510
528
  values = values[0] if values.size == 1
529
+
530
+ # if hash or array with keys we need to transform the values
531
+ if @transform && (values.is_a?(Hash) || (values.is_a?(Array) && values.keys))
532
+ values = transform_save(values)
533
+ end
534
+
511
535
  case values
512
536
  when Sequel::Model
513
537
  insert_sql(values.values)
@@ -516,14 +540,12 @@ module Sequel
516
540
  "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
517
541
  elsif values.keys
518
542
  fl = values.keys.map {|f| literal(f.is_a?(String) ? f.to_sym : f)}
519
- vl = @transform ? transform_save(values.values) : values.values
520
- vl.map! {|v| literal(v)}
543
+ vl = values.values.map {|v| literal(v)}
521
544
  "INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
522
545
  else
523
546
  "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)})"
524
547
  end
525
548
  when Hash
526
- values = transform_save(values) if @transform
527
549
  if values.empty?
528
550
  "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
529
551
  else
@@ -1,6 +1,9 @@
1
1
  module Sequel
2
2
  # Represents an error raised in Sequel code.
3
3
  class Error < StandardError
4
+
5
+ # Error raised when an invalid statement is executed.
6
+ class InvalidStatement < Error; end
4
7
 
5
8
  # Rollback is a special error used to rollback a transactions.
6
9
  # A transaction block will catch this error and wont pass further up the stack.
@@ -31,6 +31,7 @@ module Sequel
31
31
  COMMA_SEPARATOR = ', '.freeze
32
32
  UNIQUE = ' UNIQUE'.freeze
33
33
  NOT_NULL = ' NOT NULL'.freeze
34
+ NULL = ' NULL'.freeze
34
35
  UNSIGNED = ' UNSIGNED'.freeze
35
36
  PRIMARY_KEY = ' PRIMARY KEY'.freeze
36
37
 
@@ -61,9 +62,10 @@ module Sequel
61
62
  column[:size] ||= 255 if column[:type] == :varchar
62
63
  elements = column[:size] || column[:elements]
63
64
  sql << "(#{literal(elements)})" if elements
65
+ sql << UNSIGNED if column[:unsigned]
64
66
  sql << UNIQUE if column[:unique]
65
67
  sql << NOT_NULL if column[:null] == false
66
- sql << UNSIGNED if column[:unsigned]
68
+ sql << NULL if column[:null] == true
67
69
  sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
68
70
  sql << PRIMARY_KEY if column[:primary_key]
69
71
  sql << " #{auto_increment_sql}" if column[:auto_increment]
@@ -1,6 +1,9 @@
1
1
  require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
3
 
3
- INFORMIX_DB = Sequel('informix://localhost/mydb')
4
+ unless defined?(INFORMIX_URL); INFORMIX_URL = 'informix://localhost/mydb' ;end
5
+
6
+ INFORMIX_DB = Sequel(INFORMIX_URL)
4
7
  if INFORMIX_DB.table_exists?(:test)
5
8
  INFORMIX_DB.drop_table :test
6
9
  end
@@ -1,7 +1,10 @@
1
1
  require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
3
  require 'logger'
3
4
 
4
- MYSQL_DB = Sequel('mysql://root@localhost/sandbox')
5
+ unless defined?(MYSQL_URL); MYSQL_URL = 'mysql://root@localhost/sandbox' ;end
6
+
7
+ MYSQL_DB = Sequel(MYSQL_URL)
5
8
  MYSQL_DB.drop_table(:items) if MYSQL_DB.table_exists?(:items)
6
9
  MYSQL_DB.drop_table(:test2) if MYSQL_DB.table_exists?(:test2)
7
10
  MYSQL_DB.create_table :items do
@@ -1,6 +1,9 @@
1
1
  require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
3
 
3
- ORACLE_DB = Sequel('oracle://hr:hr@localhost/XE')
4
+ unless defined?(ORACLE_URL); ORACLE_URL = 'oracle://hr:hr@localhost/XE' ;end
5
+
6
+ ORACLE_DB = Sequel(ORACLE_URL)
4
7
 
5
8
  if ORACLE_DB.table_exists?(:items)
6
9
  ORACLE_DB.drop_table :items
@@ -1,6 +1,10 @@
1
1
  require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
3
 
3
- PGSQL_DB = Sequel('postgres://postgres:postgres@localhost:5432/reality_spec')
4
+ unless defined?(POSTGRES_URL); POSTGRES_URL = 'postgres://postgres:postgres@localhost:5432/reality_spec' ;end
5
+
6
+
7
+ PGSQL_DB = Sequel(POSTGRES_URL)
4
8
  PGSQL_DB.drop_table(:test) if PGSQL_DB.table_exists?(:test)
5
9
  PGSQL_DB.drop_table(:test2) if PGSQL_DB.table_exists?(:test2)
6
10
 
@@ -1,6 +1,9 @@
1
1
  require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
3
 
3
- SQLITE_DB = Sequel('sqlite:/')
4
+ unless defined?(SQLITE_URL); SQLITE_URL = 'sqlite:/' ;end
5
+
6
+ SQLITE_DB = Sequel(SQLITE_URL)
4
7
  SQLITE_DB.create_table :items do
5
8
  integer :id, :primary_key => true, :auto_increment => true
6
9
  text :name
@@ -144,6 +147,20 @@ context "An SQLite database" do
144
147
  {:id => 3, :name => 'ghi'}
145
148
  ]
146
149
  end
150
+
151
+ specify "should catch invalid SQL errors and raise them as Error::InvalidStatement" do
152
+ proc {@db.execute 'blah blah'}.should raise_error(
153
+ Sequel::Error::InvalidStatement, "blah blah\r\nnear \"blah\": syntax error")
154
+
155
+ proc {@db.execute_insert 'blah blah'}.should raise_error(
156
+ Sequel::Error::InvalidStatement, "blah blah\r\nnear \"blah\": syntax error")
157
+
158
+ proc {@db.execute_select 'blah blah'}.should raise_error(
159
+ Sequel::Error::InvalidStatement, "blah blah\r\nnear \"blah\": syntax error")
160
+
161
+ proc {@db.single_value 'blah blah'}.should raise_error(
162
+ Sequel::Error::InvalidStatement, "blah blah\r\nnear \"blah\": syntax error")
163
+ end
147
164
  end
148
165
 
149
166
  context "An SQLite dataset" do
data/spec/dataset_spec.rb CHANGED
@@ -784,6 +784,22 @@ context "Dataset#order" do
784
784
  @dataset.order('dada ASC'.lit).sql.should ==
785
785
  'SELECT * FROM test ORDER BY dada ASC'
786
786
  end
787
+
788
+ specify "should accept a nil to remove ordering" do
789
+ @dataset.order(:bah).order(nil).sql.should ==
790
+ 'SELECT * FROM test'
791
+ end
792
+ end
793
+
794
+ context "Dataset#unordered" do
795
+ setup do
796
+ @dataset = Sequel::Dataset.new(nil).from(:test)
797
+ end
798
+
799
+ specify "should remove ordering from the dataset" do
800
+ @dataset.order(:name).unordered.sql.should ==
801
+ 'SELECT * FROM test'
802
+ end
787
803
  end
788
804
 
789
805
  context "Dataset#order_by" do
@@ -810,6 +826,11 @@ context "Dataset#order_by" do
810
826
  @dataset.order_by('dada ASC'.lit).sql.should ==
811
827
  'SELECT * FROM test ORDER BY dada ASC'
812
828
  end
829
+
830
+ specify "should accept a nil to remove ordering" do
831
+ @dataset.order_by(:bah).order_by(nil).sql.should ==
832
+ 'SELECT * FROM test'
833
+ end
813
834
  end
814
835
 
815
836
  context "Dataset#order_more" do
@@ -843,6 +864,11 @@ context "Dataset#reverse_order" do
843
864
  'SELECT * FROM test ORDER BY name'
844
865
  end
845
866
 
867
+ specify "should invert the order for ASC expressions" do
868
+ @dataset.reverse_order(:name.ASC).sql.should ==
869
+ 'SELECT * FROM test ORDER BY name DESC'
870
+ end
871
+
846
872
  specify "should accept multiple arguments" do
847
873
  @dataset.reverse_order(:name, :price.DESC).sql.should ==
848
874
  'SELECT * FROM test ORDER BY name DESC, price'
@@ -855,6 +881,11 @@ context "Dataset#reverse_order" do
855
881
  'SELECT * FROM test ORDER BY clumsy, fool DESC'
856
882
  end
857
883
 
884
+ specify "should return an unordered dataset for a dataset with no order" do
885
+ @dataset.unordered.reverse_order.sql.should ==
886
+ 'SELECT * FROM test'
887
+ end
888
+
858
889
  specify "should have #reverse alias" do
859
890
  @dataset.order(:name).reverse.sql.should ==
860
891
  'SELECT * FROM test ORDER BY name DESC'
data/spec/schema_spec.rb CHANGED
@@ -61,6 +61,14 @@ context "DB#create_table" do
61
61
  @db.sqls.should == ["CREATE TABLE cats (id integer, name text NOT NULL)"]
62
62
  end
63
63
 
64
+ specify "should accept null definition" do
65
+ @db.create_table(:cats) do
66
+ integer :id
67
+ text :name, :null => true
68
+ end
69
+ @db.sqls.should == ["CREATE TABLE cats (id integer, name text NULL)"]
70
+ end
71
+
64
72
  specify "should accept unique definition" do
65
73
  @db.create_table(:cats) do
66
74
  integer :id
@@ -0,0 +1,6 @@
1
+ # connection URL's for running adapter specs
2
+ INFORMIX_URL = 'informix://localhost/mydb'
3
+ MYSQL_URL = 'mysql://root@localhost/sandbox'
4
+ ORACLE_URL = 'oracle://hr:hr@localhost/XE'
5
+ POSTGRES_URL = 'postgres://postgres:postgres@localhost:5432/reality_spec'
6
+ SQLITE_URL = 'sqlite:/'
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require 'rubygems'
2
2
  require File.join(File.dirname(__FILE__), "../lib/sequel_core")
3
3
 
4
+ if File.exists?(File.join(File.dirname(__FILE__), 'spec_config.rb'))
5
+ require File.join(File.dirname(__FILE__), 'spec_config.rb')
6
+ end
7
+
4
8
  class MockDataset < Sequel::Dataset
5
9
  def insert(*args)
6
10
  @db.execute insert_sql(*args)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: "1.3"
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: 2008-02-29 00:00:00 +02:00
12
+ date: 2008-03-08 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -57,7 +57,7 @@ dependencies:
57
57
  - !ruby/object:Gem::Version
58
58
  version: "0"
59
59
  version:
60
- description: Database access for Ruby
60
+ description: The Database Toolkit for Ruby
61
61
  email: ciconia@gmail.com
62
62
  executables:
63
63
  - sequel
@@ -87,11 +87,10 @@ files:
87
87
  - spec/pretty_table_spec.rb
88
88
  - spec/rcov.opts
89
89
  - spec/schema_generator_spec.rb
90
- - spec/schema_generator_spec.rb.rej
91
90
  - spec/schema_spec.rb
92
- - spec/schema_spec.rb.rej
93
91
  - spec/sequelizer_spec.rb
94
92
  - spec/spec.opts
93
+ - spec/spec_config.rb.example
95
94
  - spec/spec_helper.rb
96
95
  - spec/worker_spec.rb
97
96
  - lib/sequel_core
@@ -135,7 +134,7 @@ post_install_message:
135
134
  rdoc_options:
136
135
  - --quiet
137
136
  - --title
138
- - "Sequel: Database access for Ruby"
137
+ - "Sequel: The Database Toolkit for Ruby"
139
138
  - --opname
140
139
  - index.html
141
140
  - --line-numbers
@@ -166,6 +165,6 @@ rubyforge_project: sequel
166
165
  rubygems_version: 1.0.1
167
166
  signing_key:
168
167
  specification_version: 2
169
- summary: Database access for Ruby
168
+ summary: The Database Toolkit for Ruby
170
169
  test_files: []
171
170
 
@@ -1,35 +0,0 @@
1
- ***************
2
- *** 82,87 ****
3
- add_index [:fff, :ggg]
4
- drop_index :hhh
5
- add_full_text_index :blah
6
- end
7
- end
8
-
9
- --- 82,89 ----
10
- add_index [:fff, :ggg]
11
- drop_index :hhh
12
- add_full_text_index :blah
13
- + add_constraint :con1, ':fred > 100'
14
- + drop_constraint :con2
15
- end
16
- end
17
-
18
- ***************
19
- *** 94,100 ****
20
- {:op => :set_column_default, :name => :eee, :default => 1},
21
- {:op => :add_index, :columns => [:fff, :ggg]},
22
- {:op => :drop_index, :columns => [:hhh]},
23
- - {:op => :add_index, :columns => [:blah], :full_text => true}
24
- ]
25
- end
26
- - end--- 96,104 ----
27
- {:op => :set_column_default, :name => :eee, :default => 1},
28
- {:op => :add_index, :columns => [:fff, :ggg]},
29
- {:op => :drop_index, :columns => [:hhh]},
30
- + {:op => :add_index, :columns => [:blah], :full_text => true},
31
- + {:op => :add_constraint, :type => :check, :name => :con1, :check => [':fred > 100']},
32
- + {:op => :drop_constraint, :name => :con2}
33
- ]
34
- end
35
- + end
@@ -1,36 +0,0 @@
1
- ***************
2
- *** 279,281 ****
3
- @db.sqls.should == ['DROP TABLE cats']
4
- end
5
- end
6
- --- 279,308 ----
7
- @db.sqls.should == ['DROP TABLE cats']
8
- end
9
- end
10
- +
11
- + context "DB#alter_table" do
12
- + setup do
13
- + @db = SchemaDummyDatabase.new
14
- + end
15
- +
16
- + specify "should accept add constraint definitions" do
17
- + @db.alter_table(:cats) do
18
- + add_constraint :valid_score, 'score <= 100'
19
- + end
20
- + @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT valid_score CHECK (score <= 100)"]
21
- + @db.sqls.clear
22
- +
23
- + @db.alter_table(:cats) do
24
- + add_constraint(:blah_blah) {:x > 0 && :y < 1}
25
- + end
26
- + @db.sqls.should == ["ALTER TABLE cats ADD CONSTRAINT blah_blah CHECK (((x > 0) AND (y < 1)))"]
27
- + end
28
- +
29
- + specify "should accept drop constraint definitions" do
30
- + @db.alter_table(:cats) do
31
- + drop_constraint :valid_score
32
- + end
33
- + @db.sqls.should == ["ALTER TABLE cats DROP CONSTRAINT valid_score"]
34
- + end
35
- +
36
- + end