input_attributes_from_validators 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/input_attributes_from_validators.svg)](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: []
|