sequel 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sequel/dbi.rb CHANGED
@@ -65,7 +65,7 @@ module Sequel
65
65
  s = @db.execute sql
66
66
  begin
67
67
  @columns = s.column_names.map {|c| c.to_sym}
68
- s.fetch {|r| r.fields = @columns; yield r}
68
+ s.fetch {|r| r.keys = @columns; yield r}
69
69
  ensure
70
70
  s.finish rescue nil
71
71
  end
data/lib/sequel/error.rb CHANGED
@@ -1,10 +1,21 @@
1
+ # Represents an error raised in Sequel code.
1
2
  class SequelError < StandardError
2
3
  end
3
4
 
5
+ # SequelRollbackError is a special error used to rollback a transactions.
6
+ # A transaction block will catch this error and wont pass further up the stack.
4
7
  class SequelRollbackError < StandardError
5
8
  end
6
9
 
10
+ # Object extensions
7
11
  class Object
12
+ # Cancels the current transaction without an error:
13
+ #
14
+ # DB.tranaction do
15
+ # ...
16
+ # rollback! if failed_to_contact_client
17
+ # ...
18
+ # end
8
19
  def rollback!
9
20
  raise SequelRollbackError
10
21
  end
data/lib/sequel/model.rb CHANGED
@@ -79,12 +79,21 @@ module Sequel
79
79
  #
80
80
  # post = Post.create(:title => 'hello world')
81
81
  #
82
- # You can also create a new instance and save it:
82
+ # Another way is to construct a new instance and save it:
83
83
  #
84
84
  # post = Post.new
85
85
  # post.title = 'hello world'
86
86
  # post.save
87
87
  #
88
+ # You can also supply a block to Model.new and Model.create:
89
+ #
90
+ # post = Post.create {|p| p.title = 'hello world'}
91
+ #
92
+ # post = Post.new do |p|
93
+ # p.title = 'hello world'
94
+ # p.save
95
+ # end
96
+ #
88
97
  # === Hooks
89
98
  #
90
99
  # You can execute custom code when creating, updating, or deleting records by using hooks. The before_create and after_create hooks wrap record creation. The before_update and after_update wrap record updating. The before_save and after_save wrap record creation and updating. The before_destroy and after_destroy wrap destruction.
@@ -127,11 +136,14 @@ module Sequel
127
136
  # def posts; Post.filter(:author_id => pk); end
128
137
  # end
129
138
  #
130
- # Sequel also provides two macros to assist with common types of associations. The one_to_one macro is roughly equivalent to ActiveRecord's belongs_to macro:
139
+ # Sequel also provides two macros to assist with common types of associations. The one_to_one macro is roughly equivalent to ActiveRecord?'s belongs_to macro. It defines both getter and setter methods for the association:
131
140
  #
132
141
  # class Post < Sequel::Model(:posts)
133
142
  # one_to_one :author, :from => Author
134
143
  # end
144
+ #
145
+ # post = Post.create(:name => 'hi!')
146
+ # post.author = Author[:name => 'Sharon']
135
147
  #
136
148
  # The one_to_many macro is roughly equivalent to ActiveRecord's has_many macro:
137
149
  #
@@ -270,24 +282,18 @@ module Sequel
270
282
  def self.delete_all
271
283
  dataset.delete
272
284
  end
273
-
274
- FIND_BY_REGEXP = /^find_by_(.*)/.freeze
275
- FILTER_BY_REGEXP = /^filter_by_(.*)/.freeze
276
- ALL_BY_REGEXP = /^all_by_(.*)/.freeze
277
-
285
+
286
+ def self.is_dataset_magic_method?(m)
287
+ method_name = m.to_s
288
+ Sequel::Dataset::MAGIC_METHODS.each_key do |r|
289
+ return true if method_name =~ r
290
+ end
291
+ false
292
+ end
293
+
278
294
  def self.method_missing(m, *args, &block) #:nodoc:
