sequel 0.2.0.2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,10 @@ require 'enumerator'
3
3
  module Sequel
4
4
  class Dataset
5
5
  module Convenience
6
+ def empty?
7
+ count == 0
8
+ end
9
+
6
10
  # Returns the first record in the dataset.
7
11
  def single_record(opts = nil)
8
12
  each(opts) {|r| return r}
@@ -186,6 +190,48 @@ module Sequel
186
190
  end
187
191
  end
188
192
  end
193
+
194
+ module QueryBlockCopy
195
+ def each(*args); raise SequelError, "#each cannot be invoked inside a query block."; end
196
+ def insert(*args); raise SequelError, "#insert cannot be invoked inside a query block."; end
197
+ def update(*args); raise SequelError, "#update cannot be invoked inside a query block."; end
198
+ def delete(*args); raise SequelError, "#delete cannot be invoked inside a query block."; end
199
+
200
+ def clone_merge(opts)
201
+ @opts.merge!(opts)
202
+ end
203
+ end
204
+
205
+ # Translates a query block into a dataset. Query blocks can be useful
206
+ # when expressing complex SELECT statements, e.g.:
207
+ #
208
+ # dataset = DB[:items].query do
209
+ # select :x, :y, :z
210
+ # where {:x > 1 && :y > 2}
211
+ # order_by :z.DESC
212
+ # end
213
+ #
214
+ def query(&block)
215
+ copy = clone_merge({})
216
+ copy.extend(QueryBlockCopy)
217
+ copy.instance_eval(&block)
218
+ clone_merge(copy.opts)
219
+ end
220
+
221
+ MUTATION_RE = /^(.+)!$/.freeze
222
+
223
+ def method_missing(m, *args, &block)
224
+ if m.to_s =~ MUTATION_RE
225
+ m = $1.to_sym
226
+ super unless respond_to?(m)
227
+ copy = send(m, *args, &block)
228
+ super if copy.class != self.class
229
+ @opts.merge!(copy.opts)
230
+ self
231
+ else
232
+ super
233
+ end
234
+ end
189
235
  end
190
236
  end
191
237
  end
@@ -108,7 +108,11 @@ class Sequel::Dataset
108
108
  when :>, :<, :>=, :<=
109
109
  l = eval_expr(e[1], b)
110
110
  r = eval_expr(e[3][1], b)
111
- "(#{literal(l)} #{op} #{literal(r)})"
111
+ if (Symbol === l) || (Sequel::LiteralString === l) || (Symbol === r) || (Sequel::LiteralString === r)
112
+ "(#{literal(l)} #{op} #{literal(r)})"
113
+ else
114
+ ext_expr(e, b)
115
+ end
112
116
  when :==
113
117
  l = eval_expr(e[1], b)
114
118
  r = eval_expr(e[3][1], b)
@@ -120,7 +124,11 @@ class Sequel::Dataset
120
124
  when :+, :-, :*, :/, :%
121
125
  l = eval_expr(e[1], b)
122
126
  r = eval_expr(e[3][1], b)
123
- "(#{literal(l)} #{op} #{literal(r)})".lit
127
+ if (Symbol === l) || (Sequel::LiteralString === l) || (Symbol === r) || (Sequel::LiteralString === r)
128
+ "(#{literal(l)} #{op} #{literal(r)})".lit
129
+ else
130
+ ext_expr(e, b)
131
+ end
124
132
  when :in, :in?
125
133
  # in/in? operators are supported using two forms:
126
134
  # :x.in([1, 2, 3])
@@ -146,12 +154,32 @@ class Sequel::Dataset
146
154
  end
147
155
  end
148
156
 
157
+ def fcall_expr(e, b)
158
+ ext_expr(e, b)
159
+ end
160
+
161
+ def vcall_expr(e, b)
162
+ eval(e[1].to_s, b)
163
+ end
164
+
165
+ def iter_expr(e, b)
166
+ if e[1] == [:fcall, :proc]
167
+ eval_expr(e[3], b) # inline proc
168
+ else
169
+ ext_expr(e, b) # method call with inline proc
170
+ end
171
+ end
172
+
149
173
  # Evaluates a parse-tree into an SQL expression.
150
174
  def eval_expr(e, b)
151
175
  case e[0]
152
176
  when :call # method call
153
177
  call_expr(e, b)
