drpentode-scrivener 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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,205 @@
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. By doing this, all projects using `Ohm::Validations`
27
+ will be able to profit from extra assertions such as those provided by
28
+ [ohm-contrib](https://github.com/cyx/ohm-contrib).
29
+
30
+ Usage
31
+ -----
32
+
33
+ Using Scrivener feels very natural no matter what underlying model you are
34
+ using. As it provides its own validation and whitelisting features, you can
35
+ choose to ignore the ones that come bundled with ORMs.
36
+
37
+ This short example illustrates how to move the validation and whitelisting
38
+ responsibilities away from the model and into Scrivener:
39
+
40
+ ```ruby
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
+
56
+ title = "Bartleby, the Scrivener"
57
+ body = "I am a rather elderly man..."
58
+
59
+ # When using the model...
60
+ article = Article.new(title: title, body: body)
61
+
62
+ article.valid? #=> false
63
+ article.errors.on(:state) #=> ["cannot be empty"]
64
+ ```
65
+
66
+ Of course, what you would do instead is declare `:title` and `:body` as allowed
67
+ columns, then assign `:state` using the attribute accessor. The reason for this
68
+ example is to show how you need to work around the fact that there's a single
69
+ declaration for allowed columns and validations, which in many cases is a great
70
+ feature and in others is a minor obstacle.
71
+
72
+ Now see what happens with Scrivener:
73
+
74
+ ```ruby
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
+ ```
119
+
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
+ Assertions
125
+ -----------
126
+
127
+ Scrivener ships with some basic assertions. The following is a brief description
128
+ for each of them:
129
+
130
+ ### assert
131
+
132
+ The `assert` method is used by all the other assertions. It pushes the
133
+ second parameter to the list of errors if the first parameter evaluates
134
+ to false.
135
+
136
+ ``` ruby
137
+ def assert(value, error)
138
+ value or errors[error.first].push(error.last) && false
139
+ end
140
+ ```
141
+
142
+ ### assert_present
143
+
144
+ Checks that the given field is not nil or empty. The error code for this
145
+ assertion is `:not_present`.
146
+
147
+ ### assert_format
148
+
149
+ Checks that the given field matches the provided regular expression.
150
+ The error code for this assertion is `:format`.
151
+
152
+ ### assert_numeric
153
+
154
+ Checks that the given field holds a number as a Fixnum or as a string
155
+ representation. The error code for this assertion is `:not_numeric`.
156
+
157
+ ### assert_url
158
+
159
+ Provides a pretty general URL regular expression match. An important
160
+ point to make is that this assumes that the URL should start with
161
+ `http://` or `https://`. The error code for this assertion is
162
+ `:not_url`.
163
+
164
+ ### assert_email
165
+
166
+ In this current day and age, almost all web applications need to
167
+ validate an email address. This pretty much matches 99% of the emails
168
+ out there. The error code for this assertion is `:not_email`.
169
+
170
+ ### assert_member
171
+
172
+ Checks that a given field is contained within a set of values (i.e.
173
+ like an `ENUM`).
174
+
175
+ ``` ruby
176
+ def validate
177
+ assert_member :state, %w{pending paid delivered}
178
+ end
179
+ ```
180
+
181
+ The error code for this assertion is `:not_valid`
182
+
183
+ ### assert_length
184
+
185
+ Checks that a given field's length falls under a specified range.
186
+
187
+ ``` ruby
188
+ def validate
189
+ assert_length :username, 3..20
190
+ end
191
+ ```
192
+
193
+ The error code for this assertion is `:not_in_range`.
194
+
195
+ ### assert_decimal
196
+
197
+ Checks that a given field looks like a number in the human sense
198
+ of the word. Valid numbers are: 0.1, .1, 1, 1.1, 3.14159, etc.
199
+
200
+ The error code for this assertion is `:not_decimal`.
201
+
202
+ Installation
203
+ ------------
204
+
205
+ $ 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,58 @@
1
+ require_relative "scrivener/validations"
2
+
3
+ class Scrivener
4
+ VERSION = "0.0.3"
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
+ # 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
+ # @example
15
+ #
16
+ # class EditPost < Scrivener
17
+ # attr_accessor :title
18
+ # attr_accessor :body
19
+ #
20
+ # def validate
21
+ # assert_present :title
22
+ # assert_present :body
23
+ # end
24
+ # end
25
+ #
26
+ # edit = EditPost.new(title: "Software Tools")
27
+ #
28
+ # edit.valid? #=> false
29
+ #
30
+ # edit.errors[:title] #=> []
31
+ # edit.errors[:body] #=> [:not_present]
32
+ #
33
+ # edit.body = "Recommended reading..."
34
+ #
35
+ # edit.valid? #=> true
36
+ #
37
+ # # Now it's safe to initialize the model.
38
+ # post = Post.new(edit.attributes)
39
+ # post.save
40
+ def initialize(attrs)
41
+ attrs.each do |key, val|
42
+ send(:"#{key}=", val)
43
+ end
44
+ end
45
+
46
+ # Return hash of attributes and values.
47
+ def attributes
48
+ Hash.new.tap do |atts|
49
+ instance_variables.each do |ivar|
50
+ next if ivar == :@errors
51
+
52
+ att = ivar[1..-1].to_sym
53
+ atts[att] = send(att)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,193 @@
1
+ class Scrivener
2
+ require "uri"
3
+
4
+ # Provides a base implementation for extensible validation routines.
5
+ # {Scrivener::Validations} currently only provides the following assertions:
6
+ #
7
+ # * assert
8
+ # * assert_present
9
+ # * assert_format
10
+ # * assert_numeric
11
+ # * assert_url
12
+ # * assert_email
13
+ # * assert_member
14
+ # * assert_length
15
+ # * assert_decimal
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
+ # If you want other validations you may want to take a peek at Ohm::Contrib
29
+ # and all of the validation modules it provides.
30
+ #
31
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/WebValidations.html
32
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
33
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/ExtraValidations.html
34
+ #
35
+ # @example
36
+ #
37
+ # class Quote
38
+ # attr_accessor :title
39
+ # attr_accessor :price
40
+ # attr_accessor :date
41
+ #
42
+ # def validate
43
+ # assert_present :title
44
+ # assert_numeric :price
45
+ # assert_format :date, /\A[\d]{4}-[\d]{1,2}-[\d]{1,2}\z
46
+ # end
47
+ # end
48
+ #
49
+ # s = Quote.new
50
+ # s.valid?
51
+ # # => false
52
+ #
53
+ # s.errors
54
+ # # => { :title => [:not_present],
55
+ # :price => [:not_numeric],
56
+ # :date => [:format] }
57
+ #
58
+ module Validations
59
+
60
+ # Check if the current model state is valid. Each call to {#valid?} will
61
+ # reset the {#errors} array.
62
+ #
63
+ # All validations should be declared in a `validate` method.
64
+ #
65
+ # @example
66
+ #
67
+ # class Login
68
+ # attr_accessor :username
69
+ # attr_accessor :password
70
+ #
71
+ # def validate
72
+ # assert_present :user
73
+ # assert_present :password
74
+ # end
75
+ # end
76
+ #
77
+ def valid?
78
+ errors.clear
79
+ validate
80
+ errors.empty?
81
+ end
82
+
83
+ # Base validate implementation. Override this method in subclasses.
84
+ def validate
85
+ end
86
+
87
+ # Hash of errors for each attribute in this model.
88
+ def errors
89
+ @errors ||= Hash.new { |hash, key| hash[key] = [] }
90
+ end
91
+
92
+ protected
93
+
94
+ # Allows you to do a validation check against a regular expression.
95
+ # It's important to note that this internally calls {#assert_present},
96
+ # therefore you need not structure your regular expression to check
97
+ # for a non-empty value.
98
+ #
99
+ # @param [Symbol] att The attribute you want to verify the format of.
100
+ # @param [Regexp] format The regular expression with which to compare
101
+ # the value of att with.
102
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
103
+ # when the validation fails.
104
+ def assert_format(att, format, error = [att, :format])
105
+ if assert_present(att, error)
106
+ assert(send(att).to_s.match(format), error)
107
+ end
108
+ end
109
+
110
+ # The most basic and highly useful assertion. Simply checks if the
111
+ # value of the attribute is empty.
112
+ #
113
+ # @param [Symbol] att The attribute you wish to verify the presence of.
114
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
115
+ # when the validation fails.
116
+ def assert_present(att, error = [att, :not_present])
117
+ assert(!send(att).to_s.empty?, error)
118
+ end
119
+
120
+ # Checks if all the characters of an attribute is a digit. If you want to
121
+ # verify that a value is a decimal, try looking at Ohm::Contrib's
122
+ # assert_decimal assertion.
123
+ #
124
+ # @param [Symbol] att The attribute you wish to verify the numeric format.
125
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
126
+ # when the validation fails.
127
+ # @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
128
+ def assert_numeric(att, error = [att, :not_numeric])
129
+ if assert_present(att, error)
130
+ assert_format(att, /\A\d+\z/, error)
131
+ end
132
+ end
133
+
134
+ URL = URI::regexp(%w(http https))
135
+
136
+ def assert_url(att, error = [att, :not_url])
137
+ if assert_present(att, error)
138
+ assert_format(att, URL, error)
139
+ end
140
+ end
141
+
142
+ EMAIL = /\A([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*
143
+ [\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@
144
+ ((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+
145
+ [a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)\z/ix
146
+
147
+ def assert_email(att, error = [att, :not_email])
148
+ if assert_present(att, error)
149
+ assert_format(att, EMAIL, error)
150
+ end
151
+ end
152
+
153
+ def assert_member(att, set, err = [att, :not_valid])
154
+ assert(set.include?(send(att)), err)
155
+ end
156
+
157
+ def assert_length(att, range, error = [att, :not_in_range])
158
+ if assert_present(att, error)
159
+ val = send(att).to_s
160
+ assert range.include?(val.length), error
161
+ end
162
+ end
163
+
164
+ DECIMAL = /\A(\d+)?(\.\d+)?\z/
165
+
166
+ def assert_decimal(att, error = [att, :not_decimal])
167
+ assert_format att, DECIMAL, error
168
+ end
169
+
170
+ # The grand daddy of all assertions. If you want to build custom
171
+ # assertions, or even quick and dirty ones, you can simply use this method.
172
+ #
173
+ # @example
174
+ #
175
+ # class CreatePost
176
+ # attr_accessor :slug
177
+ # attr_accessor :votes
178
+ #
179
+ # def validate
180
+ # assert_slug :slug
181
+ # assert votes.to_i > 0, [:votes, :not_valid]
182
+ # end
183
+ #
184
+ # protected
185
+ # def assert_slug(att, error = [att, :not_slug])
186
+ # assert send(att).to_s =~ /\A[a-z\-0-9]+\z/, error
187
+ # end
188
+ # end
189
+ def assert(value, error)
190
+ value or errors[error.first].push(error.last) && false
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,21 @@
1
+ require "./lib/scrivener"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "drpentode-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/drpentode/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"
21
+ end
@@ -0,0 +1,197 @@
1
+ require File.expand_path("../lib/scrivener", File.dirname(__FILE__))
2
+
3
+ class S < 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
+ s = S.new(atts)
14
+ end
15
+ end
16
+
17
+ test "not raise when there are less fields" do
18
+ atts = { :a => 1 }
19
+
20
+ assert s = S.new(atts)
21
+ end
22
+
23
+ test "return attributes" do
24
+ atts = { :a => 1, :b => 2 }
25
+
26
+ s = S.new(atts)
27
+
28
+ assert_equal atts, s.attributes
29
+ end
30
+ end
31
+
32
+ class T < Scrivener
33
+ attr_accessor :a
34
+ attr_accessor :b
35
+
36
+ def validate
37
+ assert_present :a
38
+ assert_present :b
39
+ end
40
+ end
41
+
42
+ scope do
43
+ test "validations" do
44
+ atts = { :a => 1, :b => 2 }
45
+
46
+ t = T.new(atts)
47
+
48
+ assert t.valid?
49
+ end
50
+
51
+ test "validation errors" do
52
+ atts = { :a => 1 }
53
+
54
+ t = T.new(atts)
55
+
56
+ assert_equal false, t.valid?
57
+ assert_equal [], t.errors[:a]
58
+ assert_equal [:not_present], t.errors[:b]
59
+ end
60
+
61
+ test "attributes without @errors" do
62
+ atts = { :a => 1, :b => 2 }
63
+
64
+ t = T.new(atts)
65
+
66
+ t.valid?
67
+ assert_equal atts, t.attributes
68
+ end
69
+ end
70
+
71
+ class Quote
72
+ include Scrivener::Validations
73
+
74
+ attr_accessor :foo
75
+
76
+ def validate
77
+ assert_present :foo
78
+ end
79
+ end
80
+
81
+ scope do
82
+ test "validations without Scrivener" do
83
+ q = Quote.new
84
+ q.foo = 1
85
+ assert q.valid?
86
+
87
+ q = Quote.new
88
+ assert_equal false, q.valid?
89
+ assert_equal [:not_present], q.errors[:foo]
90
+ end
91
+ end
92
+
93
+ class Post < Scrivener
94
+ attr_accessor :url, :email
95
+
96
+ def validate
97
+ assert_url :url
98
+ assert_email :email
99
+ end
100
+ end
101
+
102
+ scope do
103
+ test "email & url" do
104
+ p = Post.new({})
105
+
106
+ assert ! p.valid?
107
+ assert_equal [:not_url], p.errors[:url]
108
+ assert_equal [:not_email], p.errors[:email]
109
+
110
+ p = Post.new(url: "google.com", email: "egoogle.com")
111
+
112
+ assert ! p.valid?
113
+ assert_equal [:not_url], p.errors[:url]
114
+ assert_equal [:not_email], p.errors[:email]
115
+
116
+ p = Post.new(url: "http://google.com", email: "me@google.com")
117
+ assert p.valid?
118
+
119
+ # server name without a / but a ? is a valid URL
120
+ p = Post.new(url: "http://google.com?blah=blah", email: "me@google.com")
121
+ assert p.valid?
122
+ end
123
+ end
124
+
125
+ class Person < Scrivener
126
+ attr_accessor :username
127
+
128
+ def validate
129
+ assert_length :username, 3..10
130
+ end
131
+ end
132
+
133
+ scope do
134
+ test "length validation" do
135
+ p = Person.new({})
136
+
137
+ assert ! p.valid?
138
+ assert p.errors[:username].include?(:not_in_range)
139
+
140
+ p = Person.new(username: "fo")
141
+ assert ! p.valid?
142
+ assert p.errors[:username].include?(:not_in_range)
143
+
144
+ p = Person.new(username: "foofoofoofo")
145
+ assert ! p.valid?
146
+ assert p.errors[:username].include?(:not_in_range)
147
+
148
+ p = Person.new(username: "foo")
149
+ assert p.valid?
150
+ end
151
+ end
152
+
153
+ class Order < Scrivener
154
+ attr_accessor :status
155
+
156
+ def validate
157
+ assert_member :status, %w{pending paid delivered}
158
+ end
159
+ end
160
+
161
+ scope do
162
+ test "member validation" do
163
+ o = Order.new({})
164
+ assert ! o.valid?
165
+ assert_equal [:not_valid], o.errors[:status]
166
+
167
+ o = Order.new(status: "foo")
168
+ assert ! o.valid?
169
+ assert_equal [:not_valid], o.errors[:status]
170
+
171
+ %w{pending paid delivered}.each do |status|
172
+ o = Order.new(status: status)
173
+ assert o.valid?
174
+ end
175
+ end
176
+ end
177
+
178
+ class Product < Scrivener
179
+ attr_accessor :price
180
+
181
+ def validate
182
+ assert_decimal :price
183
+ end
184
+ end
185
+
186
+ scope do
187
+ test "decimal validation" do
188
+ p = Product.new({})
189
+ assert ! p.valid?
190
+ assert_equal [:not_decimal], p.errors[:price]
191
+
192
+ %w{10 10.1 10.100000 0.100000 .1000}.each do |price|
193
+ p = Product.new(price: price)
194
+ assert p.valid?
195
+ end
196
+ end
197
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drpentode-scrivener
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michel Martens
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cutest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Scrivener removes the validation responsibility from models and acts
31
+ as a filter for whitelisted attributes.
32
+ email:
33
+ - michel@soveran.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - LICENSE
39
+ - AUTHORS
40
+ - README.md
41
+ - Rakefile
42
+ - lib/scrivener/validations.rb
43
+ - lib/scrivener.rb
44
+ - scrivener.gemspec
45
+ - test/scrivener_test.rb
46
+ homepage: http://github.com/drpentode/scrivener
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.23
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Validation frontend for models.
70
+ test_files: []