279
295
  Thread.exclusive do
280
- method_name = m.to_s
281
- if method_name =~ FIND_BY_REGEXP
282
- c = $1.to_sym
283
- meta_def(method_name) {|arg| find(c => arg)}
284
- elsif method_name =~ FILTER_BY_REGEXP
285
- c = $1.to_sym
286
- meta_def(method_name) {|arg| filter(c => arg)}
287
- elsif method_name =~ ALL_BY_REGEXP
288
- c = $1.to_sym
289
- meta_def(method_name) {|arg| filter(c => arg).all}
290
- elsif dataset.respond_to?(m)
296
+ if dataset.respond_to?(m) || is_dataset_magic_method?(m)
291
297
  instance_eval("def #{m}(*args, &block); dataset.#{m}(*args, &block); end")
292
298
  end
293
299
  end
@@ -301,12 +307,12 @@ module Sequel
301
307
  end
302
308
 
303
309
  # Returns value of attribute.
304
- def [](field)
305
- @values[field]
310
+ def [](column)
311
+ @values[column]
306
312
  end
307
313
  # Sets value of attribute.
308
- def []=(field, value)
309
- @values[field] = value
314
+ def []=(column, value)
315
+ @values[column] = value
310
316
  end
311
317
 
312
318
  # Enumerates through all attributes.
@@ -133,7 +133,7 @@ module Sequel
133
133
  #
134
134
  # This method guesses whether the record exists when
135
135
  # <tt>new_record</tt> is set to false.
136
- def initialize(values = {}, new_record = false)
136
+ def initialize(values = {}, new_record = false, &block)
137
137
  @values = values
138
138
 
139
139
  @new = new_record
@@ -144,6 +144,8 @@ module Sequel
144
144
  # we regard this instance as new.
145
145
  @new = (k == nil) || (!(Array === k) && !@values[k])
146
146
  end
147
+
148
+ block[self] if block
147
149
  end
148
150
 
149
151
  # Returns true if the current instance represents a new record.
data/lib/sequel/mysql.rb CHANGED
@@ -61,7 +61,7 @@ class Mysql::Result
61
61
  row[i] = v.send(t)
62
62
  end
63
63
  end
64
- row.fields = c
64
+ row.keys = c
65
65
  yield row
66
66
  end
67
67
  end
@@ -173,21 +173,21 @@ module Sequel
173
173
 
174
174
  class Dataset < Sequel::Dataset
175
175
  UNQUOTABLE_FIELD_RE = /^(`(.+)`)|\*$/.freeze
176
- def quote_field(f)
176
+ def quote_column(f)
177
177
  (f.nil? || f.empty? || f =~ UNQUOTABLE_FIELD_RE) ? f : "`#{f}`"
178
178
  end
179
179
 
180
180
  FIELD_EXPR_RE = /^([^\(]+\()?([^\.]+\.)?([^\s\)]+)?(\))?(\sAS\s(.+))?$/i.freeze
181
181
  FIELD_ORDER_RE = /^(.*) (DESC|ASC)$/i.freeze
182
- def quoted_field_name(name)
182
+ def quoted_column_name(name)
183
183
  case name
184
184
  when FIELD_EXPR_RE:
185
185
  $6 ? \
186
- "#{$1}#{$2}#{quote_field($3)}#{$4} AS #{quote_field($6)}" : \
187
- "#{$1}#{$2}#{quote_field($3)}#{$4}"
188
- when FIELD_ORDER_RE: "#{quote_field($1)} #{$2}"
186
+ "#{$1}#{$2}#{quote_column($3)}#{$4} AS #{quote_column($6)}" : \
187
+ "#{$1}#{$2}#{quote_column($3)}#{$4}"
188
+ when FIELD_ORDER_RE: "#{quote_column($1)} #{$2}"
189
189
  else
190
- quote_field(name)
190
+ quote_column(name)
191
191
  end
192
192
  end
193
193
 