154
- when :ivar, :cvar, :dvar, :vcall, :const, :gvar # local ref
178
+ when :fcall
179
+ fcall_expr(e, b)
180
+ when :vcall
181
+ vcall_expr(e, b)
182
+ when :ivar, :cvar, :dvar, :const, :gvar # local ref
155
183
  eval(e[1].to_s, b)
156
184
  when :nth_ref:
157
185
  eval("$#{e[1]}", b)
@@ -182,16 +210,14 @@ class Sequel::Dataset
182
210
  r = eval_expr(e[1], b)
183
211
  compare_expr(l, r)
184
212
  when :iter
185
- if e[1] == [:fcall, :proc]
186
- eval_expr(e[3], b) # inline proc
187
- else
188
- ext_expr(e, b) # method call with inline proc
189
- end
213
+ iter_expr(e, b)
190
214
  when :dasgn, :dasgn_curr
191
215
  # assignment
192
216
  l = e[1]
193
217
  r = eval_expr(e[2], b)
194
218
  raise SequelError, "Invalid expression #{l} = #{r}. Did you mean :#{l} == #{r}?"
219
+ when :if, :dstr
220
+ ext_expr(e, b)
195
221
  else
196
222
  raise SequelError, "Invalid expression tree: #{e.inspect}"
197
223
  end
@@ -145,6 +145,8 @@ module Sequel
145
145
  def order(*order)
146
146
  clone_merge(:order => order)
147
147
  end
148
+
149
+ alias_method :order_by, :order
148
150
 
149
151
  # Returns a copy of the dataset with the order reversed. If no order is
150
152
  # given, the existing order is inverted.
@@ -176,6 +178,8 @@ module Sequel
176
178
  def group(*fields)
177
179
  clone_merge(:group => fields)
178
180
  end
181
+
182
+ alias_method :group_by, :group
179
183
 
180
184
  # Returns a copy of the dataset with the given conditions imposed upon it.
181
185
  # If the query has been grouped, then the conditions are imposed in the
@@ -424,15 +428,14 @@ module Sequel
424
428
  values = values[0] if values.size == 1
425
429
  case values
426
430
  when Hash
427
- field_list = []
428
- value_list = []
429
- values.each do |k, v|
430
- field_list << field_name(k)
431
- value_list << literal(v)
431
+ values = transform_save(values) if @transform
432
+ if values.empty?
433
+ "INSERT INTO #{@opts[:from]} DEFAULT VALUES;"
434
+ else
435
+ fl, vl = [], []
436
+ values.each {|k, v| fl << field_name(k); vl << literal(v)}
437
+ "INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)});"
432
438
  end
433
- fl = field_list.join(COMMA_SEPARATOR)
434
- vl = value_list.join(COMMA_SEPARATOR)
435
- "INSERT INTO #{@opts[:from]} (#{fl}) VALUES (#{vl});"
436
439
  when Dataset
437
440
  "INSERT INTO #{@opts[:from]} #{literal(values)}"
438
441
  else
@@ -454,6 +457,7 @@ module Sequel
454
457
  raise SequelError, "Can't update a joined dataset"
455
458
  end
456
459
 
460
+ values = transform_save(values) if @transform
457
461
  set_list = values.map {|k, v| "#{field_name(k)} = #{literal(v)}"}.
458
462
  join(COMMA_SEPARATOR)
459
463
  sql = "UPDATE #{@opts[:from]} SET #{set_list}"
data/lib/sequel/model.rb CHANGED
@@ -1,161 +1,27 @@
1
1
  module Sequel
2
2
  class Model
3
- @@db = nil
4
-
5
- def self.db
6
- @db ||= ((superclass != Object) && (superclass.db)) || nil
7
- end
8
- def self.db=(db); @db = db; end
9
- def self.database_opened(db)
10
- @db = db if self == Model && !@db
11
- end
12
-
13
- def self.table_name
14
- @table_name ||= ((superclass != Model) && (superclass.table_name)) || nil
15
- end
16
- def self.set_table_name(t); @table_name = t; end
3
+ end
4
+ end
17
5
 
18
- def self.dataset
19
- return @dataset if @dataset
20
- if !table_name
21
- raise SequelError, "Table name not specified for #{self}."
22
- elsif !db
23
- raise SequelError, "Database not specified for #{self}."
24
- end
25
- @dataset = db[table_name]
26
- @dataset.set_model(self)
27
- @dataset
28
- end
6
+ require File.join(File.dirname(__FILE__), 'model/base')
7
+ require File.join(File.dirname(__FILE__), 'model/hooks')
8
+ require File.join(File.dirname(__FILE__), 'model/record')
9
+ require File.join(File.dirname(__FILE__), 'model/schema')
10
+ require File.join(File.dirname(__FILE__), 'model/relations')
29
11
 
30
- def self.set_dataset(ds)
31
- @db = ds.db
32
- @dataset = ds
33
- @dataset.set_model(self)
34
- end
35
-
36
- def self.cache_by(column, expiration)
37
- @cache_column = column
38
-
39
- prefix = "#{name}.#{column}."
40
- define_method(:cache_key) do
41
- prefix + @values[column].to_s
42
- end
43
-
44
- define_method("find_by_#{column}".to_sym) do |arg|
45
- key = cache_key
46
- rec = CACHE[key]
47
- if !rec
48
- rec = find(column => arg)
49
- CACHE.set(key, rec, expiration)
50
- end
51
- rec
52
- end
53
-
54
- alias_method :destroy, :destroy_and_invalidate_cache
55
- alias_method :set, :set_and_update_cache
56
- end
57
-
58
- def self.cache_column
59
- @cache_column
60
- end
61
-
62
- def self.primary_key; @primary_key ||= :id; end
63
- def self.set_primary_key(k); @primary_key = k; end
64
-
65
- def self.set_schema(name = nil, &block)
66
- name ? set_table_name(name) : name = table_name
67
- @schema = Schema::Generator.new(db, name, &block)
68
- if @schema.primary_key_name
69
- set_primary_key @schema.primary_key_name
70
- end
71
- end
72
- def self.schema
73
- @schema || ((superclass != Model) && (superclass.schema))
74
- end
75
-
76
- def self.table_exists?
77
- db.table_exists?(table_name)
78
- end
79
-
80
- def self.create_table
81
- db.create_table_sql_list(*schema.create_info).each {|s| db << s}
82
- end
83
-
84
- def self.drop_table
85
- db.execute db.drop_table_sql(table_name)
86
- end
87
-
88
- def self.recreate_table
89
- drop_table if table_exists?
90
- create_table
91
- end
92
-
12
+ module Sequel
13
+ class Model
93
14
  def self.subset(name, *args, &block)
94
15
  meta_def(name) {filter(*args, &block)}
95
16
  end
96
17
 
97
- ONE_TO_ONE_PROC = "proc {i = @values[:%s]; %s[i] if i}".freeze
98
- ID_POSTFIX = "_id".freeze
99
- FROM_DATASET = "db[%s]".freeze
100
-
101
- def self.one_to_one(name, opts)
102
- klass = opts[:class] ? opts[:class] : (FROM_DATASET % name.inspect)
103
- key = opts[:key] || (name.to_s + ID_POSTFIX)
104
- define_method name, &eval(ONE_TO_ONE_PROC % [key, klass])
105
- end
106
-
107
- ONE_TO_MANY_PROC = "proc {%s.filter(:%s => @pkey)}".freeze
108
- ONE_TO_MANY_ORDER_PROC = "proc {%s.filter(:%s => @pkey).order(%s)}".freeze
109
- def self.one_to_many(name, opts)
110
- klass = opts[:class] ? opts[:class] :
111
- (FROM_DATASET % (opts[:table] || name.inspect))
112
- key = opts[:on]
113
- order = opts[:order]
114
- define_method name, &eval(
115
- (order ? ONE_TO_MANY_ORDER_PROC : ONE_TO_MANY_PROC) %
116
- [klass, key, order.inspect]
117
- )
118
- end
119
-
120
- def self.get_hooks(key)
121
- @hooks ||= {}
122
- @hooks[key] ||= []
123
- end
124
-
125
- def self.has_hooks?(key)
126
- !get_hooks(key).empty?
127
- end
128
-
129
- def run_hooks(key)
130
- self.class.get_hooks(key).each {|h| instance_eval(&h)}
131
- end
132
-
133
- def self.before_save(&block)
134
- get_hooks(:before_save).unshift(block)
135
- end
136
-
137
- def self.before_create(&block)
138
- get_hooks(:before_create).unshift(block)
139
- end
140
-
141
- def self.before_destroy(&block)
142
- get_hooks(:before_destroy).unshift(block)
143
- end
144
-
145
- def self.after_save(&block)
146
- get_hooks(:after_save) << block
147
- end
148
-
149
- def self.after_create(&block)
150
- get_hooks(:after_create) << block
151
- end
152
-
153
- def self.after_destroy(&block)
154
- get_hooks(:after_destroy) << block
18
+ def primary_key_hash(value)
19
+ # stock implementation
20
+ {:id => value}
155
21
  end
156
22
 
157
23
  def self.find(cond)
158
- dataset[cond.is_a?(Hash) ? cond : {primary_key => cond}]
24
+ dataset[cond.is_a?(Hash) ? cond : primary_key_hash(cond)]
159
25
  end
160
26
 
161
27
  def self.find_or_create(cond)
@@ -166,56 +32,11 @@ module Sequel
166
32
 
167
33
  ############################################################################
168
34
 
169
- attr_reader :values, :pkey
170
-
171
- def model
172
- self.class
173
- end
174
-
175
- def primary_key
176
- self.class.primary_key
177
- end
178
-
179
- def initialize(values)
180
- @values = values
181
- @pkey = values[self.class.primary_key]
182
- end
183
-
184
- def exists?
185
- model.filter(primary_key => @pkey).count == 1
186
- end
187
-
188
- def refresh
189
- @values = self.class.dataset.naked[primary_key => @pkey] ||
190
- (raise SequelError, "Record not found")
191
- self
192
- end
193
-
194
35
  def self.destroy_all
195
36
  has_hooks?(:before_destroy) ? dataset.destroy : dataset.delete
196
37
  end
197
38
  def self.delete_all; dataset.delete; end
198
39
 
199
- def self.create(values = nil)
200
- db.transaction do
201
- obj = new(values || {})
202
- obj.save
203
- obj
204
- end
205
- end
206
-
207
- def destroy
208
- db.transaction do
209
- run_hooks(:before_destroy)
210
- delete
211
- run_hooks(:after_destroy)
212
- end
213
- end
214
-
215
- def delete
216
- model.dataset.filter(primary_key => @pkey).delete
217
- end
218
-
219
40
  FIND_BY_REGEXP = /^find_by_(.*)/.freeze
220
41
  FILTER_BY_REGEXP = /^filter_by_(.*)/.freeze
221
42
  ALL_BY_REGEXP = /^all_by_(.*)/.freeze
@@ -244,63 +65,18 @@ module Sequel
244
65
  dataset.join(*args).select(table_name.to_sym.ALL)
245
66
  end
246
67
 
247
- def db; self.class.db; end
248
-
249
68
  def [](field); @values[field]; end
250
69
 
251
70
  def []=(field, value); @values[field] = value; end
252
71
 
253
- WRITE_ATTR_REGEXP = /(.*)=$/.freeze
254
-
255
- def method_missing(m, value = nil)
256
- write = m.to_s =~ WRITE_ATTR_REGEXP
257
- att = write ? $1.to_sym : m
258
- # raise unless the att is recognized or this is a new unaved record
259
- super unless @values.include?(att) || !@pkey
260
-
261
- write ? (self[att] = value) : self[att]
262
- end
263
-
264
72
  def each(&block); @values.each(&block); end
265
73
  def keys; @values.keys; end
266
74
 
267
75
  def id; @values[:id]; end
268
76
 
269
- def save
270
- run_hooks(:before_save)
271
- if @pkey
272
- run_hooks(:before_update)
273
- model.dataset.filter(primary_key => @pkey).update(@values)
274
- run_hooks(:after_update)
275
- else
276
- run_hooks(:before_create)
277
- @pkey = model.dataset.insert(@values)
278
- refresh
279
- run_hooks(:after_create)
280
- end
281
- run_hooks(:after_save)
282
- end
283
-
284
77
  def ==(obj)
285
78
  (obj.class == model) && (obj.values == @values)
286
79
  end
287
-
288
- def set(values)
289
- model.dataset.filter(primary_key => @pkey).update(values)
290
- @values.merge!(values)
291
- end
292
80
  end
293
81
 
294
- def self.Model(table)
295
- @models ||= {}
296
- @models[table] ||= Class.new(Sequel::Model) do
297
- meta_def(:inherited) do |c|
298
- if table.is_a?(Dataset)
299
- c.set_dataset(table)
300
- else
301
- c.set_table_name(table)
302
- end
303
- end
304
- end
305
- end
306
82
  end