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.
- data/CHANGELOG +64 -0
- data/Rakefile +41 -1
- data/lib/sequel.rb +37 -37
- data/lib/sequel/core_ext.rb +3 -3
- data/lib/sequel/database.rb +8 -2
- data/lib/sequel/dataset.rb +103 -42
- data/lib/sequel/dataset/convenience.rb +46 -0
- data/lib/sequel/dataset/sequelizer.rb +34 -8
- data/lib/sequel/dataset/sql.rb +12 -8
- data/lib/sequel/model.rb +13 -237
- data/lib/sequel/model/base.rb +84 -0
- data/lib/sequel/model/hooks.rb +40 -0
- data/lib/sequel/model/record.rb +154 -0
- data/lib/sequel/model/relations.rb +26 -0
- data/lib/sequel/model/schema.rb +36 -0
- data/lib/sequel/mysql.rb +9 -7
- data/lib/sequel/postgres.rb +5 -4
- data/lib/sequel/schema/schema_generator.rb +1 -0
- data/lib/sequel/sqlite.rb +2 -2
- data/spec/core_ext_spec.rb +14 -0
- data/spec/database_spec.rb +12 -1
- data/spec/dataset_spec.rb +274 -0
- data/spec/model_spec.rb +286 -3
- data/spec/sequelizer_spec.rb +49 -9
- data/spec/spec_helper.rb +27 -5
- metadata +8 -2
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.
|
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
|
data/lib/sequel/core_ext.rb
CHANGED
@@ -106,9 +106,9 @@ class Symbol
|
|
106
106
|
include FieldCompositionMethods
|
107
107
|
|
108
108
|
|
109
|
-
FIELD_REF_RE1 = /^(
|
110
|
-
FIELD_REF_RE2 = /^(
|
111
|
-
FIELD_REF_RE3 = /^(
|
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
|
data/lib/sequel/database.rb
CHANGED
@@ -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)
|
data/lib/sequel/dataset.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
242
|
-
#
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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
|
-
|
293
|
+
#
|
294
|
+
# @transform.each do |k, tt|
|
295
|
+
# r[k] = tt[0][r[k]]
|
296
|
+
# end
|
297
|
+
# r
|
255
298
|
end
|
256
299
|
|
257
|
-
#
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
267
|
-
|
268
|
-
|
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
|
|