@@ -196,7 +196,7 @@ module Sequel
196
196
 
197
197
  def literal(v)
198
198
  case v
199
- when LiteralString: quoted_field_name(v)
199
+ when LiteralString: quoted_column_name(v)
200
200
  when String: "'#{v.gsub(/'|\\/, '\&\&')}'"
201
201
  when true: TRUE
202
202
  when false: FALSE
@@ -223,7 +223,7 @@ module Sequel
223
223
  opts = opts ? @opts.merge(opts) : @opts
224
224
 
225
225
  if order = opts[:order]
226
- sql << " ORDER BY #{field_list(order)}"
226
+ sql << " ORDER BY #{column_list(order)}"
227
227
  end
228
228
 
229
229
  if limit = opts[:limit]
data/lib/sequel/odbc.rb CHANGED
@@ -105,7 +105,7 @@ module Sequel
105
105
  end
106
106
 
107
107
  def array_tuples_make_row(row)
108
- row.fields = @columns
108
+ row.keys = @columns
109
109
  row.each_with_index do |v, idx|
110
110
  # When fetching a result set, the Ruby ODBC driver converts all ODBC
111
111
  # SQL types to an equivalent Ruby type; with the exception of
data/lib/sequel/oracle.rb CHANGED
@@ -14,7 +14,7 @@ module Sequel
14
14
  # def auto_increment_sql
15
15
  # AUTO_INCREMENT
16
16
  # end
17
- #
17
+
18
18
  def connect
19
19
  if @opts[:database]
20
20
  dbname = @opts[:host] ? \
@@ -38,7 +38,7 @@ module Sequel
38
38
 
39
39
  def execute(sql)
40
40
  @logger.info(sql) if @logger
41
- @pool.hold {|conn| conn.Execute(sql)}
41
+ @pool.hold {|conn| conn.exec(sql)}
42
42
  end
43
43
 
44
44
  alias_method :do, :execute
@@ -419,31 +419,30 @@ module Sequel
419
419
  @@converters = {}
420
420
 
421
421
  def row_converter(result)
422
- fields = []; translators = []
422
+ @columns = []; translators = []
423
423
  result.fields.each_with_index do |f, idx|
424
- fields << f.to_sym
424
+ @columns << f.to_sym
425
425
  translators << PG_TYPES[result.type(idx)]
426
426
  end
427
- @columns = fields
428
427
 
429
428
  # create result signature and memoize the converter
430
- sig = [fields, translators].hash
429
+ sig = [@columns, translators].hash
431
430
  @@converters_mutex.synchronize do
432
- @@converters[sig] ||= compile_converter(fields, translators)
431
+ @@converters[sig] ||= compile_converter(@columns, translators)
433
432
  end
434
433
  end
435
434
 
436
- def compile_converter(fields, translators)
437
- used_fields = []
435
+ def compile_converter(columns, translators)
436
+ used_columns = []
438
437
  kvs = []
439
- fields.each_with_index do |field, idx|
440
- next if used_fields.include?(field)
441
- used_fields << field
438
+ columns.each_with_index do |column, idx|
439
+ next if used_columns.include?(column)
440
+ used_columns << column
442
441
 
443
442
  if !AUTO_TRANSLATE and translator = translators[idx]
444
- kvs << ":\"#{field}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
443
+ kvs << ":\"#{column}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
445
444
  else
446
- kvs << ":\"#{field}\" => r[#{idx}]"
445
+ kvs << ":\"#{column}\" => r[#{idx}]"
447
446
  end
448
447
  end
449
448
  eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
@@ -465,28 +464,27 @@ module Sequel
465
464
  @@array_tuples_converters = {}
466
465
 
467
466
  def array_tuples_row_converter(result)
468
- fields = []; translators = []
467
+ @columns = []; translators = []
469
468
  result.fields.each_with_index do |f, idx|
470
- fields << f.to_sym
469
+ @columns << f.to_sym
471
470
  translators << PG_TYPES[result.type(idx)]
