standalone_validator 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bca879be6bf42bd2a02eae2221a168206782348c
4
- data.tar.gz: a82ee314d96bf3d10b3dbc16538de68d6311fbbe
3
+ metadata.gz: 43bfee850787c7744539eb0695223feae9bce1d5
4
+ data.tar.gz: dbf0df8cce8600097a85a8aefef28633febc0fae
5
5
  SHA512:
6
- metadata.gz: 1eb6e22ab8bd654da4d87a0917b52d1ffa25a4ae81bf9e6ab30dc9c07460bd8a7de3022f4dc7a74ecee7c3259b69e75284009fe27c3cdfec07e45a88f57ba9cf
7
- data.tar.gz: 97822fabfa6deb523853f135df9ce830b2b05b3f785a524e6f0f3fc02a1dce48df91353037d38711d5e94bf2519b321fe8b0991cdcb7015b182ec6a73e75c6c6
6
+ metadata.gz: e6e66ab4b98ec778fd1e8f8aaaecb30446ac6c2582ee3f5fdc92418cda5ba147733c6814a094c0460430b142d30539f73452be950068ccb046563ed9b070976a
7
+ data.tar.gz: 3abd3dd8ed13f2b94b3e91d639c23525c025203b239e679b315b98a9851df4de931d92674cd35df4bfd0d9f74928b22322a285d3ff6ca2c2de88cb3050cab743
@@ -0,0 +1,78 @@
1
+ require 'standalone_validator'
2
+ require_relative 'common_rails_options'
3
+
4
+ class StandaloneValidator
5
+ module NamedValidations
6
+ class ValidatesNumericalityOf < StandaloneValidator
7
+ register_as :validates_numericality_of
8
+
9
+ include CommonRailsOptions
10
+
11
+ # Stolen from ActiveSupport
12
+ INTEGER_REGEX = /\A[+-]?\d+\Z/.freeze
13
+
14
+ COMPARISON_OPTIONS = {
15
+ :greater_than => lambda { |base, x| x > base },
16
+ :greater_than_or_equal_to => lambda { |base, x| x >= base },
17
+ :equal_to => lambda { |base, x| x == base },
18
+ :less_than => lambda { |base, x| x < base },
19
+ :less_than_or_equal_to => lambda { |base, x| x <= base },
20
+ }.freeze
21
+
22
+ include_validation do |object, result|
23
+ each_validated_attribute_on(object) do |attribute_name, value|
24
+ next if ignore_value?(value)
25
+
26
+ begin
27
+ coerced_value = coerce_to_number(value)
28
+
29
+ each_comparison_violation_of(coerced_value) do |violation, base|
30
+ result.add_violation(attribute_name, violation, :count => base)
31
+ end
32
+ rescue TypeError, ArgumentError
33
+ result.add_violation(attribute_name, :not_a_number)
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def ignore_value?(value)
41
+ if options[:allow_nil]
42
+ value.nil?
43
+ elsif options[:allow_blank]
44
+ value.blank?
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ def each_comparison_violation_of(value)
51
+ selected_comparisons.each do |option, (base, check)|
52
+ yield(option, base) unless check.call(base, value)
53
+ end
54
+
55
+ self
56
+ end
57
+
58
+ def selected_comparisons
59
+ return @selected_comparisons if defined?(@selected_comparisons)
60
+
61
+ passed = COMPARISON_OPTIONS.keys & options.keys
62
+ @selected_comparisons = passed.each_with_object({}) do |option, result|
63
+ result[option] = [options[option], COMPARISON_OPTIONS[option]]
64
+ end
65
+ end
66
+
67
+ def coerce_to_number(value)
68
+ if options[:only_integer]
69
+ raise ArgumentError unless value.to_s =~ INTEGER_REGEX
70
+ Kernel.Integer(value)
71
+ else
72
+ Kernel.Float(value)
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -15,6 +15,10 @@ class StandaloneValidator
15
15
  end
16
16
  end
17
17
  end
18
+
19
+ def requires_field?(field)
20
+ attributes.include?(field.to_sym)
21
+ end
18
22
  end
19
23
  end
20
24
  end
@@ -25,6 +25,8 @@ class StandaloneValidator
25
25
  @violations = violations.to_list
26
26
  end
27
27
 
28
+ OK = new({})
29
+
28
30
  def add_errors_to(errors_object)
29
31
  violations.each do |violation|
30
32
  violation.add_to(errors_object)
@@ -33,15 +35,13 @@ class StandaloneValidator
33
35
  self
34
36
  end
35
37
 
36
- OK = new({})
37
-
38
38
  include Enumerable
39
39
 
40
40
  def each(&block)
41
41
  violations.each(&block)
42
42
  end
43
43
 
44
- def empty
44
+ def empty?
45
45
  violations.empty?
46
46
  end
47
47
 
@@ -50,5 +50,26 @@ class StandaloneValidator
50
50
  end
51
51
 
52
52
  alias_method :valid?, :ok?
53
+
54
+ def [](attribute)
55
+ violations.select do |violation|
56
+ violation.attribute == attribute
57
+ end
58
+ end
59
+
60
+ def of_type(type)
61
+ subset { |violation| violation.type == type }
62
+ end
63
+
64
+ def on_attribute(attribute)
65
+ subset { |violation| violation.attribute == attribute }
66
+ end
67
+
68
+ private
69
+
70
+ def subset(&block)
71
+ self.class.new(:violations => violations.select(&block),
72
+ :validated_object => validated_object)
73
+ end
53
74
  end
54
75
  end
@@ -10,10 +10,11 @@ class StandaloneValidator
10
10
  self
11
11
  end
12
12
 
13
- def add_violation(attribute_name, violation_type_or_message)
13
+ def add_violation(attribute_name, violation_type_or_message, options = {})
14
14
  creation_attributes = {
15
15
  :attribute => attribute_name.to_sym,
16
- :source_object => validated_object
16
+ :source_object => validated_object,
17
+ :options => options,
17
18
  }
18
19
 
19
20
  if violation_type_or_message.kind_of?(Symbol)
@@ -1,3 +1,3 @@
1
1
  class StandaloneValidator
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -8,9 +8,10 @@ class StandaloneValidator
8
8
  attribute :attribute, Symbol, :default => :base
9
9
  attribute :type, Symbol, :default => :invalid
10
10
  attribute :message, String, :default => nil
11
+ attribute :options, Hash, :default => {}
11
12
 
12
13
  def add_to(errors_object)
13
- errors_object.add(attribute, message || type)
14
+ errors_object.add(attribute, message || type, options)
14
15
  self
15
16
  end
16
17
  end
@@ -32,21 +32,35 @@ class StandaloneValidator
32
32
  end
33
33
  end
34
34
 
35
+ def required_fields
36
+ return @required_fields if defined?(@required_fields)
37
+
38
+ if superclass.kind_of?(StandaloneValidator)
39
+ @required_fields = superclass.required_fields
40
+ else
41
+ @required_fields = Hamster.set
42
+ end
43
+ end
44
+
35
45
  private
36
46
 
37
47
  def include_validation(*args, &block)
38
48
  validation = Definitions.coerce_to_validation(*args, &block)
39
49
  @validations = validations.add(validation)
40
50
  end
51
+
52
+ def requires_field(name)
53
+ @required_fields = required_fields.add(name.to_sym)
54
+ end
41
55
  end
42
56
 
43
57
  require 'standalone_validator/named_validations'
44
58
 
45
- def violations_of(object)
46
- builder = ValidationResultBuilder.new
59
+ def violations_of(object, appliable_validations = validations)
60
+ builder = StandaloneValidator::ValidationResultBuilder.new
47
61
  builder.validated_object = object
48
62
 
49
- validations.each do |validation|
63
+ appliable_validations.each do |validation|
50
64
  if validation.respond_to?(:to_proc)
51
65
  result = instance_exec(object, &validation)
52
66
  else
@@ -67,6 +81,16 @@ class StandaloneValidator
67
81
  validation_result
68
82
  end
69
83
 
84
+ def requires_field?(field)
85
+ return true if self.class.required_fields.include?(field.to_sym)
86
+
87
+ validations.any? do |validation|
88
+ if validation.respond_to?(:requires_field?)
89
+ validation.requires_field?(field)
90
+ end
91
+ end
92
+ end
93
+
70
94
  private
71
95
 
72
96
  def validations
@@ -0,0 +1,166 @@
1
+ require "spec_helper"
2
+ require "ostruct"
3
+ require "standalone_validator"
4
+
5
+ module StandaloneValidator::NamedValidations
6
+ describe "validates_numericality_of" do
7
+ let(:object) { OpenStruct.new }
8
+ let(:validator) { ValidatesNumericalityOf.new(:foo) }
9
+
10
+ it "adds a 'not_a_number' violation if the attribute isn't a number" do
11
+ object.foo = "not a number"
12
+ violations = validator.violations_of(object).on_attribute(:foo)
13
+ expect(violations.of_type(:not_a_number)).to_not be_empty
14
+ end
15
+
16
+ it "doesn't add the violation if the attribute is a number" do
17
+ object.foo = 10
18
+ violations = validator.violations_of(object).on_attribute(:foo)
19
+ expect(violations.of_type(:not_a_number)).to be_empty
20
+ end
21
+
22
+ it "accepts strings that represent integer numbers" do
23
+ object.foo = "10"
24
+ violations = validator.violations_of(object).on_attribute(:foo)
25
+ expect(violations.of_type(:not_a_number)).to be_empty
26
+ end
27
+
28
+ describe "the :only_integer option" do
29
+ context "when the :only_integer option is set to true" do
30
+ let(:validator) { ValidatesNumericalityOf.new(:foo, :only_integer => true) }
31
+
32
+ it "does not accept floats" do
33
+ object.foo = 10.5
34
+ violations = validator.violations_of(object).on_attribute(:foo)
35
+ expect(violations.of_type(:not_a_number)).to_not be_empty
36
+ end
37
+
38
+ it "does not accept strings that represent floating numbers" do
39
+ object.foo = "10.5"
40
+ violations = validator.violations_of(object).on_attribute(:foo)
41
+ expect(violations.of_type(:not_a_number)).to_not be_empty
42
+ end
43
+ end
44
+
45
+ context "when the :only_integer is not set" do
46
+ let(:validator) { ValidatesNumericalityOf.new(:foo) }
47
+
48
+ it "accepts floats" do
49
+ object.foo = 10.5
50
+ violations = validator.violations_of(object).on_attribute(:foo)
51
+ expect(violations.of_type(:not_a_number)).to be_empty
52
+ end
53
+
54
+ it "accepts strings that represent floating numbers" do
55
+ object.foo = "10.5"
56
+ violations = validator.violations_of(object).on_attribute(:foo)
57
+ expect(violations.of_type(:not_a_number)).to be_empty
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "the :allow_blank option" do
63
+ context "when the :allow_blank option is set" do
64
+ let(:validator) { ValidatesNumericalityOf.new(:foo, :allow_blank => true) }
65
+
66
+ it "ignores 'blank' values" do
67
+ object.foo = stub(:blank? => true, :to_f => 0)
68
+ violations = validator.violations_of(object).on_attribute(:foo)
69
+ expect(violations).to be_empty
70
+ end
71
+ end
72
+
73
+ context "when the :allow_blank option is not set" do
74
+ let(:validator) { ValidatesNumericalityOf.new(:foo) }
75
+
76
+ it "does not ignore 'blank' values" do
77
+ object.foo = stub(:blank? => true, :to_f => 0)
78
+ violations = validator.violations_of(object).on_attribute(:foo)
79
+ expect(violations.of_type(:not_a_number)).to_not be_empty
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "the :allow_nil option" do
85
+ context "when the :allow_nil option is set" do
86
+ let(:validator) { ValidatesNumericalityOf.new(:foo, :allow_nil => true) }
87
+
88
+ it "ignores nil values" do
89
+ object.foo = nil
90
+ violations = validator.violations_of(object).on_attribute(:foo)
91
+ expect(violations).to be_empty
92
+ end
93
+ end
94
+
95
+ context "when the :allow_blank option is not set" do
96
+ let(:validator) { ValidatesNumericalityOf.new(:foo) }
97
+
98
+ it "does not ignore 'nil' values" do
99
+ object.foo = nil
100
+ violations = validator.violations_of(object).on_attribute(:foo)
101
+ expect(violations.of_type(:not_a_number)).to_not be_empty
102
+ end
103
+ end
104
+ end
105
+
106
+ compared_value = v = 1
107
+
108
+ comparison_options = {
109
+ :greater_than => {:ok => v + 1, :not_ok => [v, v - 1] },
110
+ :greater_than_or_equal_to => {:ok => [v, v + 1], :not_ok => v - 1 },
111
+ :equal_to => {:ok => v, :not_ok => [v - 1, v + 1]},
112
+ :less_than => {:ok => v - 1, :not_ok => [v, v + 1] },
113
+ :less_than_or_equal_to => {:ok => [v, v - 1], :not_ok => v + 1 },
114
+ }.freeze
115
+
116
+ comparison_options.each do |option_name, comparisons|
117
+ describe "when '#{compared_value}' is given on :#{option_name}" do
118
+ let(:validator) do
119
+ ValidatesNumericalityOf.new(:foo, option_name => compared_value)
120
+ end
121
+
122
+ Array(comparisons[:ok]).each do |ok_value|
123
+ it "accepts the value '#{ok_value}'" do
124
+ object.foo = ok_value
125
+ violations = validator.violations_of(object).on_attribute(:foo)
126
+ expect(violations.of_type(option_name)).to be_empty
127
+ end
128
+ end
129
+
130
+ Array(comparisons[:not_ok]).each do |not_ok_value|
131
+ it "rejects the value '#{not_ok_value}'" do
132
+ object.foo = not_ok_value
133
+ violations = validator.violations_of(object).on_attribute(:foo)
134
+ expect(violations.of_type(option_name)).to_not be_empty
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ it "accepts an :if condition that can block the validation" do
141
+ validator = ValidatesNumericalityOf.new :foo, :if => :bar
142
+
143
+ triggers = stub(:foo => "not a number", :bar => true)
144
+ doesnt_trigger = stub(:foo => "not a number", :bar => false)
145
+
146
+ result = validator.violations_of(triggers)
147
+ expect(result).to_not be_ok
148
+
149
+ result = validator.violations_of(doesnt_trigger)
150
+ expect(result).to be_ok
151
+ end
152
+
153
+ it "accepts an :unless condition that can block the validation" do
154
+ validator = ValidatesNumericalityOf.new :foo, :unless => :bar
155
+
156
+ triggers = stub(:foo => "not a number", :bar => false)
157
+ doesnt_trigger = stub(:foo => "not a number", :bar => true)
158
+
159
+ result = validator.violations_of(triggers)
160
+ expect(result).to_not be_ok
161
+
162
+ result = validator.violations_of(doesnt_trigger)
163
+ expect(result).to be_ok
164
+ end
165
+ end
166
+ end
@@ -43,5 +43,11 @@ module StandaloneValidator::NamedValidations
43
43
  result = validator.violations_of(doesnt_trigger)
