scrivener 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -37,6 +37,7 @@ chose to ignore the ones that come bundled with ORMs.
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
+ ```ruby
40
41
  # We use Sequel::Model in this example, but it applies to other ORMs such
41
42
  # as Ohm or ActiveRecord.
42
43
  class Article < Sequel::Model
@@ -60,15 +61,17 @@ responsibilities away from the model and into Scrivener:
60
61
 
61
62
  article.valid? #=> false
62
63
  article.errors.on(:state) #=> ["cannot be empty"]
64
+ ```
63
65
 
64
66
  Of course, what you would do instead is declare `:title` and `:body` as allowed
65
67
  columns, then assign `:state` using the attribute accessor. The reason for this
66
68
  example is to show how you need to work around the fact that there's a single
67
69
  declaration for allowed columns and validations, which in many cases is a great
68
- feature and in other is a minor obstacle.
70
+ feature and in others is a minor obstacle.
69
71
 
70
72
  Now see what happens with Scrivener:
71
73
 
74
+ ```ruby
72
75
  # Now the model has no validations or whitelists. It may still have schema
73
76
  # constraints, which is a good practice to enforce data integrity.
74
77
  class Article < Sequel::Model
@@ -110,13 +113,94 @@ Now see what happens with Scrivener:
110
113
  article.update_attributes(publish.attributes)
111
114
 
112
115
  # If we try to change other fields...
113
- publish = Publish.new(status: "published", title: title)
116
+ publish = Publish.new(status: "published", title: "foo")
114
117
  #=> NoMethodError: undefined method `title=' for #<Publish...>
118
+ ```
115
119
 
116
120
  It's important to note that using Scrivener implies a greater risk than using
117
121
  the model validations. Having a central repository of mass assignable
118
122
  attributes and validations is more secure in most scenarios.
119
123
 
124
+
125
+ Assertions
126
+ -----------
127
+
128
+ Scrivener ships with some basic assertions. The following is a brief description
129
+ for each of them:
130
+
131
+ ### assert
132
+
133
+ The `assert` method is used by all the other assertions. It pushes the
134
+ second parameter to the list of errors if the first parameter evaluates
135
+ to false.
136
+
137
+ ``` ruby
138
+ def assert(value, error)
139
+ value or errors[error.first].push(error.last) && false
140
+ end
141
+ ```
142
+
143
+ ### assert_present
144
+
145
+ Checks that the given field is not nil or empty. The error code for this
146
+ assertion is `:not_present`.
147
+
148
+ ### assert_format
149
+
150
+ Checks that the given field matches the provided regular expression.
151
+ The error code for this assertion is `:format`.
152
+
153
+ ### assert_numeric
154
+
155
+ Checks that the given field holds a number as a Fixnum or as a string
156
+ representation. The error code for this assertion is `:not_numeric`.
157
+
158
+ ### assert_url
159
+
160
+ Provides a pretty general URL regular expression match. An important
161
+ point to make is that this assumes that the URL should start with
162
+ `http://` or `https://`. The error code for this assertion is
163
+ `:not_url`.
164
+
165
+ ### assert_email
166
+
167
+ In this current day and age, almost all web applications need to
168
+ validate an email address. This pretty much matches 99% of the emails
169
+ out there. The error code for this assertion is `:not_email`.
170
+
171
+ ### assert_member
172
+
173
+ Checks that a given field is contained within a set of values (i.e.
174
+ like an `ENUM`).
175
+
176
+ ``` ruby
177
+ def validate
178
+ assert_member :state, %w{pending paid delivered}
179
+ end
180
+ ```
181
+
182
+ The error code for this assertion is `:not_valid`
183
+
184
+ ### assert_length
185
+
186
+ Checks that a given field's length falls under a specified range.
187
+
188
+ ``` ruby
189
+ def validate
190
+ assert_length :username, 3..20
191
+ end
192
+ ```
193
+
194
+ The error code for this assertion is `:not_in_range`.
195
+
196
+ ### assert_decimal
197
+
198
+ Checks that a given field looks like a number in the human sense
199
+ of the word. Valid numbers are: 0.1, .1, 1, 1.1, 3.14159, etc.
200
+
201
+ The error code for this assertion is `:not_decimal`.
202
+
203
+
120
204
  Installation
121
205
  ------------
122
206
 
@@ -1,7 +1,7 @@
1
1
  require File.expand_path("scrivener/validations", File.dirname(__FILE__))
2
2
 
3
3
  class Scrivener
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
 
6
6
  include Validations
7
7
 
@@ -49,7 +49,7 @@ class Scrivener
49
49
  instance_variables.each do |ivar|
50
50
  next if ivar == :@errors
51
51
 
52
- att = ivar.to_s.sub(/@/, "").to_sym
52
+ att = ivar[1..-1].to_sym
53
53
  atts[att] = send(att)
54
54
  end
55
55
  end
@@ -7,6 +7,11 @@ class Scrivener
7
7
  # * assert_present
8
8
  # * assert_format
9
9
  # * assert_numeric
10
+ # * assert_url
11
+ # * assert_email
12
+ # * assert_member
13
+ # * assert_length
14
+ # * assert_decimal
10
15
  #
11
16
  # The core tenets that Scrivener::Validations advocates can be summed up in a
12
17
  # few bullet points:
@@ -121,10 +126,48 @@ class Scrivener
121
126
  # @see http://cyx.github.com/ohm-contrib/doc/Ohm/NumberValidations.html
