be_valid 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: abbbdea456536973df02299690f700bab640ac727c65fd257048e6427a20964c
4
+ data.tar.gz: 43bbbba7ed6a4ef05ca0a0f0a713108f17dc3aa97a4a981e6a65b1022015136b
5
+ SHA512:
6
+ metadata.gz: 6389b8bf085014beb16d64fc0a559d945ebbef1f2e8d2f78900323c5650a69ba66abef5cc74158fd1093424031c1aefc3b915cb32b63036e2461219b4a36ac49
7
+ data.tar.gz: e59092fc516be70996b024c886fb21562298d1022582b42b689fa27cf8ef1b20d3439856584a518687c36bb6d8deabbbc09d7d9e8185a11a06e7647c26893e07
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in be_valid.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ be_valid (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activemodel (5.2.5)
10
+ activesupport (= 5.2.5)
11
+ activerecord (5.2.5)
12
+ activemodel (= 5.2.5)
13
+ activesupport (= 5.2.5)
14
+ arel (>= 9.0)
15
+ activesupport (5.2.5)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 0.7, < 2)
18
+ minitest (~> 5.1)
19
+ tzinfo (~> 1.1)
20
+ arel (9.0.0)
21
+ concurrent-ruby (1.1.8)
22
+ i18n (1.8.10)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.14.4)
25
+ rake (10.5.0)
26
+ sqlite3 (1.4.2)
27
+ thread_safe (0.3.6)
28
+ tzinfo (1.2.9)
29
+ thread_safe (~> 0.1)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ activerecord (~> 5)
36
+ be_valid!
37
+ bundler (~> 1.17)
38
+ minitest (~> 5.0)
39
+ rake (~> 10.0)
40
+ sqlite3 (~> 1.4)
41
+
42
+ BUNDLED WITH
43
+ 1.17.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 John Stewart (johnsinco)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # BeValid
2
+
3
+ BeValid is a custom ActiveModel / Rails validator that helps with "conditional" validation. IE you can setup field-level validations that depend
4
+ upon complex state within the object.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'be_valid'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install be_valid
21
+
22
+ ## Usage
23
+
24
+ Examples:
25
+
26
+ ```ruby
27
+ class Account
28
+ validates :email,
29
+ must_be: {
30
+ present: true,
31
+ when: {
32
+ status: 'active'
33
+ }
34
+ }
35
+
36
+ validates :birthdate,
37
+ must_be: {
38
+ before: -> { 18.years.ago }
39
+ },
40
+ when: {
41
+ account_type: :adult?
42
+ }
43
+
44
+ validates :open_date, date: {time: true, before: :now}
45
+
46
+ validates :late_fee,
47
+ must_be: {
48
+ equal_to: 0,
49
+ when: {
50
+ status: 'current',
51
+ customer_type: :premier?
52
+ },
53
+ rule_name: :premier_customer_waived_fee
54
+ },
55
+ allow_blank: true
56
+
57
+ end
58
+ ```
59
+
60
+ ## License
61
+
62
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/be_valid.gemspec ADDED
@@ -0,0 +1,32 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "be_valid/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "be_valid"
8
+ spec.version = BeValid::VERSION
9
+ spec.authors = ["John Stewart (johnsinco)"]
10
+ spec.email = ["johnstewartco@outlook.com"]
11
+
12
+ spec.summary = %q{Custom Ruby on Rails Validation library supporting 'conditional' validation}
13
+ spec.description = %q{Provides more advanced and flexible 'if then then that' style conditional validation. Validate fields based on the values of other fields in your model.}
14
+ spec.homepage = "https://github.com/johnsinco/be_then"
15
+ spec.license = "MIT"
16
+
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.17"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "minitest", "~> 5.0"
30
+ spec.add_development_dependency "activerecord", "~> 5"
31
+ spec.add_development_dependency "sqlite3", "~> 1.4"
32
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "be_valid"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/be_valid.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'be_valid/be_valid'
2
+
3
+ begin
4
+ require 'validators/must_be_validator'
5
+ require 'validators/date_validator'
6
+ rescue
7
+ 'unable to loan be_valid validator'
8
+ nil
9
+ end
@@ -0,0 +1,18 @@
1
+ module BeValid
2
+ def self.config
3
+ @@config ||= Configuration.new
4
+ end
5
+
6
+ def self.configure
7
+ self.config ||= Configuration.new
8
+ yield(config)
9
+ end
10
+
11
+ class Configuration
12
+ attr_accessor :notice_rules
13
+
14
+ def initialize
15
+ @notice_rules= []
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module BeValid
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,90 @@
1
+ class DateValidator < ActiveModel::EachValidator
2
+
3
+ # https://stackoverflow.com/questions/28538080/rails-validate-dates-in-model-before-type-cast
4
+
5
+ def validate_each(record, attribute, value)
6
+
7
+ @error_hash = options[:error_level].present? ? record.send(options[:error_level]) : record.errors
8
+
9
+ original_value = record.read_attribute_before_type_cast( attribute )
10
+ # dont display date format error unless date could not be parsed
11
+ if value.nil?
12
+ # if blank date was given it's still not a format issue, still show message if desired
13
+ return if original_value.blank? && options[:allow_blank]
14
+
15
+ # display helpful date format validation message with original value
16
+ message = options[:time] ? ": #{original_value} is not a valid value. Value must be a date in YYYY-MM-DD or YYYY-MM-DD HH:MM:SS format." : ": #{original_value} is not a valid value. Value must be a date in YYYY-MM-DD."
17
+ @error_hash.add(attribute, (options[:message] || message))
18
+ else
19
+ # handle validation options on valid date instances
20
+ return unless value.respond_to?(:strftime)
21
+ if(options[:after] && (after_message = validate_after_option(record, attribute, value, original_value)) != true)
22
+ @error_hash.add(attribute, (options[:after_message] || "#{after_message}."))
23
+ end
24
+ if(options[:before] && (before_message = validate_before_option(record, attribute, value, original_value)) != true)
25
+ @error_hash.add(attribute, (options[:before_message] || "#{before_message}."))
26
+ end
27
+ end
28
+ end
29
+
30
+ def validate_after_option(record, attribute, value, original_value)
31
+ date, value = date_for(record, value, options[:after])
32
+ return true unless date.present?
33
+ return true if value.present? && date.present? && (value && date && value >= date)
34
+
35
+ after_message = ": #{original_value} is not a valid value."
36
+ if options[:after] == :now || options[:after] == :today
37
+ after_message << " Date cannot be in the past"
38
+ elsif options[:after].respond_to?(:strftime)
39
+ after_message << " Date cannot be before #{options[:after]}"
40
+ elsif options[:after].is_a? Proc
41
+ after_message << " Date cannot be before #{options[:after].call(record)}"
42
+ elsif record.respond_to?(options[:after])
43
+ after_message << " Date cannot be before #{options[:after]}"
44
+ end
45
+ after_message
46
+ end
47
+
48
+ def validate_before_option(record, attribute, value, original_value)
49
+ date, value = date_for(record, value, options[:before])
50
+ return true unless date.present?
51
+ return true if value.present? && date.present? && (value && date && value <= date)
52
+
53
+ before_message = ": #{original_value} is not a valid value."
54
+ if options[:before] == :now || options[:before] == :today
55
+ before_message << " Date cannot be in the future"
56
+ elsif options[:before].respond_to?(:strftime)
57
+ before_message << " Date cannot be after #{options[:before]}"
58
+ elsif options[:before].is_a? Proc
59
+ before_message << " Date cannot be after #{options[:before].call(record)}"
60
+ elsif record.respond_to?(options[:before])
61
+ before_message << " Date cannot be after #{options[:before]}"
62
+ end
63
+ before_message
64
+ end
65
+
66
+ # source: https://github.com/fnando/validators/blob/main/lib/validators/validates_datetime.rb
67
+ def date_for(record, value, option)
68
+ date = case option
69
+ when :today
70
+ Date.today
71
+ when :now
72
+ Time.now + 60 # be lenient on now for server clocks
73
+ when Time, Date, DateTime, ActiveSupport::TimeWithZone
74
+ option
75
+ when Proc
76
+ option.call(record)
77
+ else
78
+ record.__send__(option) if record.respond_to?(option)
79
+ end
80
+ return unless date.present?
81
+ date = date.to_date unless options[:time]
82
+ if date.is_a?(Time)
83
+ value = value.to_time
84
+ elsif date.is_a?(Date)
85
+ value = value.to_date
86
+ end
87
+ [date, value]
88
+ end
89
+
90
+ end
@@ -0,0 +1,97 @@
1
+ class MustBeValidator < ActiveModel::EachValidator
2
+ MATCHERS = {
3
+ equal_to: :==,
4
+ greater_than: :>,
5
+ greater_or_equal_to: :>=,
6
+ less_than: :<,
7
+ less_or_equal_to: :<=,
8
+ not_equal_to: :!=,
9
+ matching: :=~,
10
+ # one_of: :in?
11
+ }.freeze
12
+
13
+ MESSAGE_PREFIX = "must be"
14
+ ERRORS_METHOD = :errors
15
+
16
+ def validate_each(record, attribute, value)
17
+ raise "must_be_validator requires at least one comparison operator for attribute." unless options.slice(MATCHERS.keys + [:blank, :present])
18
+
19
+ original_value = record.read_attribute_before_type_cast( attribute )
20
+
21
+ message = self.class::MESSAGE_PREFIX.dup
22
+
23
+ return if options[:blank] && value.blank?
24
+ message << " blank" if options[:blank]
25
+
26
+ return if options[:present] && value.present?
27
+ message << " present" if options[:present]
28
+
29
+ return if options[:one_of] && options[:one_of].include?(value)
30
+ message = ": '#{value}' is not a valid value" if options[:one_of]
31
+
32
+ return if options[:not_any_of] && !(options[:not_any_of].include?(value))
33
+ message = ": '#{value}' is not a valid value" if options[:not_any_of]
34
+
35
+ return if options[:only_from] && (options[:only_from] & Array(value) == Array(value))
36
+ message = ": #{Array(value).join(",").gsub('"', '\'')} is not a valid value" if options[:only_from]
37
+
38
+ # handle before and after date comparisons using date validator
39
+ if options[:before]
40
+ before_resp = DateValidator.new(options.merge(attributes: attributes)).validate_before_option(record, attribute, value, original_value)
41
+ return if before_resp == true
42
+ message = before_resp
43
+ end
44
+ if options[:after]
45
+ after_resp = DateValidator.new(options.merge(attributes: attributes)).validate_after_option(record, attribute, value, original_value)
46
+ return if after_resp == true
47
+ message = after_resp
48
+ end
49
+
50
+ options.slice(*MATCHERS.keys).each do |key, operand|
51
+ operand_msg = operand.is_a?(Regexp) ? operand.inspect : operand.to_s
52
+ operand = record.send(operand) if operand.is_a? Symbol
53
+ return if operand.nil?
54
+ return if value&.send(MATCHERS[key], operand)
55
+ return if key == :not_equal_to && value != operand
56
+ message << " #{key.to_s.humanize(capitalize: false)} #{operand_msg}"
57
+ end
58
+
59
+ if options[:when].present?
60
+ # check if conditions to validate satisfied
61
+ conditions = options[:when]
62
+ raise "Invalid :when option provided to must_be, must be a Hash or method" unless (conditions.is_a? Hash || conditions.is_a?(Method))
63
+ # add error message with predicate info
64
+ message << " when "
65
+ condition_errors = []
66
+ conditions.each do |field, values|
67
+ case values
68
+ when Regexp
69
+ return if !values.match?(record[field])
70
+ condition_errors << "#{field} = #{record[field]}"
71
+ when Array
72
+ return if !values.include?(record[field])
73
+ condition_errors << "#{field} = #{record[field]}"
74
+ when Symbol
75
+ return if !record.send(field)&.send(values)
76
+ condition_errors << "#{field} is #{values.to_s.gsub('?', '')}"
77
+ else
78
+ return if values != record[field]
79
+ condition_errors << "#{field} = #{record[field]}"
80
+ end
81
+ end
82
+ message << condition_errors.join(' and ')
83
+ end
84
+
85
+ if options[:one_of] && options.fetch(:show_values, true)
86
+ message << ". Valid values: #{options[:one_of].join(', ')}"
87
+ end
88
+ if options[:only_from] && options.fetch(:show_values, true)
89
+ message << ". Valid values: #{options[:only_from].join(', ')}"
90
+ end
91
+ if options[:not_any_of] && options.fetch(:show_values, true)
92
+ message << ". Invalid values: #{options[:not_any_of].join(', ')}"
93
+ end
94
+
95
+ record.send(self.class::ERRORS_METHOD).add(attribute, (options[:message] || "#{message}."), rule_name: options[:rule_name])
96
+ end
97
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: be_valid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - John Stewart (johnsinco)
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-04-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activerecord
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ description: Provides more advanced and flexible 'if then then that' style conditional
84
+ validation. Validate fields based on the values of other fields in your model.
85
+ email:
86
+ - johnstewartco@outlook.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - be_valid.gemspec
98
+ - bin/console
99
+ - bin/setup
100
+ - lib/be_valid.rb
101
+ - lib/be_valid/be_valid.rb
102
+ - lib/be_valid/version.rb
103
+ - lib/validators/date_validator.rb
104
+ - lib/validators/must_be_validator.rb
105
+ homepage: https://github.com/johnsinco/be_then
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubygems_version: 3.0.3
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Custom Ruby on Rails Validation library supporting 'conditional' validation
128
+ test_files: []