44
44
  expect(result).to be_ok
45
45
  end
46
+
47
+ it "requires the attributes it was given on construction" do
48
+ validator = ValidatesPresenceOf.new :foo
49
+ expect(validator.requires_field?(:foo)).to be_true
50
+ expect(validator.requires_field?(:bar)).to be_false
51
+ end
46
52
  end
47
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standalone_validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renato Zannon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-03-07 00:00:00.000000000 Z
11
+ date: 2013-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backports
@@ -114,12 +114,14 @@ files:
114
114
  - lib/standalone_validator/definitions.rb
115
115
  - lib/standalone_validator/named_validations.rb
116
116
  - lib/standalone_validator/named_validations/common_rails_options.rb
117
+ - lib/standalone_validator/named_validations/validates_numericality_of.rb
117
118
  - lib/standalone_validator/named_validations/validates_presence_of.rb
118
119
  - lib/standalone_validator/validation_result.rb
119
120
  - lib/standalone_validator/validation_result_builder.rb
120
121
  - lib/standalone_validator/version.rb
121
122
  - lib/standalone_validator/violation.rb
122
123
  - spec/integration_spec.rb
124
+ - spec/named_validations/validates_numericality_of_spec.rb
123
125
  - spec/named_validations/validates_presence_of_spec.rb
124
126
  - spec/spec_helper.rb
125
127
  - standalone_validator.gemspec
@@ -143,12 +145,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
145
  version: '0'
144
146
  requirements: []
145
147
  rubyforge_project:
146
- rubygems_version: 2.0.0
148
+ rubygems_version: 2.0.3
147
149
  signing_key:
148
150
  specification_version: 4
149
151
  summary: PORO standalone validators compatible with ActiveRecord
150
152
  test_files:
151
153
  - spec/integration_spec.rb
154
+ - spec/named_validations/validates_numericality_of_spec.rb
152
155
  - spec/named_validations/validates_presence_of_spec.rb
153
156
  - spec/spec_helper.rb
154
157
  has_rdoc: