input_attributes_from_validators 0.0.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 +7 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +90 -0
- data/LICENSE.txt +21 -0
- data/README.md +113 -0
- data/Rakefile +79 -0
- data/app/helpers/attributes_helper.rb +20 -0
- data/app/helpers/input_mode_helper.rb +55 -0
- data/app/helpers/input_type_helper.rb +56 -0
- data/app/helpers/limits_helper.rb +70 -0
- data/app/helpers/validators_reflection_helper.rb +65 -0
- data/input_attributes_from_validators.gemspec +40 -0
- data/lib/input_attributes_from_validators/engine.rb +11 -0
- data/lib/input_attributes_from_validators/version.rb +5 -0
- data/lib/input_attributes_from_validators.rb +16 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea33dbf007b19181f857847dc0cb7e9c9d505b84b8c421e6949f1f01c25e79b2
|
4
|
+
data.tar.gz: 390cd060a49ffb1e89c4ac164d1a8286ae786b2ae6e8a736f9fae6a57e4984d2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bca3ae056bf69d856efd25590436b2ab4c04743bf55e00f86513d830c7f6c827f9d1aeb928ad33826fda693ae144c2a938432110ad3abcfb3d585cb3384e3a54
|
7
|
+
data.tar.gz: 1495b8ab30226d5ac3f614aae92448c59ddbd84b2dac9a1ea4c2983fa86b20aa8b5372a2a2670b2b2c7681ce6b91cf3ff0cab7eda10e1c1bfb265b7be19bcd5b
|
data/CHANGELOG.md
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
input_attributes_from_validators (0.0.1)
|
5
|
+
railties (>= 4)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionpack (7.0.3)
|
11
|
+
actionview (= 7.0.3)
|
12
|
+
activesupport (= 7.0.3)
|
13
|
+
rack (~> 2.0, >= 2.2.0)
|
14
|
+
rack-test (>= 0.6.3)
|
15
|
+
rails-dom-testing (~> 2.0)
|
16
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
17
|
+
actionview (7.0.3)
|
18
|
+
activesupport (= 7.0.3)
|
19
|
+
builder (~> 3.1)
|
20
|
+
erubi (~> 1.4)
|
21
|
+
rails-dom-testing (~> 2.0)
|
22
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
23
|
+
activesupport (7.0.3)
|
24
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
25
|
+
i18n (>= 1.6, < 2)
|
26
|
+
minitest (>= 5.1)
|
27
|
+
tzinfo (~> 2.0)
|
28
|
+
builder (3.2.4)
|
29
|
+
concurrent-ruby (1.1.10)
|
30
|
+
crass (1.0.6)
|
31
|
+
diff-lcs (1.5.0)
|
32
|
+
erubi (1.10.0)
|
33
|
+
i18n (1.10.0)
|
34
|
+
concurrent-ruby (~> 1.0)
|
35
|
+
loofah (2.18.0)
|
36
|
+
crass (~> 1.0.2)
|
37
|
+
nokogiri (>= 1.5.9)
|
38
|
+
method_source (1.0.0)
|
39
|
+
minitest (5.15.0)
|
40
|
+
nokogiri (1.13.6-arm64-darwin)
|
41
|
+
racc (~> 1.4)
|
42
|
+
racc (1.6.0)
|
43
|
+
rack (2.2.3.1)
|
44
|
+
rack-test (1.1.0)
|
45
|
+
rack (>= 1.0, < 3)
|
46
|
+
rails-dom-testing (2.0.3)
|
47
|
+
activesupport (>= 4.2.0)
|
48
|
+
nokogiri (>= 1.6)
|
49
|
+
rails-html-sanitizer (1.4.3)
|
50
|
+
loofah (~> 2.3)
|
51
|
+
railties (7.0.3)
|
52
|
+
actionpack (= 7.0.3)
|
53
|
+
activesupport (= 7.0.3)
|
54
|
+
method_source
|
55
|
+
rake (>= 12.2)
|
56
|
+
thor (~> 1.0)
|
57
|
+
zeitwerk (~> 2.5)
|
58
|
+
rake (13.0.6)
|
59
|
+
rspec (3.11.0)
|
60
|
+
rspec-core (~> 3.11.0)
|
61
|
+
rspec-expectations (~> 3.11.0)
|
62
|
+
rspec-mocks (~> 3.11.0)
|
63
|
+
rspec-core (3.11.0)
|
64
|
+
rspec-support (~> 3.11.0)
|
65
|
+
rspec-expectations (3.11.0)
|
66
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
67
|
+
rspec-support (~> 3.11.0)
|
68
|
+
rspec-mocks (3.11.1)
|
69
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
70
|
+
rspec-support (~> 3.11.0)
|
71
|
+
rspec-support (3.11.0)
|
72
|
+
thor (1.2.1)
|
73
|
+
tzinfo (2.0.4)
|
74
|
+
concurrent-ruby (~> 1.0)
|
75
|
+
webrick (1.7.0)
|
76
|
+
yard (0.9.28)
|
77
|
+
webrick (~> 1.7.0)
|
78
|
+
zeitwerk (2.5.4)
|
79
|
+
|
80
|
+
PLATFORMS
|
81
|
+
arm64-darwin-21
|
82
|
+
|
83
|
+
DEPENDENCIES
|
84
|
+
input_attributes_from_validators!
|
85
|
+
rake (~> 13)
|
86
|
+
rspec (~> 3)
|
87
|
+
yard (>= 0.9.20, < 1)
|
88
|
+
|
89
|
+
BUNDLED WITH
|
90
|
+
2.3.15
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Sergey Pedan
|
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,113 @@
|
|
1
|
+
# HTML input attributes helper
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/input_attributes_from_validators)
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem "input_attributes_from_validators"
|
9
|
+
```
|
10
|
+
|
11
|
+
## The main idea
|
12
|
+
|
13
|
+
HTML inputs have powerful controls over browser via attributes:
|
14
|
+
|
15
|
+
- validations ([`min`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/min), [`max`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/max), [`minlength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/minlength), [`maxlength`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength), [`required`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/required)) and
|
16
|
+
- keyboards variations ([`inputmode`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode), [`step`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/step)).
|
17
|
+
|
18
|
+
Normally you write those HTML attributes manually or forget about them, but most of them can be inferred from model validations and the DB column name.
|
19
|
+
|
20
|
+
## How to use
|
21
|
+
|
22
|
+
Provided that you have validators on your model:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class Review < ActiveRecord::Base
|
26
|
+
|
27
|
+
validates :comment, presence: true, length: { min: 3, max: 1_000 }
|
28
|
+
validates :score, numericality: { only_integer: true, in: 1..5 }
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
### Individual helper methods
|
33
|
+
|
34
|
+
This gem exposes a set of helper methods, one for the corresponding HTML attribute:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
validated_inputmode @rating, :comment #=> "text"
|
38
|
+
validated_minlength @review, :comment #=> 3
|
39
|
+
validated_maxlength @review, :comment #=> 1_000
|
40
|
+
validated_required @review, :comment #=> true
|
41
|
+
```
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
validated_inputmode @rating, :score #=> "numeric"
|
45
|
+
validated_min @rating, :score #=> 1
|
46
|
+
validated_max @rating, :score #=> 5
|
47
|
+
validated_step @rating, :score #=> 1
|
48
|
+
```
|
49
|
+
|
50
|
+
### Aggregate method
|
51
|
+
|
52
|
+
There is also a method that returns a hash with all the values:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
resolved_input_attributes(record, attribute_name) #=>
|
56
|
+
{
|
57
|
+
inputmode: <String || NilClass>,,
|
58
|
+
max: <Integer || NilClass>,
|
59
|
+
maxlength: <Integer || NilClass>,
|
60
|
+
min: <Integer || NilClass>,
|
61
|
+
minlength: <Integer || NilClass>,
|
62
|
+
required: <Boolean>,
|
63
|
+
step: <Integer || Decimal>,
|
64
|
+
}
|
65
|
+
```
|
66
|
+
|
67
|
+
so you don’t have to repeadedly pass the record and the attribute name:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
= form_for model: @review do |f|
|
71
|
+
- attrs = resolved_input_attributes(@review, :score)
|
72
|
+
|
73
|
+
= f.number_field :size \
|
74
|
+
inputmode: attrs[:inputmode], \
|
75
|
+
max: attrs[:max], \
|
76
|
+
min: attrs[:min], \
|
77
|
+
required: attrs[:required], \
|
78
|
+
step: attrs[:step]
|
79
|
+
|
80
|
+
= f.text_field :description \
|
81
|
+
inputmode: attrs[:inputmode], \
|
82
|
+
maxlength: attrs[:maxlength], \
|
83
|
+
minlength: attrs[:minlength], \
|
84
|
+
required: attrs[:required]
|
85
|
+
```
|
86
|
+
|
87
|
+
## Less useful methods
|
88
|
+
|
89
|
+
Search for validators with certain conditions:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
validators_for(record: @review, attr: :comment, type: :length, options: [:maximm])
|
93
|
+
#=> 255
|
94
|
+
|
95
|
+
validators_for(record: @review, attr: :score, type: :numericality, options: [:less_than, :less_than_or_equal_to])
|
96
|
+
#=> 5
|
97
|
+
```
|
98
|
+
|
99
|
+
Also
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
validated_value(record: @review, attr: :comment, type: :length, options: [:maximm])
|
103
|
+
#=> 255
|
104
|
+
```
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
validator_type_to_class :presence #=> ActiveRecord::Validations::PresenceValidator
|
108
|
+
validator_type_to_class :numericality #=> ActiveRecord::Validations::NumericalityValidator
|
109
|
+
```
|
110
|
+
|
111
|
+
## Todo
|
112
|
+
|
113
|
+
- [ ] pattern attribute
|
data/Rakefile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
# require "term/ansicolor"
|
7
|
+
require "yard"
|
8
|
+
|
9
|
+
require "./lib/input_attributes_from_validators"
|
10
|
+
|
11
|
+
Bundler::GemHelper.install_tasks
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
13
|
+
|
14
|
+
desc "Generate documentation"
|
15
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
16
|
+
end
|
17
|
+
task doc: :yard
|
18
|
+
|
19
|
+
# APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
20
|
+
# load "rails/tasks/engine.rake"
|
21
|
+
load "rails/tasks/statistics.rake"
|
22
|
+
|
23
|
+
task default: :spec
|
24
|
+
|
25
|
+
# desc "Publish"
|
26
|
+
# task :publish do
|
27
|
+
# puts `gem push pkg/input_attributes_from_validators-#{InputAttributesFromValidators::VERSION}.gem`
|
28
|
+
# end
|
29
|
+
|
30
|
+
# require 'rake/testtask'
|
31
|
+
# Rake::TestTask.new do |t|
|
32
|
+
# t.libs << 'test'
|
33
|
+
# t.test_files = FileList['test/**/*_test.rb']
|
34
|
+
# t.verbose = false
|
35
|
+
# t.warning = false
|
36
|
+
# end
|
37
|
+
|
38
|
+
# desc 'Test all Gemfiles from test/*.gemfile'
|
39
|
+
# task :test_all_gemfiles do
|
40
|
+
# require 'term/ansicolor'
|
41
|
+
# require 'pty'
|
42
|
+
# require 'shellwords'
|
43
|
+
# cmd = 'bundle install --quiet && bundle exec rake --trace'
|
44
|
+
# statuses = Dir.glob('./test/gemfiles/*{[!.lock]}').map do |gemfile|
|
45
|
+
# env = {'BUNDLE_GEMFILE' => gemfile}
|
46
|
+
# cmd_with_env = " (#{env.map { |k, v| "export #{k}=#{Shellwords.escape v}" } * ' '}; #{cmd})"
|
47
|
+
# $stderr.puts Term::ANSIColor.cyan("Testing\n#{cmd_with_env}")
|
48
|
+
# Bundler.with_clean_env do
|
49
|
+
# PTY.spawn(env, cmd) do |r, _w, pid|
|
50
|
+
# begin
|
51
|
+
# r.each_line { |l| puts l }
|
52
|
+
# rescue Errno::EIO
|
53
|
+
# # Errno:EIO error means that the process has finished giving output.
|
54
|
+
# ensure
|
55
|
+
# ::Process.wait pid
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
# [$? && $?.exitstatus == 0, cmd_with_env]
|
60
|
+
# end
|
61
|
+
# failed_cmds = statuses.reject(&:first).map { |(_status, cmd_with_env)| cmd_with_env }
|
62
|
+
# if failed_cmds.empty?
|
63
|
+
# $stderr.puts Term::ANSIColor.green('Tests pass with all gemfiles')
|
64
|
+
# else
|
65
|
+
# $stderr.puts Term::ANSIColor.red("Failing (#{failed_cmds.size} / #{statuses.size})\n#{failed_cmds * "\n"}")
|
66
|
+
# exit 1
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
|
70
|
+
# desc 'Start a dummy Rails app server'
|
71
|
+
# task :rails_server do
|
72
|
+
# require 'rack'
|
73
|
+
# require 'term/ansicolor'
|
74
|
+
# port = ENV['PORT'] || 9292
|
75
|
+
# puts %Q(Starting on #{Term::ANSIColor.cyan "http://localhost:#{port}"})
|
76
|
+
# Rack::Server.start(
|
77
|
+
# config: 'test/dummy_rails/config.ru',
|
78
|
+
# Port: port)
|
79
|
+
# end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AttributesHelper
|
4
|
+
|
5
|
+
# @param [ActiveRecord::Base] record
|
6
|
+
# @param [String, Symbol] attr
|
7
|
+
# @return [Hash]
|
8
|
+
#
|
9
|
+
def resolved_input_attributes(record, attr)
|
10
|
+
{
|
11
|
+
inputmode: validated_inputmode(record, attr),
|
12
|
+
max: validated_max(record, attr),
|
13
|
+
maxlength: validated_maxlength(record, attr),
|
14
|
+
min: validated_min(record, attr),
|
15
|
+
minlength: validated_minlength(record, attr),
|
16
|
+
required: validated_required(record, attr),
|
17
|
+
}.compact
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InputModeHelper
|
4
|
+
|
5
|
+
# @param [ActiveRecord::Base] record
|
6
|
+
# @param [String, Symbol] attr
|
7
|
+
# @return [String, NilClass]
|
8
|
+
# @example
|
9
|
+
# validated_inputmode @user, :email #=> "email"
|
10
|
+
# validated_inputmode @rating, :comment #=> "text"
|
11
|
+
# validated_inputmode @rating, :score #=> "numeric"
|
12
|
+
#
|
13
|
+
def validated_inputmode(record, attr)
|
14
|
+
v_types = validators_for_attr(record, attr).map(&:kind)
|
15
|
+
|
16
|
+
# TODO: try using pattern matching instead
|
17
|
+
if v_types.include?("numericality")
|
18
|
+
needs_integer?(record, attr) ? "numeric" : "decimal"
|
19
|
+
elsif v_types.include?("email")
|
20
|
+
"email"
|
21
|
+
elsif v_types.include?("url")
|
22
|
+
"url"
|
23
|
+
elsif ["tel", "phone"].any? { |i| v_types.include?(i) }
|
24
|
+
"tel"
|
25
|
+
elsif ["search"].any? { |i| v_types.include?(i) } || attr.to_s.include?("search")
|
26
|
+
"search"
|
27
|
+
else
|
28
|
+
"text"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [ActiveRecord::Base] record
|
33
|
+
# @param [String, Symbol] attr
|
34
|
+
# @return [Numeric, NilClass]
|
35
|
+
# @example
|
36
|
+
# validated_step @rating, :score #=> 1
|
37
|
+
# validated_step @dumbbell, :weight #=> 0.25
|
38
|
+
#
|
39
|
+
def validated_step(record, attr)
|
40
|
+
case validated_inputmode(record, attr)
|
41
|
+
when "numeric" then 1
|
42
|
+
when "decimal" then 0.1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern
|
48
|
+
# def validated_pattern(record, attr)
|
49
|
+
# end
|
50
|
+
|
51
|
+
private def needs_integer?(record, attr)
|
52
|
+
validated_value(record: record, attr: attr, type: :numericality, options: [:only_integer])
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InputTypeHelper
|
4
|
+
|
5
|
+
# @param [ActiveRecord::Base] record
|
6
|
+
# @param [String, Symbol] attr
|
7
|
+
# @return [String, NilClass]
|
8
|
+
# @example
|
9
|
+
# resolved_input_type @user, :email #=> "email"
|
10
|
+
# resolved_input_type @rating, :comment #=> "text"
|
11
|
+
# resolved_input_type @rating, :score #=> "numeric"
|
12
|
+
#
|
13
|
+
def resolved_input_type(record, attr)
|
14
|
+
v_types = validators_for_attr(record, attr).map(&:kind)
|
15
|
+
klass = record.class
|
16
|
+
|
17
|
+
# TODO: try using pattern matching instead
|
18
|
+
if v_types.include? "numericality" or [:integer, :bigint, :float, :decimal].include? klass.column_for_attribute(attr).type
|
19
|
+
"number"
|
20
|
+
|
21
|
+
elsif v_types.include? "email" or attr.to_s.include? "email"
|
22
|
+
"email"
|
23
|
+
|
24
|
+
elsif v_types.include? "url" or attr.to_s.include? "url"
|
25
|
+
"url"
|
26
|
+
|
27
|
+
elsif v_types.include? "tel" or attr.to_s.include? "tel"
|
28
|
+
"tel"
|
29
|
+
|
30
|
+
elsif v_types.include? "phone" or attr.to_s.include? "phone"
|
31
|
+
"tel"
|
32
|
+
|
33
|
+
elsif v_types.include? "color" or attr.to_s.include? "color"
|
34
|
+
"color"
|
35
|
+
|
36
|
+
elsif ["search"].any? { |i| v_types.include?(i) } or attr.to_s.include? "search"
|
37
|
+
"search"
|
38
|
+
|
39
|
+
elsif klass.column_for_attribute(attr).type == :text
|
40
|
+
validated_value(record: record, attr: attr, type: :length, options: [:maximm]).to_i > 256 ? "textarea" : "text"
|
41
|
+
|
42
|
+
elsif klass.column_for_attribute(attr).type == :date
|
43
|
+
"date"
|
44
|
+
|
45
|
+
elsif klass.column_for_attribute(attr).type == :datetime
|
46
|
+
"datetimelocal"
|
47
|
+
|
48
|
+
elsif klass.column_for_attribute(attr).type == :time
|
49
|
+
"time"
|
50
|
+
|
51
|
+
else
|
52
|
+
"text"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LimitsHelper
|
4
|
+
|
5
|
+
# @example
|
6
|
+
# validated_maxlength @review, :comment #=> 1_000
|
7
|
+
# @param [ActiveRecord::Base] record
|
8
|
+
# @param [String, Symbol] attr
|
9
|
+
# @return [Integer, NilClass]
|
10
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength
|
11
|
+
# @raise [TypeError]
|
12
|
+
#
|
13
|
+
def validated_maxlength(record, attr)
|
14
|
+
maximum = validated_value(record: record, attr: attr, type: :length, options: [:maximum])
|
15
|
+
range = validated_value(record: record, attr: attr, type: :length, options: [:in])
|
16
|
+
range&.max || maximum
|
17
|
+
end
|
18
|
+
|
19
|
+
# @example
|
20
|
+
# validated_minlength @review, :comment #=> 3
|
21
|
+
# @param [ActiveRecord::Base] record
|
22
|
+
# @param [String, Symbol] attr
|
23
|
+
# @return [Integer, NilClass]
|
24
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/minlength
|
25
|
+
# @raise [TypeError]
|
26
|
+
#
|
27
|
+
def validated_minlength(record, attr)
|
28
|
+
minimum = validated_value(record: record, attr: attr, type: :length, options: [:minimum])
|
29
|
+
range = validated_value(record: record, attr: attr, type: :length, options: [:in])
|
30
|
+
range&.min || minimum
|
31
|
+
end
|
32
|
+
|
33
|
+
# @example
|
34
|
+
# validated_required @rating, :score #=> 5
|
35
|
+
# @param [ActiveRecord::Base] record
|
36
|
+
# @param [String, Symbol] attr
|
37
|
+
# @return [Integer, NilClass]
|
38
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/max
|
39
|
+
# @raise [TypeError]
|
40
|
+
#
|
41
|
+
def validated_max(record, attr)
|
42
|
+
validated_value(record: record, attr: attr, type: :numericality, options: [:less_than, :less_than_or_equal_to])
|
43
|
+
end
|
44
|
+
|
45
|
+
# @example
|
46
|
+
# validated_min @rating, :score #=> 1
|
47
|
+
# @param [ActiveRecord::Base] record
|
48
|
+
# @param [String, Symbol] attr
|
49
|
+
# @return [Integer, NilClass]
|
50
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/min
|
51
|
+
# @raise [TypeError]
|
52
|
+
#
|
53
|
+
def validated_min(record, attr)
|
54
|
+
validated_value(record: record, attr: attr, type: :numericality, options: [:greater_than, :greater_than_or_equal_to])
|
55
|
+
end
|
56
|
+
|
57
|
+
# @example
|
58
|
+
# validated_required @user, :email #=> true
|
59
|
+
# @param [ActiveRecord::Base] record
|
60
|
+
# @param [String, Symbol] attr
|
61
|
+
# @return [Boolean]
|
62
|
+
# @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/required
|
63
|
+
# @raise [TypeError]
|
64
|
+
#
|
65
|
+
def validated_required(record, attr)
|
66
|
+
# validated_value(record: record, attr: attr, type: :presence, options: [:presece]) ?
|
67
|
+
validators_for(record: record, attr: attr, type: :presence).any?
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ValidatorsReflectionHelper
|
4
|
+
|
5
|
+
# @example
|
6
|
+
# validators_for_attr(User.new, :email) #=>
|
7
|
+
# [
|
8
|
+
# <ActiveRecord::Validations::PresenceValidator:0x @attributes=[:email], @options={}>,
|
9
|
+
# <EmailValidator:0x @attributes=[:email], @options={}>,
|
10
|
+
# <ActiveRecord::Validations::UniquenessValidator:0x @attributes=[:email], @options={}>,
|
11
|
+
# <ActiveRecord::Validations::PresenceValidator:0x @attributes=[:email], @options={:if=>:email_required?}>,
|
12
|
+
# <ActiveRecord::Validations::UniquenessValidator:0x @attributes=[:email], @options={:allow_blank=>true, :case_sensitive=>true, :if=>:will_save_change_to_email?}>,
|
13
|
+
# <ActiveModel::Validations::FormatValidator:0x @attributes=[:email], @options= {:allow_blank=>true, :if=>:will_save_change_to_email?}>
|
14
|
+
# ]
|
15
|
+
# @param [ActiveRecord::Base] record
|
16
|
+
# @param [String, Symbol] attr
|
17
|
+
# @raise [TypeError]
|
18
|
+
#
|
19
|
+
def validators_for_attr(record, attr)
|
20
|
+
fail TypeError, "`attr` must be a Symbol or String, you pass a #{attr.class}" unless [Symbol, String].include? attr.class
|
21
|
+
# TODO: fail TypeError if record.class.ancestors.include?()
|
22
|
+
|
23
|
+
record.class.validators.select { _1.attributes.include? attr }
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [ActiveRecord::Base] record
|
27
|
+
# @param [String, Symbol] attr
|
28
|
+
# @param [Array<Symbol>] options
|
29
|
+
# @raise [TypeError]
|
30
|
+
#
|
31
|
+
def validators_for(record:, attr:, type: nil, options: [])
|
32
|
+
fail TypeError, "`options` must be an Array, you pass a #{options.class}" unless options.is_a? Array
|
33
|
+
result = validators_for_attr(record, attr).select { _1.options.keys & options }
|
34
|
+
result = result.select { _1.is_a? validator_type_to_class(type) } if type.present?
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
# @example
|
39
|
+
# validated_value(record: @review, attr: :comment, type: :length, options: [:maximm]) #=> 255
|
40
|
+
# @param [ActiveRecord::Base] record
|
41
|
+
# @param [String, Symbol] attr
|
42
|
+
# @return [Integer, NilClass, Boolean]
|
43
|
+
# @raise [TypeError]
|
44
|
+
# @raise [TypeError]
|
45
|
+
#
|
46
|
+
def validated_value(record:, attr:, type: nil, options: [])
|
47
|
+
fail TypeError, "`options` must be an Array, you pass a #{options.class}" unless options.is_a? Array
|
48
|
+
validators_for(record: record, attr: attr, type: type, options: options).map { first_value(_1, options) }.compact.first
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param [String, Symbol, NilClass] type
|
52
|
+
# @example
|
53
|
+
# validator_type_to_class(:presence) #=> ActiveRecord::Validations::PresenceValidator
|
54
|
+
# validator_type_to_class(:numericality) #=> ActiveRecord::Validations::NumericalityValidator
|
55
|
+
#
|
56
|
+
def validator_type_to_class(type)
|
57
|
+
fail TypeError, "`type` must be a Symbol or String, you pass a #{type.class}" unless [Symbol, String, NilClass].include? type.class
|
58
|
+
["ActiveRecord::Validations::", type.to_s.capitalize, "Validator"].join.constantize if type.present?
|
59
|
+
end
|
60
|
+
|
61
|
+
private def first_value(validator, options)
|
62
|
+
options.map { |key| validator.options&.fetch(key, nil) }.compact.first
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/input_attributes_from_validators/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "input_attributes_from_validators"
|
7
|
+
spec.version = InputAttributesFromValidators::VERSION
|
8
|
+
spec.authors = ["Sergey Pedan"]
|
9
|
+
spec.email = ["sergey.pedan@gmail.com"]
|
10
|
+
spec.license = "MIT"
|
11
|
+
|
12
|
+
spec.summary = "A collection of helper methods for Rails to automate assigning HTML input attributes like maxlength, required, inputmode etc."
|
13
|
+
spec.description = <<~HEREDOC
|
14
|
+
#{spec.summary}.
|
15
|
+
HEREDOC
|
16
|
+
|
17
|
+
spec.homepage = "https://github.com/sergeypedan/#{spec.name.gsub('_', '-')}"
|
18
|
+
spec.extra_rdoc_files = ["README.md", "CHANGELOG.md"]
|
19
|
+
spec.rdoc_options = ["--charset=UTF-8"]
|
20
|
+
spec.metadata = { "changelog_uri" => "#{spec.homepage}/blob/master/CHANGELOG.md",
|
21
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/#{spec.name}",
|
22
|
+
"homepage_uri" => "https://sergeypedan.ru/open_source_projects/#{spec.name.gsub('_', '-')}",
|
23
|
+
"source_code_uri" => spec.homepage }
|
24
|
+
|
25
|
+
spec.require_paths = ["app", "lib"]
|
26
|
+
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = []
|
29
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
30
|
+
`git ls-files`.split("\n")
|
31
|
+
.reject { |f| %w[bin spec test].any? { |dir| f.start_with? dir } }
|
32
|
+
.reject { |f| f.start_with? "." }
|
33
|
+
end
|
34
|
+
|
35
|
+
spec.add_runtime_dependency "railties", ">= 4"
|
36
|
+
|
37
|
+
spec.add_development_dependency "rake", "~> 13"
|
38
|
+
spec.add_development_dependency "rspec", "~> 3"
|
39
|
+
spec.add_development_dependency "yard", ">= 0.9.20", "< 1"
|
40
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InputAttributesFromValidators
|
4
|
+
|
5
|
+
# This is standard Rails way to autoload gem’s contents dynamically as an “engine”
|
6
|
+
# @see https://guides.rubyonrails.org/engines.html Rails guide on engines
|
7
|
+
#
|
8
|
+
class Engine < ::Rails::Engine
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require_relative "input_attributes_from_validators/version"
|
6
|
+
require_relative "input_attributes_from_validators/engine"
|
7
|
+
|
8
|
+
require_relative "../app/helpers/attributes_helper"
|
9
|
+
require_relative "../app/helpers/input_mode_helper"
|
10
|
+
require_relative "../app/helpers/input_type_helper"
|
11
|
+
require_relative "../app/helpers/limits_helper"
|
12
|
+
require_relative "../app/helpers/validators_reflection_helper"
|
13
|
+
|
14
|
+
|
15
|
+
module InputAttributesFromValidators
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: input_attributes_from_validators
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sergey Pedan
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: railties
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.20
|
62
|
+
- - "<"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '1'
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 0.9.20
|
72
|
+
- - "<"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1'
|
75
|
+
description: 'A collection of helper methods for Rails to automate assigning HTML
|
76
|
+
input attributes like maxlength, required, inputmode etc..
|
77
|
+
|
78
|
+
'
|
79
|
+
email:
|
80
|
+
- sergey.pedan@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files:
|
84
|
+
- README.md
|
85
|
+
- CHANGELOG.md
|
86
|
+
files:
|
87
|
+
- CHANGELOG.md
|
88
|
+
- Gemfile
|
89
|
+
- Gemfile.lock
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- app/helpers/attributes_helper.rb
|
94
|
+
- app/helpers/input_mode_helper.rb
|
95
|
+
- app/helpers/input_type_helper.rb
|
96
|
+
- app/helpers/limits_helper.rb
|
97
|
+
- app/helpers/validators_reflection_helper.rb
|
98
|
+
- input_attributes_from_validators.gemspec
|
99
|
+
- lib/input_attributes_from_validators.rb
|
100
|
+
- lib/input_attributes_from_validators/engine.rb
|
101
|
+
- lib/input_attributes_from_validators/version.rb
|
102
|
+
homepage: https://github.com/sergeypedan/input-attributes-from-validators
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata:
|
106
|
+
changelog_uri: https://github.com/sergeypedan/input-attributes-from-validators/blob/master/CHANGELOG.md
|
107
|
+
documentation_uri: https://www.rubydoc.info/gems/input_attributes_from_validators
|
108
|
+
homepage_uri: https://sergeypedan.ru/open_source_projects/input-attributes-from-validators
|
109
|
+
source_code_uri: https://github.com/sergeypedan/input-attributes-from-validators
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options:
|
112
|
+
- "--charset=UTF-8"
|
113
|
+
require_paths:
|
114
|
+
- app
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubygems_version: 3.0.3.1
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: A collection of helper methods for Rails to automate assigning HTML input
|
131
|
+
attributes like maxlength, required, inputmode etc.
|
132
|
+
test_files: []
|