472
471
  end
473
- @columns = fields
474
472
 
475
473
  # create result signature and memoize the converter
476
- sig = [fields, translators].hash
474
+ sig = [@columns, translators].hash
477
475
  @@array_tuples_converters_mutex.synchronize do
478
- @@array_tuples_converters[sig] ||= array_tuples_compile_converter(fields, translators)
476
+ @@array_tuples_converters[sig] ||= array_tuples_compile_converter(@columns, translators)
479
477
  end
480
478
  end
481
479
 
482
- def array_tuples_compile_converter(fields, translators)
480
+ def array_tuples_compile_converter(columns, translators)
483
481
  tr = []
484
- fields.each_with_index do |field, idx|
482
+ columns.each_with_index do |column, idx|
485
483
  if !AUTO_TRANSLATE and t = translators[idx]
486
484
  tr << "if (v = r[#{idx}]); r[#{idx}] = v.#{t}; end"
487
485
  end
488
486
  end
489
- eval("lambda {|r| r.fields = fields; #{tr.join(';')}; r}")
487
+ eval("lambda {|r| r.keys = columns; #{tr.join(';')}; r}")
490
488
  end
491
489
  end
492
490
  end
@@ -10,8 +10,8 @@ module Sequel
10
10
  def self.records_columns(records)
11
11
  columns = []
12
12
  records.each do |r|
13
- if Array === r && (f = r.fields)
14
- return r.fields
13
+ if Array === r && (k = r.keys)
14
+ return k
15
15
  elsif Hash === r
16
16
  r.keys.each {|k| columns << k unless columns.include?(k)}
17
17
  end
@@ -60,12 +60,12 @@ module Sequel
60
60
  end
61
61
 
62
62
  def index_definition_sql(table_name, index)
63
- fields = index[:columns].join(COMMA_SEPARATOR)
63
+ columns = index[:columns].join(COMMA_SEPARATOR)
64
64
  index_name = index[:name] || default_index_name(table_name, index[:columns])
65
65
  if index[:unique]
66
- "CREATE UNIQUE INDEX #{index_name} ON #{table_name} (#{fields});"
66
+ "CREATE UNIQUE INDEX #{index_name} ON #{table_name} (#{columns});"
67
67
  else
68
- "CREATE INDEX #{index_name} ON #{table_name} (#{fields});"
68
+ "CREATE INDEX #{index_name} ON #{table_name} (#{columns});"
69
69
  end
70
70
  end
71
71
 
data/lib/sequel/sqlite.rb CHANGED
@@ -19,6 +19,10 @@ module Sequel
19
19
  end
20
20
  db = ::SQLite3::Database.new(@opts[:database])
21
21
  db.type_translation = true
22
+ # fix for timestamp translation
23
+ db.translator.add_translator("timestamp") do |t, v|
24
+ v =~ /^\d+$/ ? Time.at(v.to_i) : Time.parse(v)
25
+ end
22
26
  db
23
27
  end
24
28
 
@@ -78,7 +78,7 @@ context "A MySQL dataset" do
78
78
  proc {@d.literal(false)}.should_not raise_error
79
79
  end
80
80
 
81
- specify "should quote fields using back-ticks" do
81
+ specify "should quote columns using back-ticks" do
82
82
  @d.select(:name).sql.should == \
83
83
  'SELECT `name` FROM items'
84
84
 
@@ -6,6 +6,7 @@ SQLITE_DB.create_table :items do
6
6
  text :name
7
7
  float :value
8
8
  end
9
+ SQLITE_DB.create_table(:time) {timestamp :t}
9
10
 
10
11
  context "An SQLite database" do
11
12
  setup do
@@ -120,6 +121,13 @@ context "An SQLite database" do
120
121
  @db.disconnect
121
122
  @db.pool.size.should == 0
122
123
  end
