activevalidation 0.1.0 → 1.0.0
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 +4 -4
- data/.gitignore +32 -9
- data/.overcommit.yml +93 -0
- data/.rspec +1 -1
- data/.rubocop.yml +80 -0
- data/.rubocop_todo.yml +7 -0
- data/.travis.yml +42 -2
- data/.yardops +9 -0
- data/Appraisals +27 -0
- data/Gemfile +24 -1
- data/MIT-LICENSE +20 -0
- data/README.md +142 -20
- data/Rakefile +21 -3
- data/activevalidation.gemspec +35 -25
- data/bin/console +4 -7
- data/gemfiles/am_5.0.gemfile +17 -0
- data/gemfiles/am_5.0.gemfile.lock +159 -0
- data/gemfiles/am_5.1.gemfile +17 -0
- data/gemfiles/am_5.1.gemfile.lock +159 -0
- data/gemfiles/am_5.2.gemfile +17 -0
- data/gemfiles/am_5.2.gemfile.lock +159 -0
- data/gemfiles/am_6.0.gemfile +18 -0
- data/gemfiles/am_6.0.gemfile.lock +158 -0
- data/lib/active_validation.rb +27 -0
- data/lib/active_validation/base_adapter.rb +103 -0
- data/lib/active_validation/configuration.rb +52 -0
- data/lib/active_validation/decorator.rb +27 -0
- data/lib/active_validation/decorators/consistent_registry.rb +36 -0
- data/lib/active_validation/decorators/disallows_duplicates_registry.rb +17 -0
- data/lib/active_validation/errors.rb +28 -0
- data/lib/active_validation/ext/add_active_validation_context_check.rb +21 -0
- data/lib/active_validation/formatters/manifest_name_formatter.rb +13 -0
- data/lib/active_validation/formatters/validation_context_formatter.rb +28 -0
- data/lib/active_validation/frameworks/rspec.rb +10 -0
- data/lib/active_validation/frameworks/rspec/helpers.rb +15 -0
- data/lib/active_validation/internal/models/check.rb +51 -0
- data/lib/active_validation/internal/models/concerns/to_internal.rb +27 -0
- data/lib/active_validation/internal/models/manifest.rb +122 -0
- data/lib/active_validation/internal/models/manifest/installer.rb +86 -0
- data/lib/active_validation/internal/observers/manifest.rb +114 -0
- data/lib/active_validation/model_extension_base.rb +33 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/adapter.rb +59 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/internals/active_validation/internal_model_extensions/check.rb +11 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/model_extension/active_validation/active_record_model_extension.rb +25 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check.rb +31 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/concerns/method_must_be_allowed.rb +38 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validate_method.rb +9 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_method.rb +9 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_with_method.rb +19 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/concerns/protect_from_mutable_instance_methods.rb +31 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/manifest.rb +21 -0
- data/lib/active_validation/orm_plugins/active_record_plugin/types/active_validation/type/version.rb +17 -0
- data/lib/active_validation/registry.rb +55 -0
- data/lib/active_validation/values/base.rb +39 -0
- data/lib/active_validation/values/method_name.rb +22 -0
- data/lib/active_validation/values/version.rb +17 -0
- data/lib/active_validation/verifier.rb +150 -0
- data/lib/active_validation/version.rb +5 -0
- data/spec/active_validation/base_adapter_spec.rb +23 -0
- data/spec/active_validation/configuration_spec.rb +52 -0
- data/spec/active_validation/decorators/consistent_registry_spec.rb +117 -0
- data/spec/active_validation/decorators/disallows_duplicates_registry_spec.rb +21 -0
- data/spec/active_validation/formatters/manifest_name_formatter_spec.rb +7 -0
- data/spec/active_validation/formatters/validation_context_formatter_spec.rb +39 -0
- data/spec/active_validation/internal/models/check_spec.rb +67 -0
- data/spec/active_validation/internal/models/manifest/installer_spec.rb +177 -0
- data/spec/active_validation/internal/models/manifest_spec.rb +136 -0
- data/spec/active_validation/internal/observers/manifest_spec.rb +201 -0
- data/spec/active_validation/model_extension_base_spec.rb +71 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/adapter_spec.rb +31 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/adapter_spec_orm_specific_spec.rb +84 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validate_method_spec.rb +26 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_method_spec.rb +26 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_with_method_spec.rb +34 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check_spec.rb +48 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/manifest_spec.rb +61 -0
- data/spec/active_validation/orm_plugins/active_record_plugin/readme_spec.rb +89 -0
- data/spec/active_validation/registry_spec.rb +76 -0
- data/spec/active_validation/values/base_spec.rb +61 -0
- data/spec/active_validation/values/method_name_spec.rb +16 -0
- data/spec/active_validation/values/version_spec.rb +36 -0
- data/spec/active_validation/verifier_spec.rb +214 -0
- data/spec/active_validation_spec.rb +19 -0
- data/spec/factories/internal/internal_check.rb +43 -0
- data/spec/features/active_record/child_record.feature +32 -0
- data/spec/features/active_record/new_record.feature +22 -0
- data/spec/features/no_orm/install.feature +19 -0
- data/spec/features/no_orm/validate.feature +27 -0
- data/spec/features/no_orm/validate_with_multiple_manifests.feature +29 -0
- data/spec/features/no_orm/validate_with_multiple_versions.feature +42 -0
- data/spec/features/placeholders/be_matcher.rb +7 -0
- data/spec/features/placeholders/klass.rb +5 -0
- data/spec/features/placeholders/version.rb +11 -0
- data/spec/features/placeholders/whether_to.rb +11 -0
- data/spec/features/step_definitions/active_record_steps.rb +7 -0
- data/spec/features/step_definitions/steps.rb +85 -0
- data/spec/orm/active_record/db_adapters/database.mysql.yml +12 -0
- data/spec/orm/active_record/db_adapters/database.postgres.yml +11 -0
- data/spec/orm/active_record/db_adapters/database.sqlite.yml +8 -0
- data/spec/orm/active_record/factories/check/check_validate.rb +8 -0
- data/spec/orm/active_record/factories/check/check_validates.rb +8 -0
- data/spec/orm/active_record/factories/check/check_validates_with.rb +19 -0
- data/spec/orm/active_record/factories/manifest.rb +29 -0
- data/spec/orm/active_record/prepare_db.rb +89 -0
- data/spec/orm/active_record/setup.rb +11 -0
- data/spec/orm/mongoid/setup.rb +15 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/database_cleaner.rb +16 -0
- data/spec/support/deferred_garbage_collection.rb +31 -0
- data/spec/support/define_constant_macros.rb +17 -0
- data/spec/support/factory_bot.rb +12 -0
- data/spec/support/helpers.rb +3 -0
- data/spec/support/helpers/only_with_active_record.rb +15 -0
- data/spec/support/matchers/delegate.rb +50 -0
- data/spec/support/matchers/have_attr.rb +58 -0
- data/spec/support/mongoid.yml +6 -0
- data/spec/support/shared_examples/check_attributes.rb +9 -0
- data/spec/support/shared_examples/verifiers_registry.rb +10 -0
- data/spec/support/simplecov.rb +11 -0
- data/spec/turnip_helper.rb +4 -0
- metadata +304 -20
- data/lib/activevalidation.rb +0 -6
- data/lib/activevalidation/version.rb +0 -3
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
using ActiveValidation::Internal::Models::Concerns::ToInternal::Check
|
|
4
|
+
|
|
5
|
+
module ActiveValidation
|
|
6
|
+
module Internal
|
|
7
|
+
module Models
|
|
8
|
+
class Manifest
|
|
9
|
+
class Installer
|
|
10
|
+
attr_reader :base_class, :installed_callbacks, :checks, :context
|
|
11
|
+
|
|
12
|
+
def initialize(base_class:, checks: [], context:)
|
|
13
|
+
@base_class = base_class
|
|
14
|
+
@checks = checks
|
|
15
|
+
@context = context
|
|
16
|
+
@installed_callbacks = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Add all checks (validations) to base_class
|
|
20
|
+
#
|
|
21
|
+
# @note we have to use the hack with
|
|
22
|
+
# `callbacks_chain.each.to_a # => chain` since `chain` method is
|
|
23
|
+
# protected.
|
|
24
|
+
#
|
|
25
|
+
# @return [TrueClass]
|
|
26
|
+
def install
|
|
27
|
+
@installed_callbacks = chain_mutex.synchronize do
|
|
28
|
+
before = callbacks_chain_elements
|
|
29
|
+
checks.each { |check| base_class.public_send(*check.to_validation_arguments(context: context)) }
|
|
30
|
+
callbacks_chain_elements - before
|
|
31
|
+
end
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Remove all checks (validations) from base_class
|
|
36
|
+
#
|
|
37
|
+
# we can not use ActiveSupport provided methods, since we already have
|
|
38
|
+
# our installed callbacks.
|
|
39
|
+
#
|
|
40
|
+
# @return [FalseClass]
|
|
41
|
+
def uninstall
|
|
42
|
+
installed_validators = installed_callbacks.map(&:filter).select { |f| f.is_a? ActiveModel::Validator }
|
|
43
|
+
|
|
44
|
+
([base_class] + ActiveSupport::DescendantsTracker.descendants(base_class)).reverse_each do |target|
|
|
45
|
+
uninstall! target: target, installed_validators: installed_validators
|
|
46
|
+
end
|
|
47
|
+
installed_callbacks.clear
|
|
48
|
+
false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def callbacks_chain
|
|
52
|
+
base_class._validate_callbacks
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# We cannot call `chain` method directly, so we have to use this hack
|
|
58
|
+
#
|
|
59
|
+
# @return [Array<ActiveSupport::Callbacks>]
|
|
60
|
+
def callbacks_chain_elements
|
|
61
|
+
callbacks_chain.each.to_a
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Mutex from ActiveSupport::Callbacks::CallbackChain for `base_class`
|
|
65
|
+
#
|
|
66
|
+
# @return [Mutex]
|
|
67
|
+
def chain_mutex
|
|
68
|
+
callbacks_chain.instance_variable_get(:@mutex)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def uninstall!(target:, installed_validators:)
|
|
72
|
+
chain = target._validate_callbacks
|
|
73
|
+
|
|
74
|
+
chain.instance_variable_get(:@mutex).synchronize do
|
|
75
|
+
target._validators
|
|
76
|
+
.each_value { |v| v.reject! { |el| installed_validators.include?(el) } }
|
|
77
|
+
.delete_if { |_, v| v.empty? }
|
|
78
|
+
|
|
79
|
+
installed_callbacks.each { |callback| chain.delete(callback) }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
module Internal
|
|
5
|
+
module Observers
|
|
6
|
+
class Manifest
|
|
7
|
+
DISABLED_VERIFIER = :verifier_is_not_enabled
|
|
8
|
+
RECENT_FAILURE = :it_was_failed_too_recently
|
|
9
|
+
NOT_FOUND = :not_found
|
|
10
|
+
ALREADY_INSTALLED = :already_installed
|
|
11
|
+
INSTALLED = :installed
|
|
12
|
+
UNINSTALLED = :uninstalled
|
|
13
|
+
|
|
14
|
+
attr_reader :verifier, :installed_manifests, :last_failed_attempt_time
|
|
15
|
+
|
|
16
|
+
delegate :manifest, :failed_attempt_retry_time, :enabled?, to: :verifier
|
|
17
|
+
|
|
18
|
+
def initialize(verifier)
|
|
19
|
+
@verifier = verifier
|
|
20
|
+
@installed_manifests = Concurrent::Set.new
|
|
21
|
+
@lock = Concurrent::ReadWriteLock.new
|
|
22
|
+
@last_failed_attempt_time = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Install manifest and store the result. Load priority:
|
|
26
|
+
# * `manifest_id` from the arguments
|
|
27
|
+
# * `verifier.manifest` - defined by user in verifier block
|
|
28
|
+
# * `current_manifest` - the latest manifest with current version
|
|
29
|
+
#
|
|
30
|
+
# The process will be skipped if:
|
|
31
|
+
# - `verifier` not `enabled?`
|
|
32
|
+
# - The last failed attempt was too recent.
|
|
33
|
+
#
|
|
34
|
+
# @return Symbol
|
|
35
|
+
def install(manifest_id: nil)
|
|
36
|
+
return DISABLED_VERIFIER unless enabled?
|
|
37
|
+
return RECENT_FAILURE if attempt_to_was_failed_recently?
|
|
38
|
+
|
|
39
|
+
found = lock.with_read_lock { find(manifest_id: manifest_id) } || current_manifest
|
|
40
|
+
return NOT_FOUND unless found
|
|
41
|
+
return ALREADY_INSTALLED if found.installed?
|
|
42
|
+
|
|
43
|
+
install! internal_manifest: found
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Uninstall the manifest.
|
|
47
|
+
#
|
|
48
|
+
# @return Symbol
|
|
49
|
+
def uninstall(manifest_id:)
|
|
50
|
+
lock.with_write_lock do
|
|
51
|
+
internal_manifest = find(manifest_id: manifest_id)
|
|
52
|
+
return NOT_FOUND unless internal_manifest&.installed?
|
|
53
|
+
|
|
54
|
+
internal_manifest.uninstall
|
|
55
|
+
installed_manifests.delete internal_manifest
|
|
56
|
+
end
|
|
57
|
+
UNINSTALLED
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# We omit the error and only save the error time to prevent
|
|
61
|
+
# DB flooding, when there is no any manifest.
|
|
62
|
+
#
|
|
63
|
+
# We do not know all possible errors so we can not be more
|
|
64
|
+
# specific here.
|
|
65
|
+
#
|
|
66
|
+
# @return [Internal::Manifest]
|
|
67
|
+
def current_manifest
|
|
68
|
+
verifier.current_manifest.to_internal_manifest
|
|
69
|
+
rescue StandardError => _e
|
|
70
|
+
@last_failed_attempt_time = Time.now
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# We need this method for been able to restore manifest lookup
|
|
75
|
+
#
|
|
76
|
+
# @return nil
|
|
77
|
+
def reset_last_failed_attempt_time
|
|
78
|
+
@last_failed_attempt_time = nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The actual install process
|
|
82
|
+
#
|
|
83
|
+
# @return Symbol
|
|
84
|
+
def install!(internal_manifest:)
|
|
85
|
+
lock.with_write_lock do
|
|
86
|
+
return ALREADY_INSTALLED if find(manifest_id: internal_manifest.id)&.installed?
|
|
87
|
+
|
|
88
|
+
internal_manifest.install
|
|
89
|
+
installed_manifests << internal_manifest
|
|
90
|
+
end
|
|
91
|
+
INSTALLED
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
attr_reader :lock
|
|
97
|
+
|
|
98
|
+
def find(manifest_id: nil)
|
|
99
|
+
return unless manifest_id
|
|
100
|
+
|
|
101
|
+
@installed_manifests.detect do |manifest|
|
|
102
|
+
manifest.id == manifest_id
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def attempt_to_was_failed_recently?
|
|
107
|
+
return unless last_failed_attempt_time
|
|
108
|
+
|
|
109
|
+
Time.now < @last_failed_attempt_time + failed_attempt_retry_time
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
module ModelExtensionBase
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
module ClassMethods
|
|
7
|
+
# This is entry point, which setup or return existed Verifier.
|
|
8
|
+
#
|
|
9
|
+
# @return Verifier
|
|
10
|
+
def active_validation(&block)
|
|
11
|
+
register_active_validation_relations unless ActiveValidation::Verifier.registry.registered? self
|
|
12
|
+
::ActiveValidation::Verifier.find_or_build self, &block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Invoke the checks that validations are in place. Should be overwritten in ORM
|
|
18
|
+
# adapter
|
|
19
|
+
#
|
|
20
|
+
# @return nil
|
|
21
|
+
def register_active_validation_relations; end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
# We need to be sure, that all validation call backs are in place before validation
|
|
27
|
+
#
|
|
28
|
+
# @return nil
|
|
29
|
+
def process_active_validation
|
|
30
|
+
::ActiveValidation::Verifier.find_or_build(self.class).install!(manifest_id: manifest_id)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
module OrmPlugins
|
|
5
|
+
module ActiveRecordPlugin
|
|
6
|
+
class Adapter < ActiveValidation::BaseAdapter
|
|
7
|
+
loading_paths << "models"
|
|
8
|
+
loading_paths << "types"
|
|
9
|
+
loading_paths << "internals"
|
|
10
|
+
loading_paths << "model_extension"
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
setup unless self.class.initialised
|
|
14
|
+
self.class.initialised = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [true]
|
|
18
|
+
def setup
|
|
19
|
+
return installer if defined?(::ActiveRecord::Base)
|
|
20
|
+
|
|
21
|
+
::ActiveSupport.on_load(:active_record_adapter, &method(:installer))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @see BaseAdapter
|
|
25
|
+
def add_manifest(manifest)
|
|
26
|
+
Manifest.create manifest.as_json(checks: { only: %i[type argument options], as: :checks_attributes })
|
|
27
|
+
manifest
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @see BaseAdapter
|
|
31
|
+
def find_manifests(wheres)
|
|
32
|
+
search(wheres)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @see BaseAdapter
|
|
36
|
+
def find_manifest(wheres)
|
|
37
|
+
search(wheres, &:first!)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# @api internal
|
|
43
|
+
def search(wheres)
|
|
44
|
+
relation = Manifest.includes(:checks).where(wheres).order(created_at: :desc, id: :desc)
|
|
45
|
+
relation = yield relation if block_given?
|
|
46
|
+
relation.is_a?(ActiveRecord::Base) ? relation.to_internal_manifest : relation.map(&:to_internal_manifest)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [true]
|
|
50
|
+
def installer
|
|
51
|
+
ActiveValidation::OrmPlugins::ActiveRecordPlugin::Adapter.loader
|
|
52
|
+
ActiveValidation::Internal::Models::Check.include(ActiveValidation::InternalModelExtensions::Check)
|
|
53
|
+
::ActiveRecord::Base.include ActiveValidation::ActiveRecordModelExtension
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
module ActiveRecordModelExtension
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
include ModelExtensionBase
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def register_active_validation_relations
|
|
12
|
+
before_validation :process_active_validation
|
|
13
|
+
|
|
14
|
+
belongs_to :manifest, class_name: "::ActiveValidation::Manifest", dependent: :destroy
|
|
15
|
+
|
|
16
|
+
ActiveValidation::Manifest.has_many realtion_plural_name
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def realtion_plural_name
|
|
21
|
+
name.underscore.pluralize.to_sym
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
class Check < ActiveRecord::Base
|
|
5
|
+
prepend ActiveValidation::Concerns::ProtectFromMutableInstanceMethods
|
|
6
|
+
|
|
7
|
+
serialize :options
|
|
8
|
+
|
|
9
|
+
self.table_name = :active_validation_checks
|
|
10
|
+
self.store_full_sti_class = false
|
|
11
|
+
|
|
12
|
+
belongs_to :manifest, inverse_of: :checks
|
|
13
|
+
|
|
14
|
+
validates :argument, presence: true
|
|
15
|
+
|
|
16
|
+
def method_name
|
|
17
|
+
raise NotImplementedError, "abstract" unless self.class < Check
|
|
18
|
+
|
|
19
|
+
self.class.name.demodulize.sub(/Method\z/, "").underscore
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def method_name=(other)
|
|
23
|
+
self.type = other.camelcase + "Method"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_internal_check
|
|
27
|
+
json_options = { methods: %i[method_name], root: false }
|
|
28
|
+
ActiveValidation::Internal::Models::Check.new as_json(json_options).to_options!
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
class Check < ActiveRecord::Base
|
|
5
|
+
module Concerns
|
|
6
|
+
module MethodMustBeAllowed
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
validate :method_must_be_allowed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def method_must_be_allowed
|
|
16
|
+
verify_options
|
|
17
|
+
verify_argument
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def verify_options
|
|
21
|
+
return unless options
|
|
22
|
+
|
|
23
|
+
danger_values = options.slice([:if, :unless, "if", "unless"]).values
|
|
24
|
+
return if (danger_values & restricted_instance_methods).empty?
|
|
25
|
+
|
|
26
|
+
errors.add :options, "Options contain dangerous checks"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def verify_argument
|
|
30
|
+
return unless argument && manifest.try(:base_klass)
|
|
31
|
+
return unless restricted_instance_methods.include?(argument.to_sym)
|
|
32
|
+
|
|
33
|
+
errors.add :argument, "method #{argument} is restricted for usage in validation"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveValidation
|
|
4
|
+
class Check < ActiveRecord::Base
|
|
5
|
+
class ValidatesWithMethod < Check
|
|
6
|
+
validate :validator_must_be_defined
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def validator_must_be_defined
|
|
11
|
+
return if argument.blank?
|
|
12
|
+
|
|
13
|
+
argument.constantize.is_a? ActiveModel::Validator
|
|
14
|
+
rescue NameError
|
|
15
|
+
errors.add :argument, "Validator not exist"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|