action_validator 0.1.0 → 0.1.1

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: 532de573ba9a7413bdfc6ffa8740a6ee28226d95ade42b47855fc5ab269fb01c
4
- data.tar.gz: db4a16bf7690207e95bcb04503ca38f13512cc04d0a7acca5252a135bc32de3a
3
+ metadata.gz: 4aaba049d97346476208fb2f0f732a068c6ab921e369424507f45360fffe45e6
4
+ data.tar.gz: 370ca30364a45c315341897690e5e7abb5789f49e4b4325d3fe2db717c024a8b
5
5
  SHA512:
6
- metadata.gz: b107cc5fdfef31a1b03cf762f4e18b3cf576de2c1bb060a5dbce9b3c577b8b1412e1c2329990b5e63ffd2d5e19bb658d146a249212213ef80c7617e646b4b183
7
- data.tar.gz: 500aa58f0c13bde306b4bfe682cb285819e53ba3cfea8ea71e58210d527b8cab134c01e2fbecdcfde122c8b8778e821e90357ea2759ade1f89287f57176ce2e6
6
+ metadata.gz: d875711628f3f58dc72fd06d4269a8a639a86022e40035dd799b75f4f548c7c0c5311fc83afc415bac190d0c895f3cbe71b52a7589362bc268004c253640c0d8
7
+ data.tar.gz: fc7e2dffa723cfd2b685cb7898b1d2b2cd2eb155dc8140264764cc9057ea43dc00b54fc89d2831aaf00a62d8aad582545861795852703baaab323c284e6e3e8e
@@ -14,16 +14,19 @@ module ActionValidator
14
14
  end
15
15
 
16
16
  def validate(data)
17
- model_class, params = parse_params(data["formData"])
18
- if model_class.nil?
19
- Rails.logger.warn("No valid model class found, cannot perform remote validation.")
20
- ActionCable.server.broadcast("action_validator_form_channel", { errors: {} })
21
- return
22
- end
17
+ Rails.logger.tagged("ActionValidator") do
18
+ model_class, params = parse_params(data["formData"])
19
+
20
+ if model_class.nil?
21
+ Rails.logger.warn("No valid model class found, cannot perform remote validation.")
22
+ ActionCable.server.broadcast("action_validator_form_channel", { errors: {} })
23
+ return
24
+ end
23
25
 
24
- instance = validated_instance(model_class, params)
26
+ instance = validated_instance(model_class, params)
25
27
 
26
- ActionCable.server.broadcast("action_validator_form_channel", { errors: parse_model_errors(instance) })
28
+ ActionCable.server.broadcast("action_validator_form_channel", { errors: parse_model_errors(instance) })
29
+ end
27
30
  end
28
31
 
29
32
  private
@@ -35,10 +38,6 @@ module ActionValidator
35
38
  end
36
39
 
37
40
  def parse_model_errors(instance)
38
- # TODO: What about errors on :base? Or any other random shit people might add?
39
- # I think we can just tell people to add a div with the proper attribute?
40
- # Or just don't allow it?
41
- #
42
41
  instance.errors.each_with_object({}) { |error, hash| hash[error.attribute] = error.full_message }
43
42
  end
44
43
 
@@ -7,7 +7,8 @@ module ActionValidator
7
7
  module FormHelper
8
8
  # A wrapper around the Rails form_for helper. This method can be used exactly the same as the standard Rails helper.
9
9
  #
10
- def validator_form_for(record, options = {}, &block) merge_form_options(options)
10
+ def validator_form_for(record, options = {}, &block)
11
+ merge_form_options(options)
11
12
 
12
13
  form_for(record, options, &block)
13
14
  end
@@ -15,7 +16,7 @@ module ActionValidator
15
16
  # A wrapper around the Rails form_with helper. This method can be used exactly the same as the standard Rails
16
17
  # helper.
17
18
  #
18
- def validator_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
19
+ def validator_form_with(model: false, scope: nil, url: nil, format: nil, **options, &block)
19
20
  merge_form_options(options)
20
21
 
21
22
  form_with(model: model, scope: scope, url: url, format: format, **options, &block)
@@ -28,13 +29,13 @@ module ActionValidator
28
29
  options[:data][ActionValidator::STIMULUS_SELECTORS[:target]] = :error
29
30
  options[:data][:attribute] = attribute
30
31
 
31
- tag.div({ **options, id: attribute }) { '' }
32
+ tag.div(id: attribute, **options)
32
33
  end
33
34
 
34
35
  private
35
36
 
36
37
  def merge_form_options(options)
37
- default_builder = ActionView::Base.default_form_builder
38
+ default_builder = self.class.default_form_builder
38
39
  options[:builder] ||= default_builder || ActionValidator::FormBuilder
39
40
  options[:data] ||= {}
40
41
  options[:data].merge!(
@@ -1,26 +1,24 @@
1
- import { Controller } from "@hotwired/stimulus"
1
+ import { Controller } from "@hotwired/stimulus";
2
2
  import consumer from "channels/consumer";
3
3
 
4
4
  export default class extends Controller {
5
- static targets = ['submit', 'input', 'error'];
5
+ static targets = ["submit", "input", "error"];
6
6
  static values = { modelName: String };
7
7
 
8
8
  connect() {
9
- // TODO: This might not JUST be inputs. It could be any DOM element. Maybe a better name is needed here.
10
- // `remoteValidatableElements` or something. The current set up doesn't really work well for this though
11
- // because the `remoteValidatableInputs` is directly tied to an actual input. Maybe the cableReceived method
12
- // should check for another `target` that matches the `errors` key in the return value if the error element
13
- // doesn't exist for that attribute...
14
- //
15
- this.remoteValidatableInputs = []
16
- this.channel = consumer.subscriptions.create('ActionValidator::FormChannel', {
17
- connected: this.#cableConnected.bind(this),
18
- disconnected: this.#cableDisconnected.bind(this),
19
- received: this.#cableReceived.bind(this),
20
- });
21
-
22
- this.inputTargets.forEach(input => {
23
- if (input.dataset.remoteValidate === 'true') this.remoteValidatableInputs.push(input);
9
+ this.remoteValidatableInputs = [];
10
+ this.channel = consumer.subscriptions.create(
11
+ "ActionValidator::FormChannel",
12
+ {
13
+ connected: this.#cableConnected.bind(this),
14
+ disconnected: this.#cableDisconnected.bind(this),
15
+ received: this.#cableReceived.bind(this),
16
+ },
17
+ );
18
+
19
+ this.inputTargets.forEach((input) => {
20
+ if (input.dataset.remoteValidate === "true")
21
+ this.remoteValidatableInputs.push(input);
24
22
  });
25
23
 
26
24
  this.#disableSubmit();
@@ -36,31 +34,35 @@ export default class extends Controller {
36
34
 
37
35
  validate(ev) {
38
36
  const inputElement = ev.target;
39
- const { dataset: { remoteValidate } } = inputElement;
37
+ const {
38
+ dataset: { remoteValidate },
39
+ } = inputElement;
40
40
  this.#checkAndSetDirty(inputElement);
41
- if (inputElement.dataset.isDirty === 'false') return;
41
+ if (inputElement.dataset.isDirty === "false") return;
42
42
 
43
43
  const htmlValid = this.validateInput(ev.target);
44
44
 
45
- if (remoteValidate === 'true' && htmlValid) {
46
- this.channel.perform('validate', { formData: this.#serializeForm() });
45
+ if (remoteValidate === "true" && htmlValid) {
46
+ this.channel.perform("validate", { formData: this.#serializeForm() });
47
47
  } else {
48
48
  this.validateForm();
49
49
  }
50
50
  }
51
51
 
52
52
  validateInput(input) {
53
- input.setCustomValidity('');
53
+ input.setCustomValidity("");
54
54
  const isValid = input.checkValidity();
55
- const errorElement = this.errorTargets.find(target => input.dataset.attribute === target.dataset.attribute);
55
+ const errorElement = this.errorTargets.find(
56
+ (target) => input.dataset.attribute === target.dataset.attribute,
57
+ );
56
58
 
57
59
  if (isValid) {
58
- errorElement.innerHTML = '';
60
+ if (errorElement) errorElement.innerHTML = "";
59
61
  input.dataset.valid = true;
60
62
 
61
63
  return true;
62
64
  } else {
63
- errorElement.innerHTML = input.validationMessage;
65
+ if (errorElement) errorElement.innerHTML = input.validationMessage;
64
66
  input.dataset.valid = false;
65
67
 
66
68
  return false;
@@ -77,18 +79,20 @@ export default class extends Controller {
77
79
  const currentlyDirty = element.dataset.isDirty;
78
80
  const value = element.value;
79
81
 
80
- if (currentlyDirty === 'false' && value) element.dataset.isDirty = true;
82
+ if (currentlyDirty === "false" && value) element.dataset.isDirty = true;
81
83
  }
82
84
 
83
85
  #serializeForm() {
84
86
  const formData = new FormData(this.element);
85
- const queryString = new URLSearchParams(formData).toString()
87
+ const queryString = new URLSearchParams(formData).toString();
86
88
 
87
89
  return queryString;
88
90
  }
89
91
 
90
92
  #formValid() {
91
- return this.inputTargets.every(element => element.dataset.valid === 'true');
93
+ return this.inputTargets.every(
94
+ (element) => element.dataset.valid === "true",
95
+ );
92
96
  }
93
97
 
94
98
  // TODO: Add an option to just show errors globally somewhere and not next to each input. We should support arbitrary
@@ -97,16 +101,18 @@ export default class extends Controller {
97
101
  #cableReceived(data) {
98
102
  const { errors } = data;
99
103
 
100
- this.remoteValidatableInputs.forEach(inputElement => {
101
- if (inputElement.dataset.isDirty === 'false') return;
104
+ this.remoteValidatableInputs.forEach((inputElement) => {
105
+ if (inputElement.dataset.isDirty === "false") return;
102
106
 
103
107
  const attributeName = inputElement.dataset.attribute;
104
108
  const error = errors[attributeName];
105
- const errorElement = this.errorTargets.find(target => attributeName === target.dataset.attribute);
109
+ const errorElement = this.errorTargets.find(
110
+ (target) => attributeName === target.dataset.attribute,
111
+ );
106
112
  if (!errorElement) {
107
113
  Stimulus.logDebugActivity(
108
114
  `No error element found for input with attribute '${attributeName}'. Cannot add error.`,
109
- 'cableReceived'
115
+ "cableReceived",
110
116
  );
111
117
  return;
112
118
  }
@@ -116,36 +122,48 @@ export default class extends Controller {
116
122
  inputElement.setCustomValidity(errors[attributeName]);
117
123
  inputElement.dataset.valid = false;
118
124
  } else {
119
- errorElement.innerHTML = '';
120
- inputElement.setCustomValidity('');
125
+ errorElement.innerHTML = "";
126
+ inputElement.setCustomValidity("");
121
127
  inputElement.dataset.valid = true;
122
128
  }
123
- })
129
+ });
124
130
 
125
131
  this.validateForm();
126
132
  }
127
133
 
128
134
  #disableSubmit() {
129
135
  if (this.hasSubmitTarget) {
130
- this.submitTarget.setAttribute('disabled', true);
136
+ this.submitTarget.setAttribute("disabled", true);
131
137
  } else {
132
- Stimulus.logDebugActivity('No submit target found for form', 'disableSubmit');
138
+ Stimulus.logDebugActivity(
139
+ "No submit target found for form",
140
+ "disableSubmit",
141
+ );
133
142
  }
134
143
  }
135
144
 
136
145
  #enableSubmit() {
137
146
  if (this.hasSubmitTarget) {
138
- this.submitTarget.removeAttribute('disabled');
147
+ this.submitTarget.removeAttribute("disabled");
139
148
  } else {
140
- Stimulus.logDebugActivity('No submit target found for form', 'enableSubmit');
149
+ Stimulus.logDebugActivity(
150
+ "No submit target found for form",
151
+ "enableSubmit",
152
+ );
141
153
  }
142
154
  }
143
155
 
144
156
  #cableConnected() {
145
- Stimulus.logDebugActivity('ActionValidator connected to cable', 'cableConnected');
157
+ Stimulus.logDebugActivity(
158
+ "ActionValidator connected to cable",
159
+ "cableConnected",
160
+ );
146
161
  }
147
162
 
148
163
  #cableDisconnected() {
149
- Stimulus.logDebugActivity('ActionValidator disconnected from cable', 'cableDisconnected');
164
+ Stimulus.logDebugActivity(
165
+ "ActionValidator disconnected from cable",
166
+ "cableDisconnected",
167
+ );
150
168
  }
151
169
  }
data/config/importmap.rb CHANGED
@@ -1,4 +1,3 @@
1
- pin "application-action-validator", to: "action_validator/application.js", preload: true
2
1
  pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
3
2
  pin_all_from(
4
3
  ActionValidator::Engine.root.join("app/javascript/action_validator/controllers"),
@@ -14,7 +14,6 @@ module ActionValidator
14
14
 
15
15
  initializer "action_validator.assets" do |app|
16
16
  app.config.assets.paths << root.join("app/javascript")
17
- app.config.assets.precompile += %w[action_validator_manifest]
18
17
  end
19
18
 
20
19
  initializer "action_validator.importmap", before: "importmap" do |app|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionValidator
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_validator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darin Haener
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-24 00:00:00.000000000 Z
10
+ date: 2025-06-08 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -137,7 +136,6 @@ metadata:
137
136
  homepage_uri: https://www.github.com/dphaener/action_validator
138
137
  source_code_uri: https://www.github.com/dphaener/action_validator
139
138
  changelog_uri: https://www.github.com/dphaener/action_validator/CHANGELOG.md
140
- post_install_message:
141
139
  rdoc_options: []
142
140
  require_paths:
143
141
  - lib
@@ -152,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
150
  - !ruby/object:Gem::Version
153
151
  version: '0'
154
152
  requirements: []
155
- rubygems_version: 3.5.22
156
- signing_key:
153
+ rubygems_version: 3.6.2
157
154
  specification_version: 4
158
155
  summary: Simple client and server side form validation.
159
156
  test_files: []