scrivener 0.0.3 → 0.1.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.
- checksums.yaml +7 -0
- data/README.md +108 -70
- data/lib/scrivener.rb +12 -8
- data/test/scrivener_test.rb +9 -1
- metadata +14 -13
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a1646faeced5318ac81a17b7fc8d05b59508b0d9
|
4
|
+
data.tar.gz: 80b3fa99edfe7c6e079bafa33fe3e90870701d5d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 207456238ab18834fa1ab9834610e19a20def26196fac1780edaee47ed6046d2581cbae5c0a07e58877c4a30b85e5a3a347b21406b0a910900ae19488c5e1ea7
|
7
|
+
data.tar.gz: 5a8fe4ca21cd06578fa4287324f44ce58420ac8c9f7d87f1dccd34e0df3348eb7c2c10aa176a2ec6dd52cce3649a68af8dc1afdc152f789af22f1b4d2caecb27
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ and its features are a subset of Bureaucrat's. For a more robust and tested
|
|
23
23
|
solution, please [check it](https://github.com/tizoc/bureaucrat).
|
24
24
|
|
25
25
|
This library exists to satify the need of extracting Ohm's validations for
|
26
|
-
reuse in other scenarios. By doing this, all projects using Ohm::Validations
|
26
|
+
reuse in other scenarios. By doing this, all projects using `Ohm::Validations`
|
27
27
|
will be able to profit from extra assertions such as those provided by
|
28
28
|
[ohm-contrib](https://github.com/cyx/ohm-contrib).
|
29
29
|
|
@@ -32,35 +32,35 @@ Usage
|
|
32
32
|
|
33
33
|
Using Scrivener feels very natural no matter what underlying model you are
|
34
34
|
using. As it provides its own validation and whitelisting features, you can
|
35
|
-
|
35
|
+
choose to ignore the ones that come bundled with ORMs.
|
36
36
|
|
37
37
|
This short example illustrates how to move the validation and whitelisting
|
38
38
|
responsibilities away from the model and into Scrivener:
|
39
39
|
|
40
40
|
```ruby
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
41
|
+
# We use Sequel::Model in this example, but it applies to other ORMs such
|
42
|
+
# as Ohm or ActiveRecord.
|
43
|
+
class Article < Sequel::Model
|
44
|
+
|
45
|
+
# Whitelist for mass assigned attributes.
|
46
|
+
set_allowed_columns :title, :body, :state
|
47
|
+
|
48
|
+
# Validations for all contexts.
|
49
|
+
def validate
|
50
|
+
validates_presence :title
|
51
|
+
validates_presence :body
|
52
|
+
validates_presence :state
|
53
|
+
end
|
54
|
+
end
|
55
55
|
|
56
|
-
|
57
|
-
|
56
|
+
title = "Bartleby, the Scrivener"
|
57
|
+
body = "I am a rather elderly man..."
|
58
58
|
|
59
|
-
|
60
|
-
|
59
|
+
# When using the model...
|
60
|
+
article = Article.new(title: title, body: body)
|
61
61
|
|
62
|
-
|
63
|
-
|
62
|
+
article.valid? #=> false
|
63
|
+
article.errors.on(:state) #=> ["cannot be empty"]
|
64
64
|
```
|
65
65
|
|
66
66
|
Of course, what you would do instead is declare `:title` and `:body` as allowed
|
@@ -72,55 +72,94 @@ feature and in others is a minor obstacle.
|
|
72
72
|
Now see what happens with Scrivener:
|
73
73
|
|
74
74
|
```ruby
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
# Now the model has no validations or whitelists. It may still have schema
|
76
|
+
# constraints, which is a good practice to enforce data integrity.
|
77
|
+
class Article < Sequel::Model
|
78
|
+
end
|
79
|
+
|
80
|
+
# The attribute accessors are the only fields that will be set. If more
|
81
|
+
# fields are sent when using mass assignment, a NoMethodError exception is
|
82
|
+
# raised.
|
83
|
+
#
|
84
|
+
# Note how in this example we don't accept the status attribute.
|
85
|
+
class Edit < Scrivener
|
86
|
+
attr_accessor :title
|
87
|
+
attr_accessor :body
|
88
|
+
|
89
|
+
def validate
|
90
|
+
assert_present :title
|
91
|
+
assert_present :body
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
edit = Edit.new(title: title, body: body)
|
96
|
+
edit.valid? #=> true
|
97
|
+
|
98
|
+
article = Article.new(edit.attributes)
|
99
|
+
article.save
|
100
|
+
|
101
|
+
# And now we only ask for the status.
|
102
|
+
class Publish < Scrivener
|
103
|
+
attr_accessor :status
|
104
|
+
|
105
|
+
def validate
|
106
|
+
assert_format :status, /^(published|draft)$/
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
publish = Publish.new(status: "published")
|
111
|
+
publish.valid? #=> true
|
112
|
+
|
113
|
+
article.update_attributes(publish.attributes)
|
114
|
+
|
115
|
+
# If we try to change other fields...
|
116
|
+
publish = Publish.new(status: "published", title: "foo")
|
117
|
+
#=> NoMethodError: undefined method `title=' for #<Publish...>
|
118
|
+
```
|
79
119
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
attr_accessor :title
|
87
|
-
attr_accessor :body
|
88
|
-
|
89
|
-
def validate
|
90
|
-
assert_present :title
|
91
|
-
assert_present :body
|
92
|
-
end
|
93
|
-
end
|
120
|
+
It's important to note that using Scrivener implies a greater risk than using
|
121
|
+
the model validations. Having a central repository of mass assignable
|
122
|
+
attributes and validations is more secure in most scenarios.
|
123
|
+
|
124
|
+
Slices
|
125
|
+
------
|
94
126
|
|
95
|
-
|
96
|
-
|
127
|
+
If you don't need all the attributes after the filtering is done,
|
128
|
+
you can fetch just the ones you need. For example:
|
97
129
|
|
98
|
-
|
99
|
-
|
130
|
+
```ruby
|
131
|
+
class SignUp < Scrivener
|
132
|
+
attr_accessor :email
|
133
|
+
attr_accessor :password
|
134
|
+
attr_accessor :password_confirmation
|
100
135
|
|
101
|
-
|
102
|
-
|
103
|
-
attr_accessor :status
|
136
|
+
def validate
|
137
|
+
assert_email :email
|
104
138
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
139
|
+
if assert_present :password
|
140
|
+
assert_equal :password, :password_confirmation
|
108
141
|
end
|
142
|
+
end
|
109
143
|
|
110
|
-
|
111
|
-
|
144
|
+
def assert_equal(f1, f2)
|
145
|
+
assert send(f1) == send(f2), [f1, f2, :not_equal]
|
146
|
+
end
|
147
|
+
end
|
112
148
|
|
113
|
-
|
149
|
+
filter = SignUp.new(email: "info@example.com",
|
150
|
+
password: "monkey",
|
151
|
+
password_confirmation: "monkey")
|
114
152
|
|
115
|
-
# If we try to change other fields...
|
116
|
-
publish = Publish.new(status: "published", title: "foo")
|
117
|
-
#=> NoMethodError: undefined method `title=' for #<Publish...>
|
118
|
-
```
|
119
153
|
|
120
|
-
|
121
|
-
|
122
|
-
|
154
|
+
# If the validation succeeds, we only need email and password to
|
155
|
+
# create a new user, and we can discard the password_confirmation.
|
156
|
+
if filter.valid?
|
157
|
+
User.create(filter.slice(:email, :password))
|
158
|
+
end
|
159
|
+
```
|
123
160
|
|
161
|
+
By calling `slice` with a list of attributes, you get a hash with only
|
162
|
+
those key/value pairs.
|
124
163
|
|
125
164
|
Assertions
|
126
165
|
-----------
|
@@ -135,9 +174,9 @@ second parameter to the list of errors if the first parameter evaluates
|
|
135
174
|
to false.
|
136
175
|
|
137
176
|
``` ruby
|
138
|
-
|
139
|
-
|
140
|
-
|
177
|
+
def assert(value, error)
|
178
|
+
value or errors[error.first].push(error.last) && false
|
179
|
+
end
|
141
180
|
```
|
142
181
|
|
143
182
|
### assert_present
|
@@ -174,9 +213,9 @@ Checks that a given field is contained within a set of values (i.e.
|
|
174
213
|
like an `ENUM`).
|
175
214
|
|
176
215
|
``` ruby
|
177
|
-
|
178
|
-
|
179
|
-
|
216
|
+
def validate
|
217
|
+
assert_member :state, %w{pending paid delivered}
|
218
|
+
end
|
180
219
|
```
|
181
220
|
|
182
221
|
The error code for this assertion is `:not_valid`
|
@@ -186,9 +225,9 @@ The error code for this assertion is `:not_valid`
|
|
186
225
|
Checks that a given field's length falls under a specified range.
|
187
226
|
|
188
227
|
``` ruby
|
189
|
-
|
190
|
-
|
191
|
-
|
228
|
+
def validate
|
229
|
+
assert_length :username, 3..20
|
230
|
+
end
|
192
231
|
```
|
193
232
|
|
194
233
|
The error code for this assertion is `:not_in_range`.
|
@@ -200,7 +239,6 @@ of the word. Valid numbers are: 0.1, .1, 1, 1.1, 3.14159, etc.
|
|
200
239
|
|
201
240
|
The error code for this assertion is `:not_decimal`.
|
202
241
|
|
203
|
-
|
204
242
|
Installation
|
205
243
|
------------
|
206
244
|
|
data/lib/scrivener.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
|
-
|
1
|
+
require_relative "scrivener/validations"
|
2
2
|
|
3
3
|
class Scrivener
|
4
|
-
VERSION = "0.0
|
4
|
+
VERSION = "0.1.0"
|
5
5
|
|
6
6
|
include Validations
|
7
7
|
|
8
8
|
# Initialize with a hash of attributes and values.
|
9
9
|
# If extra attributes are sent, a NoMethodError exception will be raised.
|
10
10
|
#
|
11
|
-
# The grand daddy of all assertions. If you want to build custom
|
12
|
-
# assertions, or even quick and dirty ones, you can simply use this method.
|
13
|
-
#
|
14
11
|
# @example
|
15
12
|
#
|
16
13
|
# class EditPost < Scrivener
|
@@ -37,8 +34,8 @@ class Scrivener
|
|
37
34
|
# # Now it's safe to initialize the model.
|
38
35
|
# post = Post.new(edit.attributes)
|
39
36
|
# post.save
|
40
|
-
def initialize(
|
41
|
-
|
37
|
+
def initialize(atts)
|
38
|
+
atts.each do |key, val|
|
42
39
|
send(:"#{key}=", val)
|
43
40
|
end
|
44
41
|
end
|
@@ -54,5 +51,12 @@ class Scrivener
|
|
54
51
|
end
|
55
52
|
end
|
56
53
|
end
|
57
|
-
end
|
58
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
|
data/test/scrivener_test.rb
CHANGED
@@ -27,6 +27,14 @@ scope do
|
|
27
27
|
|
28
28
|
assert_equal atts, s.attributes
|
29
29
|
end
|
30
|
+
|
31
|
+
test "return only the required attributes" do
|
32
|
+
atts = { :a => 1, :b => 2 }
|
33
|
+
|
34
|
+
s = S.new(atts)
|
35
|
+
|
36
|
+
assert_equal s.slice(:a), { :a => 1 }
|
37
|
+
end
|
30
38
|
end
|
31
39
|
|
32
40
|
class T < Scrivener
|
@@ -190,4 +198,4 @@ scope do
|
|
190
198
|
assert p.valid?
|
191
199
|
end
|
192
200
|
end
|
193
|
-
end
|
201
|
+
end
|
metadata
CHANGED
@@ -1,27 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scrivener
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Michel Martens
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-04-25 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: cutest
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
25
27
|
description: Scrivener removes the validation responsibility from models and acts
|
26
28
|
as a filter for whitelisted attributes.
|
27
29
|
email:
|
@@ -40,26 +42,25 @@ files:
|
|
40
42
|
- test/scrivener_test.rb
|
41
43
|
homepage: http://github.com/soveran/scrivener
|
42
44
|
licenses: []
|
45
|
+
metadata: {}
|
43
46
|
post_install_message:
|
44
47
|
rdoc_options: []
|
45
48
|
require_paths:
|
46
49
|
- lib
|
47
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
-
none: false
|
49
51
|
requirements:
|
50
|
-
- -
|
52
|
+
- - '>='
|
51
53
|
- !ruby/object:Gem::Version
|
52
54
|
version: '0'
|
53
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
56
|
requirements:
|
56
|
-
- -
|
57
|
+
- - '>='
|
57
58
|
- !ruby/object:Gem::Version
|
58
59
|
version: '0'
|
59
60
|
requirements: []
|
60
61
|
rubyforge_project:
|
61
|
-
rubygems_version:
|
62
|
+
rubygems_version: 2.0.3
|
62
63
|
signing_key:
|
63
|
-
specification_version:
|
64
|
+
specification_version: 4
|
64
65
|
summary: Validation frontend for models.
|
65
66
|
test_files: []
|