122
127
  def assert_numeric(att, error = [att, :not_numeric])
123
128
  if assert_present(att, error)
124
- assert_format(att, /^\d+$/, error)
129
+ assert_format(att, /\A\d+\z/, error)
125
130
  end
126
131
  end
127
132
 
133
+ URL = /\A(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(2
134
+ 5[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}
135
+ |localhost)(:[0-9]{1,5})?(\/.*)?\z/ix
136
+
137
+ def assert_url(att, error = [att, :not_url])
138
+ if assert_present(att, error)
139
+ assert_format(att, URL, error)
140
+ end
141
+ end
142
+
143
+ EMAIL = /\A([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*
144
+ [\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@
145
+ ((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+
146
+ [a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)\z/ix
147
+
148
+ def assert_email(att, error = [att, :not_email])
149
+ if assert_present(att, error)
150
+ assert_format(att, EMAIL, error)
151
+ end
152
+ end
153
+
154
+ def assert_member(att, set, err = [att, :not_valid])
155
+ assert(set.include?(send(att)), err)
156
+ end
157
+
158
+ def assert_length(att, range, error = [att, :not_in_range])
159
+ if assert_present(att, error)
160
+ val = send(att).to_s
161
+ assert range.include?(val.length), error
162
+ end
163
+ end
164
+
165
+ DECIMAL = /\A(\d+)?(\.\d+)?\z/
166
+
167
+ def assert_decimal(att, error = [att, :not_decimal])
168
+ assert_format att, DECIMAL, error
169
+ end
170
+
128
171
  # The grand daddy of all assertions. If you want to build custom
129
172
  # assertions, or even quick and dirty ones, you can simply use this method.
130
173
  #
@@ -149,4 +192,3 @@ class Scrivener
149
192
  end
150
193
  end
151
194
  end
152
-
@@ -89,3 +89,105 @@ scope do
89
89
  assert_equal [:not_present], q.errors[:foo]
90
90
  end
91
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
+ end
119
+ end
120
+
121
+ class Person < Scrivener
122
+ attr_accessor :username
123
+
124
+ def validate
125
+ assert_length :username, 3..10
126
+ end
127
+ end
128
+
129
+ scope do
130
+ test "length validation" do
131
+ p = Person.new({})
132
+
133
+ assert ! p.valid?
134
+ assert p.errors[:username].include?(:not_in_range)
135
+
136
+ p = Person.new(username: "fo")
137
+ assert ! p.valid?
138
+ assert p.errors[:username].include?(:not_in_range)
139
+
140
+ p = Person.new(username: "foofoofoofo")
141
+ assert ! p.valid?
142
+ assert p.errors[:username].include?(:not_in_range)
143
+
144
+ p = Person.new(username: "foo")
145
+ assert p.valid?
146
+ end
147
+ end
148
+
149
+ class Order < Scrivener
150
+ attr_accessor :status
151
+
152
+ def validate
153
+ assert_member :status, %w{pending paid delivered}
154
+ end
155
+ end
156
+
157
+ scope do
158
+ test "member validation" do
159
+ o = Order.new({})
160
+ assert ! o.valid?
161
+ assert_equal [:not_valid], o.errors[:status]
162
+
163
+ o = Order.new(status: "foo")
164
+ assert ! o.valid?
165
+ assert_equal [:not_valid], o.errors[:status]
166
+
167
+ %w{pending paid delivered}.each do |status|
168
+ o = Order.new(status: status)
169
+ assert o.valid?
170
+ end
171
+ end
172
+ end
173
+
174
+ class Product < Scrivener
175
+ attr_accessor :price
176
+
177
+ def validate
178
+ assert_decimal :price
179
+ end
180
+ end
181
+
182
+ scope do
183
+ test "decimal validation" do
184
+ p = Product.new({})
185
+ assert ! p.valid?
186
+ assert_equal [:not_decimal], p.errors[:price]
187
+
188
+ %w{10 10.1 10.100000 0.100000 .1000}.each do |price|
189
+ p = Product.new(price: price)
190
+ assert p.valid?
191
+ end
192
+ end
193
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scrivener
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-24 00:00:00.000000000 -03:00
13
- default_executable:
12
+ date: 2012-01-24 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: cutest
17
- requirement: &2156684860 !ruby/object:Gem::Requirement
16
+ requirement: &2156191960 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ! '>='
@@ -22,7 +21,7 @@ dependencies:
22
21
  version: '0'
23
22
  type: :development
24
23
  prerelease: false
25
- version_requirements: *2156684860
24
+ version_requirements: *2156191960
26
25
  description: Scrivener removes the validation responsibility from models and acts
27
26
  as a filter for whitelisted attributes.
28
27
  email:
@@ -39,7 +38,6 @@ files:
39
38
  - lib/scrivener.rb
40
39
  - scrivener.gemspec
41
40
  - test/scrivener_test.rb
42
- has_rdoc: true
43
41
  homepage: http://github.com/soveran/scrivener
44
42
  licenses: []
45
43
  post_install_message:
@@ -60,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
58
  version: '0'
61
59
  requirements: []
62
60
  rubyforge_project:
63
- rubygems_version: 1.6.2
61
+ rubygems_version: 1.8.10
64
62
  signing_key:
65
63
  specification_version: 3
66
64
  summary: Validation frontend for models.