activemodel-validators 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +11 -0
- data/Rakefile +24 -0
- data/Readme.md +132 -0
- data/activemodel-validators.gemspec +25 -0
- data/config/locales/en.yml +12 -0
- data/lib/activemodel-validators.rb +26 -0
- data/lib/activemodel-validators/address_state_validator.rb +12 -0
- data/lib/activemodel-validators/all.rb +3 -0
- data/lib/activemodel-validators/at_least_one_present_validator.rb +13 -0
- data/lib/activemodel-validators/at_least_validator.rb +16 -0
- data/lib/activemodel-validators/blank_validator.rb +11 -0
- data/lib/activemodel-validators/boolean_presence_validator.rb +17 -0
- data/lib/activemodel-validators/date_validator.rb +22 -0
- data/lib/activemodel-validators/greater_than_validator.rb +16 -0
- data/lib/activemodel-validators/less_or_greater_than_validator.rb +74 -0
- data/lib/activemodel-validators/less_than_validator.rb +16 -0
- data/lib/activemodel-validators/multiple_of_validator.rb +18 -0
- data/lib/activemodel-validators/phone_validator.rb +8 -0
- data/lib/activemodel-validators/postal_code_validator.rb +8 -0
- data/lib/activemodel-validators/rails.rb +4 -0
- data/lib/activemodel-validators/restrict_to_validator.rb +51 -0
- data/lib/activemodel-validators/sum_of_validator.rb +23 -0
- data/lib/activemodel-validators/version.rb +5 -0
- data/lib/activemodel-validators/year_validator.rb +8 -0
- data/spec/less_or_greater_than_validator_spec.rb +13 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/models/response.rb +8 -0
- data/spec/support/models/user.rb +3 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 795554ae591fe014e540d898361f2a4aaa2a588a
|
4
|
+
data.tar.gz: c33cdc6d6417a0dfddec20ed28b0f3c43032b3eb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b3e2e2d049271ea0b24acfce842c5db974f11ff81203faed628c688823ec30d8b5113b8f973b288482a29a82fa271aee6011406d884fe462592c5b2606cbd794
|
7
|
+
data.tar.gz: e307cc4d0699d8d541bbb0c7a8b021f765c005023c5840222ad9e8b98d7c1679ca400d238c4c87f5109b13624883d4e948d9e9fa87cc0e3e4d3a702474017d85
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
#require 'bundler'
|
4
|
+
#begin
|
5
|
+
# Bundler.setup(:default, :development)
|
6
|
+
#rescue Bundler::BundlerError => e
|
7
|
+
# $stderr.puts e.message
|
8
|
+
# $stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
# exit e.status_code
|
10
|
+
#end
|
11
|
+
|
12
|
+
#---------------------------------------------------------------------------------------------------
|
13
|
+
require 'rspec/core'
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
16
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
20
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
21
|
+
spec.rcov = true
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => :spec
|
data/Readme.md
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
ActiveModel Validators
|
2
|
+
=======================
|
3
|
+
|
4
|
+
A collection of Rails 3 ActiveModel Validators
|
5
|
+
|
6
|
+
Example Usage
|
7
|
+
-------------
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class Thing < ActiveRecord::Base
|
11
|
+
validates :birthdate, greater_than: { value: Date.new(1900), message: 'must be later than 1900' }
|
12
|
+
validates :birthdate, less_than: { value: Time.zone.now.years_ago(1).to_date+1, message: 'must be at least 1 year ago' }
|
13
|
+
validates :end_date, greater_than: {attr: :begin_date, operator_text: 'later than'}
|
14
|
+
|
15
|
+
# shortcut for greater_than: { value: 1, operator: :>=, operator_text: 'at least' }
|
16
|
+
validates :how_many_pies, at_least: { value: 1 }
|
17
|
+
|
18
|
+
# Because presence: true doesn't work for boolean attributes!
|
19
|
+
validates :is_18_or_older, boolean_presence: true
|
20
|
+
|
21
|
+
validates :model, restrict_to: {allowed_options: ['Model A', 'Model B']}, allow_blank: true
|
22
|
+
|
23
|
+
validates_with AtLeastOnePresentValidator, at_least_one_of: [:parent_1_home_phone, :parent_1_work_phone, :parent_1_mobile_phone], message: :at_least_one_phone_parent_1
|
24
|
+
|
25
|
+
validates :option_b, blank: {message: :must_be_blank_if_option_a}, if: :option_a?
|
26
|
+
|
27
|
+
validates :rating, multiple_of: {of: '0.5'}
|
28
|
+
|
29
|
+
validates :total_profit, sum_of: { attr_names: (1..4).map {|quarter| :"total_profit_q#{quarter}" } }
|
30
|
+
|
31
|
+
validates :end_date, date: {required: true}
|
32
|
+
validates :graduation_year, year: true
|
33
|
+
|
34
|
+
validates :phone, phone: true
|
35
|
+
validates :address_postal_code, postal_code: true
|
36
|
+
validates :address_state, address_state: true
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
Documentation
|
41
|
+
-------------
|
42
|
+
|
43
|
+
* [GreaterThanValidator](activemodel-validators/blob/master/lib/activemodel-validators/greater_than_validator.rb)
|
44
|
+
* [LessOrGreaterThanValidator](activemodel-validators/blob/master/lib/activemodel-validators/less_or_greater_than_validator.rb)
|
45
|
+
* [LessThanValidator](activemodel-validators/blob/master/lib/activemodel-validators/less_than_validator.rb)
|
46
|
+
* [AtLeastValidator](activemodel-validators/blob/master/lib/activemodel-validators/at_least_validator.rb)
|
47
|
+
|
48
|
+
* [BooleanPresenceValidator](activemodel-validators/blob/master/lib/activemodel-validators/boolean_presence_validator.rb)
|
49
|
+
* [RestrictToValidator](activemodel-validators/blob/master/lib/activemodel-validators/restrict_to_validator.rb)
|
50
|
+
* [AtLeastOnePresentValidator](activemodel-validators/blob/master/lib/activemodel-validators/at_least_one_present_validator.rb)
|
51
|
+
* [BlankValidator](activemodel-validators/blob/master/lib/activemodel-validators/blank_validator.rb)
|
52
|
+
* [MultipleOfValidator](activemodel-validators/blob/master/lib/activemodel-validators/multiple_of_validator.rb)
|
53
|
+
* [SumOfValidator](activemodel-validators/blob/master/lib/activemodel-validators/sum_of_validator.rb)
|
54
|
+
|
55
|
+
* [DateValidator](activemodel-validators/blob/master/lib/activemodel-validators/date_validator.rb)
|
56
|
+
* [YearValidator](activemodel-validators/blob/master/lib/activemodel-validators/year_validator.rb)
|
57
|
+
|
58
|
+
* [PhoneValidator](activemodel-validators/blob/master/lib/activemodel-validators/phone_validator.rb)
|
59
|
+
* [PostalCodeValidator](activemodel-validators/blob/master/lib/activemodel-validators/postal_code_validator.rb)
|
60
|
+
* [AddressStateValidator](activemodel-validators/blob/master/lib/activemodel-validators/address_state_validator.rb)
|
61
|
+
|
62
|
+
Installation
|
63
|
+
============
|
64
|
+
|
65
|
+
Add to your Gemfile :
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
gem 'activemodel-validators'
|
69
|
+
```
|
70
|
+
|
71
|
+
|
72
|
+
Comparison to other validation gems/collections
|
73
|
+
===============================================
|
74
|
+
|
75
|
+
I like this idea from https://github.com/fnando/validators:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class Server < ActiveRecord::Base
|
79
|
+
validates_datetime :starts_at
|
80
|
+
validates_datetime :ends_at, :after => :starts_at, :if => :starts_at?
|
81
|
+
validates_datetime :ends_at, :after => :now
|
82
|
+
validates_datetime :ends_at, :before => :today
|
83
|
+
|
84
|
+
validates :starts_at, :datetime => true
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
Other collections worth taking a look at:
|
89
|
+
-----------------------------------------------
|
90
|
+
|
91
|
+
* https://github.com/e-travel/evelpidon_validators
|
92
|
+
* Different
|
93
|
+
* Less
|
94
|
+
* More
|
95
|
+
* Associated
|
96
|
+
* Credit Card
|
97
|
+
* https://github.com/cesario/activevalidators
|
98
|
+
* :postal_code => { :country => :us }
|
99
|
+
* :phone => true
|
100
|
+
* :password => { :strength => :medium }
|
101
|
+
* https://github.com/fnando/validators
|
102
|
+
* https://github.com/jeremiahishere/format_validators
|
103
|
+
* :phone_format => true
|
104
|
+
* Days of the Week
|
105
|
+
|
106
|
+
Other collections not worth taking a look at:
|
107
|
+
-----------------------------------------------
|
108
|
+
|
109
|
+
* https://github.com/willian/rails_validators
|
110
|
+
* https://github.com/infosimples/more_validators
|
111
|
+
* https://github.com/aurels/extra_validators
|
112
|
+
|
113
|
+
Specialized validators that stand on their own:
|
114
|
+
-----------------------------------------------
|
115
|
+
|
116
|
+
* https://github.com/balexand/email_validator
|
117
|
+
|
118
|
+
|
119
|
+
To do
|
120
|
+
=====
|
121
|
+
|
122
|
+
* Write actual documentation
|
123
|
+
* Add tests
|
124
|
+
* Merge into/with an other existing gem
|
125
|
+
|
126
|
+
|
127
|
+
License
|
128
|
+
=======
|
129
|
+
|
130
|
+
Copyright 2012, Tyler Rick
|
131
|
+
|
132
|
+
This is free software, distributed under the terms of the MIT License.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "activemodel-validators/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "activemodel-validators"
|
7
|
+
s.version = Activemodel::Validators::Version
|
8
|
+
s.authors = ["Tyler Rick"]
|
9
|
+
s.email = ["tyler@k3integrations.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Some reusable ActiveModel validations}
|
12
|
+
s.description = %q{Some reusable ActiveModel validations, including greater_than, boolean_presence, and at_least_one_present }
|
13
|
+
|
14
|
+
s.rubyforge_project = "activemodel-validators"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "activemodel"
|
22
|
+
|
23
|
+
# For config/locales
|
24
|
+
#s.add_runtime_dependency "railties"
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
en:
|
2
|
+
errors:
|
3
|
+
messages:
|
4
|
+
restrict_to: "is not one of the allowed options (%{allowed_options})"
|
5
|
+
|
6
|
+
less_or_greater_than: "must be %{operator_text} %{other_value}"
|
7
|
+
less_or_greater_than_attr: "must be %{operator_text} %{attr_name} (%{other_value})"
|
8
|
+
|
9
|
+
sum_of: "must be the sum of %{attr_names} (%{sum}) but was %{value}"
|
10
|
+
sum_of_with_addends: "must be the sum of %{attr_names} (%{addends} = %{sum}) but was %{value}"
|
11
|
+
|
12
|
+
must_be_blank: "must be blank"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "active_model"
|
2
|
+
require "activemodel-validators/version"
|
3
|
+
require "activemodel-validators/rails"
|
4
|
+
require "activemodel-validators/all"
|
5
|
+
|
6
|
+
#module ActiveModel
|
7
|
+
# module Validations
|
8
|
+
# autoload :LessOrGreaterThanValidator , 'activemodel-validators/less_or_greater_than_validator'
|
9
|
+
# autoload :GreaterThanValidator , 'activemodel-validators/greater_than_validator'
|
10
|
+
# autoload :LessThanValidator , 'activemodel-validators/less_than_validator'
|
11
|
+
#
|
12
|
+
# autoload :BooleanPresenceValidator , 'activemodel-validators/boolean_presence_validator'
|
13
|
+
# autoload :RestrictToValidator , 'activemodel-validators/restrict_to_validator'
|
14
|
+
# autoload :AtLeastOnePresentValidator , 'activemodel-validators/at_least_one_present_validator'
|
15
|
+
# autoload :BlankValidator , 'activemodel-validators/blank_validator'
|
16
|
+
# autoload :MultipleOfValidator , 'activemodel-validators/multiple_of_validator'
|
17
|
+
# autoload :SumOfValidator , 'activemodel-validators/sum_of_validator'
|
18
|
+
#
|
19
|
+
# autoload :DateValidator , 'activemodel-validators/date_validator'
|
20
|
+
# autoload :YearValidator , 'activemodel-validators/year_validator'
|
21
|
+
#
|
22
|
+
# autoload :PhoneValidator , 'activemodel-validators/phone_validator'
|
23
|
+
# autoload :PostalCodeValidator , 'activemodel-validators/postal_code_validator'
|
24
|
+
# autoload :AddressStateValidator , 'activemodel-validators/address_state_validator'
|
25
|
+
# end
|
26
|
+
#end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
class AddressStateValidator < ActiveModel::EachValidator
|
4
|
+
def validate_each(record, attribute, value)
|
5
|
+
return if value.blank?
|
6
|
+
unless value.match /\A^[A-Z ]{2,}\z/i
|
7
|
+
record.errors.add attribute, :state_invalid
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
class AtLeastOnePresentValidator < ActiveModel::Validator
|
4
|
+
def validate(record)
|
5
|
+
attr_names = options[:at_least_one_of]
|
6
|
+
#puts %(attr_names=#{attr_names.inspect})
|
7
|
+
unless attr_names.map {|attr_name| record[attr_name]}.any?(&:present?)
|
8
|
+
record.errors.add :base, options[:message] || "at least 1 of these fields must be present: #{attr_names.to_sentence(last_word_connector: ', or ')}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'activemodel-validators/greater_than_validator'
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class AtLeastValidator < GreaterThanValidator
|
6
|
+
protected
|
7
|
+
def default_operator
|
8
|
+
:>=
|
9
|
+
end
|
10
|
+
def operator_text
|
11
|
+
options[:operator_text] || 'at least' || super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
class BooleanPresenceValidator < ActiveModel::EachValidator
|
4
|
+
# You can't use validates :presence for boolean attributes!
|
5
|
+
# You could do this instead:
|
6
|
+
# validates_inclusion_of :attr, :in => [true, false]
|
7
|
+
# but the main reason to use this validator instead of is that the error message is better ("not
|
8
|
+
# in list? what list?") and the meaning is clearer when reading the code (we're validating the
|
9
|
+
# presence of a boolean attribute -- true or false).
|
10
|
+
def validate_each(record, attribute, value)
|
11
|
+
unless [true, false].include? value
|
12
|
+
record.errors.add attribute, :blank
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
class DateValidator < ActiveModel::EachValidator
|
4
|
+
def validate_each(record, attr_name, value)
|
5
|
+
before_type_cast = "#{attr_name}_before_type_cast"
|
6
|
+
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
|
7
|
+
#puts "raw_value=#{raw_value.inspect} (#{raw_value.class}), value=#{value.inspect} (#{value.class})"
|
8
|
+
raw_value ||= value
|
9
|
+
|
10
|
+
if raw_value.present? and !value.is_a?(Date)
|
11
|
+
record.errors.add attr_name, :date_invalid
|
12
|
+
return # don't want it to show *both* errors (:date_invalid and :blank)
|
13
|
+
end
|
14
|
+
|
15
|
+
if options[:required] && value.blank?
|
16
|
+
message = (options[:required] == true ? :blank : options[:required])
|
17
|
+
record.errors.add attr_name, message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'activemodel-validators/less_or_greater_than_validator'
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class GreaterThanValidator < LessOrGreaterThanValidator
|
6
|
+
protected
|
7
|
+
def default_operator
|
8
|
+
:>
|
9
|
+
end
|
10
|
+
|
11
|
+
def allowed_operators
|
12
|
+
[:>, :>=]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module Validations
|
3
|
+
class LessOrGreaterThanValidator < ActiveModel::EachValidator
|
4
|
+
def check_validity!
|
5
|
+
unless options[:attr] or options[:value]
|
6
|
+
# TODO: If options[:attr], check that it is an actual column or virtual attribute
|
7
|
+
raise ArgumentError, "must supply :attr or :value option"
|
8
|
+
end
|
9
|
+
unless allowed_operators.include?(operator)
|
10
|
+
# TODO: If options[:attr], check that it is an actual column or virtual attribute
|
11
|
+
raise ArgumentError, ":operator must be one of #{allowed_operators.join(', ')} but was #{operator}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
def default_operator
|
17
|
+
raise NotImplementedError, 'must be defined in subclass'
|
18
|
+
end
|
19
|
+
|
20
|
+
def operator
|
21
|
+
options[:operator] || default_operator
|
22
|
+
end
|
23
|
+
|
24
|
+
# Defaults to the standard mathematical name for these operators, but you're free to override with
|
25
|
+
# something that makes more sense ('later than' might make more sense for dates, for example)
|
26
|
+
def operator_text
|
27
|
+
options[:operator_text] ||
|
28
|
+
{
|
29
|
+
:< => 'less than',
|
30
|
+
:<= => 'less than or equal to',
|
31
|
+
:> => 'greater than',
|
32
|
+
:>= => 'greater than or equal to',
|
33
|
+
:== => 'equal to',
|
34
|
+
}[operator]
|
35
|
+
end
|
36
|
+
|
37
|
+
# The value we're comparing against.
|
38
|
+
# If we're comparing to another attribute, options[:attr] must be a symbol.
|
39
|
+
# Otherwise, options[:value] may be any comparable value (number, date).
|
40
|
+
def other_value
|
41
|
+
if comparing_to_attr?
|
42
|
+
@record.send options[:attr]
|
43
|
+
else
|
44
|
+
options[:value]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def comparing_to_attr?
|
49
|
+
!!options[:attr]
|
50
|
+
end
|
51
|
+
|
52
|
+
def message_key
|
53
|
+
comparing_to_attr? ? :less_or_greater_than_attr : :less_or_greater_than
|
54
|
+
end
|
55
|
+
public
|
56
|
+
|
57
|
+
def validate_each(record, attribute, value)
|
58
|
+
@record = record
|
59
|
+
return if value.blank? or other_value.blank?
|
60
|
+
unless value.send(operator, other_value)
|
61
|
+
record.errors.add(attribute, message_key,
|
62
|
+
options.merge(
|
63
|
+
value: value,
|
64
|
+
operator: operator,
|
65
|
+
operator_text: operator_text,
|
66
|
+
attr_name: options[:attr],
|
67
|
+
other_value: other_value,
|
68
|
+
)
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'activemodel-validators/less_or_greater_than_validator'
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module Validations
|
5
|
+
class LessThanValidator < LessOrGreaterThanValidator
|
6
|
+
protected
|
7
|
+
def default_operator
|
8
|
+
:<
|
9
|
+
end
|
10
|
+
|
11
|
+
def allowed_operators
|
12
|
+
[:<, :<=]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class MultipleOfValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
return if value.blank?
|
4
|
+
#tolerance = options[:tolerance] || 0.00001
|
5
|
+
#puts %(#{value} % #{options[:of]}=#{(value % options[:of]).inspect})
|
6
|
+
|
7
|
+
#unless BigDecimal.new(value) % BigDecimal.new(options[:of]) <= tolerance
|
8
|
+
begin
|
9
|
+
unless BigDecimal.new(value.to_s) % BigDecimal.new(options[:of].to_s) == BigDecimal.new('0.0')
|
10
|
+
record.errors.add attribute, :not_multiple_of, of: options[:of]
|
11
|
+
end
|
12
|
+
rescue
|
13
|
+
puts "#{$!} for either #{value.inspect} or #{options[:of].inspect}"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class RestrictToValidator < ActiveModel::EachValidator
|
2
|
+
ErrorMessage = "An object with the method #include? or a proc or lambda is required, " <<
|
3
|
+
"and must be supplied as the :allowed_options option of the configuration hash"
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
@allowed_options = options[:allowed_options]
|
8
|
+
end
|
9
|
+
|
10
|
+
def check_validity!
|
11
|
+
unless [:include?, :call].any?{ |method| options[:allowed_options].respond_to?(method) }
|
12
|
+
raise ArgumentError, ErrorMessage
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def allowed_options(record)
|
17
|
+
@allowed_options.respond_to?(:call) ? @allowed_options.call(record) : @allowed_options
|
18
|
+
end
|
19
|
+
def allowed_options_string(record)
|
20
|
+
allowed_options = allowed_options(record)
|
21
|
+
if allowed_options.is_a?(Range)
|
22
|
+
"#{allowed_options}"
|
23
|
+
else
|
24
|
+
allowed_options.to_sentence(last_word_connector: ', or ')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_each(record, attribute, value)
|
29
|
+
allowed_options = allowed_options(record)
|
30
|
+
inclusion_method = inclusion_method(allowed_options)
|
31
|
+
unless allowed_options.send(inclusion_method, value)
|
32
|
+
record.errors.add(attribute, :restrict_to,
|
33
|
+
options.except(:in).merge!(
|
34
|
+
value: value,
|
35
|
+
allowed_options: allowed_options_string(record)
|
36
|
+
)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
|
45
|
+
# range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
|
46
|
+
# uses the previous logic of comparing a value with the range endpoints.
|
47
|
+
def inclusion_method(enumerable)
|
48
|
+
enumerable.is_a?(Range) ? :cover? : :include?
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class SumOfValidator < ActiveModel::EachValidator
|
2
|
+
def check_validity!
|
3
|
+
unless options[:attr_names]
|
4
|
+
raise ArgumentError, "must supply :attr_names option"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate_each(record, attribute, value)
|
9
|
+
addends = options[:attr_names].map {|_| record[_] }
|
10
|
+
return if value.blank? || addends.any?(&:blank?)
|
11
|
+
sum = addends.sum
|
12
|
+
unless value == sum
|
13
|
+
record.errors.add(attribute, options[:message] || :sum_of,
|
14
|
+
options.merge(
|
15
|
+
attr_names: options[:attr_names].to_sentence,
|
16
|
+
addends: addends.join(' + '),
|
17
|
+
sum: sum,
|
18
|
+
value: value,
|
19
|
+
)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'test that config/locales/en.yml is being loaded' do
|
4
|
+
it { I18n.t(:'errors.messages.must_be_blank').should == 'must be blank' }
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Response do
|
8
|
+
it { should_not allow_value(0).for(:how_many_people).with_message('must be greater than or equal to 1') }
|
9
|
+
it { should allow_value(1).for(:how_many_people) }
|
10
|
+
|
11
|
+
it { should_not allow_value(0).for(:how_many_pies). with_message('must be at least 1') }
|
12
|
+
it { should allow_value(1).for(:how_many_pies) }
|
13
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
__DIR__ = Pathname.new(__FILE__).dirname
|
5
|
+
$LOAD_PATH.unshift __DIR__
|
6
|
+
$LOAD_PATH.unshift __DIR__ + '../lib'
|
7
|
+
|
8
|
+
#---------------------------------------------------------------------------------------------------
|
9
|
+
# ActiveRecord
|
10
|
+
|
11
|
+
#require 'active_record'
|
12
|
+
#driver = ENV["DB"] || 'sqlite3'
|
13
|
+
##require driver
|
14
|
+
#database_config = YAML::load(File.open(__DIR__ + "support/database.#{driver}.yml"))
|
15
|
+
#ActiveRecord::Base.establish_connection(database_config)
|
16
|
+
|
17
|
+
#---------------------------------------------------------------------------------------------------
|
18
|
+
# RSpec
|
19
|
+
|
20
|
+
# TODO: Why does this seem to have no effect? Why don't I see gems like railties listed in $LOADED_FEATURES?
|
21
|
+
Bundler.setup(:default, :development)
|
22
|
+
#pp $LOADED_FEATURES
|
23
|
+
|
24
|
+
require 'rails/engine'
|
25
|
+
require 'rspec'
|
26
|
+
module ActiveRecord; end # Trick lib/shoulda/matchers/integrations/rspec.rb into including the ActiveModel matchers
|
27
|
+
require 'shoulda/matchers'
|
28
|
+
|
29
|
+
require 'activemodel-validators'
|
30
|
+
|
31
|
+
# Requires supporting ruby files in spec/support/
|
32
|
+
Dir[__DIR__ + 'support/**/*.rb'].each do |f|
|
33
|
+
require f
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: Why is this necessary? http://api.rubyonrails.org/classes/Rails/Engine.html says that the
|
37
|
+
# Engine will "load locales at config/locales/*"? Maybe that only applies when you have a
|
38
|
+
# Rails::Application?
|
39
|
+
I18n.load_path << __DIR__ + '../config/locales/en.yml'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Response < Struct.new(:how_many_people, :how_many_pies)
|
2
|
+
#extend ActiveModel::Naming
|
3
|
+
#include ActiveModel::Conversion
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
validates :how_many_people, greater_than: { value: 1, operator: :>= }, allow_blank: true
|
7
|
+
validates :how_many_pies, at_least: { value: 1 }, allow_blank: true
|
8
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activemodel-validators
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tyler Rick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activemodel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: 'Some reusable ActiveModel validations, including greater_than, boolean_presence,
|
28
|
+
and at_least_one_present '
|
29
|
+
email:
|
30
|
+
- tyler@k3integrations.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- .gitignore
|
36
|
+
- .rspec
|
37
|
+
- Gemfile
|
38
|
+
- Rakefile
|
39
|
+
- Readme.md
|
40
|
+
- activemodel-validators.gemspec
|
41
|
+
- config/locales/en.yml
|
42
|
+
- lib/activemodel-validators.rb
|
43
|
+
- lib/activemodel-validators/address_state_validator.rb
|
44
|
+
- lib/activemodel-validators/all.rb
|
45
|
+
- lib/activemodel-validators/at_least_one_present_validator.rb
|
46
|
+
- lib/activemodel-validators/at_least_validator.rb
|
47
|
+
- lib/activemodel-validators/blank_validator.rb
|
48
|
+
- lib/activemodel-validators/boolean_presence_validator.rb
|
49
|
+
- lib/activemodel-validators/date_validator.rb
|
50
|
+
- lib/activemodel-validators/greater_than_validator.rb
|
51
|
+
- lib/activemodel-validators/less_or_greater_than_validator.rb
|
52
|
+
- lib/activemodel-validators/less_than_validator.rb
|
53
|
+
- lib/activemodel-validators/multiple_of_validator.rb
|
54
|
+
- lib/activemodel-validators/phone_validator.rb
|
55
|
+
- lib/activemodel-validators/postal_code_validator.rb
|
56
|
+
- lib/activemodel-validators/rails.rb
|
57
|
+
- lib/activemodel-validators/restrict_to_validator.rb
|
58
|
+
- lib/activemodel-validators/sum_of_validator.rb
|
59
|
+
- lib/activemodel-validators/version.rb
|
60
|
+
- lib/activemodel-validators/year_validator.rb
|
61
|
+
- spec/less_or_greater_than_validator_spec.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- spec/support/models/response.rb
|
64
|
+
- spec/support/models/user.rb
|
65
|
+
homepage: ''
|
66
|
+
licenses: []
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project: activemodel-validators
|
84
|
+
rubygems_version: 2.0.3
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Some reusable ActiveModel validations
|
88
|
+
test_files: []
|
89
|
+
has_rdoc:
|