sequel 0.3.1 → 0.3.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/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