sequel 0.2.0.2 → 0.2.1

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.
@@ -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