drpentode-scrivener 0.0.3

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.
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: []