sequel 0.4.4.2 → 0.4.5

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/Rakefile CHANGED
@@ -5,19 +5,24 @@ require "rake/rdoctask"
5
5
  require "fileutils"
6
6
  include FileUtils
7
7
 
8
+ ##############################################################################
9
+ # Configuration
10
+ ##############################################################################
8
11
  NAME = "sequel"
9
- VERS = "0.4.4.2"
12
+ VERS = "0.4.5"
10
13
  CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
11
- RDOC_OPTS = ["--quiet", "--title", "Sequel: Concise ORM for Ruby",
14
+ RDOC_OPTS = [
15
+ "--quiet",
16
+ "--title", "Sequel: Concise ORM for Ruby",
12
17
  "--opname", "index.html",
13
18
  "--line-numbers",
14
19
  "--main", "README",
15
- "--inline-source"]
16
-
17
- desc "Packages up Sequel."
18
- task :default => [:package]
19
- task :package => [:clean]
20
+ "--inline-source"
21
+ ]
20
22
 
23
+ ##############################################################################
24
+ # RDoc
25
+ ##############################################################################
21
26
  task :doc => [:rdoc]
22
27
 
23
28
  Rake::RDocTask.new do |rdoc|
@@ -28,8 +33,16 @@ Rake::RDocTask.new do |rdoc|
28
33
  rdoc.rdoc_files.add ["README", "COPYING", "lib/sequel.rb", "lib/**/*.rb"]
29
34
  end
30
35
 
36
+ ##############################################################################
37
+ # Gem packaging
38
+ ##############################################################################
39
+ desc "Packages up Sequel."
40
+ task :default => [:package]
41
+ task :package => [:clean]
42
+
31
43
  spec = Gem::Specification.new do |s|
32
44
  s.name = NAME
45
+ s.rubyforge_project = NAME
33
46
  s.version = VERS
34
47
  s.platform = Gem::Platform::RUBY
35
48
  s.has_rdoc = true
@@ -42,40 +55,25 @@ spec = Gem::Specification.new do |s|
42
55
  s.email = "ciconia@gmail.com"
43
56
  s.homepage = "http://sequel.rubyforge.org"
44
57
  s.executables = ["sequel"]
45
-
46
- s.add_dependency("metaid")
47
- s.add_dependency("ParseTree", ">= 2.0.0")
48
- s.add_dependency("ruby2ruby")
49
-
50
58
  s.required_ruby_version = ">= 1.8.4"
51
59
 
52
- s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib}/**/*")
53
-
54
- s.require_path = "lib"
55
- s.bindir = "bin"
56
- end
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")
60
+ # Instead of requiring this, how about we simply use it if it's available
61
+ # by rescuing LoadError where we require it in model/validations.rb?
62
+ # s.add_dependency("validatable")
63
+ case RUBY_PLATFORM
64
+ when /mswin/
65
+ s.platform = Gem::Platform::CURRENT
66
+ when /java/
67
+ s.platform = "jruby"
68
+ else
69
+ s.platform = Gem::Platform::RUBY
70
+ s.add_dependency("metaid")
71
+ s.add_dependency("ParseTree", ">= 2.0.0")
72
+ s.add_dependency("ruby2ruby")
73
+ end
74
74
 
75
- s.required_ruby_version = ">= 1.8.4"
76
-
77
75
  s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib}/**/*")
78
-
76
+
79
77
  s.require_path = "lib"
80
78
  s.bindir = "bin"
81
79
  end
@@ -85,11 +83,9 @@ Rake::GemPackageTask.new(spec) do |p|
85
83
  p.gem_spec = spec
86
84
  end
87
85
 
88
- Rake::GemPackageTask.new(win_spec) do |p|
89
- p.need_tar = true
90
- p.gem_spec = win_spec
91
- end
92
-
86
+ ##############################################################################
87
+ # installation & removal
88
+ ##############################################################################
93
89
  task :install do
94
90
  sh %{rake package}