124
+
125
+ specify "should support timestamps" do
126
+ t1 = Time.at(Time.now.to_i) #normalize time
127
+
128
+ SQLITE_DB[:time] << {:t => t1}
129
+ SQLITE_DB[:time].first[:t].should == t1
130
+ end
123
131
  end
124
132
 
125
133
  context "An SQLite dataset" do
@@ -127,47 +127,47 @@ context "Symbol#ALL" do
127
127
  end
128
128
  end
129
129
 
130
- context "Symbol#to_field_name" do
130
+ context "Symbol#to_column_name" do
131
131
  specify "should convert qualified symbol notation into dot notation" do
132
- :abc__def.to_field_name.should == 'abc.def'
132
+ :abc__def.to_column_name.should == 'abc.def'
133
133
  end
134
134
 
135
135
  specify "should convert AS symbol notation into SQL AS notation" do
136
- :xyz___x.to_field_name.should == 'xyz AS x'
137
- :abc__def___x.to_field_name.should == 'abc.def AS x'
136
+ :xyz___x.to_column_name.should == 'xyz AS x'
137
+ :abc__def___x.to_column_name.should == 'abc.def AS x'
138
138
  end
139
139
 
140
140
  specify "should support names with digits" do
141
- :abc2.to_field_name.should == 'abc2'
142
- :xx__yy3.to_field_name.should == 'xx.yy3'
143
- :ab34__temp3_4ax.to_field_name.should == 'ab34.temp3_4ax'
144
- :x1___y2.to_field_name.should == 'x1 AS y2'
145
- :abc2__def3___ggg4.to_field_name.should == 'abc2.def3 AS ggg4'
141
+ :abc2.to_column_name.should == 'abc2'
142
+ :xx__yy3.to_column_name.should == 'xx.yy3'
143
+ :ab34__temp3_4ax.to_column_name.should == 'ab34.temp3_4ax'
144
+ :x1___y2.to_column_name.should == 'x1 AS y2'
145
+ :abc2__def3___ggg4.to_column_name.should == 'abc2.def3 AS ggg4'
146
146
  end
147
147
 
148
148
  specify "should support upper case and lower case" do
149
- :ABC.to_field_name.should == 'ABC'
150
- :Zvashtoy__aBcD.to_field_name.should == 'Zvashtoy.aBcD'
149
+ :ABC.to_column_name.should == 'ABC'
150
+ :Zvashtoy__aBcD.to_column_name.should == 'Zvashtoy.aBcD'
151
151
 
152
152
  end
153
153
  end
154
154
 
155
- context "FieldCompositionMethods#field_title" do
156
- specify "should return the field name for non aliased fields" do
157
- :xyz.field_title.should == 'xyz'
158
- :abc__xyz.field_title.should == 'xyz'
155
+ context "FieldCompositionMethods#column_title" do
156
+ specify "should return the column name for non aliased columns" do
157
+ :xyz.column_title.should == 'xyz'
158
+ :abc__xyz.column_title.should == 'xyz'
159
159
 
160
- 'abc'.field_title.should == 'abc'
161
- 'abc.def'.field_title.should == 'def'
160
+ 'abc'.column_title.should == 'abc'
161
+ 'abc.def'.column_title.should == 'def'
162
162
  end
163
163
 
164
- specify "should return the field alias for aliased fields" do
165
- :xyz___x.field_title.should == 'x'
166
- :abc__xyz___y.field_title.should == 'y'
164
+ specify "should return the column alias for aliased columns" do
165
+ :xyz___x.column_title.should == 'x'
166
+ :abc__xyz___y.column_title.should == 'y'
167
167
 
168
- 'abc AS x'.field_title.should == 'x'
169
- 'abc as y'.field_title.should == 'y'
170
- 'abc.def AS d'.field_title.should == 'd'
168
+ 'abc AS x'.column_title.should == 'x'
169
+ 'abc as y'.column_title.should == 'y'
170
+ 'abc.def AS d'.column_title.should == 'd'
171
171
  end
172
172
  end
173
173