scrivener-cj 0.2.0

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.
@@ -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: