active_record_auto_validations 0.0.2 → 0.0.3

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
  SHA256:
3
- metadata.gz: db14026d46494a83057c52f72b6bc2d7c61e9bea8321d3f25830a04ccf4a01a0
4
- data.tar.gz: 989f1988c0b10ae172a9735705458025db3c141349b1324e75278e7178681097
3
+ metadata.gz: d82df8dff2fe3dd3b03523499df936304e262721ea812792749059393602b662
4
+ data.tar.gz: 2f34c86ef85893c79c215c8227458ab42e0503c46cca848527ddef8f71dc1642
5
5
  SHA512:
6
- metadata.gz: c57baea430f577cecdfb69d9890d2ebcee10662dd0dac05eb3e0baca884f2499bfabfd2867940f6af3ce168373a983f403af5e269f9df6866afa0bd7f07cb091
7
- data.tar.gz: f4c749d363d248d89733da4d9260eb2a01df123afd23cf1c74ac6aefe3b366596dbd306f04f2c2a77a3c2b490ee2ae557d159fc79ff92400859ecec1dfbc11bc
6
+ metadata.gz: a3cee5f5a370c5c800efc0976eea8a350bb08d75bb9eab11d6fab4446d7ca2eab18096777daeff71f790bf51dd228c2a6c12e1d961c4eeda03541f1055f2be11
7
+ data.tar.gz: f2eb99db19909b63de9de904a86f0dc9b119117c6391b27d5595e8b0ca0ad5c4013b3bfa164a2314dafe393e315ca148db60206f2a99fb79d593da69c382608f
data/README.md CHANGED
@@ -21,6 +21,32 @@ Or install it yourself as:
21
21
  $ gem install active_record_auto_validations
22
22
  ```
23
23
 
24
+ Add this initializer:
25
+ ```ruby
26
+ Rails.autoloaders.main.on_load do |_klass_name, klass, _defined_by_file|
27
+ ActiveRecordAutoValidations::OnLoad.execute!(klass: klass)
28
+ end
29
+ ```
30
+
31
+ To run auto-validations on all models you can add something like this to ApplicationRecord:
32
+ ```ruby
33
+ class ApplicationRecord < ActiveRecord::Base
34
+ primary_abstract_class
35
+
36
+ def self.inherited(child)
37
+ super
38
+ child.include ActiveRecordAutoValidations::ModelConcern
39
+ end
40
+ end
41
+ ```
42
+
43
+ To run auto-validations on a single model you can do this:
44
+ ```ruby
45
+ class Project < ApplicationRecord
46
+ include ActiveRecordAutoValidations::ModelConcern
47
+ end
48
+ ```
49
+
24
50
  ## Contributing
25
51
  Contribution directions go here.
26
52
 
@@ -2,6 +2,10 @@ module ActiveRecordAutoValidations::ModelConcern
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do |base|
5
- ActiveRecordAutoValidations::AutoValidateModelClass.execute!(model_class: base)
5
+ # The actual loading needs to be done after the model has been fully loaded,
6
+ # so this is actually done through a "on_load"-callback in an initializer.
7
+ #
8
+ # Kind of a hack but haven't been able to find another way of doing this
9
+ base.instance_variable_set(:@active_record_auto_validations_marked, true)
6
10
  end
7
11
  end
@@ -0,0 +1,41 @@
1
+ class ActiveRecordAutoValidations::AutoUniqueIndex
2
+ def self.execute!(**args)
3
+ ActiveRecordAutoValidations::AutoUniqueIndex.new(**args).perform
4
+ end
5
+
6
+ attr_reader :columns, :index, :model_class
7
+
8
+ def initialize(columns:, index:, model_class:)
9
+ @columns = columns
10
+ @index = index
11
+ @model_class = model_class
12
+ end
13
+
14
+ def perform
15
+ model_class.validates last_column_name.to_sym, **validates_args
16
+ end
17
+
18
+ def index_columns
19
+ @index_columns ||= index.columns.map { |column_name| columns.find { |column| column.name == column_name } }
20
+ end
21
+
22
+ def last_column_name
23
+ @last_column_name ||= index.columns.last
24
+ end
25
+
26
+ def validates_args
27
+ rest_of_columns = index.columns.clone
28
+ rest_of_columns.pop
29
+
30
+ validates_args = {}
31
+
32
+ if rest_of_columns.length.positive?
33
+ validates_args[:uniqueness] ||= {}
34
+ validates_args[:uniqueness][:scope] = rest_of_columns
35
+ end
36
+
37
+ validates_args[:allow_blank] = true if index_columns.any?(&:null)
38
+ validates_args[:uniqueness] ||= true
39
+ validates_args
40
+ end
41
+ end
@@ -1,28 +1,50 @@
1
- class ActiveRecordAutoValidations::AutoValidateModelClass < ApplicationService
1
+ class ActiveRecordAutoValidations::AutoValidateModelClass
2
2
  attr_reader :model_class
3
3
 
4
+ def self.execute!(model_class:)
5
+ ActiveRecordAutoValidations::AutoValidateModelClass.new(model_class: model_class).perform
6
+ end
7
+
4
8
  def initialize(model_class:)
5
9
  @model_class = model_class
6
10
  end
7
11
 
8
12
  def perform
13
+ check_if_already_loaded_on_model_class!
14
+ register_loaded_on_model_class!
15
+
9
16
  begin
10
17
  columns
18
+ indexes
11
19
  rescue ActiveRecord::StatementInvalid => e
12
20
  # Database is probably not running - we need to ignore this to make stuff like db:migrate, db:schema:load work
13
- Rails.logger.error "AutoValidate: Ignoring error while loading columns, because database might not be initialized: #{e.message}"
14
- return succeed!
21
+ Rails.logger.error { "AutoValidate: Ignoring error while loading columns, because database might not be initialized: #{e.message}" }
22
+ return
15
23
  end
16
24
 
17
- insert_active_record_auto_validations!
18
- succeed!
25
+ insert_active_record_auto_validations_from_columns!
26
+ insert_active_record_auto_validations_from_indexes!
27
+ end
28
+
29
+ def check_if_already_loaded_on_model_class!
30
+ auto_validations_loaded = model_class.instance_variable_get(:@_active_record_auto_validations_loaded)
31
+
32
+ raise "AutoValidations already loaded for #{model_class.name}" if auto_validations_loaded
33
+ end
34
+
35
+ def register_loaded_on_model_class!
36
+ model_class.instance_variable_set(:@_active_record_auto_validations_loaded, true)
19
37
  end
20
38
 
21
39
  def columns
22
40
  @columns ||= model_class.columns
23
41
  end
24
42
 
25
- def insert_active_record_auto_validations!
43
+ def indexes
44
+ @indexes ||= model_class.connection.indexes(model_class.table_name)
45
+ end
46
+
47
+ def insert_active_record_auto_validations_from_columns!
26
48
  columns.each do |column|
27
49
  next if column.name == "id" || column.name == "created_at" || column.name == "updated_at"
28
50
 
@@ -31,21 +53,51 @@ class ActiveRecordAutoValidations::AutoValidateModelClass < ApplicationService
31
53
  end
32
54
  end
33
55
 
56
+ def insert_active_record_auto_validations_from_indexes!
57
+ indexes.each do |index|
58
+ next unless index.unique
59
+
60
+ # Dont add uniqueness validation to ActsAsList position columns
61
+ if index.columns.include?("position") && model_class.respond_to?(:acts_as_list_top)
62
+ Rails.logger.info { "AutoValidate: Skipping unique validation on #{model_class.table_name}##{index.columns.join(",")} because it looks like ActsAsList" }
63
+ next
64
+ end
65
+
66
+ ActiveRecordAutoValidations::AutoUniqueIndex.execute!(columns: columns, model_class: model_class, index: index)
67
+ end
68
+ end
69
+
70
+ def presence_validation_exists_on_column?(column)
71
+ model_class.validators_on(column.name.to_sym).each do |validator|
72
+ return true if validator.kind == :presence
73
+ end
74
+
75
+ false
76
+ end
77
+
34
78
  def auto_validate_presence_on_column?(column)
35
- !column.null && !column.name.end_with?("_id") && column.default.nil?
79
+ !column.null && !column.name.end_with?("_id") && column.default.nil? && !presence_validation_exists_on_column?(column)
36
80
  end
37
81
 
38
82
  def auto_validate_pesence_on_column!(column)
39
- Rails.logger.info "AutoValidate: Adding presence validation to #{model_class.table_name}##{column.name}"
83
+ Rails.logger.info { "AutoValidate: Adding presence validation to #{model_class.table_name}##{column.name}" }
40
84
  model_class.validates column.name.to_sym, presence: true
41
85
  end
42
86
 
43
87
  def auto_validate_max_length_on_column?(column)
44
- column.type == :string && column.limit
88
+ column.type == :string && column.limit && !max_length_validation_exists_on_column?(column)
45
89
  end
46
90
 
47
91
  def auto_validate_max_length_on_column!(column)
48
- Rails.logger.info "AutoValidate: Adding maxlength of #{column.limit} validation to #{model_class.table_name}##{column.name}"
92
+ Rails.logger.info { "AutoValidate: Adding maxlength of #{column.limit} validation to #{model_class.table_name}##{column.name}" }
49
93
  model_class.validates column.name.to_sym, allow_blank: true, length: {maximum: column.limit}
50
94
  end
95
+
96
+ def max_length_validation_exists_on_column?(column)
97
+ model_class.validators_on(column.name.to_sym).each do |validator|
98
+ return true if validator.kind == :length
99
+ end
100
+
101
+ false
102
+ end
51
103
  end
@@ -0,0 +1,9 @@
1
+ class ActiveRecordAutoValidations::OnLoad
2
+ def self.execute!(klass:)
3
+ # This callback is going to be called for all classes. We should only run auto-validations on the ones that have had the module included
4
+ # which is done through the marked-variable.
5
+ marked = klass.instance_variable_get(:@active_record_auto_validations_marked)
6
+
7
+ ActiveRecordAutoValidations::AutoValidateModelClass.execute!(model_class: klass) if marked
8
+ end
9
+ end
data/config/routes.rb CHANGED
@@ -1,2 +1,2 @@
1
- ActiveRecordAutoValidations::Engine.routes.draw do
1
+ ActiveRecordAutoValidations::Engine.routes.draw do # rubocop:disable Lint/EmptyBlock
2
2
  end
@@ -1,5 +1,5 @@
1
- module ActiveRecordAutoValidations
2
- class Engine < ::Rails::Engine
3
- isolate_namespace ActiveRecordAutoValidations
4
- end
1
+ module ActiveRecordAutoValidations; end
2
+
3
+ class ActiveRecordAutoValidations::Engine < ::Rails::Engine
4
+ isolate_namespace ActiveRecordAutoValidations
5
5
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordAutoValidations
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3".freeze
3
3
  end
@@ -2,5 +2,4 @@ require "active_record_auto_validations/version"
2
2
  require "active_record_auto_validations/engine"
3
3
 
4
4
  module ActiveRecordAutoValidations
5
- # Your code goes here...
6
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_auto_validations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - kaspernj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-19 00:00:00.000000000 Z
11
+ date: 2022-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,104 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 7.0.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mysql2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-performance
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
27
125
  description: Scans ActiveRecord models and adds automatic validations based on null,
28
126
  max length etc.
29
127
  email:
@@ -35,16 +133,10 @@ files:
35
133
  - MIT-LICENSE
36
134
  - README.md
37
135
  - Rakefile
38
- - app/assets/config/active_record_auto_validations_manifest.js
39
- - app/assets/stylesheets/active_record_auto_validations/application.css
40
- - app/controllers/active_record_auto_validations/application_controller.rb
41
- - app/helpers/active_record_auto_validations/application_helper.rb
42
- - app/jobs/active_record_auto_validations/application_job.rb
43
- - app/mailers/active_record_auto_validations/application_mailer.rb
44
- - app/models/active_record_auto_validations/application_record.rb
45
136
  - app/models/concerns/active_record_auto_validations/model_concern.rb
137
+ - app/services/active_record_auto_validations/auto_unique_index.rb
46
138
  - app/services/active_record_auto_validations/auto_validate_model_class.rb
47
- - app/views/layouts/active_record_auto_validations/application.html.erb
139
+ - app/services/active_record_auto_validations/on_load.rb
48
140
  - config/routes.rb
49
141
  - lib/active_record_auto_validations.rb
50
142
  - lib/active_record_auto_validations/engine.rb
@@ -57,6 +149,7 @@ metadata:
57
149
  homepage_uri: https://github.com/kaspernj/active_record_auto_validations
58
150
  source_code_uri: https://github.com/kaspernj/active_record_auto_validations
59
151
  changelog_uri: https://github.com/kaspernj/active_record_auto_validations
152
+ rubygems_mfa_required: 'true'
60
153
  post_install_message:
61
154
  rdoc_options: []
62
155
  require_paths:
@@ -1 +0,0 @@
1
- //= link_directory ../stylesheets/active_record_auto_validations .css
@@ -1,15 +0,0 @@
1
- /*
2
- * This is a manifest file that'll be compiled into application.css, which will include all the files
3
- * listed below.
4
- *
5
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
- * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
- *
8
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
- * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
- * files in this directory. Styles in this file should be added after the last require_* statement.
11
- * It is generally better to create a new file per style scope.
12
- *
13
- *= require_tree .
14
- *= require_self
15
- */
@@ -1,4 +0,0 @@
1
- module ActiveRecordAutoValidations
2
- class ApplicationController < ActionController::Base
3
- end
4
- end
@@ -1,4 +0,0 @@
1
- module ActiveRecordAutoValidations
2
- module ApplicationHelper
3
- end
4
- end
@@ -1,4 +0,0 @@
1
- module ActiveRecordAutoValidations
2
- class ApplicationJob < ActiveJob::Base
3
- end
4
- end
@@ -1,6 +0,0 @@
1
- module ActiveRecordAutoValidations
2
- class ApplicationMailer < ActionMailer::Base
3
- default from: "from@example.com"
4
- layout "mailer"
5
- end
6
- end
@@ -1,5 +0,0 @@
1
- module ActiveRecordAutoValidations
2
- class ApplicationRecord < ActiveRecord::Base
3
- self.abstract_class = true
4
- end
5
- end
@@ -1,15 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Auto validations</title>
5
- <%= csrf_meta_tags %>
6
- <%= csp_meta_tag %>
7
-
8
- <%= stylesheet_link_tag "active_record_auto_validations/application", media: "all" %>
9
- </head>
10
- <body>
11
-
12
- <%= yield %>
13
-
14
- </body>
15
- </html>