95
91
  sh %{sudo gem install pkg/#{NAME}-#{VERS}}
@@ -104,6 +100,9 @@ task :uninstall => [:clean] do
104
100
  sh %{sudo gem uninstall #{NAME}}
105
101
  end
106
102
 
103
+ ##############################################################################
104
+ # gem and rdoc release
105
+ ##############################################################################
107
106
  task :release => [:package] do
108
107
  sh %{rubyforge login}
109
108
  sh %{rubyforge add_release sequel sequel #{VERS} pkg/sequel-#{VERS}.tgz}
@@ -117,29 +116,44 @@ task :doc_rforge do
117
116
  sh %{scp -r doc/rdoc/* ciconia@rubyforge.org:/var/www/gforge-projects/sequel}
118
117
  end
119
118
 
119
+ ##############################################################################
120
+ # specs
121
+ ##############################################################################
120
122
  require "spec/rake/spectask"
121
123
 
122
124
  desc "Run specs with coverage"
123
125
  Spec::Rake::SpecTask.new("spec") do |t|
124
126
  t.spec_files = FileList["spec/*_spec.rb"]
125
- t.rcov_opts = ["--exclude", "gems", "--exclude", "spec"]
127
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
128
+ t.rcov_opts = File.read("spec/rcov.opts").split("\n")
126
129
  t.rcov = true
127
130
  end
128
131
 
129
132
  desc "Run specs without coverage"
130
133
  Spec::Rake::SpecTask.new("spec_no_cov") do |t|
131
134
  t.spec_files = FileList["spec/*_spec.rb"]
135
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
132
136
  end
133
137
 
134
138
  desc "Run adapter specs without coverage"
135
139
  Spec::Rake::SpecTask.new("spec_adapters") do |t|
136
140
  t.spec_files = FileList["spec/adapters/*_spec.rb"]
141
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
137
142
  end
138
143
 
139
144
  desc "Run all specs with coverage"
140
145
  Spec::Rake::SpecTask.new("spec_all") do |t|
141
146
  t.spec_files = FileList["spec/*_spec.rb", "spec/adapters/*_spec.rb"]
142
- t.rcov_opts = ["--exclude", "gems", "--exclude", "spec"]
147
+ t.rcov_opts = File.read("spec/rcov.opts").split("\n")
148
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
149
+ t.rcov = true
150
+ end
151
+
152
+ desc "Run rcov only"
153
+ Spec::Rake::SpecTask.new("rcov") do |t|
154
+ t.rcov_opts = File.read("spec/rcov.opts").split("\n")
155
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
156
+ t.spec_files = FileList["spec/*_spec.rb"]
143
157
  t.rcov = true
144
158
  end
145
159
 
@@ -188,39 +188,86 @@ module Sequel
188
188
  names.each {|n| execute(drop_table_sql(n))}
189
189
  end
190
190
 
191
+ # Renames a table:
192
+ #
193
+ # DB.tables #=> [:items]
194
+ # DB.rename_table :items, :old_items
195
+ # DB.tables #=> [:old_items]
191
196
  def rename_table(*args)
192
197
  execute(rename_table_sql(*args))
193
198
  end
194
199
 
200
+ # Alters the given table with the specified block. Here are the currently
201
+ # available operations:
202
+ #
203
+ # DB.alter_table :items do
204
+ # add_column :category, :text, :default => 'ruby'
205
+ # drop_column :category
206
+ # rename_column :cntr, :counter
207
+ # set_column_type :value, :float
208
+ # set_column_default :value, :float
209
+ # add_index [:group, :category]
210
+ # drop_index [:group, :category]
211
+ # end
212
+ #
213
+ # Note that #add_column accepts all the options available for column
214
+ # definitions using create_table, and #add_index accepts all the options
215
+ # available for index definition.
195
216
  def alter_table(name, &block)
196
217
  g = Schema::AlterTableGenerator.new(self, &block)
197
218
  alter_table_sql_list(name, g.operations).each {|sql| execute(sql)}
198
219
  end
199
220
 
221
+ # Adds a column to the specified table. This method expects a column name,
222
+ # a datatype and optionally a hash with additional constraints and options:
223
+ #
224
+ # DB.add_column :items, :name, :text, :unique => true, :null => false
225
+ # DB.add_column :items, :category, :text, :default => 'ruby'
200
226
  def add_column(table, *args)
201
227
  alter_table(table) {add_column(*args)}
202
228
  end
203
229
 
230
+ # Removes a column from the specified table:
231
+ #
232
+ # DB.drop_column :items, :category
204
233
  def drop_column(table, *args)
205
234
  alter_table(table) {drop_column(*args)}
206
235
  end
207
236
 
237
+ # Renames a column in the specified table. This method expects the current
238
+ # column name and the new column name:
239
+ #
240
+ # DB.rename_column :items, :cntr, :counter
208
241
  def rename_column(table, *args)
209
242
  alter_table(table) {rename_column(*args)}
210
243
  end
211
244
 
245
+ # Set the data type for the given column in the given table:
246
+ #
247
+ # DB.set_column_type :items, :price, :float
212
248
  def set_column_type(table, *args)
213
249
  alter_table(table) {set_column_type(*args)}
214
250
  end
215
251
 
252
+ # Sets the default value for the given column in the given table:
253
+ #
254
+ # DB.set_column_default :items, :category, 'perl!'
255
+ def set_column_default(table, *args)
256
+ alter_table(table) {set_column_default(*args)}
257
+ end
258
+
216
259
  # Adds an index to a table for the given columns:
217
260
  #
218
- # DB.add_index(:posts, :title)
219
- # DB.add_index(:posts, [:author, :title], :unique => true)
261
+ # DB.add_index :posts, :title
262
+ # DB.add_index :posts, [:author, :title], :unique => true
220
263
  def add_index(table, *args)
221
264
  alter_table(table) {add_index(*args)}
222
265
  end
223
266
 
267
+ # Removes an index for the given table and column/s:
268
+ #
269
+ # DB.drop_index :posts, :title
270
+ # DB.drop_index :posts, [:author, :title]
224
271
  def drop_index(table, *args)
225
272
  alter_table(table) {drop_index(*args)}
226
273
  end
@@ -236,16 +283,27 @@ module Sequel
236
283
  false
237
284
  end
238
285
 
286
+ # Creates a view based on a dataset or an SQL string:
287
+ #
288
+ # DB.create_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
289
+ # DB.create_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
239
290
  def create_view(name, source)
240
291
  source = source.sql if source.is_a?(Dataset)
241
292
  execute("CREATE VIEW #{name} AS #{source}")
242
293
  end
243
294
 
295
+ # Creates a view, replacing it if it already exists:
296
+ #
297
+ # DB.create_or_replace_view(:cheap_items, "SELECT * FROM items WHERE price < 100")
298
+ # DB.create_or_replace_view(:ruby_items, DB[:items].filter(:category => 'ruby'))
244
299
  def create_or_replace_view(name, source)
245
300
  source = source.sql if source.is_a?(Dataset)
246
301
  execute("CREATE OR REPLACE VIEW #{name} AS #{source}")
247
302
  end
248
303
 
304
+ # Drops a view:
305
+ #
306
+ # DB.drop_view(:cheap_items)
249
307
  def drop_view(name)
250
308
  execute("DROP VIEW #{name}")
251
309
  end
@@ -88,6 +88,7 @@ module Sequel
88
88
  @db = db
89
89
  @opts = opts || {}
90
90
  @row_proc = nil
91
+ @transform = nil
91
92
  end
92
93
 
93
94
  # Returns a new instance of the dataset with with the give options merged.
@@ -454,7 +454,8 @@ module Sequel
454
454
  "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
455
455
  elsif values.keys
456
456
  fl = values.keys.map {|f| literal(f.to_sym)}
457
- vl = transform_save(values.values).map {|v| literal(v)}
457
+ vl = @transform ? transform_save(values.values) : values.values
458
+ vl.map! {|v| literal(v)}
458
459
  "INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
459
460
  else
460
461
  "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)})"
@@ -498,7 +499,11 @@ module Sequel
498
499
  if values.is_a?(Hash)
499
500
  # get values from hash
500
501
  values = transform_save(values) if @transform
501
- set = values.map {|k, v| "#{literal(k)} = #{literal(v)}"}.join(COMMA_SEPARATOR)
502
+ set = values.map do |k, v|
503
+ # convert string key into symbol
504
+ k = k.to_sym if String === k
505
+ "#{literal(k)} = #{literal(v)}"
506
+ end.join(COMMA_SEPARATOR)
502
507
  else
503
508
  # copy values verbatim
504
509
  set = values
data/lib/sequel/model.rb CHANGED
@@ -227,18 +227,22 @@ module Sequel
227
227
  class Model
228
228
  alias_method :model, :class
229
229
  end
230
+
230
231
  end
231
232
 
232
- require File.join(File.dirname(__FILE__), 'model/base')
233
- require File.join(File.dirname(__FILE__), 'model/hooks')
234
- require File.join(File.dirname(__FILE__), 'model/record')
235
- require File.join(File.dirname(__FILE__), 'model/schema')
236
- require File.join(File.dirname(__FILE__), 'model/relations')
237
- require File.join(File.dirname(__FILE__), 'model/caching')
238
- require File.join(File.dirname(__FILE__), 'model/plugins')
233
+ require File.join(File.dirname(__FILE__), "model/base")
234
+ require File.join(File.dirname(__FILE__), "model/hooks")
235
+ require File.join(File.dirname(__FILE__), "model/record")
236
+ require File.join(File.dirname(__FILE__), "model/schema")
237
+ require File.join(File.dirname(__FILE__), "model/relations")
238
+ require File.join(File.dirname(__FILE__), "model/caching")
239
+ require File.join(File.dirname(__FILE__), "model/plugins")
240
+ require File.join(File.dirname(__FILE__), "model/validations")
239
241
 
240
242
  module Sequel
243
+
241
244
  class Model
245
+
242
246
  # Defines a method that returns a filtered dataset.
243
247
  def self.subset(name, *args, &block)
244
248
  dataset.meta_def(name) {filter(*args, &block)}
@@ -253,6 +257,7 @@ module Sequel
253
257
  dataset.filter(*args, &block).first
254
258
  end
255
259
 
260
+ # TODO: doc
256
261
  def self.[](*args)
257
262
  args = args.first if (args.size == 1)
258
263
  if args === true || args === false
@@ -261,6 +266,7 @@ module Sequel
261
266
  dataset[(Hash === args) ? args : primary_key_hash(args)]
262
267
  end
263
268
 
269
+ # TODO: doc
264
270
  def self.fetch(*args)
265
271
  db.fetch(*args).set_model(self)
266
272
  end
@@ -273,6 +279,11 @@ module Sequel
273
279
 
274
280
  ############################################################################
275
281
 
282
+ # Deletes all records in the model's table.
283
+ def self.delete_all
284
+ dataset.delete
285
+ end
286
+
276
287
  # Like delete_all, but invokes before_destroy and after_destroy hooks if used.
277
288
  def self.destroy_all
278
289
  if has_hooks?(:before_destroy) || has_hooks?(:after_destroy)
@@ -281,11 +292,7 @@ module Sequel
281
292
  dataset.delete
282
293
  end
283
294
  end
284
- # Deletes all records.
285
- def self.delete_all
286
- dataset.delete
287
- end
288
-
295
+
289
296
  def self.is_dataset_magic_method?(m)
290
297
  method_name = m.to_s
291
298
  Sequel::Dataset::MAGIC_METHODS.each_key do |r|
@@ -303,15 +310,16 @@ module Sequel
303
310
  respond_to?(m) ? send(m, *args, &block) : super(m, *args)
304
311
  end
305
312
 
306
- # Comprehensive description goes here!
313
+ # TODO: Comprehensive description goes here!
307
314
  def self.join(*args)
308
315
  table_name = dataset.opts[:from].first
309
316
  dataset.join(*args).select(table_name.to_sym.ALL)
310
317
  end
311
318
 
312
- # Returns an array containing all model records
319
+ # Returns an array containing all of the models records.
313
320
  def self.all
314
321
  dataset.all
315
322
  end
316
323
  end
324
+
317
325
  end
@@ -93,4 +93,5 @@ module Sequel
93
93
  end
94
94
  end
95
95
  end
96
+
96
97
  end
@@ -198,6 +198,7 @@ module Sequel
198
198
  def new?
199
199
  @new
200
200
  end
201
+ alias :new_record? :new?
201
202
 
202
203
  # Returns true when current instance exists, false otherwise.
203
204
  def exists?
@@ -248,6 +249,7 @@ module Sequel
248
249
  this.update(values)
249
250
  values.each {|k, v| @values[k] = v}
250
251
  end
252
+ alias_method :update, :set
251
253
 
252
254
  # Reloads values from database and returns self.
253
255
  def refresh
@@ -49,4 +49,4 @@ module Sequel
49
49
  create_table!
50
50
  end
51
51
  end
52
- end
52
+ end
@@ -0,0 +1,117 @@
1
+ module Sequel
2
+ class Model
3
+ # =Basic Sequel Validations
4
+ #
5
+ # Sequel validations are based on the Validatable gem http://validatable.rubyforge.org/
6
+ #
7
+ # To assign default validations to a sequel model:
8
+ #
9
+ # class MyModel < SequelModel(:items)
10
+ # validates do
11
+ # format_of...
12
+ # presence_of...
13
+ # acceptance_of...
14
+ # confirmation_of...
15
+ # length_of...
16
+ # true_for...
17
+ # numericality_of...
18
+ # format_of...
19
+ # validates_base...
20
+ # validates_each...
21
+ # end
22
+ # end
23
+ #
24
+ # You may also perform the usual 'longhand' way to assign default model validates
25
+ # directly within the model class itself:
26
+ #
27
+ # class MyModel < SequelModel(:items)
28
+ # validates_format_of...
29
+ # validates_presence_of...
30
+ # validates_acceptance_of...
31
+ # validates_confirmation_of...
32
+ # validates_length_of...
33
+ # validates_true_for...
34
+ # validates_numericality_of...
35
+ # validates_format_of...
36
+ # validates_base...
37
+ # validates_each...
38
+ # end
39
+ #
40
+ # Each validation allows for arguments:
41
+ # TODO: fill the argument options in here
42
+ #
43
+ # =Advanced Sequel Validations
44
+ #
45
+ # TODO: verify that advanced validates work as stated (aka write specs)
46
+ # NOTE: experimental
47
+ #
48
+ # To store validates for conditional usage simply specify a name with which to store them
49
+ # class User < Sequel::Model
50
+ #
51
+ # # This set of validates becomes stored as :default and gets injected into the model.
52
+ # validates do
53
+ # # standard validates calls
54
+ # end
55
+ #
56
+ # validates(:registration) do
57
+ # # user registration specific validates
58
+ # end
59
+ #
60
+ # validates(:promotion) do
61
+ # # user promotion validates
62
+ # end
63
+ #
64
+ # end
65
+ #
66
+ # To use the above validates:
67
+ #
68
+ # @user.valid? # Runs the default validations only.
69
+ # @user.valid?(:registration) # Runs both default and registration validations
70
+ # @user.valid?(:promotion) # Runs both default and promotion validations
71
+ #
72
+ # You may determine whether the model has validates via:
73
+ #
74
+ # has_validations? # will return true / false based on existence of validations on the model.
75
+ #
76
+ # You may also retrieve the validations block if needed:
77
+ #
78
+ # validates(:registration) # returns the registration validation block.
79
+ #
80
+ # validates() method parameters:
81
+ # a validations block - runs the validations block on the model & stores as :default
82
+ # a name and a validations block - stores the block under the name
83
+ # a name - returns a stored block of that name or nil
84
+ # nothing - returns true / false based on if validations exist for the model.
85
+ #
86
+ module Validations
87
+ class Generator
88
+ def initialize(model_class ,&block)
89
+ @model_class = model_class
90
+ instance_eval(&block)
91
+ end
92
+
93
+ def method_missing(method, *args)
94
+ method = :"validates_#{method}"
95
+ @model_class.send(method, *args)
96
+ end
97
+ end
98
+ end
99
+
100
+ begin
101
+ require "validatable"
102
+ include ::Validatable
103
+ def self.validates(&block)
104
+ Validations::Generator.new(self, &block)
105
+ end
106
+ # return true if there are validations stored, false otherwise
107
+ def self.has_validations?
108
+ validations.length > 0 ? true : false
109
+ end
110
+ rescue LoadError
111
+ STDERR.puts <<-MESSAGE
112
+ Install the validatable gem in order to use Sequel Model validations
113
+ If you would like model validations to work, install the validatable gem
114
+ MESSAGE
115
+ end
116
+ end
117
+ end