scrivener 0.3.0 → 1.1.1
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.
- checksums.yaml +5 -5
- data/.gems +1 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING +19 -0
- data/LICENSE +1 -1
- data/README.md +130 -114
- data/lib/scrivener.rb +15 -7
- data/lib/scrivener/validations.rb +5 -5
- data/makefile +4 -0
- data/scrivener.gemspec +11 -16
- data/test/scrivener_test.rb +92 -30
- metadata +16 -12
- data/Rakefile +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e94b74d62fa478e7e64a92078e84e8734897c3b2e5e7e7de2c158185aab79e34
|
|
4
|
+
data.tar.gz: c25c6bbb36c7c7d55ec6edbc037154dae6af10fd07210f790d17338148458020
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c908c3f6be324ea81a909ec081669e6615b1810c692c3e1ef5cb95342188fcb133fdb5c69ff6312879ae37b26361b5abdf01382974e1560c257fffb662e28b2
|
|
7
|
+
data.tar.gz: acfb6f2cac8005c9d13e0c767cdec34258b85d6f29d59a5138e885a298e109ee6d56aea724e7b4cb1fc536e7299801e2c69fd11a0f192f1f458d5cdc4ff331e3
|
data/.gems
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cutest -v 1.2.2
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/pkg
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
## 1.1.1
|
|
2
|
+
|
|
3
|
+
* Avoid warning when forwarding arguments to #validate.
|
|
4
|
+
|
|
5
|
+
## 1.1.0
|
|
6
|
+
|
|
7
|
+
* Allow passing arguments to Scrivener#validate.
|
|
8
|
+
|
|
9
|
+
## 1.0.0
|
|
10
|
+
|
|
11
|
+
* Extra attributes are ignored.
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Before:
|
|
15
|
+
|
|
16
|
+
publish = Publish.new(status: "published", title: "foo")
|
|
17
|
+
publis.attributes # => { :status => "published" }
|
|
18
|
+
# => NoMethodError: undefined method `title=' for #<Publish...>
|
|
19
|
+
|
|
20
|
+
# Now:
|
|
21
|
+
|
|
22
|
+
# Extra fields are discarded
|
|
23
|
+
publish = Publish.new(status: "published", title: "foo")
|
|
24
|
+
publish.attributes # => { :status => "published" }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 0.4.1
|
|
28
|
+
|
|
29
|
+
* Fix creation of symbols for extra attributes.
|
|
30
|
+
|
|
31
|
+
## 0.4.0
|
|
32
|
+
|
|
33
|
+
* Fix `assert_email` and `assert_url` to support longer tld's.
|
|
34
|
+
|
|
35
|
+
## 0.3.0
|
|
36
|
+
|
|
37
|
+
* Add support for negative numbers.
|
|
38
|
+
|
|
39
|
+
## 0.2.0
|
|
40
|
+
|
|
41
|
+
* Add `assert_equal` validation.
|
data/CONTRIBUTING
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
This code tries to solve a particular problem with a very simple
|
|
2
|
+
implementation. We try to keep the code to a minimum while making
|
|
3
|
+
it as clear as possible. The design is very likely finished, and
|
|
4
|
+
if some feature is missing it is possible that it was left out on
|
|
5
|
+
purpose. That said, new usage patterns may arise, and when that
|
|
6
|
+
happens we are ready to adapt if necessary.
|
|
7
|
+
|
|
8
|
+
A good first step for contributing is to meet us on IRC and discuss
|
|
9
|
+
ideas. We spend a lot of time on #lesscode at freenode, always ready
|
|
10
|
+
to talk about code and simplicity. If connecting to IRC is not an
|
|
11
|
+
option, you can create an issue explaining the proposed change and
|
|
12
|
+
a use case. We pay a lot of attention to use cases, because our
|
|
13
|
+
goal is to keep the code base simple. Usually the result of a
|
|
14
|
+
conversation is the creation of a different tool.
|
|
15
|
+
|
|
16
|
+
Please don't start the conversation with a pull request. The code
|
|
17
|
+
should come at last, and even though it may help to convey an idea,
|
|
18
|
+
more often than not it draws the attention to a particular
|
|
19
|
+
implementation.
|
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -6,81 +6,18 @@ Validation frontend for models.
|
|
|
6
6
|
Description
|
|
7
7
|
-----------
|
|
8
8
|
|
|
9
|
-
Scrivener removes the validation responsibility from models and
|
|
10
|
-
filter for whitelisted attributes.
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
|
9
|
+
Scrivener removes the validation responsibility from models and
|
|
10
|
+
acts as a filter for whitelisted attributes. Read about the
|
|
11
|
+
[motivation](#motivation) to understand why this separation of
|
|
12
|
+
concerns is important.
|
|
27
13
|
|
|
28
14
|
Usage
|
|
29
15
|
-----
|
|
30
16
|
|
|
31
|
-
|
|
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:
|
|
17
|
+
A very basic example would be creating a blog post:
|
|
37
18
|
|
|
38
19
|
```ruby
|
|
39
|
-
|
|
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
|
|
20
|
+
class CreateBlogPost < Scrivener
|
|
84
21
|
attr_accessor :title
|
|
85
22
|
attr_accessor :body
|
|
86
23
|
|
|
@@ -89,44 +26,42 @@ class Edit < Scrivener
|
|
|
89
26
|
assert_present :body
|
|
90
27
|
end
|
|
91
28
|
end
|
|
29
|
+
```
|
|
92
30
|
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
31
|
+
In order to use it, you have to create an instance of `CreateBlogPost`
|
|
32
|
+
by passing a hash with the attributes `title` and `body` and their
|
|
33
|
+
corresponding values:
|
|
102
34
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
35
|
+
```ruby
|
|
36
|
+
params = {
|
|
37
|
+
title: "Bartleby",
|
|
38
|
+
body: "I am a rather elderly man..."
|
|
39
|
+
}
|
|
107
40
|
|
|
108
|
-
|
|
109
|
-
|
|
41
|
+
filter = CreateBlogPost.new(params)
|
|
42
|
+
```
|
|
110
43
|
|
|
111
|
-
|
|
44
|
+
Now you can run the validations by calling `filter.valid?`, and you
|
|
45
|
+
can retrieve the attributes by calling `filter.attributes`. If the
|
|
46
|
+
validation fails, a hash of attributes and error codes will be
|
|
47
|
+
available by calling `filter.errors`. For example:
|
|
112
48
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
49
|
+
```ruby
|
|
50
|
+
if filter.valid?
|
|
51
|
+
puts filter.attributes
|
|
52
|
+
else
|
|
53
|
+
puts filter.errors
|
|
54
|
+
end
|
|
116
55
|
```
|
|
117
56
|
|
|
118
|
-
|
|
119
|
-
the
|
|
120
|
-
|
|
57
|
+
For now, we are just printing the attributes and the list of errors,
|
|
58
|
+
but often you will use the attributes to create an instance of a
|
|
59
|
+
model, and you will display the error messages in a view.
|
|
121
60
|
|
|
122
|
-
|
|
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:
|
|
61
|
+
Let's consider the case of creating a new user:
|
|
127
62
|
|
|
128
63
|
```ruby
|
|
129
|
-
class
|
|
64
|
+
class CreateUser < Scrivener
|
|
130
65
|
attr_accessor :email
|
|
131
66
|
attr_accessor :password
|
|
132
67
|
attr_accessor :password_confirmation
|
|
@@ -135,41 +70,79 @@ class SignUp < Scrivener
|
|
|
135
70
|
assert_email :email
|
|
136
71
|
|
|
137
72
|
if assert_present :password
|
|
138
|
-
assert_equal :password,
|
|
73
|
+
assert_equal :password, password_confirmation
|
|
139
74
|
end
|
|
140
75
|
end
|
|
141
|
-
|
|
142
|
-
def assert_equal(f1, f2)
|
|
143
|
-
assert send(f1) == send(f2), [f1, f2, :not_equal]
|
|
144
|
-
end
|
|
145
76
|
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The filter looks very similar, but as you can see the validations
|
|
80
|
+
return booleans, thus they can be nested. In this example, we don't
|
|
81
|
+
want to bother asserting if the password and the password confirmation
|
|
82
|
+
are equal if the password was not provided.
|
|
83
|
+
|
|
84
|
+
Let's instantiate the filter:
|
|
146
85
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
86
|
+
```ruby
|
|
87
|
+
params = {
|
|
88
|
+
email: "info@example.com",
|
|
89
|
+
password: "monkey",
|
|
90
|
+
password_confirmation: "monkey"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
filter = CreateUser.new(params)
|
|
94
|
+
```
|
|
150
95
|
|
|
96
|
+
If the validation succeeds, we only need email and password to
|
|
97
|
+
create a new user, and we can discard the password_confirmation.
|
|
98
|
+
The `filter.slice` method receives a list of attributes and returns
|
|
99
|
+
the attributes hash with any other attributes removed. In this
|
|
100
|
+
example, the hash returned by `filter.slice` will contain only the
|
|
101
|
+
`email` and `password` fields:
|
|
151
102
|
|
|
152
|
-
|
|
153
|
-
# create a new user, and we can discard the password_confirmation.
|
|
103
|
+
```ruby
|
|
154
104
|
if filter.valid?
|
|
155
105
|
User.create(filter.slice(:email, :password))
|
|
156
106
|
end
|
|
157
107
|
```
|
|
158
108
|
|
|
159
|
-
|
|
160
|
-
|
|
109
|
+
Sometimes we might want to use parameters from the outside for validation,
|
|
110
|
+
but don't want the validator to treat them as attributes. In that case we
|
|
111
|
+
can pass arguments to `#valid?`, and they will be forwarded to `#validate`.
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
class CreateComment < Scrivener
|
|
115
|
+
attr_accessor :content
|
|
116
|
+
attr_accessor :article_id
|
|
117
|
+
|
|
118
|
+
def validate(available_articles:)
|
|
119
|
+
assert_present :content
|
|
120
|
+
assert_member :article_id, available_articles.map(&:id)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
params = {
|
|
127
|
+
content: "this is a comment",
|
|
128
|
+
article_id: 57,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
filter = CreateComment.new(params)
|
|
132
|
+
|
|
133
|
+
filter.valid?(available_articles: user.articles)
|
|
134
|
+
```
|
|
161
135
|
|
|
162
136
|
Assertions
|
|
163
137
|
-----------
|
|
164
138
|
|
|
165
|
-
Scrivener ships with some basic assertions.
|
|
166
|
-
for each of them:
|
|
139
|
+
Scrivener ships with some basic assertions.
|
|
167
140
|
|
|
168
141
|
### assert
|
|
169
142
|
|
|
170
143
|
The `assert` method is used by all the other assertions. It pushes the
|
|
171
144
|
second parameter to the list of errors if the first parameter evaluates
|
|
172
|
-
to false
|
|
145
|
+
to `false` or `nil`.
|
|
173
146
|
|
|
174
147
|
``` ruby
|
|
175
148
|
def assert(value, error)
|
|
@@ -177,10 +150,23 @@ def assert(value, error)
|
|
|
177
150
|
end
|
|
178
151
|
```
|
|
179
152
|
|
|
153
|
+
New assertions can be built upon existing ones. For example, let's
|
|
154
|
+
define an assertion for positive numbers:
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
def assert_positive(att, error = [att, :not_positive])
|
|
158
|
+
assert(send(att) > 0, error)
|
|
159
|
+
end
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
This assertion calls `assert` and passes both the result of evaluating
|
|
163
|
+
`send(att) > 0` and the array with the attribute and the error code.
|
|
164
|
+
All assertions respect this API.
|
|
165
|
+
|
|
180
166
|
### assert_present
|
|
181
167
|
|
|
182
|
-
Checks that the given field is not nil or empty. The error code for
|
|
183
|
-
assertion is `:not_present`.
|
|
168
|
+
Checks that the given field is not nil or empty. The error code for
|
|
169
|
+
this assertion is `:not_present`.
|
|
184
170
|
|
|
185
171
|
### assert_equal
|
|
186
172
|
|
|
@@ -245,6 +231,36 @@ of the word. Valid numbers are: 0.1, .1, 1, 1.1, 3.14159, etc.
|
|
|
245
231
|
|
|
246
232
|
The error code for this assertion is `:not_decimal`.
|
|
247
233
|
|
|
234
|
+
Motivation
|
|
235
|
+
----------
|
|
236
|
+
|
|
237
|
+
A model may expose different APIs to satisfy different purposes.
|
|
238
|
+
For example, the set of validations for a User in a sign up process
|
|
239
|
+
may not be the same as the one exposed to an Admin when editing a
|
|
240
|
+
user profile. While you want the User to provide an email, a password
|
|
241
|
+
and a password confirmation, you probably don't want the admin to
|
|
242
|
+
mess with those attributes at all.
|
|
243
|
+
|
|
244
|
+
In a wizard, different model states ask for different validations,
|
|
245
|
+
and a single set of validations for the whole process is not the
|
|
246
|
+
best solution.
|
|
247
|
+
|
|
248
|
+
This library exists to satisfy the need for extracting
|
|
249
|
+
[Ohm](http://ohm.keyvalue.org)'s validations for reuse in other
|
|
250
|
+
scenarios.
|
|
251
|
+
|
|
252
|
+
Using Scrivener feels very natural no matter what underlying model
|
|
253
|
+
you are using. As it provides its own validation and whitelisting
|
|
254
|
+
features, you can choose to ignore those that come bundled with
|
|
255
|
+
ORMs.
|
|
256
|
+
|
|
257
|
+
See also
|
|
258
|
+
--------
|
|
259
|
+
|
|
260
|
+
Scrivener is [Bureaucrat](https://github.com/tizoc/bureaucrat)'s
|
|
261
|
+
little brother. It draws all the inspiration from it and its features
|
|
262
|
+
are a subset of Bureaucrat's.
|
|
263
|
+
|
|
248
264
|
Installation
|
|
249
265
|
------------
|
|
250
266
|
|
data/lib/scrivener.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
require_relative "scrivener/validations"
|
|
2
2
|
|
|
3
3
|
class Scrivener
|
|
4
|
-
VERSION = "
|
|
4
|
+
VERSION = "1.1.1"
|
|
5
5
|
|
|
6
6
|
include Validations
|
|
7
7
|
|
|
8
8
|
# Initialize with a hash of attributes and values.
|
|
9
|
-
#
|
|
9
|
+
# Extra attributes are discarded.
|
|
10
10
|
#
|
|
11
11
|
# @example
|
|
12
12
|
#
|
|
@@ -36,17 +36,25 @@ class Scrivener
|
|
|
36
36
|
# post.save
|
|
37
37
|
def initialize(atts)
|
|
38
38
|
atts.each do |key, val|
|
|
39
|
-
|
|
39
|
+
accessor = "#{key}="
|
|
40
|
+
|
|
41
|
+
if respond_to?(accessor)
|
|
42
|
+
send(accessor, val)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def _accessors
|
|
48
|
+
public_methods(false).select do |name|
|
|
49
|
+
name[-1] == "="
|
|
40
50
|
end
|
|
41
51
|
end
|
|
42
52
|
|
|
43
53
|
# Return hash of attributes and values.
|
|
44
54
|
def attributes
|
|
45
55
|
Hash.new.tap do |atts|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
att = ivar[1..-1].to_sym
|
|
56
|
+
_accessors.each do |accessor|
|
|
57
|
+
att = accessor[0..-2].to_sym
|
|
50
58
|
atts[att] = send(att)
|
|
51
59
|
end
|
|
52
60
|
end
|
|
@@ -67,14 +67,14 @@ class Scrivener
|
|
|
67
67
|
# end
|
|
68
68
|
# end
|
|
69
69
|
#
|
|
70
|
-
def valid?
|
|
70
|
+
def valid?(*args, **kargs)
|
|
71
71
|
errors.clear
|
|
72
|
-
validate
|
|
72
|
+
validate(*args, **kargs)
|
|
73
73
|
errors.empty?
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Base validate implementation. Override this method in subclasses.
|
|
77
|
-
def validate
|
|
77
|
+
def validate(*args, **kargs)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
# Hash of errors for each attribute in this model.
|
|
@@ -121,7 +121,7 @@ class Scrivener
|
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
URL = /\A(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,
|
|
124
|
+
URL = /\A(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,12}|(2
|
|
125
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
126
|
|localhost)(:[0-9]{1,5})?(\/.*)?\z/ix
|
|
127
127
|
|
|
@@ -134,7 +134,7 @@ class Scrivener
|
|
|
134
134
|
EMAIL = /\A([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*
|
|
135
135
|
[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@
|
|
136
136
|
((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+
|
|
137
|
-
[a-z]{2,
|
|
137
|
+
[a-z]{2,12})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)\z/ix
|
|
138
138
|
|
|
139
139
|
def assert_email(att, error = [att, :not_email])
|
|
140
140
|
if assert_present(att, error)
|
data/makefile
ADDED
data/scrivener.gemspec
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
require "./lib/scrivener"
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |s|
|
|
4
|
-
s.name
|
|
5
|
-
s.version
|
|
6
|
-
s.summary
|
|
7
|
-
s.description
|
|
8
|
-
s.authors
|
|
9
|
-
s.email
|
|
10
|
-
s.homepage
|
|
11
|
-
s.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"Rakefile",
|
|
16
|
-
"lib/**/*.rb",
|
|
17
|
-
"*.gemspec",
|
|
18
|
-
"test/**/*.rb"
|
|
19
|
-
]
|
|
4
|
+
s.name = "scrivener"
|
|
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.license = "MIT"
|
|
12
|
+
|
|
13
|
+
s.files = `git ls-files`.split("\n")
|
|
14
|
+
|
|
20
15
|
s.add_development_dependency "cutest"
|
|
21
16
|
end
|
data/test/scrivener_test.rb
CHANGED
|
@@ -6,12 +6,14 @@ class A < Scrivener
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
scope do
|
|
9
|
-
test "
|
|
9
|
+
test "ignore extra fields" do
|
|
10
10
|
atts = { :a => 1, :b => 2, :c => 3 }
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
filter = A.new(atts)
|
|
13
|
+
|
|
14
|
+
atts.delete(:c)
|
|
15
|
+
|
|
16
|
+
assert_equal atts, filter.attributes
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
test "not raise when there are less fields" do
|
|
@@ -123,6 +125,9 @@ scope do
|
|
|
123
125
|
|
|
124
126
|
filter = D.new(url: "http://google.com", email: "me@google.com")
|
|
125
127
|
assert filter.valid?
|
|
128
|
+
|
|
129
|
+
filter = D.new(url: "http://example.versicherung", email: "me@example.versicherung")
|
|
130
|
+
assert filter.valid?
|
|
126
131
|
end
|
|
127
132
|
end
|
|
128
133
|
|
|
@@ -202,61 +207,118 @@ end
|
|
|
202
207
|
|
|
203
208
|
class H < Scrivener
|
|
204
209
|
attr_accessor :a
|
|
210
|
+
attr_accessor :b
|
|
205
211
|
|
|
206
212
|
def validate
|
|
207
|
-
|
|
213
|
+
assert_equal :a, "foo"
|
|
214
|
+
assert_equal :b, Integer
|
|
208
215
|
end
|
|
209
216
|
end
|
|
210
217
|
|
|
211
218
|
scope do
|
|
212
|
-
test "
|
|
219
|
+
test "equality validation" do
|
|
213
220
|
filter = H.new({})
|
|
214
221
|
|
|
215
222
|
assert ! filter.valid?
|
|
216
|
-
assert filter.errors[:a].include?(:
|
|
223
|
+
assert filter.errors[:a].include?(:not_equal)
|
|
224
|
+
assert filter.errors[:b].include?(:not_equal)
|
|
217
225
|
|
|
218
|
-
filter = H.new(a: "
|
|
226
|
+
filter = H.new(a: "foo", b: "bar")
|
|
219
227
|
assert ! filter.valid?
|
|
220
|
-
assert filter.errors[:a].include?(:not_in_range)
|
|
221
228
|
|
|
222
|
-
filter = H.new(a: "
|
|
229
|
+
filter = H.new(a: "foo")
|
|
223
230
|
assert ! filter.valid?
|
|
224
|
-
assert filter.errors[:a].
|
|
231
|
+
assert filter.errors[:a].empty?
|
|
232
|
+
assert filter.errors[:b].include?(:not_equal)
|
|
225
233
|
|
|
226
|
-
filter = H.new(a: "foo")
|
|
234
|
+
filter = H.new(a: "foo", b: 42)
|
|
235
|
+
filter.valid?
|
|
227
236
|
assert filter.valid?
|
|
228
237
|
end
|
|
229
238
|
end
|
|
230
239
|
|
|
240
|
+
class Scrivener
|
|
241
|
+
def assert_filter(att, filter, error = nil)
|
|
242
|
+
filter = filter.new(send(att))
|
|
243
|
+
|
|
244
|
+
unless filter.valid?
|
|
245
|
+
assert(false, error || [att, filter.errors])
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
231
250
|
class I < Scrivener
|
|
232
|
-
attr_accessor :
|
|
233
|
-
|
|
251
|
+
attr_accessor :name
|
|
252
|
+
|
|
253
|
+
def validate
|
|
254
|
+
assert_equal :name, "I"
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
class J < Scrivener
|
|
259
|
+
attr_accessor :name
|
|
260
|
+
attr_accessor :i
|
|
234
261
|
|
|
235
262
|
def validate
|
|
236
|
-
assert_equal :
|
|
237
|
-
|
|
263
|
+
assert_equal :name, "J"
|
|
264
|
+
assert_filter :i, I
|
|
238
265
|
end
|
|
239
266
|
end
|
|
240
267
|
|
|
268
|
+
scope do
|
|
269
|
+
test "nested filters" do
|
|
270
|
+
j1 = J.new(name: "J", i: { name: "I" })
|
|
271
|
+
j2 = J.new(name: "J", i: { name: "H" })
|
|
272
|
+
|
|
273
|
+
assert_equal true, j1.valid?
|
|
274
|
+
assert_equal false, j2.valid?
|
|
275
|
+
|
|
276
|
+
errors = {
|
|
277
|
+
i: [{ name: [:not_equal] }]
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
assert_equal errors, j2.errors
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
class K < Scrivener
|
|
285
|
+
def validate(argument)
|
|
286
|
+
assert argument == "K", [:k, :not_valid]
|
|
287
|
+
end
|
|
288
|
+
end
|
|
241
289
|
|
|
242
290
|
scope do
|
|
243
|
-
test "
|
|
244
|
-
|
|
291
|
+
test "passing arguments" do
|
|
292
|
+
k = K.new({})
|
|
245
293
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
assert filter.errors[:b].include?(:not_equal)
|
|
294
|
+
assert_equal true, k.valid?("K")
|
|
295
|
+
assert_equal false, k.valid?("L")
|
|
249
296
|
|
|
250
|
-
|
|
251
|
-
|
|
297
|
+
errors = {
|
|
298
|
+
k: [:not_valid]
|
|
299
|
+
}
|
|
252
300
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
assert filter.errors[:b].include?(:not_equal)
|
|
301
|
+
assert_equal errors, k.errors
|
|
302
|
+
end
|
|
303
|
+
end
|
|
257
304
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
assert
|
|
305
|
+
class L < Scrivener
|
|
306
|
+
def validate(argument, key:)
|
|
307
|
+
assert argument == "L" && key == "L", [:l, :not_valid]
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
scope do
|
|
312
|
+
test "passing keyword arguments" do
|
|
313
|
+
l = L.new({})
|
|
314
|
+
|
|
315
|
+
assert_equal true, l.valid?("L", key: "L")
|
|
316
|
+
assert_equal false, l.valid?("M", key: "M")
|
|
317
|
+
|
|
318
|
+
errors = {
|
|
319
|
+
l: [:not_valid]
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
assert_equal errors, l.errors
|
|
261
323
|
end
|
|
262
324
|
end
|
metadata
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: scrivener
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michel Martens
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-07-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: cutest
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '0'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
27
|
description: Scrivener removes the validation responsibility from models and acts
|
|
@@ -32,16 +32,21 @@ executables: []
|
|
|
32
32
|
extensions: []
|
|
33
33
|
extra_rdoc_files: []
|
|
34
34
|
files:
|
|
35
|
-
-
|
|
35
|
+
- ".gems"
|
|
36
|
+
- ".gitignore"
|
|
36
37
|
- AUTHORS
|
|
38
|
+
- CHANGELOG.md
|
|
39
|
+
- CONTRIBUTING
|
|
40
|
+
- LICENSE
|
|
37
41
|
- README.md
|
|
38
|
-
- Rakefile
|
|
39
|
-
- lib/scrivener/validations.rb
|
|
40
42
|
- lib/scrivener.rb
|
|
43
|
+
- lib/scrivener/validations.rb
|
|
44
|
+
- makefile
|
|
41
45
|
- scrivener.gemspec
|
|
42
46
|
- test/scrivener_test.rb
|
|
43
47
|
homepage: http://github.com/soveran/scrivener
|
|
44
|
-
licenses:
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
45
50
|
metadata: {}
|
|
46
51
|
post_install_message:
|
|
47
52
|
rdoc_options: []
|
|
@@ -49,17 +54,16 @@ require_paths:
|
|
|
49
54
|
- lib
|
|
50
55
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
56
|
requirements:
|
|
52
|
-
- -
|
|
57
|
+
- - ">="
|
|
53
58
|
- !ruby/object:Gem::Version
|
|
54
59
|
version: '0'
|
|
55
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
61
|
requirements:
|
|
57
|
-
- -
|
|
62
|
+
- - ">="
|
|
58
63
|
- !ruby/object:Gem::Version
|
|
59
64
|
version: '0'
|
|
60
65
|
requirements: []
|
|
61
|
-
|
|
62
|
-
rubygems_version: 2.0.14
|
|
66
|
+
rubygems_version: 3.1.2
|
|
63
67
|
signing_key:
|
|
64
68
|
specification_version: 4
|
|
65
69
|
summary: Validation frontend for models.
|