scrivener-cj 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48787f153f18050703d4e6f12f55ba39bb022aa7
4
+ data.tar.gz: 15ed1b2b576e08bdd4d9b6af2791199af9f37039
5
+ SHA512:
6
+ metadata.gz: 22eb38b1532c790c09a6b2e823744f0941fc877ee6fdc3f9ee059dfe667de8e425f63f5f6778d1f5c24e557a311f56970734811ce55765cf1f9c727835a4c06e
7
+ data.tar.gz: 7e925f74e05a2e7f0d9d250ac54656d5a029a39a8c767ca64d98ce58c01300d5af2ab1fa5fe9615d31bd3cc783b57ae504ee6e71fe2f8efeae0e9946cdddda4d
data/AUTHORS ADDED
@@ -0,0 +1,3 @@
1
+ * Cyril David (cyx)
2
+ * Lucas Nasif (ehqhvm)
3
+ * Michel Martens (soveran)
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Michel Martens
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,251 @@
1
+ Scrivener
2
+ =========
3
+
4
+ Validation frontend for models.
5
+
6
+ Description
7
+ -----------
8
+
9
+ Scrivener removes the validation responsibility from models and acts as a
10
+ filter for whitelisted attributes.
11
+
12
+ A model may expose different APIs to satisfy different purposes. For example,
13
+ the set of validations for a User in a Sign up process may not be the same
14
+ as the one exposed to an Admin when editing a user profile. While you want
15
+ the User to provide an email, a password and a password confirmation, you
16
+ probably don't want the admin to mess with those attributes at all.
17
+
18
+ In a wizard, different model states ask for different validations, and a single
19
+ set of validations for the whole process is not the best solution.
20
+
21
+ Scrivener is Bureaucrat's little brother. It draws all the inspiration from it
22
+ and its features are a subset of Bureaucrat's. For a more robust and tested
23
+ solution, please [check it](https://github.com/tizoc/bureaucrat).
24
+
25
+ This library exists to satify the need of extracting Ohm's validations for
26
+ reuse in other scenarios.
27
+
28
+ Usage
29
+ -----
30
+
31
+ Using Scrivener feels very natural no matter what underlying model you are
32
+ using. As it provides its own validation and whitelisting features, you can
33
+ choose to ignore the ones that come bundled with ORMs.
34
+
35
+ This short example illustrates how to move the validation and whitelisting
36
+ responsibilities away from the model and into Scrivener:
37
+
38
+ ```ruby
39
+ # We use Sequel::Model in this example, but it applies to other ORMs such
40
+ # as Ohm or ActiveRecord.
41
+ class Article < Sequel::Model
42
+
43
+ # Whitelist for mass assigned attributes.
44
+ set_allowed_columns :title, :body, :state
45
+
46
+ # Validations for all contexts.
47
+ def validate
48
+ validates_presence :title
49
+ validates_presence :body
50
+ validates_presence :state
51
+ end
52
+ end
53
+
54
+ title = "Bartleby, the Scrivener"
55
+ body = "I am a rather elderly man..."
56
+
57
+ # When using the model...
58
+ article = Article.new(title: title, body: body)
59
+
60
+ article.valid? #=> false
61
+ article.errors[:state] #=> [:not_present]
62
+ ```
63
+
64
+ Of course, what you would do instead is declare `:title` and `:body` as allowed
65
+ columns, then assign `:state` using the attribute accessor. The reason for this
66
+ example is to show how you need to work around the fact that there's a single
67
+ declaration for allowed columns and validations, which in many cases is a great
68
+ feature and in others is a minor obstacle.
69
+
70
+ Now see what happens with Scrivener:
71
+
72
+ ```ruby
73
+ # Now the model has no validations or whitelists. It may still have schema
74
+ # constraints, which is a good practice to enforce data integrity.
75
+ class Article < Sequel::Model
76
+ end
77
+
78
+ # The attribute accessors are the only fields that will be set. If more
79
+ # fields are sent when using mass assignment, a NoMethodError exception is
80
+ # raised.
81
+ #
82
+ # Note how in this example we don't accept the status attribute.
83
+ class Edit < Scrivener
84
+ attr_accessor :title
85
+ attr_accessor :body
86
+
87
+ def validate
88
+ assert_present :title
89
+ assert_present :body
90
+ end
91
+ end
92
+
93
+ edit = Edit.new(title: title, body: body)
94
+ edit.valid? #=> true
95
+
96
+ article = Article.new(edit.attributes)
97
+ article.save
98
+
99
+ # And now we only ask for the status.
100
+ class Publish < Scrivener
101
+ attr_accessor :status
102
+
103
+ def validate
104
+ assert_format :status, /^(published|draft)$/
105
+ end
106
+ end
107
+
108
+ publish = Publish.new(status: "published")
109
+ publish.valid? #=> true
110
+
111
+ article.update_attributes(publish.attributes)
112
+
113
+ # If we try to change other fields...
114
+ publish = Publish.new(status: "published", title: "foo")
115
+ #=> NoMethodError: undefined method `title=' for #<Publish...>
116
+ ```
117
+
118
+ It's important to note that using Scrivener implies a greater risk than using
119
+ the model validations. Having a central repository of mass assignable
120
+ attributes and validations is more secure in most scenarios.
121
+
122
+ Slices
123
+ ------
124
+
125
+ If you don't need all the attributes after the filtering is done,
126
+ you can fetch just the ones you need. For example:
127
+
128
+ ```ruby
129
+ class SignUp < Scrivener
130
+ attr_accessor :email
131
+ attr_accessor :password
132
+ attr_accessor :password_confirmation
133
+
134
+ def validate
135
+ assert_email :email
136
+
137
+ if assert_present :password
138
+ assert_equal :password, :password_confirmation
139
+ end
140
+ end
141
+
142
+ def assert_equal(f1, f2)
143
+ assert send(f1) == send(f2), [f1, f2, :not_equal]
144
+ end
145
+ end
146
+
147
+ filter = SignUp.new(email: "info@example.com",
148
+ password: "monkey",
149
+ password_confirmation: "monkey")
150
+
151
+
152
+ # If the validation succeeds, we only need email and password to
153
+ # create a new user, and we can discard the password_confirmation.
154
+ if filter.valid?
155
+ User.create(filter.slice(:email, :password))
156
+ end
157
+ ```
158
+
159
+ By calling `slice` with a list of attributes, you get a hash with only
160
+ those key/value pairs.
161
+
162
+ Assertions
163
+ -----------
164
+
165
+ Scrivener ships with some basic assertions. The following is a brief description
166
+ for each of them:
167
+
168
+ ### assert
169
+
170
+ The `assert` method is used by all the other assertions. It pushes the
171
+ second parameter to the list of errors if the first parameter evaluates
172
+ to false.
173
+
174
+ ``` ruby
175
+ def assert(value, error)
176
+ value or errors[error.first].push(error.last) && false
177
+ end
178
+ ```
179
+
180
+ ### assert_present
181
+
182
+ Checks that the given field is not nil or empty. The error code for this
183
+ assertion is `:not_present`.
184
+
185
+ ### assert_equal
186
+
187
+ Check that the attribute has the expected value. It uses === for
188
+ comparison, so type checks are possible too. Note that in order to
189
+ make the case equality work, the check inverts the order of the
190
+ arguments: `assert_equal :foo, Bar` is translated to the expression
191
+ `Bar === send(:foo)`.
192
+
193
+ ### assert_format
194
+
195
+ Checks that the given field matches the provided regular expression.
196
+ The error code for this assertion is `:format`.
197
+
198
+ ### assert_numeric
199
+
200
+ Checks that the given field holds a number as a Fixnum or as a string
201
+ representation. The error code for this assertion is `:not_numeric`.
202
+
203
+ ### assert_url
204
+
205
+ Provides a pretty general URL regular expression match. An important
206
+ point to make is that this assumes that the URL should start with
207
+ `http://` or `https://`. The error code for this assertion is
208
+ `:not_url`.
209
+
210
+ ### assert_email
211
+
212
+ In this current day and age, almost all web applications need to
213
+ validate an email address. This pretty much matches 99% of the emails
214
+ out there. The error code for this assertion is `:not_email`.
215
+
216
+ ### assert_member
217
+
218
+ Checks that a given field is contained within a set of values (i.e.
219
+ like an `ENUM`).
220
+
221
+ ``` ruby
222
+ def validate
223
+ assert_member :state, %w{pending paid delivered}
224
+ end
225
+ ```
226
+
227
+ The error code for this assertion is `:not_valid`
228
+
229
+ ### assert_length
230
+
231
+ Checks that a given field's length falls under a specified range.
232
+
233
+ ``` ruby
234
+ def validate
235
+ assert_length :username, 3..20
236
+ end
237
+ ```
238
+
239
+ The error code for this assertion is `:not_in_range`.
240
+
241
+ ### assert_decimal
242
+
243
+ Checks that a given field looks like a number in the human sense
244
+ of the word. Valid numbers are: 0.1, .1, 1, 1.1, 3.14159, etc.
245
+
246
+ The error code for this assertion is `:not_decimal`.
247
+
248
+ Installation
249
+ ------------
250
+
251
+ $ gem install scrivener
@@ -0,0 +1,6 @@
1
+ task :test do
2
+ require "cutest"
3
+ Cutest.run(Dir["test/*.rb"])
4
+ end
5
+
6
+ task :default => :test
@@ -0,0 +1,62 @@
1
+ require_relative "scrivener/validations"
2
+
3
+ class Scrivener
4
+ VERSION = "0.2.0"
5
+
6
+ include Validations
7
+
8
+ # Initialize with a hash of attributes and values.
9
+ # If extra attributes are sent, a NoMethodError exception will be raised.
10
+ #
11
+ # @example
12
+ #
13
+ # class EditPost < Scrivener
14
+ # attr_accessor :title
15
+ # attr_accessor :body
16
+ #
17
+ # def validate
18
+ # assert_present :title
19
+ # assert_present :body
20
+ # end
21
+ # end
22
+ #
23
+ # edit = EditPost.new(title: "Software Tools")
24
+ #
25
+ # edit.valid? #=> false
26
+ #
27
+ # edit.errors[:title] #=> []
28
+ # edit.errors[:body] #=> [:not_present]
29
+ #
30
+ # edit.body = "Recommended reading..."
31
+ #
32
+ # edit.valid? #=> true
33
+ #
34
+ # # Now it's safe to initialize the model.
35
+ # post = Post.new(edit.attributes)
36
+ # post.save
37
+ def initialize(atts)
38
+ atts.each do |key, val|
39
+ send(:"#{key}=", val)
40
+ end
41
+ end
42
+
43
+ # Return hash of attributes and values.
44
+ def attributes
45
+ Hash.new.tap do |atts|
46
+ instance_variables.each do |ivar|
47
+ next if ivar == :@errors
48
+
49
+ att = ivar[1..-1].to_sym
50
+ atts[att] = send(att)
51
+ end
52
+ end
53
+ end
54
+
55
+ def slice(*keys)
56
+ Hash.new.tap do |atts|
57
+ keys.each do |att|
58
+ atts[att] = send(att)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,206 @@
1
+ class Scrivener
2
+
3
+ # Provides a base implementation for extensible validation routines.
4
+ # {Scrivener::Validations} currently only provides the following assertions:
5
+ #
6
+ # * assert
7
+ # * assert_present
8
+ # * assert_format
9
+ # * assert_numeric
10
+ # * assert_url
11
+ # * assert_email
12
+ # * assert_member
13
+ # * assert_length
14
+ # * assert_decimal
15
+ # * assert_equal
16
+ #
17
+ # The core tenets that Scrivener::Validations advocates can be summed up in a
18
+ # few bullet points:
19
+ #
20
+ # 1. Validations are much simpler and better done using composition rather
21
+ # than macros.
22
+ # 2. Error messages should be kept separate and possibly in the view or
23
+ # presenter layer.
24
+ # 3. It should be easy to write your own validation routine.
25
+ #
26
+ # Other validations are simply added on a per-model or per-project basis.
27
+ #
28
+ # @example
29
+ #
30
+ # class Quote
31
+ # attr_accessor :title
32
+ # attr_accessor :price
33
+ # attr_accessor :date
34
+ #
35
+ # def validate
36
+ # assert_present :title
37
+ # assert_numeric :price
38
+ # assert_format :date, /\A[\d]{4}-[\d]{1,2}-[\d]{1,2}\z
39
+ # end
40
+ # end
41
+ #
42
+ # s = Quote.new
43
+ # s.valid?
44
+ # # => false
45
+ #
46
+ # s.errors
47
+ # # => { :title => [:not_present],
48
+ # :price => [:not_numeric],
49
+ # :date => [:format] }
50
+ #
51
+ module Validations
52
+
53
+ # Check if the current model state is valid. Each call to {#valid?} will
54
+ # reset the {#errors} array.
55
+ #
56
+ # All validations should be declared in a `validate` method.
57
+ #
58
+ # @example
59
+ #
60
+ # class Login
61
+ # attr_accessor :username
62
+ # attr_accessor :password
63
+ #
64
+ # def validate
65
+ # assert_present :user
66
+ # assert_present :password
67
+ # end
68
+ # end
69
+ #
70
+ def valid?
71
+ errors.clear
72
+ validate
73
+ errors.empty?
74
+ end
75
+
76
+ # Base validate implementation. Override this method in subclasses.
77
+ def validate
78
+ end
79
+
80
+ # Hash of errors for each attribute in this model.
81
+ def errors
82
+ @errors ||= Hash.new { |hash, key| hash[key] = [] }
83
+ end
84
+
85
+ protected
86
+
87
+ # Allows you to do a validation check against a regular expression.
88
+ # It's important to note that this internally calls {#assert_present},
89
+ # therefore you need not structure your regular expression to check
90
+ # for a non-empty value.
91
+ #
92
+ # @param [Symbol] att The attribute you want to verify the format of.
93
+ # @param [Regexp] format The regular expression with which to compare
94
+ # the value of att with.
95
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
96
+ # when the validation fails.
97
+ def assert_format(att, format, error = [att, :format])
98
+ if assert_present(att, error)
99
+ assert(send(att).to_s.match(format), error)
100
+ end
101
+ end
102
+
103
+ # The most basic and highly useful assertion. Simply checks if the
104
+ # value of the attribute is empty.
105
+ #
106
+ # @param [Symbol] att The attribute you wish to verify the presence of.
107
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
108
+ # when the validation fails.
109
+ def assert_present(att, error = [att, :not_present])
110
+ assert(!send(att).to_s.empty?, error)
111
+ end
112
+
113
+ # Checks if all the characters of an attribute is a digit.
114
+ #
115
+ # @param [Symbol] att The attribute you wish to verify the numeric format.
116
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
117
+ # when the validation fails.
118
+ def assert_numeric(att, error = [att, :not_numeric])
119
+ if assert_present(att, error)
120
+ assert_format(att, /\A\-?\d+\z/, error)
121
+ end
122
+ end
123
+
124
+ URL = /\A(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(2
125
+ 5[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}
126
+ |localhost)(:[0-9]{1,5})?(\/.*)?\z/ix
127
+
128
+ def assert_url(att, error = [att, :not_url])
129
+ if assert_present(att, error)
130
+ assert_format(att, URL, error)
131
+ end
132
+ end
133
+
134
+ EMAIL = /\A([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*
135
+ [\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@
136
+ ((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+
137
+ [a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)\z/ix
138
+
139
+ def assert_email(att, error = [att, :not_email])
140
+ if assert_present(att, error)
141
+ assert_format(att, EMAIL, error)
142
+ end
143
+ end
144
+
145
+ def assert_member(att, set, err = [att, :not_valid])
146
+ assert(set.include?(send(att)), err)
147
+ end
148
+
149
+ def assert_length(att, range, error = [att, :not_in_range])
150
+ if assert_present(att, error)
151
+ val = send(att).to_s
152
+ assert range.include?(val.length), error
153
+ end
154
+ end
155
+
156
+ DECIMAL = /\A\-?(\d+)?(\.\d+)?\z/
157
+
158
+ def assert_decimal(att, error = [att, :not_decimal])
159
+ assert_format att, DECIMAL, error
160
+ end
161
+
162
+ # Check that the attribute has the expected value. It uses === for
163
+ # comparison, so type checks are possible too. Note that in order
164
+ # to make the case equality work, the check inverts the order of
165
+ # the arguments: `assert_equal :foo, Bar` is translated to the
166
+ # expression `Bar === send(:foo)`.
167
+ #
168
+ # @example
169
+ #
170
+ # def validate
171
+ # assert_equal :status, "pending"
172
+ # assert_equal :quantity, Fixnum
173
+ # end
174
+ #
175
+ # @param [Symbol] att The attribute you wish to verify for equality.
176
+ # @param [Object] value The value you want to test against.
177
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
178
+ # when the validation fails.
179
+ def assert_equal(att, value, error = [att, :not_equal])
180
+ assert value === send(att), error
181
+ end
182
+
183
+ # The grand daddy of all assertions. If you want to build custom
184
+ # assertions, or even quick and dirty ones, you can simply use this method.
185
+ #
186
+ # @example
187
+ #
188
+ # class CreatePost
189
+ # attr_accessor :slug
190
+ # attr_accessor :votes
191
+ #
192
+ # def validate
193
+ # assert_slug :slug
194
+ # assert votes.to_i > 0, [:votes, :not_valid]
195
+ # end
196
+ #
197
+ # protected
198
+ # def assert_slug(att, error = [att, :not_slug])
199
+ # assert send(att).to_s =~ /\A[a-z\-0-9]+\z/, error
200
+ # end
201
+ # end
202
+ def assert(value, error)
203
+ value or errors[error.first].push(error.last) && false
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,21 @@
1
+ require "./lib/scrivener"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "scrivener-cj"
5
+ s.version = Scrivener::VERSION
6
+ s.summary = "Validation frontend for models."
7
+ s.description = "Scrivener removes the validation responsibility from models and acts as a filter for whitelisted attributes."
8
+ s.authors = ["Michel Martens"]
9
+ s.email = ["michel@soveran.com"]
10
+ s.homepage = "http://github.com/soveran/scrivener"
11
+ s.files = Dir[
12
+ "LICENSE",
13
+ "AUTHORS",
14
+ "README.md",
15
+ "Rakefile",
16
+ "lib/**/*.rb",
17
+ "*.gemspec",
18
+ "test/**/*.rb"
19
+ ]
20
+ s.add_development_dependency "cutest-cj"
21
+ end
@@ -0,0 +1,262 @@
1
+ require File.expand_path("../lib/scrivener", File.dirname(__FILE__))
2
+
3
+ class A < Scrivener
4
+ attr_accessor :a
5
+ attr_accessor :b
6
+ end
7
+
8
+ scope do
9
+ test "raise when there are extra fields" do
10
+ atts = { :a => 1, :b => 2, :c => 3 }
11
+
12
+ assert_raise NoMethodError do
13
+ filter = A.new(atts)
14
+ end
15
+ end
16
+
17
+ test "not raise when there are less fields" do
18
+ atts = { :a => 1 }
19
+
20
+ assert filter = A.new(atts)
21
+ end
22
+
23
+ test "return attributes" do
24
+ atts = { :a => 1, :b => 2 }
25
+
26
+ filter = A.new(atts)
27
+
28
+ assert_equal atts, filter.attributes
29
+ end
30
+
31
+ test "return only the required attributes" do
32
+ atts = { :a => 1, :b => 2 }
33
+
34
+ filter = A.new(atts)
35
+
36
+ assert_equal filter.slice(:a), { :a => 1 }
37
+ end
38
+ end
39
+
40
+ class B < Scrivener
41
+ attr_accessor :a
42
+ attr_accessor :b
43
+
44
+ def validate
45
+ assert_present :a
46
+ assert_present :b
47
+ end
48
+ end
49
+
50
+ scope do
51
+ test "validations" do
52
+ atts = { :a => 1, :b => 2 }
53
+
54
+ filter = B.new(atts)
55
+
56
+ assert filter.valid?
57
+ end
58
+
59
+ test "validation errors" do
60
+ atts = { :a => 1 }
61
+
62
+ filter = B.new(atts)
63
+
64
+ assert_equal false, filter.valid?
65
+ assert_equal [], filter.errors[:a]
66
+ assert_equal [:not_present], filter.errors[:b]
67
+ end
68
+
69
+ test "attributes without @errors" do
70
+ atts = { :a => 1, :b => 2 }
71
+
72
+ filter = B.new(atts)
73
+
74
+ filter.valid?
75
+ assert_equal atts, filter.attributes
76
+ end
77
+ end
78
+
79
+ class C
80
+ include Scrivener::Validations
81
+
82
+ attr_accessor :a
83
+
84
+ def validate
85
+ assert_present :a
86
+ end
87
+ end
88
+
89
+ scope do
90
+ test "validations without Scrivener" do
91
+ filter = C.new
92
+ filter.a = 1
93
+ assert filter.valid?
94
+
95
+ filter = C.new
96
+ assert_equal false, filter.valid?
97
+ assert_equal [:not_present], filter.errors[:a]
98
+ end
99
+ end
100
+
101
+ class D < Scrivener
102
+ attr_accessor :url, :email
103
+
104
+ def validate
105
+ assert_url :url
106
+ assert_email :email
107
+ end
108
+ end
109
+
110
+ scope do
111
+ test "email & url" do
112
+ filter = D.new({})
113
+
114
+ assert ! filter.valid?
115
+ assert_equal [:not_url], filter.errors[:url]
116
+ assert_equal [:not_email], filter.errors[:email]
117
+
118
+ filter = D.new(url: "google.com", email: "egoogle.com")
119
+
120
+ assert ! filter.valid?
121
+ assert_equal [:not_url], filter.errors[:url]
122
+ assert_equal [:not_email], filter.errors[:email]
123
+
124
+ filter = D.new(url: "http://google.com", email: "me@google.com")
125
+ assert filter.valid?
126
+ end
127
+ end
128
+
129
+ class E < Scrivener
130
+ attr_accessor :a
131
+
132
+ def validate
133
+ assert_length :a, 3..10
134
+ end
135
+ end
136
+
137
+ scope do
138
+ test "length validation" do
139
+ filter = E.new({})
140
+
141
+ assert ! filter.valid?
142
+ assert filter.errors[:a].include?(:not_in_range)
143
+
144
+ filter = E.new(a: "fo")
145
+ assert ! filter.valid?
146
+ assert filter.errors[:a].include?(:not_in_range)
147
+
148
+ filter = E.new(a: "foofoofoofo")
149
+ assert ! filter.valid?
150
+ assert filter.errors[:a].include?(:not_in_range)
151
+
152
+ filter = E.new(a: "foo")
153
+ assert filter.valid?
154
+ end
155
+ end
156
+
157
+ class F < Scrivener
158
+ attr_accessor :status
159
+
160
+ def validate
161
+ assert_member :status, %w{pending paid delivered}
162
+ end
163
+ end
164
+
165
+ scope do
166
+ test "member validation" do
167
+ filter = F.new({})
168
+ assert ! filter.valid?
169
+ assert_equal [:not_valid], filter.errors[:status]
170
+
171
+ filter = F.new(status: "foo")
172
+ assert ! filter.valid?
173
+ assert_equal [:not_valid], filter.errors[:status]
174
+
175
+ %w{pending paid delivered}.each do |status|
176
+ filter = F.new(status: status)
177
+ assert filter.valid?
178
+ end
179
+ end
180
+ end
181
+
182
+ class G < Scrivener
183
+ attr_accessor :a
184
+
185
+ def validate
186
+ assert_decimal :a
187
+ end
188
+ end
189
+
190
+ scope do
191
+ test "decimal validation" do
192
+ filter = G.new({})
193
+ assert ! filter.valid?
194
+ assert_equal [:not_decimal], filter.errors[:a]
195
+
196
+ %w{10 10.1 10.100000 0.100000 .1000 -10}.each do |a|
197
+ filter = G.new(a: a)
198
+ assert filter.valid?
199
+ end
200
+ end
201
+ end
202
+
203
+ class H < Scrivener
204
+ attr_accessor :a
205
+
206
+ def validate
207
+ assert_length :a, 3..10
208
+ end
209
+ end
210
+
211
+ scope do
212
+ test "length validation" do
213
+ filter = H.new({})
214
+
215
+ assert ! filter.valid?
216
+ assert filter.errors[:a].include?(:not_in_range)
217
+
218
+ filter = H.new(a: "fo")
219
+ assert ! filter.valid?
220
+ assert filter.errors[:a].include?(:not_in_range)
221
+
222
+ filter = H.new(a: "foofoofoofo")
223
+ assert ! filter.valid?
224
+ assert filter.errors[:a].include?(:not_in_range)
225
+
226
+ filter = H.new(a: "foo")
227
+ assert filter.valid?
228
+ end
229
+ end
230
+
231
+ class I < Scrivener
232
+ attr_accessor :a
233
+ attr_accessor :b
234
+
235
+ def validate
236
+ assert_equal :a, "foo"
237
+ assert_equal :b, Fixnum
238
+ end
239
+ end
240
+
241
+
242
+ scope do
243
+ test "equality validation" do
244
+ filter = I.new({})
245
+
246
+ assert ! filter.valid?
247
+ assert filter.errors[:a].include?(:not_equal)
248
+ assert filter.errors[:b].include?(:not_equal)
249
+
250
+ filter = I.new(a: "foo", b: "bar")
251
+ assert ! filter.valid?
252
+
253
+ filter = I.new(a: "foo")
254
+ assert ! filter.valid?
255
+ assert filter.errors[:a].empty?
256
+ assert filter.errors[:b].include?(:not_equal)
257
+
258
+ filter = I.new(a: "foo", b: 42)
259
+ filter.valid?
260
+ assert filter.valid?
261
+ end
262
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scrivener-cj
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Michel Martens
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cutest-cj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Scrivener removes the validation responsibility from models and acts
28
+ as a filter for whitelisted attributes.
29
+ email:
30
+ - michel@soveran.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - AUTHORS
36
+ - LICENSE
37
+ - README.md
38
+ - Rakefile
39
+ - lib/scrivener.rb
40
+ - lib/scrivener/validations.rb
41
+ - scrivener.gemspec
42
+ - test/scrivener_test.rb
43
+ homepage: http://github.com/soveran/scrivener
44
+ licenses: []
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.2.2
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Validation frontend for models.
66
+ test_files: []
67
+ has_rdoc: