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.
data/CHANGELOG CHANGED
@@ -1,3 +1,67 @@
1
+ === 0.2.1 (2007-09-24)
2
+
3
+ * Added default implementation of Model.primary_key_hash.
4
+
5
+ * Fixed Sequel::Model() to set dataset for inherited classes.
6
+
7
+ * Rewrote Model.serialize to use Dataset#transform.
8
+
9
+ * Implemented Dataset#transform.
10
+
11
+ * Added gem spec for Windows (without ParseTree dependency).
12
+
13
+ * Added support for dynamic strings in Sequelizer (#49).
14
+
15
+ * Query branch merged into trunk.
16
+
17
+ * Implemented self-changing methods.
18
+
19
+ * Add support for ternary operator to Sequelizer.
20
+
21
+ * Fixed sequelizer to evaluate expressions if they don't involve symbols or literal strings.
22
+
23
+ * Added protection against using #each, #delete, #insert, #update inside query blocks.
24
+
25
+ * Improved Model#method_missing to deal with invalid attributes.
26
+
27
+ * Implemented Dataset#query.
28
+
29
+ * Added Dataset#group_by as alias for Dataset#group.
30
+
31
+ * Added Dataset#order_by as alias for Dataset#order.
32
+
33
+ * More model refactoring. Added support for composite keys.
34
+
35
+ * Added Dataset#empty? method (#46).
36
+
37
+ * Fixed Symbol#to_field_name to support names with numbers and upper-case characters (#45).
38
+
39
+ * Added install_no_doc rake task.
40
+
41
+ * Partial refactoring of model code.
42
+
43
+ * Refactored dataset-model association and added Dataset#set_row_filter method.
44
+
45
+ * Added support for case-sensitive regexps to mysql adapter.
46
+
47
+ * Changed mysql adapter to support encoding option as well.
48
+
49
+ * Added charset/encoding option to postgres adapter.
50
+
51
+ * Implemented Model.serialize (thanks Aman Gupta.)
52
+
53
+ * Changed Model.create to INSERT DEFAULT VALUES instead of (id) VALUES (null) (brings back #41.)
54
+
55
+ * Fixed Model.new to work without arguments.
56
+
57
+ * Added Model.no_primary_key method to allow models without primary keys.
58
+
59
+ * Added Model#this method (#42 thanks Duane Johnson).
60
+
61
+ * Fixed Dataset#insert_sql to use DEFAULT VALUES clause if argument is an empty hash.
62
+
63
+ * Fixed Model.create to work correctly when no argument is passed (#41).
64
+
1
65
  === 0.2.0.2 (2007-09-07)
2
66
 
3
67
  * Dataset#insert can now accept subqueries.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.2.0.2"
9
+ VERS = "0.2.1"
10
10
  CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
11
11
  RDOC_OPTS = ['--quiet', '--title', "Sequel: Concise ORM for Ruby",
12
12
  "--opname", "index.html",
@@ -55,16 +55,51 @@ spec = Gem::Specification.new do |s|
55
55
  s.bindir = "bin"
56
56
  end
57
57
 
58
+ win_spec = Gem::Specification.new do |s|
59
+ s.name = NAME
60
+ s.version = VERS
61
+ s.platform = Gem::Platform::WIN32
62
+ s.has_rdoc = true
63
+ s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
64
+ s.rdoc_options += RDOC_OPTS +
65
+ ['--exclude', '^(examples|extras)\/', '--exclude', 'lib/sequel.rb']
66
+ s.summary = "Lightweight ORM library for Ruby"
67
+ s.description = s.summary
68
+ s.author = "Sharon Rosner"
69
+ s.email = 'ciconia@gmail.com'
70
+ s.homepage = 'http://sequel.rubyforge.org'
71
+ s.executables = ['sequel']
72
+
73
+ s.add_dependency('metaid')
74
+
75
+ s.required_ruby_version = '>= 1.8.4'
76
+
77
+ s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib}/**/*")
78
+
79
+ s.require_path = "lib"
80
+ s.bindir = "bin"
81
+ end
82
+
58
83
  Rake::GemPackageTask.new(spec) do |p|
59
84
  p.need_tar = true
60
85
  p.gem_spec = spec
61
86
  end
62
87
 
88
+ Rake::GemPackageTask.new(win_spec) do |p|
89
+ p.need_tar = true
90
+ p.gem_spec = win_spec
91
+ end
92
+
63
93
  task :install do
64
94
  sh %{rake package}
65
95
  sh %{sudo gem install pkg/#{NAME}-#{VERS}}
66
96
  end
67
97
 
98
+ task :install_no_docs do
99
+ sh %{rake package}
100
+ sh %{sudo gem install pkg/#{NAME}-#{VERS} --no-rdoc --no-ri}
101
+ end
102
+
68
103
  task :uninstall => [:clean] do
69
104
  sh %{sudo gem uninstall #{NAME}}
70
105
  end
@@ -83,6 +118,11 @@ Spec::Rake::SpecTask.new('spec') do |t|
83
118
  t.rcov = true
84
119
  end
85
120
 
121
+ desc "Run specs without coverage"
122
+ Spec::Rake::SpecTask.new('spec_no_cov') do |t|
123
+ t.spec_files = FileList['spec/*_spec.rb']
124
+ end
125
+
86
126
  desc "Run adapter specs without coverage"
87
127
  Spec::Rake::SpecTask.new('spec_adapters') do |t|
88
128
  t.spec_files = FileList['spec/adapters/*_spec.rb']
data/lib/sequel.rb CHANGED
@@ -1,37 +1,37 @@
1
- require 'metaid'
2
-
3
- files = %w[
4
- core_ext error connection_pool pretty_table
5
- dataset migration model schema database
6
- ]
7
- dir = File.join(File.dirname(__FILE__), 'sequel')
8
- files.each {|f| require(File.join(dir, f))}
9
-
10
- module Sequel #:nodoc:
11
- class << self
12
- # call-seq:
13
- # Sequel::Database.connect(conn_string)
14
- # Sequel.connect(conn_string)
15
- # Sequel.open(conn_string)
16
- #
17
- # Creates a new database object based on the supplied connection string.
18
- # The specified scheme determines the database class used, and the rest
19
- # of the string specifies the connection options. For example:
20
- # DB = Sequel.open 'sqlite:///blog.db'
21
- def connect(*args)
22
- Database.connect(*args)
23
- end
24
-
25
- alias_method :open, :connect
26
-
27
- def single_threaded=(value)
28
- Database.single_threaded = value
29
- end
30
- end
31
- end
32
-
33
- class Object
34
- def Sequel(*args)
35
- Sequel.connect(*args)
36
- end
37
- end
1
+ require 'metaid'
2
+
3
+ files = %w[
4
+ core_ext error connection_pool pretty_table
5
+ dataset migration model schema database
6
+ ]
7
+ dir = File.join(File.dirname(__FILE__), 'sequel')
8
+ files.each {|f| require(File.join(dir, f))}
9
+
10
+ module Sequel #:nodoc:
11
+ class << self
12
+ # call-seq:
13
+ # Sequel::Database.connect(conn_string)
14
+ # Sequel.connect(conn_string)
15
+ # Sequel.open(conn_string)
16
+ #
17
+ # Creates a new database object based on the supplied connection string.
18
+ # The specified scheme determines the database class used, and the rest
19
+ # of the string specifies the connection options. For example:
20
+ # DB = Sequel.open 'sqlite:///blog.db'
21
+ def connect(*args)
22
+ Database.connect(*args)
23
+ end
24
+
25
+ alias_method :open, :connect
26
+
27
+ def single_threaded=(value)
28
+ Database.single_threaded = value
29
+ end
30
+ end
31
+ end
32
+
33
+ class Object
34
+ def Sequel(*args)
35
+ Sequel.connect(*args)
36
+ end
37
+ end
@@ -106,9 +106,9 @@ class Symbol
106
106
  include FieldCompositionMethods
107
107
 
108
108
 
109
- FIELD_REF_RE1 = /^([a-z_]+)__([a-z_]+)___([a-z_]+)/.freeze
110
- FIELD_REF_RE2 = /^([a-z_]+)___([a-z_]+)$/.freeze
111
- FIELD_REF_RE3 = /^([a-z_]+)__([a-z_]+)$/.freeze
109
+ FIELD_REF_RE1 = /^(\w+)__(\w+)___(\w+)/.freeze
110
+ FIELD_REF_RE2 = /^(\w+)___(\w+)$/.freeze
111
+ FIELD_REF_RE3 = /^(\w+)__(\w+)$/.freeze
112
112
 
113
113
  # Converts a symbol into a field name. This method supports underscore
114
114
  # notation in order to express qualified (two underscores) and aliased
@@ -60,9 +60,15 @@ module Sequel
60
60
 
61
61
  # Returns a blank dataset
62
62
  def dataset
63
- Sequel::Dataset.new(self)
63
+ ds = Sequel::Dataset.new(self)
64
64
  end
65
-
65
+
66
+ # Converts a query block into a dataset. For more information see
67
+ # Dataset#query.
68
+ def query(&block)
69
+ dataset.query(&block)
70
+ end
71
+
66
72
  # Returns a new dataset with the from method invoked. If a block is given,
67
73
  # it is used as a filter on the dataset.
68
74
  def from(*args, &block)
@@ -1,8 +1,8 @@
1
1
  require 'time'
2
2
  require 'date'
3
3
 
4
- require File.join(File.dirname(__FILE__), 'dataset/sequelizer')
5
4
  require File.join(File.dirname(__FILE__), 'dataset/sql')
5
+ require File.join(File.dirname(__FILE__), 'dataset/sequelizer')
6
6
  require File.join(File.dirname(__FILE__), 'dataset/convenience')
7
7
 
8
8
  module Sequel
@@ -86,6 +86,7 @@ module Sequel
86
86
  def initialize(db, opts = nil)
87
87
  @db = db
88
88
  @opts = opts || {}
89
+ @row_proc = nil
89
90
  end
90
91
 
91
92
  # Returns a new instance of the dataset with with the give options merged.
@@ -154,7 +155,7 @@ module Sequel
154
155
  def each(opts = nil, &block)
155
156
  fetch_rows(select_sql(opts), &block)
156
157
  end
157
-
158
+
158
159
  # Returns the the model classes associated with the dataset as a hash.
159
160
  def model_classes
160
161
  @opts[:models]
@@ -220,17 +221,22 @@ module Sequel
220
221
  when nil: # set_model(nil) => no
221
222
  # no argument provided, so the dataset is denuded
222
223
  @opts.merge!(:naked => true, :models => nil, :polymorphic_key => nil)
223
- extend_with_stock_each
224
+ remove_row_proc
225
+ # extend_with_stock_each
224
226
  when Class:
225
227
  # isomorphic model
226
228
  @opts.merge!(:naked => nil, :models => {nil => key}, :polymorphic_key => nil)
227
- extend_with_model(key, *args)
229
+ set_row_proc {|h| key.new(h, *args)}
228
230
  extend_with_destroy
229
231
  when Symbol:
230
232
  # polymorphic model
231
233
  hash = args.shift || raise(SequelError, "No class hash supplied for polymorphic model")
232
234
  @opts.merge!(:naked => true, :models => hash, :polymorphic_key => key)
233
- extend_with_polymorphic_model(key, hash, *args)
235
+ set_row_proc do |h|
236
+ c = hash[h[key]] || hash[nil] || \
237
+ raise(SequelError, "No matching model class for record (#{polymorphic_key} => #{h[polymorphic_key].inspect})")
238
+ c.new(h, *args)
239
+ end
234
240
  extend_with_destroy
235
241
  else
236
242
  raise SequelError, "Invalid parameters specified"
@@ -238,41 +244,106 @@ module Sequel
238
244
  self
239
245
  end
240
246
 
241
- private
242
- # Overrides the each method to convert records to model instances.
243
- def extend_with_model(c, *args)
244
- meta_def(:make_model_instance) {|r| c.new(r, *args)}
245
- m = Module.new do
246
- def each(opts = nil, &block)
247
- if opts && opts[:naked]
248
- fetch_rows(select_sql(opts), &block)
249
- else
250
- fetch_rows(select_sql(opts)) {|r| block.call(make_model_instance(r))}
251
- end
252
- end
247
+ # Overrides the each method to pass the values through a filter. The filter
248
+ # receives as argument a hash containing the column values for the current
249
+ # record. The filter should return a value which is then passed to the
250
+ # iterating block. In order to elucidate, here's a contrived example:
251
+ #
252
+ # dataset.set_row_proc {|h| h.merge(:xxx => 'yyy')}
253
+ # dataset.first[:xxx] #=> "yyy" # always!
254
+ #
255
+ def set_row_proc(&filter)
256
+ @row_proc = filter
257
+ update_each_method
258
+ end
259
+
260
+ # Removes the row making proc.
261
+ def remove_row_proc
262
+ @row_proc = nil
263
+ update_each_method
264
+ end
265
+
266
+ # Sets a value transform which is used to convert values loaded and saved
267
+ # to/from the database. The transform should be supplied as a hash. Each
268
+ # value in the hash should be an array containing two proc objects - one
269
+ # for transforming loaded values, and one for transforming saved values.
270
+ # The following example demonstrates how to store Ruby objects in a dataset
271
+ # using Marshal serialization:
272
+ #
273
+ # dataset.transform(:obj => [
274
+ # proc {|v| Marshal.load(v)},
275
+ # proc {|v| Marshal.dump(v)}
276
+ # ])
277
+ #
278
+ # dataset.insert_sql(:obj => 1234) #=>
279
+ # "INSERT INTO items (obj) VALUES ('\004\bi\002\322\004')"
280
+ #
281
+ def transform(t)
282
+ @transform = t
283
+ update_each_method
284
+ end
285
+
286
+ # Applies the value transform for data loaded from the database.
287
+ def transform_load(r)
288
+ r.inject({}) do |m, kv|
289
+ k, v = kv[0], kv[1]
290
+ m[k] = (tt = @transform[k]) ? tt[0][v] : v
291
+ m
253
292
  end
254
- extend(m)
293
+ #
294
+ # @transform.each do |k, tt|
295
+ # r[k] = tt[0][r[k]]
296
+ # end
297
+ # r
255
298
  end
256
299
 
257
- # Overrides the each method to convert records to polymorphic model
258
- # instances. The model class is determined according to the value in the
259
- # key column.
260
- def extend_with_polymorphic_model(key, hash, *args)
261
- meta_def(:make_model_instance) do |r|
262
- c = hash[r[key]] || hash[nil] || \
263
- raise(SequelError, "No matching model class for record (#{polymorphic_key} => #{r[polymorphic_key].inspect})")
264
- c.new(r, *args)
300
+ # Applies the value transform for data saved to the database.
301
+ def transform_save(r)
302
+ r.inject({}) do |m, kv|
303
+ k, v = kv[0], kv[1]
304
+ m[k] = (tt = @transform[k]) ? tt[1][v] : v
305
+ m
265
306
  end
266
- m = Module.new do
267
- def each(opts = nil, &block)
268
- if opts && opts[:naked]
307
+ end
308
+
309
+ private
310
+ # Updates the each method according to whether @row_proc and @transform are
311
+ # set or not.
312
+ def update_each_method
313
+ # warning: ugly code generation ahead
314
+ if @row_proc && @transform
315
+ class << self
316
+ def each(opts = nil, &block)
317
+ if opts && opts[:naked]
318
+ fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]}
319
+ else
320
+ fetch_rows(select_sql(opts)) {|r| block[@row_proc[transform_load(r)]]}
321
+ end
322
+ end
323
+ end
324
+ elsif @row_proc
325
+ class << self
326
+ def each(opts = nil, &block)
327
+ if opts && opts[:naked]
328
+ fetch_rows(select_sql(opts), &block)
329
+ else
330
+ fetch_rows(select_sql(opts)) {|r| block[@row_proc[r]]}
331
+ end
332
+ end
333
+ end
334
+ elsif @transform
335
+ class << self
336
+ def each(opts = nil, &block)
337
+ fetch_rows(select_sql(opts)) {|r| block[transform_load(r)]}
338
+ end
339
+ end
340
+ else
341
+ class << self
342
+ def each(opts = nil, &block)
269
343
  fetch_rows(select_sql(opts), &block)
270
- else
271
- fetch_rows(select_sql(opts)) {|r| block.call(make_model_instance(r))}
272
344
  end
273
345
  end
274
346
  end
275
- extend(m)
276
347
  end
277
348
 
278
349
  # Extends the dataset with a destroy method, that calls destroy for each
@@ -287,16 +358,6 @@ module Sequel
287
358
  end
288
359
  end
289
360
  end
290
-
291
- # Restores the stock #each implementation.
292
- def extend_with_stock_each
293
- m = Module.new do
294
- def each(opts = nil, &block)
295
- fetch_rows(select_sql(opts), &block)
296
- end
297
- end
298
- extend(m)
299
- end
300
361
  end
301
362
  end
302
363