sequel_model 0.2 → 0.3
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 +12 -0
- data/Rakefile +6 -2
- data/lib/sequel_model/record.rb +4 -3
- data/lib/sequel_model/relations.rb +65 -0
- data/lib/sequel_model/validations.rb +240 -100
- data/spec/model_spec.rb +0 -21
- data/spec/record_spec.rb +88 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/validations_spec.rb +361 -113
- metadata +2 -11
data/CHANGELOG
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
=== 0.3 (2008-01-18)
|
2
|
+
|
3
|
+
* Implemented Validatable::Errors class.
|
4
|
+
|
5
|
+
* Added Model#reload as alias to Model#refresh.
|
6
|
+
|
7
|
+
* Changed Model.create to accept a block (#126).
|
8
|
+
|
9
|
+
* Rewrote validations.
|
10
|
+
|
11
|
+
* Fixed Model#initialize to accept nil values (#115).
|
12
|
+
|
1
13
|
=== 0.2 (2008-01-02)
|
2
14
|
|
3
15
|
* Removed deprecated Model.recreate_table method.
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ include FileUtils
|
|
9
9
|
# Configuration
|
10
10
|
##############################################################################
|
11
11
|
NAME = "sequel_model"
|
12
|
-
VERS = "0.
|
12
|
+
VERS = "0.3"
|
13
13
|
CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
|
14
14
|
RDOC_OPTS = [
|
15
15
|
"--quiet",
|
@@ -66,7 +66,6 @@ spec = Gem::Specification.new do |s|
|
|
66
66
|
end
|
67
67
|
|
68
68
|
s.add_dependency("sequel_core", '>= 0.5')
|
69
|
-
s.add_dependency("validatable")
|
70
69
|
|
71
70
|
s.files = %w(COPYING README Rakefile) + Dir.glob("{doc,spec,lib}/**/*")
|
72
71
|
|
@@ -128,6 +127,11 @@ Spec::Rake::SpecTask.new("spec_no_cov") do |t|
|
|
128
127
|
t.spec_opts = File.read("spec/spec.opts").split("\n")
|
129
128
|
end
|
130
129
|
|
130
|
+
desc "check documentation coverage"
|
131
|
+
task :dcov do
|
132
|
+
sh "find lib -name '*.rb' | xargs dcov"
|
133
|
+
end
|
134
|
+
|
131
135
|
##############################################################################
|
132
136
|
# Statistics
|
133
137
|
##############################################################################
|
data/lib/sequel_model/record.rb
CHANGED
@@ -127,9 +127,9 @@ module Sequel
|
|
127
127
|
|
128
128
|
# Creates new instance with values set to passed-in Hash ensuring that
|
129
129
|
# new? returns true.
|
130
|
-
def self.create(values = {})
|
130
|
+
def self.create(values = {}, &block)
|
131
131
|
db.transaction do
|
132
|
-
obj = new(values, true)
|
132
|
+
obj = new(values, true, &block)
|
133
133
|
obj.save
|
134
134
|
obj
|
135
135
|
end
|
@@ -173,7 +173,7 @@ module Sequel
|
|
173
173
|
# This method guesses whether the record exists when
|
174
174
|
# <tt>new_record</tt> is set to false.
|
175
175
|
def initialize(values = {}, new_record = false, &block)
|
176
|
-
@values = values
|
176
|
+
@values = values || {}
|
177
177
|
@changed_columns = []
|
178
178
|
|
179
179
|
@new = new_record
|
@@ -251,6 +251,7 @@ module Sequel
|
|
251
251
|
@values = this.first || raise(Error, "Record not found")
|
252
252
|
self
|
253
253
|
end
|
254
|
+
alias_method :reload, :refresh
|
254
255
|
|
255
256
|
# Like delete but runs hooks before and after delete.
|
256
257
|
def destroy
|
@@ -85,5 +85,70 @@ module Sequel
|
|
85
85
|
class_def(name) {from.filter(key => pk)}
|
86
86
|
end
|
87
87
|
end
|
88
|
+
|
89
|
+
# TODO: Add/Replace current relations with the following specifications:
|
90
|
+
# ======================================================================
|
91
|
+
|
92
|
+
# Database modelling is generally done with an ER (Entity Relationship) diagram.
|
93
|
+
# Shouldn't ORM's facilitate simlilar specification?
|
94
|
+
|
95
|
+
# class Post < Sequel::Model(:users)
|
96
|
+
# relationships do
|
97
|
+
# # Specify the relationships that exist with the User model (users table)
|
98
|
+
# # These relationships are precisely the ER diagram connecting arrows.
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
|
102
|
+
#
|
103
|
+
# = Relationships
|
104
|
+
#
|
105
|
+
# are specifications of the ends of the ER diagrams connectors that are touching
|
106
|
+
# the current model.
|
107
|
+
#
|
108
|
+
# one_to_one, has_one
|
109
|
+
# many_to_one, belongs_to
|
110
|
+
# many_to_many, has_many
|
111
|
+
# ?parameters may be :zero, :one, :many which specifies the cardinality of the connection
|
112
|
+
|
113
|
+
# Example:
|
114
|
+
# class Post < Sequel::Model(:users)
|
115
|
+
# relationships do
|
116
|
+
# has :one, :blog, :required => true # blog_id field, cannot be null
|
117
|
+
# has :one, :account # account_id field
|
118
|
+
# has :many, :comments # comments_posts join table
|
119
|
+
# has :many, :authors, :required => true # authors_posts join table, requires at least one author
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Relationship API Details
|
125
|
+
#
|
126
|
+
|
127
|
+
#
|
128
|
+
# == belongs_to
|
129
|
+
#
|
130
|
+
|
131
|
+
# Defines an blog and blog= method
|
132
|
+
# belongs_to :blog
|
133
|
+
|
134
|
+
# Same, but uses "b_id" as the blog's id field.
|
135
|
+
# belongs_to :blog, :key => :b_id
|
136
|
+
|
137
|
+
# has_many :comments
|
138
|
+
# * Defines comments method which will query the join table appropriately.
|
139
|
+
# * Checks to see if a "comments_posts" join table exists (alphabetical order)
|
140
|
+
# ** If it does not exist, will create the join table.
|
141
|
+
# ** If options are passed in these will be used to further define the join table.
|
142
|
+
|
143
|
+
|
144
|
+
# Benefits:
|
145
|
+
# * Normalized DB
|
146
|
+
# * Easy to define join objects
|
147
|
+
# * Efficient queries, database gets to use indexed fields (pkeys) instead of a string field and an id.
|
148
|
+
#
|
149
|
+
# For example, polymorphic associations now become:
|
150
|
+
# [user] 1-* [addresses_users] *-1 [addresses]
|
151
|
+
# [companies] 1-* [addresses_companies] *-1 [addresses]
|
152
|
+
# [clients] 1-* [addresses_clients] *-1 [addresses]
|
88
153
|
end
|
89
154
|
end
|
@@ -1,113 +1,253 @@
|
|
1
|
-
|
1
|
+
gem 'assistance', '>= 0.1.1' # because we need Array#extract_options!
|
2
2
|
|
3
3
|
module Sequel
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# validates_true_for...
|
36
|
-
# validates_numericality_of...
|
37
|
-
# validates_format_of...
|
38
|
-
# validates_base...
|
39
|
-
# validates_each...
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# Each validation allows for arguments:
|
43
|
-
# TODO: fill the argument options in here
|
44
|
-
#
|
45
|
-
# =Advanced Sequel Validations
|
46
|
-
#
|
47
|
-
# TODO: verify that advanced validates work as stated (aka write specs)
|
48
|
-
# NOTE: experimental
|
49
|
-
#
|
50
|
-
# To store validates for conditional usage simply specify a name with which to store them
|
51
|
-
# class User < Sequel::Model
|
52
|
-
#
|
53
|
-
# # This set of validates becomes stored as :default and gets injected into the model.
|
54
|
-
# validates do
|
55
|
-
# # standard validates calls
|
56
|
-
# end
|
57
|
-
#
|
58
|
-
# validates(:registration) do
|
59
|
-
# # user registration specific validates
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# validates(:promotion) do
|
63
|
-
# # user promotion validates
|
64
|
-
# end
|
65
|
-
#
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# To use the above validates:
|
69
|
-
#
|
70
|
-
# @user.valid? # Runs the default validations only.
|
71
|
-
# @user.valid?(:registration) # Runs both default and registration validations
|
72
|
-
# @user.valid?(:promotion) # Runs both default and promotion validations
|
73
|
-
#
|
74
|
-
# You may determine whether the model has validates via:
|
75
|
-
#
|
76
|
-
# has_validations? # will return true / false based on existence of validations on the model.
|
77
|
-
#
|
78
|
-
# You may also retrieve the validations block if needed:
|
79
|
-
#
|
80
|
-
# validates(:registration) # returns the registration validation block.
|
81
|
-
#
|
82
|
-
# validates() method parameters:
|
83
|
-
# a validations block - runs the validations block on the model & stores as :default
|
84
|
-
# a name and a validations block - stores the block under the name
|
85
|
-
# a name - returns a stored block of that name or nil
|
86
|
-
# nothing - returns true / false based on if validations exist for the model.
|
87
|
-
#
|
88
|
-
module Validations
|
89
|
-
class Generator
|
90
|
-
def initialize(model_class ,&block)
|
91
|
-
@model_class = model_class
|
92
|
-
instance_eval(&block)
|
93
|
-
end
|
4
|
+
# The Validatable module provides validation capabilities as a mixin. When
|
5
|
+
# included into a class, it enhances the class with class and instance
|
6
|
+
# methods for defining validations and validating class instances.
|
7
|
+
#
|
8
|
+
# The Validatable emulates the validation capabilities of ActiveRecord, and
|
9
|
+
# provides methods for validating acceptance, confirmation, presence, format,
|
10
|
+
# length and numericality of attributes.
|
11
|
+
#
|
12
|
+
# To use validations, you need to include the Validatable module in your
|
13
|
+
# class:
|
14
|
+
#
|
15
|
+
# class MyClass
|
16
|
+
# include Sequel::Validatable
|
17
|
+
# validates_length_of :password, :minimum => 6
|
18
|
+
# end
|
19
|
+
module Validatable
|
20
|
+
# Includes the Validatable class methods into the including class.
|
21
|
+
def self.included(c)
|
22
|
+
c.extend ClassMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the validation errors associated with the object.
|
26
|
+
def errors
|
27
|
+
@errors ||= Errors.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Validates the object.
|
31
|
+
def validate
|
32
|
+
errors.clear
|
33
|
+
self.class.validate(self)
|
34
|
+
end
|
94
35
|
|
95
|
-
|
96
|
-
|
97
|
-
|
36
|
+
# Validates the object and returns true if no errors are reported.
|
37
|
+
def valid?
|
38
|
+
validate
|
39
|
+
errors.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Validatable::Errors represents validation errors.
|
43
|
+
class Errors
|
44
|
+
# Initializes a new instance of validation errors.
|
45
|
+
def initialize
|
46
|
+
@errors = Hash.new {|h, k| h[k] = []}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns true if no errors are stored.
|
50
|
+
def empty?
|
51
|
+
@errors.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
# Clears all errors.
|
55
|
+
def clear
|
56
|
+
@errors.clear
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the errors for the given attribute.
|
60
|
+
def on(att)
|
61
|
+
@errors[att]
|
62
|
+
end
|
63
|
+
alias_method :[], :on
|
64
|
+
|
65
|
+
# Adds an error for the given attribute.
|
66
|
+
def add(att, msg)
|
67
|
+
@errors[att] << msg
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns an array of fully-formatted error messages.
|
71
|
+
def full_messages
|
72
|
+
@errors.inject([]) do |m, kv| att, errors = *kv
|
73
|
+
errors.each {|e| m << "#{att} #{e}"}
|
74
|
+
m
|
98
75
|
end
|
99
76
|
end
|
100
77
|
end
|
78
|
+
|
79
|
+
# The Generator class is used to generate validation definitions using
|
80
|
+
# the validates {} idiom.
|
81
|
+
class Generator
|
82
|
+
# Initializes a new generator.
|
83
|
+
def initialize(receiver ,&block)
|
84
|
+
@receiver = receiver
|
85
|
+
instance_eval(&block)
|
86
|
+
end
|
101
87
|
|
102
|
-
|
88
|
+
# Delegates method calls to the receiver by calling receiver.validates_xxx.
|
89
|
+
def method_missing(m, *args)
|
90
|
+
@receiver.send(:"validates_#{m}", *args)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Validatable class methods.
|
95
|
+
module ClassMethods
|
96
|
+
# Defines validations by converting a longhand block into a series of
|
97
|
+
# shorthand definitions. For example:
|
98
|
+
#
|
99
|
+
# class MyClass
|
100
|
+
# include Sequel::Validatable
|
101
|
+
# validates do
|
102
|
+
# length_of :name, :minimum => 6
|
103
|
+
# length_of :password, :minimum => 8
|
104
|
+
# end
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# is equivalent to:
|
108
|
+
# class MyClass
|
109
|
+
# include Sequel::Validatable
|
110
|
+
# validates_length_of :name, :minimum => 6
|
111
|
+
# validates_length_of :password, :minimum => 8
|
112
|
+
# end
|
113
|
+
def validates(&block)
|
114
|
+
Generator.new(self, &block)
|
115
|
+
end
|
103
116
|
|
104
|
-
|
105
|
-
|
117
|
+
# Returns the validations hash for the class.
|
118
|
+
def validations
|
119
|
+
@validations ||= Hash.new {|h, k| h[k] = []}
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns true if validations are defined.
|
123
|
+
def has_validations?
|
124
|
+
!validations.empty?
|
125
|
+
end
|
126
|
+
|
127
|
+
# Validates the given instance.
|
128
|
+
def validate(o)
|
129
|
+
validations.each do |att, procs|
|
130
|
+
v = o.send(att)
|
131
|
+
procs.each {|p| p[o, att, v]}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Adds a validation for each of the given attributes using the supplied
|
136
|
+
# block. The block must accept three arguments: instance, attribute and
|
137
|
+
# value, e.g.:
|
138
|
+
#
|
139
|
+
# validates_each :name, :password do |object, attribute, value|
|
140
|
+
# object.errors[attribute] << 'is not nice' unless value.nice?
|
141
|
+
# end
|
142
|
+
def validates_each(*atts, &block)
|
143
|
+
atts.each {|a| validations[a] << block}
|
144
|
+
end
|
145
|
+
|
146
|
+
# Validates acceptance of an attribute.
|
147
|
+
def validates_acceptance_of(*atts)
|
148
|
+
opts = {
|
149
|
+
:message => 'is not accepted',
|
150
|
+
:allow_nil => true,
|
151
|
+
:accept => '1'
|
152
|
+
}.merge!(atts.extract_options!)
|
153
|
+
|
154
|
+
validates_each(*atts) do |o, a, v|
|
155
|
+
next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
|
156
|
+
o.errors[a] << opts[:message] unless v == opts[:accept]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Validates confirmation of an attribute.
|
161
|
+
def validates_confirmation_of(*atts)
|
162
|
+
opts = {
|
163
|
+
:message => 'is not confirmed',
|
164
|
+
}.merge!(atts.extract_options!)
|
165
|
+
|
166
|
+
validates_each(*atts) do |o, a, v|
|
167
|
+
next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
|
168
|
+
c = o.send(:"#{a}_confirmation")
|
169
|
+
o.errors[a] << opts[:message] unless v == c
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Validates the format of an attribute.
|
174
|
+
def validates_format_of(*atts)
|
175
|
+
opts = {
|
176
|
+
:message => 'is invalid',
|
177
|
+
}.merge!(atts.extract_options!)
|
178
|
+
|
179
|
+
unless opts[:with].is_a?(Regexp)
|
180
|
+
raise Sequel::Error, "A regular expression must be supplied as the :with option of the options hash"
|
181
|
+
end
|
182
|
+
|
183
|
+
validates_each(*atts) do |o, a, v|
|
184
|
+
next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
|
185
|
+
o.errors[a] << opts[:message] unless v.to_s =~ opts[:with]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Validates the length of an attribute.
|
190
|
+
def validates_length_of(*atts)
|
191
|
+
opts = {
|
192
|
+
:too_long => 'is too long',
|
193
|
+
:too_short => 'is too short',
|
194
|
+
:wrong_length => 'is the wrong length'
|
195
|
+
}.merge!(atts.extract_options!)
|
196
|
+
|
197
|
+
validates_each(*atts) do |o, a, v|
|
198
|
+
next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
|
199
|
+
if m = opts[:maximum]
|
200
|
+
o.errors[a] << (opts[:message] || opts[:too_long]) unless v && v.size <= m
|
201
|
+
end
|
202
|
+
if m = opts[:minimum]
|
203
|
+
o.errors[a] << (opts[:message] || opts[:too_short]) unless v && v.size >= m
|
204
|
+
end
|
205
|
+
if i = opts[:is]
|
206
|
+
o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && v.size == i
|
207
|
+
end
|
208
|
+
if w = opts[:within]
|
209
|
+
o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && w.include?(v.size)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
NUMBER_RE = /^\d*\.{0,1}\d+$/
|
215
|
+
INTEGER_RE = /\A[+-]?\d+\Z/
|
216
|
+
|
217
|
+
# Validates whether an attribute is a number.
|
218
|
+
def validates_numericality_of(*atts)
|
219
|
+
opts = {
|
220
|
+
:message => 'is not a number',
|
221
|
+
}.merge!(atts.extract_options!)
|
222
|
+
|
223
|
+
re = opts[:only_integer] ? INTEGER_RE : NUMBER_RE
|
224
|
+
|
225
|
+
validates_each(*atts) do |o, a, v|
|
226
|
+
next if (v.nil? && opts[:allow_nil]) || (v.blank? && opts[:allow_blank])
|
227
|
+
o.errors[a] << opts[:message] unless v.to_s =~ re
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Validates the presence of an attribute.
|
232
|
+
def validates_presence_of(*atts)
|
233
|
+
opts = {
|
234
|
+
:message => 'is not present',
|
235
|
+
}.merge!(atts.extract_options!)
|
236
|
+
|
237
|
+
validates_each(*atts) do |o, a, v|
|
238
|
+
o.errors[a] << opts[:message] unless v && !v.blank?
|
239
|
+
end
|
240
|
+
end
|
106
241
|
end
|
242
|
+
end
|
107
243
|
|
108
|
-
|
109
|
-
|
110
|
-
|
244
|
+
class Model
|
245
|
+
include Validatable
|
246
|
+
|
247
|
+
alias_method :save!, :save
|
248
|
+
def save(*args)
|
249
|
+
return false unless valid?
|
250
|
+
save!(*args)
|
111
251
|
end
|
112
252
|
end
|
113
253
|
end
|
data/spec/model_spec.rb
CHANGED
@@ -401,27 +401,6 @@ describe Sequel::Model, "(:tablename)" do
|
|
401
401
|
|
402
402
|
end
|
403
403
|
|
404
|
-
describe Sequel::Model, ".create" do
|
405
|
-
|
406
|
-
before(:each) do
|
407
|
-
MODEL_DB.reset
|
408
|
-
@c = Class.new(Sequel::Model(:items))
|
409
|
-
end
|
410
|
-
|
411
|
-
it "should be able to create rows in the associated table" do
|
412
|
-
o = @c.create(:x => 1)
|
413
|
-
o.class.should == @c
|
414
|
-
MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)', "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (1)')) LIMIT 1"]
|
415
|
-
end
|
416
|
-
|
417
|
-
it "should be able to create rows without any values specified" do
|
418
|
-
o = @c.create
|
419
|
-
o.class.should == @c
|
420
|
-
MODEL_DB.sqls.should == ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id IN ('INSERT INTO items DEFAULT VALUES')) LIMIT 1"]
|
421
|
-
end
|
422
|
-
|
423
|
-
end
|
424
|
-
|
425
404
|
describe Sequel::Model, "A model class without a primary key" do
|
426
405
|
|
427
406
|
before(:each) do
|
data/spec/record_spec.rb
CHANGED
@@ -409,3 +409,91 @@ describe Sequel::Model, "#===" do
|
|
409
409
|
end
|
410
410
|
end
|
411
411
|
|
412
|
+
describe Sequel::Model, "#initialize" do
|
413
|
+
setup do
|
414
|
+
@c = Class.new(Sequel::Model) do
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
specify "should accept values" do
|
419
|
+
m = @c.new(:id => 1, :x => 2)
|
420
|
+
m.values.should == {:id => 1, :x => 2}
|
421
|
+
end
|
422
|
+
|
423
|
+
specify "should accept no values" do
|
424
|
+
m = @c.new
|
425
|
+
m.values.should == {}
|
426
|
+
end
|
427
|
+
|
428
|
+
specify "should accept nil values" do
|
429
|
+
m = @c.new(nil)
|
430
|
+
m.values.should == {}
|
431
|
+
end
|
432
|
+
|
433
|
+
specify "should accept a block to execute" do
|
434
|
+
m = @c.new {|o| o[:id] = 1234}
|
435
|
+
m.id.should == 1234
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
describe Sequel::Model, ".create" do
|
440
|
+
|
441
|
+
before(:each) do
|
442
|
+
MODEL_DB.reset
|
443
|
+
@c = Class.new(Sequel::Model(:items)) do
|
444
|
+
def columns; [:x]; end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should be able to create rows in the associated table" do
|
449
|
+
o = @c.create(:x => 1)
|
450
|
+
o.class.should == @c
|
451
|
+
MODEL_DB.sqls.should == ['INSERT INTO items (x) VALUES (1)', "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (1)')) LIMIT 1"]
|
452
|
+
end
|
453
|
+
|
454
|
+
it "should be able to create rows without any values specified" do
|
455
|
+
o = @c.create
|
456
|
+
o.class.should == @c
|
457
|
+
MODEL_DB.sqls.should == ["INSERT INTO items DEFAULT VALUES", "SELECT * FROM items WHERE (id IN ('INSERT INTO items DEFAULT VALUES')) LIMIT 1"]
|
458
|
+
end
|
459
|
+
|
460
|
+
it "should accept a block and run it" do
|
461
|
+
o1, o2, o3 = nil, nil, nil
|
462
|
+
o = @c.create {|o3| o1 = o3; o2 = :blah; o3.x = 333}
|
463
|
+
o.class.should == @c
|
464
|
+
o1.should === o
|
465
|
+
o3.should === o
|
466
|
+
o2.should == :blah
|
467
|
+
MODEL_DB.sqls.should == ["INSERT INTO items (x) VALUES (333)", "SELECT * FROM items WHERE (id IN ('INSERT INTO items (x) VALUES (333)')) LIMIT 1"]
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
describe Sequel::Model, "#refresh" do
|
472
|
+
setup do
|
473
|
+
MODEL_DB.reset
|
474
|
+
@c = Class.new(Sequel::Model(:items)) do
|
475
|
+
def columns; [:x]; end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
specify "should reload the instance values from the database" do
|
480
|
+
@m = @c.new(:id => 555)
|
481
|
+
@m[:x] = 'blah'
|
482
|
+
@m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
|
483
|
+
@m.refresh
|
484
|
+
@m[:x].should == 'kaboom'
|
485
|
+
end
|
486
|
+
|
487
|
+
specify "should raise if the instance is not found" do
|
488
|
+
@m = @c.new(:id => 555)
|
489
|
+
@m.this.should_receive(:first).and_return(nil)
|
490
|
+
proc {@m.refresh}.should raise_error(Sequel::Error)
|
491
|
+
end
|
492
|
+
|
493
|
+
specify "should be aliased by #reload" do
|
494
|
+
@m = @c.new(:id => 555)
|
495
|
+
@m.this.should_receive(:first).and_return({:x => 'kaboom', :id => 555})
|
496
|
+
@m.reload
|
497
|
+
@m[:x].should == 'kaboom'
|
498
|
+
end
|
499
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/validations_spec.rb
CHANGED
@@ -1,5 +1,295 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
2
|
|
3
|
+
describe "Sequel::Validatable::Errors" do
|
4
|
+
setup do
|
5
|
+
@errors = Sequel::Validatable::Errors.new
|
6
|
+
class Sequel::Validatable::Errors
|
7
|
+
attr_accessor :errors
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
specify "should be clearable using #clear" do
|
12
|
+
@errors.errors = {1 => 2, 3 => 4}
|
13
|
+
@errors.clear
|
14
|
+
@errors.errors.should == {}
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "should be empty if no errors are added" do
|
18
|
+
@errors.should be_empty
|
19
|
+
@errors[:blah] << "blah"
|
20
|
+
@errors.should_not be_empty
|
21
|
+
end
|
22
|
+
|
23
|
+
specify "should return errors for a specific attribute using #on or #[]" do
|
24
|
+
@errors[:blah].should == []
|
25
|
+
@errors.on(:blah).should == []
|
26
|
+
|
27
|
+
@errors[:blah] << 'blah'
|
28
|
+
@errors[:blah].should == ['blah']
|
29
|
+
@errors.on(:blah).should == ['blah']
|
30
|
+
|
31
|
+
@errors[:bleu].should == []
|
32
|
+
@errors.on(:bleu).should == []
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "should accept errors using #[] << or #add" do
|
36
|
+
@errors[:blah] << 'blah'
|
37
|
+
@errors[:blah].should == ['blah']
|
38
|
+
|
39
|
+
@errors.add :blah, 'zzzz'
|
40
|
+
@errors[:blah].should == ['blah', 'zzzz']
|
41
|
+
end
|
42
|
+
|
43
|
+
specify "should return full messages using #full_messages" do
|
44
|
+
@errors.full_messages.should == []
|
45
|
+
|
46
|
+
@errors[:blow] << 'blieuh'
|
47
|
+
@errors[:blow] << 'blich'
|
48
|
+
@errors[:blay] << 'bliu'
|
49
|
+
msgs = @errors.full_messages
|
50
|
+
msgs.size.should == 3
|
51
|
+
msgs.should include('blow blieuh', 'blow blich', 'blay bliu')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe Sequel::Validatable do
|
56
|
+
setup do
|
57
|
+
@c = Class.new do
|
58
|
+
include Sequel::Validatable
|
59
|
+
|
60
|
+
def self.validates_coolness_of(attr)
|
61
|
+
validates_each(attr) {|o, a, v| o.errors[a] << 'is not cool' if v != :cool}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
@d = Class.new do
|
66
|
+
attr_accessor :errors
|
67
|
+
def initialize; @errors = Sequel::Validatable::Errors.new; end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
specify "should respond to validates, validations, has_validations?" do
|
72
|
+
@c.should respond_to(:validations)
|
73
|
+
@c.should respond_to(:has_validations?)
|
74
|
+
end
|
75
|
+
|
76
|
+
specify "should acccept validation definitions using validates_each" do
|
77
|
+
@c.validates_each(:xx, :yy) {|o, a, v| o.errors[a] << 'too low' if v < 50}
|
78
|
+
|
79
|
+
@c.validations[:xx].size.should == 1
|
80
|
+
@c.validations[:yy].size.should == 1
|
81
|
+
|
82
|
+
o = @d.new
|
83
|
+
@c.validations[:xx].first.call(o, :aa, 40)
|
84
|
+
@c.validations[:yy].first.call(o, :bb, 60)
|
85
|
+
|
86
|
+
o.errors.full_messages.should == ['aa too low']
|
87
|
+
end
|
88
|
+
|
89
|
+
specify "should return true/false for has_validations?" do
|
90
|
+
@c.has_validations?.should == false
|
91
|
+
@c.validates_each(:xx) {1}
|
92
|
+
@c.has_validations?.should == true
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "should provide a validates method that takes block with validation definitions" do
|
96
|
+
@c.validates do
|
97
|
+
coolness_of :blah
|
98
|
+
end
|
99
|
+
@c.validations[:blah].should_not be_empty
|
100
|
+
|
101
|
+
o = @d.new
|
102
|
+
@c.validations[:blah].first.call(o, :ttt, 40)
|
103
|
+
o.errors.full_messages.should == ['ttt is not cool']
|
104
|
+
o.errors.clear
|
105
|
+
@c.validations[:blah].first.call(o, :ttt, :cool)
|
106
|
+
o.errors.should be_empty
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "A Validatable instance" do
|
111
|
+
setup do
|
112
|
+
@c = Class.new do
|
113
|
+
attr_accessor :score
|
114
|
+
|
115
|
+
include Sequel::Validatable
|
116
|
+
|
117
|
+
validates_each :score do |o, a, v|
|
118
|
+
o.errors[a] << 'too low' if v < 87
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
@o = @c.new
|
123
|
+
end
|
124
|
+
|
125
|
+
specify "should supply a #valid? method that returns true if validations pass" do
|
126
|
+
@o.score = 50
|
127
|
+
@o.should_not be_valid
|
128
|
+
@o.score = 100
|
129
|
+
@o.should be_valid
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "should provide an errors object" do
|
133
|
+
@o.score = 100
|
134
|
+
@o.should be_valid
|
135
|
+
@o.errors.should be_empty
|
136
|
+
|
137
|
+
@o.score = 86
|
138
|
+
@o.should_not be_valid
|
139
|
+
@o.errors[:score].should == ['too low']
|
140
|
+
@o.errors[:blah].should be_empty
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe Sequel::Validatable::Generator do
|
145
|
+
setup do
|
146
|
+
$testit = nil
|
147
|
+
|
148
|
+
@c = Class.new do
|
149
|
+
include Sequel::Validatable
|
150
|
+
|
151
|
+
def self.validates_blah
|
152
|
+
$testit = 1324
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
specify "should instance_eval the block, sending everything to its receiver" do
|
158
|
+
Sequel::Validatable::Generator.new(@c) do
|
159
|
+
blah
|
160
|
+
end
|
161
|
+
$testit.should == 1324
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "Sequel validations" do
|
166
|
+
setup do
|
167
|
+
@c = Class.new do
|
168
|
+
attr_accessor :value
|
169
|
+
include Sequel::Validatable
|
170
|
+
end
|
171
|
+
@m = @c.new
|
172
|
+
end
|
173
|
+
|
174
|
+
specify "should validate acceptance_of" do
|
175
|
+
@c.validates_acceptance_of :value
|
176
|
+
@m.should be_valid
|
177
|
+
@m.value = '1'
|
178
|
+
@m.should be_valid
|
179
|
+
end
|
180
|
+
|
181
|
+
specify "should validate acceptance_of with accept" do
|
182
|
+
@c.validates_acceptance_of :value, :accept => 'true'
|
183
|
+
@m.value = '1'
|
184
|
+
@m.should_not be_valid
|
185
|
+
@m.value = 'true'
|
186
|
+
@m.should be_valid
|
187
|
+
end
|
188
|
+
|
189
|
+
specify "should validate acceptance_of with allow_nil => false" do
|
190
|
+
@c.validates_acceptance_of :value, :allow_nil => false
|
191
|
+
@m.should_not be_valid
|
192
|
+
end
|
193
|
+
|
194
|
+
specify "should validate confirmation_of" do
|
195
|
+
@c.send(:attr_accessor, :value_confirmation)
|
196
|
+
@c.validates_confirmation_of :value
|
197
|
+
|
198
|
+
@m.value = 'blah'
|
199
|
+
@m.should_not be_valid
|
200
|
+
|
201
|
+
@m.value_confirmation = 'blah'
|
202
|
+
@m.should be_valid
|
203
|
+
end
|
204
|
+
|
205
|
+
specify "should validate format_of" do
|
206
|
+
@c.validates_format_of :value, :with => /.+_.+/
|
207
|
+
@m.value = 'abc_'
|
208
|
+
@m.should_not be_valid
|
209
|
+
@m.value = 'abc_def'
|
210
|
+
@m.should be_valid
|
211
|
+
end
|
212
|
+
|
213
|
+
specify "should raise for validate_format_of without regexp" do
|
214
|
+
proc {@c.validates_format_of :value}.should raise_error(Sequel::Error)
|
215
|
+
proc {@c.validates_format_of :value, :with => :blah}.should raise_error(Sequel::Error)
|
216
|
+
end
|
217
|
+
|
218
|
+
specify "should validate length_of with maximum" do
|
219
|
+
@c.validates_length_of :value, :maximum => 5
|
220
|
+
@m.should_not be_valid
|
221
|
+
@m.value = '12345'
|
222
|
+
@m.should be_valid
|
223
|
+
@m.value = '123456'
|
224
|
+
@m.should_not be_valid
|
225
|
+
end
|
226
|
+
|
227
|
+
specify "should validate length_of with minimum" do
|
228
|
+
@c.validates_length_of :value, :minimum => 5
|
229
|
+
@m.should_not be_valid
|
230
|
+
@m.value = '12345'
|
231
|
+
@m.should be_valid
|
232
|
+
@m.value = '1234'
|
233
|
+
@m.should_not be_valid
|
234
|
+
end
|
235
|
+
|
236
|
+
specify "should validate length_of with within" do
|
237
|
+
@c.validates_length_of :value, :within => 2..5
|
238
|
+
@m.should_not be_valid
|
239
|
+
@m.value = '12345'
|
240
|
+
@m.should be_valid
|
241
|
+
@m.value = '1'
|
242
|
+
@m.should_not be_valid
|
243
|
+
@m.value = '123456'
|
244
|
+
@m.should_not be_valid
|
245
|
+
end
|
246
|
+
|
247
|
+
specify "should validate length_of with is" do
|
248
|
+
@c.validates_length_of :value, :is => 3
|
249
|
+
@m.should_not be_valid
|
250
|
+
@m.value = '123'
|
251
|
+
@m.should be_valid
|
252
|
+
@m.value = '12'
|
253
|
+
@m.should_not be_valid
|
254
|
+
@m.value = '1234'
|
255
|
+
@m.should_not be_valid
|
256
|
+
end
|
257
|
+
|
258
|
+
specify "should validate length_of with allow_nil" do
|
259
|
+
@c.validates_length_of :value, :is => 3, :allow_nil => true
|
260
|
+
@m.should be_valid
|
261
|
+
end
|
262
|
+
|
263
|
+
specify "should validate numericality_of" do
|
264
|
+
@c.validates_numericality_of :value
|
265
|
+
@m.value = 'blah'
|
266
|
+
@m.should_not be_valid
|
267
|
+
@m.value = '123'
|
268
|
+
@m.should be_valid
|
269
|
+
@m.value = '123.1231'
|
270
|
+
@m.should be_valid
|
271
|
+
end
|
272
|
+
|
273
|
+
specify "should validate numericality_of with only_integer" do
|
274
|
+
@c.validates_numericality_of :value, :only_integer => true
|
275
|
+
@m.value = 'blah'
|
276
|
+
@m.should_not be_valid
|
277
|
+
@m.value = '123'
|
278
|
+
@m.should be_valid
|
279
|
+
@m.value = '123.1231'
|
280
|
+
@m.should_not be_valid
|
281
|
+
end
|
282
|
+
|
283
|
+
specify "should validate presence_of" do
|
284
|
+
@c.validates_presence_of :value
|
285
|
+
@m.should_not be_valid
|
286
|
+
@m.value = ''
|
287
|
+
@m.should_not be_valid
|
288
|
+
@m.value = 1234
|
289
|
+
@m.should be_valid
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
3
293
|
describe Sequel::Model, "Validations" do
|
4
294
|
|
5
295
|
before(:all) do
|
@@ -31,35 +321,18 @@ describe Sequel::Model, "Validations" do
|
|
31
321
|
end
|
32
322
|
end
|
33
323
|
|
34
|
-
it "should have a hook before validating" do
|
35
|
-
class Person < Sequel::Model(:people)
|
36
|
-
before_validation do
|
37
|
-
self.name = "default name"
|
38
|
-
end
|
39
|
-
validations.clear
|
40
|
-
validates_presence_of :name
|
41
|
-
end
|
42
|
-
|
43
|
-
@person = Person.new
|
44
|
-
@person.valid?.should be_true
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should include errors from other models" do
|
48
|
-
pending("Waiting for Wayne's amazing associations!")
|
49
|
-
end
|
50
|
-
|
51
324
|
it "should validate the acceptance of a column" do
|
52
|
-
class Cow < Sequel::Model(:cows)
|
325
|
+
class Cow < Sequel::Model(:cows)
|
53
326
|
validations.clear
|
54
|
-
validates_acceptance_of :got_milk
|
327
|
+
validates_acceptance_of :got_milk, :accept => 'blah', :allow_nil => false
|
55
328
|
end
|
56
329
|
|
57
330
|
@cow = Cow.new
|
58
|
-
@cow.
|
59
|
-
@cow.errors.
|
331
|
+
@cow.should_not be_valid
|
332
|
+
@cow.errors.full_messages.should == ["got_milk is not accepted"]
|
60
333
|
|
61
|
-
@cow.got_milk = "
|
62
|
-
@cow.
|
334
|
+
@cow.got_milk = "blah"
|
335
|
+
@cow.should be_valid
|
63
336
|
end
|
64
337
|
|
65
338
|
it "should validate the confirmation of a column" do
|
@@ -73,29 +346,11 @@ describe Sequel::Model, "Validations" do
|
|
73
346
|
end
|
74
347
|
|
75
348
|
@user = User.new
|
76
|
-
@user.
|
77
|
-
@user.errors.
|
349
|
+
@user.should_not be_valid
|
350
|
+
@user.errors.full_messages.should == ["password is not confirmed"]
|
78
351
|
|
79
352
|
@user.password = "test"
|
80
|
-
@user.
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should validate each with logic" do
|
84
|
-
class ZipCodeService; end
|
85
|
-
|
86
|
-
class Address < Sequel::Model(:addresses)
|
87
|
-
validations.clear
|
88
|
-
validates_each :zip_code, :logic => lambda { errors.add(:zip_code, "is not valid") unless ZipCodeService.allows(zip_code) }
|
89
|
-
end
|
90
|
-
|
91
|
-
@address = Address.new :zip_code => "48108"
|
92
|
-
ZipCodeService.should_receive(:allows).with("48108").and_return(false)
|
93
|
-
@address.valid?.should be_false
|
94
|
-
@address.errors.on(:zip_code).should == "is not valid"
|
95
|
-
|
96
|
-
@address2 = Address.new :zip_code => "48104"
|
97
|
-
ZipCodeService.should_receive(:allows).with("48104").and_return(true)
|
98
|
-
@address2.valid?.should be_true
|
353
|
+
@user.should be_valid
|
99
354
|
end
|
100
355
|
|
101
356
|
it "should validate format of column" do
|
@@ -109,9 +364,9 @@ describe Sequel::Model, "Validations" do
|
|
109
364
|
@person.valid?.should be_true
|
110
365
|
end
|
111
366
|
|
112
|
-
it "should allow for :with_exactly => /[a-zA-Z]/, which wraps the supplied regex with ^<regex>$" do
|
113
|
-
|
114
|
-
end
|
367
|
+
# it "should allow for :with_exactly => /[a-zA-Z]/, which wraps the supplied regex with ^<regex>$" do
|
368
|
+
# pending("TODO: Add this option to Validatable#validates_format_of")
|
369
|
+
# end
|
115
370
|
|
116
371
|
it "should validate length of column" do
|
117
372
|
class Person < Sequel::Model(:people)
|
@@ -129,17 +384,20 @@ describe Sequel::Model, "Validations" do
|
|
129
384
|
:middle_name => "danger"
|
130
385
|
)
|
131
386
|
|
132
|
-
@person.
|
133
|
-
@person.errors.
|
134
|
-
@person.errors.
|
135
|
-
|
136
|
-
|
387
|
+
@person.should_not be_valid
|
388
|
+
@person.errors.full_messages.size.should == 4
|
389
|
+
@person.errors.full_messages.should include(
|
390
|
+
'first_name is too long',
|
391
|
+
'last_name is too short',
|
392
|
+
'middle_name is the wrong length',
|
393
|
+
'initials is the wrong length'
|
394
|
+
)
|
137
395
|
|
138
396
|
@person.first_name = "Lancelot"
|
139
397
|
@person.last_name = "1234567890123456789012345678901"
|
140
398
|
@person.initials = "LC"
|
141
399
|
@person.middle_name = "Will"
|
142
|
-
@person.
|
400
|
+
@person.should be_valid
|
143
401
|
end
|
144
402
|
|
145
403
|
it "should validate numericality of column" do
|
@@ -149,42 +407,27 @@ describe Sequel::Model, "Validations" do
|
|
149
407
|
end
|
150
408
|
|
151
409
|
@person = Person.new :age => "Twenty"
|
152
|
-
@person.
|
153
|
-
@person.errors.
|
410
|
+
@person.should_not be_valid
|
411
|
+
@person.errors.full_messages.should == ['age is not a number']
|
154
412
|
|
155
413
|
@person.age = 20
|
156
|
-
@person.
|
414
|
+
@person.should be_valid
|
157
415
|
end
|
158
416
|
|
159
417
|
it "should validate the presence of a column" do
|
160
|
-
class Cow < Sequel::Model(:cows)
|
418
|
+
class Cow < Sequel::Model(:cows)
|
161
419
|
validations.clear
|
162
420
|
validates_presence_of :name
|
163
421
|
end
|
164
422
|
|
165
423
|
@cow = Cow.new
|
166
|
-
@cow.
|
167
|
-
@cow.errors.
|
168
|
-
@cow.errors.full_messages.first.should == "Name can't be empty"
|
424
|
+
@cow.should_not be_valid
|
425
|
+
@cow.errors.full_messages.should == ['name is not present']
|
169
426
|
|
170
427
|
@cow.name = "Betsy"
|
171
|
-
@cow.
|
428
|
+
@cow.should be_valid
|
172
429
|
end
|
173
430
|
|
174
|
-
it "should validate true for a column" do
|
175
|
-
class Person < Sequel::Model(:people)
|
176
|
-
validations.clear
|
177
|
-
validates_true_for :first_name, :logic => lambda { first_name == "Alison" }
|
178
|
-
end
|
179
|
-
|
180
|
-
@person = Person.new :first_name => "Nina"
|
181
|
-
@person.valid?.should be_false
|
182
|
-
@person.errors.on(:first_name).should == "is invalid"
|
183
|
-
|
184
|
-
@person.first_name = "Alison"
|
185
|
-
@person.valid?.should be_true
|
186
|
-
end
|
187
|
-
|
188
431
|
it "should have a validates block that calls multple validations" do
|
189
432
|
class Person < Sequel::Model(:people)
|
190
433
|
validations.clear
|
@@ -194,7 +437,7 @@ describe Sequel::Model, "Validations" do
|
|
194
437
|
end
|
195
438
|
end
|
196
439
|
|
197
|
-
Person.validations.
|
440
|
+
Person.validations[:first_name].size.should == 2
|
198
441
|
|
199
442
|
@person = Person.new :first_name => "Lancelot99"
|
200
443
|
@person.valid?.should be_false
|
@@ -203,12 +446,6 @@ describe Sequel::Model, "Validations" do
|
|
203
446
|
@person2.valid?.should be_true
|
204
447
|
end
|
205
448
|
|
206
|
-
it "should require and include the validatable gem" do
|
207
|
-
Gem.loaded_specs["validatable"].should_not be_nil
|
208
|
-
Sequel::Model.should respond_to(:validates_format_of) # validatable gem
|
209
|
-
Sequel::Model.should respond_to(:validations) # Validations module
|
210
|
-
end
|
211
|
-
|
212
449
|
it "should allow 'longhand' validations direcly within the model." do
|
213
450
|
lambda {
|
214
451
|
class Person < Sequel::Model(:people)
|
@@ -219,26 +456,6 @@ describe Sequel::Model, "Validations" do
|
|
219
456
|
Person.validations.length.should eql(1)
|
220
457
|
end
|
221
458
|
|
222
|
-
it "should validates do should allow shorthand method for every longhand validates_* method" do
|
223
|
-
class Person
|
224
|
-
validations.clear
|
225
|
-
validates do
|
226
|
-
format_of :first_name, :with => /^[a-zA-Z]+$/
|
227
|
-
length_of :first_name, :maximum => 30
|
228
|
-
presence_of :first_name
|
229
|
-
numericality_of :age
|
230
|
-
acceptance_of :terms
|
231
|
-
confirmation_of :password
|
232
|
-
true_for :first_name, :logic => lambda { first_name == "Alison" }
|
233
|
-
#validates_each :last_name, :logic => lambda { errors.add(:zip_code, "is not valid") unless ZipCodeService.allows(zip_code) }
|
234
|
-
#base
|
235
|
-
end
|
236
|
-
|
237
|
-
# Now check to make sure that each validation exists in the model's validations.
|
238
|
-
end
|
239
|
-
pending("finish this spec for each case")
|
240
|
-
end
|
241
|
-
|
242
459
|
it "should define a has_validations? method which returns true if the model has validations, false otherwise" do
|
243
460
|
class Person < Sequel::Model(:people)
|
244
461
|
validations.clear
|
@@ -255,22 +472,53 @@ describe Sequel::Model, "Validations" do
|
|
255
472
|
Person.should have_validations
|
256
473
|
Smurf.should_not have_validations
|
257
474
|
end
|
258
|
-
|
259
475
|
end
|
260
476
|
|
261
|
-
describe
|
477
|
+
describe "Model#save!" do
|
478
|
+
setup do
|
479
|
+
@c = Class.new(Sequel::Model(:people)) do
|
480
|
+
def columns; [:id]; end
|
481
|
+
|
482
|
+
validates_each :id do |o, a, v|
|
483
|
+
o.errors[a] << 'blah' unless v == 5
|
484
|
+
end
|
485
|
+
end
|
486
|
+
@m = @c.new(:id => 4)
|
487
|
+
MODEL_DB.reset
|
488
|
+
end
|
262
489
|
|
490
|
+
specify "should save regardless of validations" do
|
491
|
+
@m.should_not be_valid
|
492
|
+
@m.save!
|
493
|
+
MODEL_DB.sqls.should == ['UPDATE people SET id = 4 WHERE (id = 4)']
|
494
|
+
end
|
495
|
+
end
|
263
496
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
497
|
+
describe "Model#save!" do
|
498
|
+
setup do
|
499
|
+
@c = Class.new(Sequel::Model(:people)) do
|
500
|
+
def columns; [:id]; end
|
501
|
+
|
502
|
+
validates_each :id do |o, a, v|
|
503
|
+
o.errors[a] << 'blah' unless v == 5
|
268
504
|
end
|
269
505
|
end
|
506
|
+
@m = @c.new(:id => 4)
|
507
|
+
MODEL_DB.reset
|
508
|
+
end
|
509
|
+
|
510
|
+
specify "should save only if validations pass" do
|
511
|
+
@m.should_not be_valid
|
512
|
+
@m.save
|
513
|
+
MODEL_DB.sqls.should be_empty
|
514
|
+
|
515
|
+
@m.id = 5
|
516
|
+
@m.should be_valid
|
517
|
+
@m.save
|
518
|
+
MODEL_DB.sqls.should == ['UPDATE people SET id = 5 WHERE (id = 5)']
|
270
519
|
end
|
271
520
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
end
|
521
|
+
specify "should return false if validations fail" do
|
522
|
+
@m.save.should == false
|
523
|
+
end
|
524
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.3"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-01-
|
12
|
+
date: 2008-01-18 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -21,15 +21,6 @@ dependencies:
|
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: "0.5"
|
23
23
|
version:
|
24
|
-
- !ruby/object:Gem::Dependency
|
25
|
-
name: validatable
|
26
|
-
version_requirement:
|
27
|
-
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
requirements:
|
29
|
-
- - ">="
|
30
|
-
- !ruby/object:Gem::Version
|
31
|
-
version: "0"
|
32
|
-
version:
|
33
24
|
description: Lightweight ORM for Ruby
|
34
25
|
email: ciconia@gmail.com
|
35
26
|
executables: []
|