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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +32 -9
  3. data/.overcommit.yml +93 -0
  4. data/.rspec +1 -1
  5. data/.rubocop.yml +80 -0
  6. data/.rubocop_todo.yml +7 -0
  7. data/.travis.yml +42 -2
  8. data/.yardops +9 -0
  9. data/Appraisals +27 -0
  10. data/Gemfile +24 -1
  11. data/MIT-LICENSE +20 -0
  12. data/README.md +142 -20
  13. data/Rakefile +21 -3
  14. data/activevalidation.gemspec +35 -25
  15. data/bin/console +4 -7
  16. data/gemfiles/am_5.0.gemfile +17 -0
  17. data/gemfiles/am_5.0.gemfile.lock +159 -0
  18. data/gemfiles/am_5.1.gemfile +17 -0
  19. data/gemfiles/am_5.1.gemfile.lock +159 -0
  20. data/gemfiles/am_5.2.gemfile +17 -0
  21. data/gemfiles/am_5.2.gemfile.lock +159 -0
  22. data/gemfiles/am_6.0.gemfile +18 -0
  23. data/gemfiles/am_6.0.gemfile.lock +158 -0
  24. data/lib/active_validation.rb +27 -0
  25. data/lib/active_validation/base_adapter.rb +103 -0
  26. data/lib/active_validation/configuration.rb +52 -0
  27. data/lib/active_validation/decorator.rb +27 -0
  28. data/lib/active_validation/decorators/consistent_registry.rb +36 -0
  29. data/lib/active_validation/decorators/disallows_duplicates_registry.rb +17 -0
  30. data/lib/active_validation/errors.rb +28 -0
  31. data/lib/active_validation/ext/add_active_validation_context_check.rb +21 -0
  32. data/lib/active_validation/formatters/manifest_name_formatter.rb +13 -0
  33. data/lib/active_validation/formatters/validation_context_formatter.rb +28 -0
  34. data/lib/active_validation/frameworks/rspec.rb +10 -0
  35. data/lib/active_validation/frameworks/rspec/helpers.rb +15 -0
  36. data/lib/active_validation/internal/models/check.rb +51 -0
  37. data/lib/active_validation/internal/models/concerns/to_internal.rb +27 -0
  38. data/lib/active_validation/internal/models/manifest.rb +122 -0
  39. data/lib/active_validation/internal/models/manifest/installer.rb +86 -0
  40. data/lib/active_validation/internal/observers/manifest.rb +114 -0
  41. data/lib/active_validation/model_extension_base.rb +33 -0
  42. data/lib/active_validation/orm_plugins/active_record_plugin/adapter.rb +59 -0
  43. data/lib/active_validation/orm_plugins/active_record_plugin/internals/active_validation/internal_model_extensions/check.rb +11 -0
  44. data/lib/active_validation/orm_plugins/active_record_plugin/model_extension/active_validation/active_record_model_extension.rb +25 -0
  45. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check.rb +31 -0
  46. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/concerns/method_must_be_allowed.rb +38 -0
  47. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validate_method.rb +9 -0
  48. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_method.rb +9 -0
  49. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_with_method.rb +19 -0
  50. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/concerns/protect_from_mutable_instance_methods.rb +31 -0
  51. data/lib/active_validation/orm_plugins/active_record_plugin/models/active_validation/manifest.rb +21 -0
  52. data/lib/active_validation/orm_plugins/active_record_plugin/types/active_validation/type/version.rb +17 -0
  53. data/lib/active_validation/registry.rb +55 -0
  54. data/lib/active_validation/values/base.rb +39 -0
  55. data/lib/active_validation/values/method_name.rb +22 -0
  56. data/lib/active_validation/values/version.rb +17 -0
  57. data/lib/active_validation/verifier.rb +150 -0
  58. data/lib/active_validation/version.rb +5 -0
  59. data/spec/active_validation/base_adapter_spec.rb +23 -0
  60. data/spec/active_validation/configuration_spec.rb +52 -0
  61. data/spec/active_validation/decorators/consistent_registry_spec.rb +117 -0
  62. data/spec/active_validation/decorators/disallows_duplicates_registry_spec.rb +21 -0
  63. data/spec/active_validation/formatters/manifest_name_formatter_spec.rb +7 -0
  64. data/spec/active_validation/formatters/validation_context_formatter_spec.rb +39 -0
  65. data/spec/active_validation/internal/models/check_spec.rb +67 -0
  66. data/spec/active_validation/internal/models/manifest/installer_spec.rb +177 -0
  67. data/spec/active_validation/internal/models/manifest_spec.rb +136 -0
  68. data/spec/active_validation/internal/observers/manifest_spec.rb +201 -0
  69. data/spec/active_validation/model_extension_base_spec.rb +71 -0
  70. data/spec/active_validation/orm_plugins/active_record_plugin/adapter_spec.rb +31 -0
  71. data/spec/active_validation/orm_plugins/active_record_plugin/adapter_spec_orm_specific_spec.rb +84 -0
  72. data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validate_method_spec.rb +26 -0
  73. data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_method_spec.rb +26 -0
  74. data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check/validates_with_method_spec.rb +34 -0
  75. data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/check_spec.rb +48 -0
  76. data/spec/active_validation/orm_plugins/active_record_plugin/models/active_validation/manifest_spec.rb +61 -0
  77. data/spec/active_validation/orm_plugins/active_record_plugin/readme_spec.rb +89 -0
  78. data/spec/active_validation/registry_spec.rb +76 -0
  79. data/spec/active_validation/values/base_spec.rb +61 -0
  80. data/spec/active_validation/values/method_name_spec.rb +16 -0
  81. data/spec/active_validation/values/version_spec.rb +36 -0
  82. data/spec/active_validation/verifier_spec.rb +214 -0
  83. data/spec/active_validation_spec.rb +19 -0
  84. data/spec/factories/internal/internal_check.rb +43 -0
  85. data/spec/features/active_record/child_record.feature +32 -0
  86. data/spec/features/active_record/new_record.feature +22 -0
  87. data/spec/features/no_orm/install.feature +19 -0
  88. data/spec/features/no_orm/validate.feature +27 -0
  89. data/spec/features/no_orm/validate_with_multiple_manifests.feature +29 -0
  90. data/spec/features/no_orm/validate_with_multiple_versions.feature +42 -0
  91. data/spec/features/placeholders/be_matcher.rb +7 -0
  92. data/spec/features/placeholders/klass.rb +5 -0
  93. data/spec/features/placeholders/version.rb +11 -0
  94. data/spec/features/placeholders/whether_to.rb +11 -0
  95. data/spec/features/step_definitions/active_record_steps.rb +7 -0
  96. data/spec/features/step_definitions/steps.rb +85 -0
  97. data/spec/orm/active_record/db_adapters/database.mysql.yml +12 -0
  98. data/spec/orm/active_record/db_adapters/database.postgres.yml +11 -0
  99. data/spec/orm/active_record/db_adapters/database.sqlite.yml +8 -0
  100. data/spec/orm/active_record/factories/check/check_validate.rb +8 -0
  101. data/spec/orm/active_record/factories/check/check_validates.rb +8 -0
  102. data/spec/orm/active_record/factories/check/check_validates_with.rb +19 -0
  103. data/spec/orm/active_record/factories/manifest.rb +29 -0
  104. data/spec/orm/active_record/prepare_db.rb +89 -0
  105. data/spec/orm/active_record/setup.rb +11 -0
  106. data/spec/orm/mongoid/setup.rb +15 -0
  107. data/spec/spec_helper.rb +38 -0
  108. data/spec/support/database_cleaner.rb +16 -0
  109. data/spec/support/deferred_garbage_collection.rb +31 -0
  110. data/spec/support/define_constant_macros.rb +17 -0
  111. data/spec/support/factory_bot.rb +12 -0
  112. data/spec/support/helpers.rb +3 -0
  113. data/spec/support/helpers/only_with_active_record.rb +15 -0
  114. data/spec/support/matchers/delegate.rb +50 -0
  115. data/spec/support/matchers/have_attr.rb +58 -0
  116. data/spec/support/mongoid.yml +6 -0
  117. data/spec/support/shared_examples/check_attributes.rb +9 -0
  118. data/spec/support/shared_examples/verifiers_registry.rb +10 -0
  119. data/spec/support/simplecov.rb +11 -0
  120. data/spec/turnip_helper.rb +4 -0
  121. metadata +304 -20
  122. data/lib/activevalidation.rb +0 -6
  123. data/lib/activevalidation/version.rb +0 -3
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :manifest, class: "ActiveValidation::Manifest" do
5
+ transient do
6
+ # Basics on this Array of Integers after build will be created
7
+ # corresponding constants for the Manifest.
8
+ with_versions { [] }
9
+ end
10
+
11
+ sequence(:name) { |n| "Manifest #{n}" }
12
+ version { 1 }
13
+ base_klass { "Foo" }
14
+
15
+ %i[validate validates validates_with].each do |trait_name|
16
+ trait(trait_name) do
17
+ after(:build) { |record| record.checks << build(:"check_#{trait_name}") }
18
+ end
19
+ end
20
+
21
+ after(:build) do |record, evaluator|
22
+ records = Set.new evaluator.with_versions + [record.version]
23
+ records.each do |n|
24
+ constant_name = "#{record.base_klass}::Validations::V#{n}"
25
+ RSpec::Mocks::ConstantMutator.stub(constant_name, Class.new)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["DB"] ||= "sqlite"
4
+
5
+ require "active_record"
6
+
7
+ class PrepareDb
8
+ module Migrations
9
+ def create_active_validation_manifests
10
+ create_table :active_validation_manifests do |t|
11
+ t.string :name
12
+ t.string :version
13
+ t.string :base_klass
14
+
15
+ t.datetime :created_at
16
+ end
17
+ end
18
+
19
+ def create_active_validation_checks
20
+ create_table :active_validation_checks do |t|
21
+ t.integer :manifest_id
22
+ t.string :type
23
+ t.string :argument
24
+
25
+ t.datetime :created_at
26
+ end
27
+
28
+ # To support ActiveModel 5.0 we have to use this hack
29
+ add_column :active_validation_checks, :options, :json
30
+ end
31
+
32
+ def create_foos
33
+ create_table :foos do |t|
34
+ t.integer :manifest_id
35
+ t.string :name
36
+
37
+ t.datetime :created_at
38
+ t.datetime :updated_at
39
+ end
40
+ end
41
+
42
+ def create_bars
43
+ create_table :bars do |t|
44
+ t.integer :manifest_id
45
+ t.string :name
46
+
47
+ t.datetime :created_at
48
+ t.datetime :updated_at
49
+ end
50
+ end
51
+ end
52
+
53
+ include Migrations
54
+
55
+ class << self
56
+ def call
57
+ new.call
58
+ end
59
+ end
60
+
61
+ def call
62
+ establish_connection
63
+
64
+ Migrations.instance_methods(false).each { |m| public_send(m) }
65
+ end
66
+
67
+ private
68
+
69
+ def establish_connection
70
+ relative_path = "db_adapters/database.#{ENV['DB']}.yml"
71
+ full_path = File.expand_path(relative_path, File.dirname(__FILE__))
72
+
73
+ # Keep the format of database.*.yml files similar with multi-env configs
74
+ # intentionally. It provide simpler support
75
+
76
+ ActiveRecord::Base.establish_connection YAML.load_file(full_path).fetch("test")
77
+ end
78
+
79
+ def connection
80
+ ActiveRecord::Base.connection
81
+ end
82
+
83
+ def create_table(name, *args, &blk)
84
+ connection.drop_table(name, if_exists: true)
85
+ connection.create_table(name, *args, &blk)
86
+ end
87
+
88
+ delegate :add_column, to: :connection
89
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ ActiveRecord::Migration.verbose = false
6
+ ActiveRecord::Base.logger = Logger.new(nil)
7
+ ActiveRecord::Base.include_root_in_json = true
8
+
9
+ require_relative "prepare_db"
10
+
11
+ PrepareDb.call
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mongoid/version"
4
+
5
+ Mongoid.configure do |config|
6
+ config.load!("spec/support/mongoid.yml")
7
+ config.use_utc = true
8
+ config.include_root_in_json = true
9
+ end
10
+
11
+ class ActiveSupport::TestCase
12
+ setup do
13
+ Mongoid.default_session.drop
14
+ end
15
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["ORM"] = "active_record"
4
+
5
+ puts "\n==> ORM = #{ENV['ORM'].inspect}"
6
+
7
+ RSpec.configure do |config|
8
+ config.example_status_persistence_file_path = ".rspec_results"
9
+ config.expect_with :rspec do |expectations|
10
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
+ end
12
+ config.mock_with :rspec do |mocks|
13
+ mocks.verify_partial_doubles = true
14
+ end
15
+ config.filter_run :focus
16
+ config.run_all_when_everything_filtered = true
17
+ config.warnings = false
18
+ config.default_formatter = "doc" if config.files_to_run.one?
19
+ config.order = :random
20
+ Kernel.srand config.seed
21
+
22
+ config.before do
23
+ ActiveValidation.configuration.verifiers_registry.clear
24
+ end
25
+ end
26
+
27
+ require "active_validation"
28
+
29
+ # load ORM related support modules
30
+ require_relative "orm/#{ENV['ORM']}/setup"
31
+
32
+ # Load models for selected ORM
33
+
34
+ ActiveValidation.config.orm_adapter = ENV["ORM"]
35
+
36
+ Dir["#{File.dirname(__FILE__)}/support/*.rb"].each { |f| require f }
37
+ Dir["#{File.dirname(__FILE__)}/support/shared_examples/*.rb"].each { |f| require f }
38
+ Dir["#{File.dirname(__FILE__)}/support/matchers/*.rb"].each { |f| require f }
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "database_cleaner"
4
+
5
+ RSpec.configure do |config|
6
+ config.before(:suite) do
7
+ DatabaseCleaner.strategy = :transaction
8
+ DatabaseCleaner.clean_with(:truncation)
9
+ end
10
+
11
+ config.around do |example|
12
+ DatabaseCleaner.cleaning do
13
+ example.run
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module help to improve RSpec speed to 5-10%
4
+ # It is switched on by default, and accepts `DEFER_GC` ENV variable
5
+ # with Float value.
6
+
7
+ class DeferredGarbageCollection
8
+ DEFERRED_GC_THRESHOLD = (ENV["DEFER_GC"] || 15.0).to_f
9
+
10
+ @last_gc_run = Time.now
11
+
12
+ def self.start
13
+ GC.disable
14
+ end
15
+
16
+ def self.reconsider
17
+ return unless Time.now - @last_gc_run >= DEFERRED_GC_THRESHOLD
18
+
19
+ GC.enable
20
+ GC.start
21
+ GC.disable
22
+ @last_gc_run = Time.now
23
+ end
24
+ end
25
+
26
+ unless ENV["DEFER_GC"] == "0" || ENV["DEFER_GC"] == "false"
27
+ RSpec.configure do |config|
28
+ config.before(:all) { DeferredGarbageCollection.start }
29
+ config.after(:all) { DeferredGarbageCollection.reconsider }
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefineConstantMacros
4
+ def define_const(path, superclass: Object, type: Class, &block)
5
+ const = stub_const(path, type.new(superclass))
6
+ const.class_eval(&block) if block_given?
7
+ const
8
+ end
9
+
10
+ def define_consts(*paths, **opt, &block)
11
+ paths.map { |path| define_const(path, opt, &block) }
12
+ end
13
+ end
14
+
15
+ RSpec.configure do |config|
16
+ config.include DefineConstantMacros
17
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "factory_bot"
4
+
5
+ RSpec.configure do |config|
6
+ config.include FactoryBot::Syntax::Methods
7
+
8
+ config.before(:suite) do
9
+ FactoryBot.definition_file_paths << "spec/orm/#{ENV['ORM']}/factories"
10
+ FactoryBot.find_definitions
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers/only_with_active_record"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyWithActiveRecord
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before do
8
+ skip unless ENV["ORM"] == "active_record"
9
+ end
10
+ end
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.include OnlyWithActiveRecord, type: :active_record
15
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Taken from Thoughtbot FactoryBot
4
+
5
+ # rubocop:disable Metrics/BlockLength
6
+ # rubocop:disable RSpec/VerifiedDoubles
7
+ # rubocop:disable Style/CaseEquality
8
+ RSpec::Matchers.define :delegate do |delegated_method|
9
+ chain :to do |target_method|
10
+ @target_method = target_method
11
+ end
12
+
13
+ chain :as do |method_on_target|
14
+ @method_on_target = method_on_target
15
+ end
16
+
17
+ chain :with_arguments do |args|
18
+ @args = args
19
+ end
20
+
21
+ match do |instance|
22
+ @instance = instance
23
+ @args ||= []
24
+ return_value = "stubbed return value"
25
+ method_on_target = @method_on_target || delegated_method
26
+ stubbed_target = double("stubbed_target", method_on_target => return_value)
27
+ allow(@instance).to receive(@target_method).and_return stubbed_target
28
+ begin
29
+ @instance.send(delegated_method, *@args) == return_value
30
+ rescue NoMethodError
31
+ false
32
+ end
33
+ end
34
+
35
+ failure_message do
36
+ if Class === @instance
37
+ message = "expected #{@instance.name} "
38
+ prefix = "."
39
+ else
40
+ message = "expected #{@instance.class.name} "
41
+ prefix = "#"
42
+ end
43
+ message << "to delegate #{prefix}#{delegated_method} to #{prefix}#{@target_method}"
44
+ message << ".#{@method_on_target}" if @method_on_target
45
+ message
46
+ end
47
+ end
48
+ # rubocop:enable Metrics/BlockLength
49
+ # rubocop:enable RSpec/VerifiedDoubles
50
+ # rubocop:enable Style/CaseEquality
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # `have_attr_reader` matcher for attr_reader
4
+ RSpec::Matchers.define :have_attr_reader do |field|
5
+ match do |object_instance|
6
+ object_instance.respond_to?(field)
7
+ end
8
+
9
+ failure_message do |object_instance|
10
+ "expected attr_reader for #{field} on #{object_instance}"
11
+ end
12
+
13
+ failure_message_when_negated do |object_instance|
14
+ "expected attr_reader for #{field} not to be defined on #{object_instance}"
15
+ end
16
+
17
+ description do
18
+ "checks to see if there is an attr reader #{field} on the instance."
19
+ end
20
+ end
21
+
22
+ # `have_attr_writer` matcher for attr_writer
23
+ RSpec::Matchers.define :have_attr_writer do |field|
24
+ match do |object_instance|
25
+ object_instance.respond_to?("#{field}=")
26
+ end
27
+
28
+ failure_message do |object_instance|
29
+ "expected attr_writer for #{field} on #{object_instance}"
30
+ end
31
+
32
+ failure_message_when_negated do |object_instance|
33
+ "expected attr_writer for #{field} not to be defined on #{object_instance}"
34
+ end
35
+
36
+ description do
37
+ "checks to see if there is an attr writer #{field} on the instance."
38
+ end
39
+ end
40
+
41
+ # `have_attr_accessor` matcher for attr_accessor
42
+ RSpec::Matchers.define :have_attr_accessor do |field|
43
+ match do |object_instance|
44
+ object_instance.respond_to?(field) && object_instance.respond_to?("#{field}=")
45
+ end
46
+
47
+ failure_message do |object_instance|
48
+ "expected attr_accessor for #{field} on #{object_instance}"
49
+ end
50
+
51
+ failure_message_when_negated do |object_instance|
52
+ "expected attr_accessor for #{field} not to be defined on #{object_instance}"
53
+ end
54
+
55
+ description do
56
+ "checks to see if there is an attr accessor #{field} on the instance."
57
+ end
58
+ end
@@ -0,0 +1,6 @@
1
+ test:
2
+ clients:
3
+ default:
4
+ database: devise-test-suite
5
+ hosts:
6
+ - localhost:<%= ENV['MONGODB_PORT'] || '27017' %>
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples "check attributes check" do |_parameter|
4
+ %w[argument options manifest_id].each do |field|
5
+ it "contains field #{field}" do
6
+ expect(subject.attributes).to have_key(field)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context "verifiers registry" do |name = :registry|
4
+ let(name) do
5
+ registry = ActiveValidation::Decorators::DisallowsDuplicatesRegistry.new ActiveValidation::Registry.new("Dummy")
6
+ ActiveValidation::Decorators::ConsistentRegistry.new ActiveValidation::Verifier, registry
7
+ end
8
+
9
+ before { allow(ActiveValidation.configuration).to receive(:verifiers_registry).and_return(send(name)) }
10
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ if ENV["COVERAGE"]
4
+ require "simplecov"
5
+ SimpleCov.command_name "RSpec"
6
+ SimpleCov.start
7
+ SimpleCov.at_exit do
8
+ @exit_status = 0
9
+ SimpleCov.result.format!
10
+ end
11
+ end