sequel 0.4.4.2 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
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