rails_bootstrap_form 0.9.7 → 0.9.8
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 +4 -4
- data/.codeclimate.yml +47 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +4 -1
- data/lib/rails_bootstrap_form/bootstrap_form_builder.rb +1 -1
- data/lib/rails_bootstrap_form/field_wrapper_builder.rb +64 -36
- data/lib/rails_bootstrap_form/helpers/buttons.rb +50 -20
- data/lib/rails_bootstrap_form/helpers/choice.rb +70 -54
- data/lib/rails_bootstrap_form/helpers/errors.rb +48 -38
- data/lib/rails_bootstrap_form/helpers/help_text.rb +27 -30
- data/lib/rails_bootstrap_form/helpers/labels.rb +31 -34
- data/lib/rails_bootstrap_form/helpers/required_field.rb +41 -37
- data/lib/rails_bootstrap_form/input_group_builder.rb +36 -39
- data/lib/rails_bootstrap_form/inputs/base.rb +32 -16
- data/lib/rails_bootstrap_form/inputs/check_box.rb +2 -12
- data/lib/rails_bootstrap_form/inputs/radio_button.rb +2 -12
- data/lib/rails_bootstrap_form/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73b8b86f78e40f77d36ba3857ef15c9efe939480d327011ba083132d48668654
|
4
|
+
data.tar.gz: 897ed641bdc6147d29698901a0b9f37ce71a46d0581734a844cf03666fbf5c20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b5ee6a90da2f11246c01c6f41f01d0fe067cd07ba26b56773e50b6c67c3b87e67d8714694c0c11240d31cb8d58255c58610dbf6304cea21fd547abdc718f8db
|
7
|
+
data.tar.gz: 1554395fcabb6df7f38722c5a26eb680b8436c7bd54f9567477db6b302208872d53417abca1d219a89f48c7fcff8ff8b22e9c26aafa194c79bd683eb83a2ed85
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
version: "2"
|
2
|
+
checks:
|
3
|
+
argument-count:
|
4
|
+
enabled: true
|
5
|
+
config:
|
6
|
+
threshold: 4
|
7
|
+
complex-logic:
|
8
|
+
enabled: true
|
9
|
+
config:
|
10
|
+
threshold: 4
|
11
|
+
file-lines:
|
12
|
+
enabled: true
|
13
|
+
config:
|
14
|
+
threshold: 250
|
15
|
+
method-complexity:
|
16
|
+
enabled: true
|
17
|
+
config:
|
18
|
+
threshold: 5
|
19
|
+
method-count:
|
20
|
+
enabled: true
|
21
|
+
config:
|
22
|
+
threshold: 20
|
23
|
+
method-lines:
|
24
|
+
enabled: true
|
25
|
+
config:
|
26
|
+
threshold: 25
|
27
|
+
nested-control-flow:
|
28
|
+
enabled: true
|
29
|
+
config:
|
30
|
+
threshold: 4
|
31
|
+
return-statements:
|
32
|
+
enabled: true
|
33
|
+
config:
|
34
|
+
threshold: 4
|
35
|
+
exclude_patterns:
|
36
|
+
- "config/"
|
37
|
+
- "db/"
|
38
|
+
- "dist/"
|
39
|
+
- "features/"
|
40
|
+
- "demo/"
|
41
|
+
- "**/node_modules/"
|
42
|
+
- "script/"
|
43
|
+
- "**/spec/"
|
44
|
+
- "**/test/"
|
45
|
+
- "**/tests/"
|
46
|
+
- "**/vendor/"
|
47
|
+
- "**/*.d.ts"
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,12 @@ You can find recent releases with docs in GitHub:
|
|
4
4
|
|
5
5
|
https://github.com/shivam091/rails_bootstrap_form/releases
|
6
6
|
|
7
|
+
## [0.9.8](https://github.com/shivam091/rails_bootstrap_form/compare/v0.9.7...v0.9.8) - 2023-07-01
|
8
|
+
|
9
|
+
### What's fixed
|
10
|
+
|
11
|
+
- Fixed bug causing button helpers not to work with block and options ([#50](https://github.com/shivam091/rails_bootstrap_form/issues/50))
|
12
|
+
|
7
13
|
## [0.9.7](https://github.com/shivam091/rails_bootstrap_form/compare/v0.9.6...v0.9.7) - 2023-06-26
|
8
14
|
|
9
15
|
### What's new
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
[](https://github.com/shivam091/rails_bootstrap_form/actions/workflows/main.yml)
|
4
4
|
[](https://badge.fury.io/rb/rails_bootstrap_form)
|
5
|
+
[](http://rubygems.org/gems/rails_bootstrap_form)
|
6
|
+
[](https://codeclimate.com/github/shivam091/rails_bootstrap_form/test_coverage)
|
7
|
+
[](https://codeclimate.com/github/shivam091/rails_bootstrap_form/maintainability)
|
5
8
|
|
6
9
|
**rails_bootstrap_form** is a Rails form builder that makes it super easy to integrate [Bootstrap 5](https://getbootstrap.com/) forms into your Rails application.
|
7
10
|
`rails_bootstrap_form`'s form helpers generate the form field and its label along with all the Bootstrap mark-up required for proper Bootstrap display.
|
@@ -28,7 +31,7 @@ for setting up `application.scss` and `application.js`.
|
|
28
31
|
Add the `rails_bootstrap_form` gem to your `Gemfile`:
|
29
32
|
|
30
33
|
```ruby
|
31
|
-
gem "rails_bootstrap_form", "~> 0.9.
|
34
|
+
gem "rails_bootstrap_form", "~> 0.9.8"
|
32
35
|
```
|
33
36
|
|
34
37
|
Then:
|
@@ -13,7 +13,7 @@ module RailsBootstrapForm
|
|
13
13
|
include RailsBootstrapForm::InputGroupBuilder
|
14
14
|
include RailsBootstrapForm::Inputs
|
15
15
|
|
16
|
-
delegate :capture, :concat, :tag, to: :@template
|
16
|
+
delegate :capture, :concat, :tag, :button_tag, to: :@template
|
17
17
|
|
18
18
|
attr_accessor :bootstrap_form_options
|
19
19
|
|
@@ -4,6 +4,9 @@
|
|
4
4
|
|
5
5
|
module RailsBootstrapForm
|
6
6
|
module FieldWrapperBuilder
|
7
|
+
|
8
|
+
private
|
9
|
+
|
7
10
|
def field_wrapper_builder(attribute, bootstrap, options, html_options = nil, &block)
|
8
11
|
field_options = field_css_options(attribute, bootstrap, options, html_options.try(:symbolize_keys!))
|
9
12
|
|
@@ -15,37 +18,17 @@ module RailsBootstrapForm
|
|
15
18
|
help_text = help_text(attribute, bootstrap)
|
16
19
|
wrapper_content = ActiveSupport::SafeBuffer.new
|
17
20
|
|
18
|
-
if bootstrap.layout_horizontal?
|
19
|
-
|
20
|
-
wrapper_content << tag.div(class: bootstrap.field_col_wrapper_class) do
|
21
|
-
input_group_wrapper(attribute, bootstrap) do
|
22
|
-
capture(&block)
|
23
|
-
end + help_text
|
24
|
-
end
|
21
|
+
wrapper_content = if bootstrap.layout_horizontal?
|
22
|
+
build_horizontal_layout_content(attribute, bootstrap, label, help_text, &block)
|
25
23
|
else
|
26
24
|
if bootstrap.floating?
|
27
|
-
|
28
|
-
tag.div(class: floating_label_classes(attribute)) do
|
29
|
-
capture(&block) + label
|
30
|
-
end
|
31
|
-
end
|
32
|
-
wrapper_content << help_text
|
25
|
+
build_floating_layout_content(attribute, bootstrap, label, help_text, &block)
|
33
26
|
else
|
34
|
-
|
35
|
-
wrapper_content << input_group_wrapper(attribute, bootstrap) do
|
36
|
-
capture(&block)
|
37
|
-
end
|
38
|
-
wrapper_content << help_text
|
27
|
+
build_default_layout_content(attribute, bootstrap, label, help_text, &block)
|
39
28
|
end
|
40
29
|
end
|
41
30
|
|
42
|
-
|
43
|
-
tag.div(**field_wrapper_options(bootstrap)) do
|
44
|
-
wrapper_content
|
45
|
-
end
|
46
|
-
else
|
47
|
-
wrapper_content
|
48
|
-
end
|
31
|
+
build_wrapper_element(bootstrap, wrapper_content)
|
49
32
|
end
|
50
33
|
|
51
34
|
def field_wrapper_options(bootstrap)
|
@@ -72,18 +55,11 @@ module RailsBootstrapForm
|
|
72
55
|
def field_css_options(attribute, bootstrap, options, html_options)
|
73
56
|
css_options = (html_options || options)
|
74
57
|
|
75
|
-
field_classes =
|
76
|
-
field_classes << "is-invalid" if is_invalid?(attribute)
|
77
|
-
if is_size_valid?(bootstrap)
|
78
|
-
field_classes << "#{bootstrap.field_class}-#{bootstrap.size}"
|
79
|
-
end
|
58
|
+
field_classes = build_field_classes(attribute, bootstrap, css_options)
|
80
59
|
|
81
60
|
css_options[:class] = field_classes.flatten.compact
|
82
61
|
css_options.merge!(required_field_options(attribute, options))
|
83
|
-
|
84
|
-
if placeholder_required?(bootstrap)
|
85
|
-
css_options[:placeholder] ||= label_text(attribute, bootstrap)
|
86
|
-
end
|
62
|
+
add_placeholder_if_required!(css_options, attribute, bootstrap)
|
87
63
|
|
88
64
|
css_options
|
89
65
|
end
|
@@ -96,11 +72,63 @@ module RailsBootstrapForm
|
|
96
72
|
classes
|
97
73
|
end
|
98
74
|
|
75
|
+
def build_field_classes(attribute, bootstrap, css_options)
|
76
|
+
field_classes = Array(bootstrap.field_class) <<
|
77
|
+
field_classes << [bootstrap.additional_field_class || css_options[:class]]
|
78
|
+
field_classes << "is-invalid" if is_invalid?(attribute)
|
79
|
+
field_classes << "#{bootstrap.field_class}-#{bootstrap.size}" if is_size_valid?(bootstrap)
|
80
|
+
field_classes
|
81
|
+
end
|
82
|
+
|
99
83
|
def placeholder_required?(bootstrap)
|
100
84
|
(bootstrap.floating? && !bootstrap.layout_horizontal?) || bootstrap.layout_inline?
|
101
85
|
end
|
102
86
|
|
103
|
-
|
104
|
-
|
87
|
+
def add_placeholder_if_required!(css_options, attribute, bootstrap)
|
88
|
+
css_options[:placeholder] ||= label_text(attribute, bootstrap) if placeholder_required?(bootstrap)
|
89
|
+
css_options
|
90
|
+
end
|
91
|
+
|
92
|
+
def build_horizontal_layout_content(attribute, bootstrap, label, help_text, &block)
|
93
|
+
wrapper_content = ActiveSupport::SafeBuffer.new
|
94
|
+
wrapper_content << label
|
95
|
+
wrapper_content << tag.div(class: bootstrap.field_col_wrapper_class) do
|
96
|
+
input_group_wrapper(attribute, bootstrap) do
|
97
|
+
capture(&block)
|
98
|
+
end + help_text
|
99
|
+
end
|
100
|
+
wrapper_content
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_floating_layout_content(attribute, bootstrap, label, help_text, &block)
|
104
|
+
wrapper_content = ActiveSupport::SafeBuffer.new
|
105
|
+
wrapper_content << input_group_wrapper(attribute, bootstrap) do
|
106
|
+
tag.div(class: floating_label_classes(attribute)) do
|
107
|
+
capture(&block) + label
|
108
|
+
end
|
109
|
+
end
|
110
|
+
wrapper_content << help_text
|
111
|
+
wrapper_content
|
112
|
+
end
|
113
|
+
|
114
|
+
def build_default_layout_content(attribute, bootstrap, label, help_text, &block)
|
115
|
+
wrapper_content = ActiveSupport::SafeBuffer.new
|
116
|
+
wrapper_content << label
|
117
|
+
wrapper_content << input_group_wrapper(attribute, bootstrap) do
|
118
|
+
capture(&block)
|
119
|
+
end
|
120
|
+
wrapper_content << help_text
|
121
|
+
wrapper_content
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_wrapper_element(bootstrap, wrapper_content)
|
125
|
+
if bootstrap.wrapper
|
126
|
+
tag.div(**field_wrapper_options(bootstrap)) do
|
127
|
+
wrapper_content
|
128
|
+
end
|
129
|
+
else
|
130
|
+
wrapper_content
|
131
|
+
end
|
132
|
+
end
|
105
133
|
end
|
106
134
|
end
|
@@ -7,37 +7,67 @@ module RailsBootstrapForm
|
|
7
7
|
module Buttons
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
def
|
12
|
-
value, options = nil, value if value.is_a?(Hash)
|
10
|
+
included do
|
11
|
+
def button(value, options, &block)
|
13
12
|
bootstrap = bootstrap_form_options.scoped(options.delete(:bootstrap))
|
13
|
+
value, options = extract_button_value_and_options(value, options)
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if bootstrap.layout_inline?
|
22
|
-
tag.div(class: "col-12") { button_html }
|
23
|
-
else
|
24
|
-
button_html
|
25
|
-
end
|
15
|
+
add_button_css_classes!(options, bootstrap)
|
16
|
+
|
17
|
+
button_html = render_button_html(value, options, bootstrap, &block)
|
18
|
+
button_html = wrap_button_html(button_html, bootstrap)
|
19
|
+
|
20
|
+
button_html
|
26
21
|
end
|
27
22
|
|
28
23
|
def secondary(value = nil, options = {}, &block)
|
29
|
-
|
30
|
-
render_button(value, options, &block)
|
24
|
+
button(value, options.merge!(variant: "secondary"), &block)
|
31
25
|
end
|
32
26
|
|
33
27
|
def primary(value = nil, options = {}, &block)
|
34
|
-
|
35
|
-
render_button(value, options, &block)
|
28
|
+
button(value, options.merge!(variant: "primary"), &block)
|
36
29
|
end
|
37
30
|
|
38
31
|
def danger(value = nil, options = {}, &block)
|
39
|
-
|
40
|
-
|
32
|
+
button(value, options.merge!(variant: "danger"), &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def button_variant_class(options)
|
39
|
+
case options.delete(:variant)
|
40
|
+
when "secondary" then "btn-secondary"
|
41
|
+
when "primary" then "btn-primary"
|
42
|
+
when "danger" then "btn-danger"
|
43
|
+
else ""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def extract_button_value_and_options(value, options)
|
48
|
+
return [nil, value.merge(options)] if value.is_a?(Hash)
|
49
|
+
|
50
|
+
[value, options]
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_button_css_classes!(options, bootstrap)
|
54
|
+
add_css_class!(options, "btn")
|
55
|
+
add_css_class!(options, button_variant_class(options))
|
56
|
+
end
|
57
|
+
|
58
|
+
def render_button_html(value, options, bootstrap, &block)
|
59
|
+
if bootstrap.render_as_button? || block
|
60
|
+
button_tag(value, options, &block)
|
61
|
+
else
|
62
|
+
submit(value, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def wrap_button_html(button_html, bootstrap)
|
67
|
+
if bootstrap.layout_inline?
|
68
|
+
tag.div(class: "col-12") { button_html }
|
69
|
+
else
|
70
|
+
button_html
|
41
71
|
end
|
42
72
|
end
|
43
73
|
end
|
@@ -7,76 +7,92 @@ module RailsBootstrapForm
|
|
7
7
|
module Choice
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
[:check_box, :radio_button].each do |tag_name|
|
12
|
-
define_method("#{tag_name}_label") do |attribute, value, options, bootstrap|
|
13
|
-
unless bootstrap.skip_label?
|
14
|
-
label_options = {
|
15
|
-
class: choice_label_classes(attribute, bootstrap, options)
|
16
|
-
}
|
17
|
-
label_options[:value] = value if tag_name.eql?(:radio_button)
|
18
|
-
label_options[:for] = options[:id] if options[:id].present?
|
10
|
+
private
|
19
11
|
|
20
|
-
|
12
|
+
[:check_box, :radio_button].each do |tag_name|
|
13
|
+
define_method("#{tag_name}_label") do |attribute, value, options, bootstrap|
|
14
|
+
unless bootstrap.skip_label?
|
15
|
+
label_options = {
|
16
|
+
class: choice_label_classes(attribute, bootstrap, options)
|
17
|
+
}
|
18
|
+
label_options[:value] = value if tag_name.eql?(:radio_button)
|
19
|
+
label_options[:for] = options[:id] if options[:id].present?
|
21
20
|
|
22
|
-
|
23
|
-
|
21
|
+
label_text = label_text(attribute, bootstrap)
|
22
|
+
|
23
|
+
label(attribute, label_text, label_options)
|
24
24
|
end
|
25
25
|
end
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
if tag_name.eql?(:check_box)
|
32
|
-
choice_field = check_box_without_bootstrap(attribute, options, value, nil)
|
33
|
-
choice_label = check_box_label(attribute, value, options, bootstrap)
|
34
|
-
else
|
35
|
-
choice_field = radio_button_without_bootstrap(attribute, value, options)
|
36
|
-
choice_label = radio_button_label(attribute, value, options, bootstrap)
|
37
|
-
end
|
28
|
+
[:check_box, :radio_button].each do |tag_name|
|
29
|
+
define_method("bootstrap_#{tag_name}") do |attribute, value, options, bootstrap|
|
30
|
+
options[:class] = choice_classes(attribute, bootstrap, options)
|
38
31
|
|
39
|
-
|
32
|
+
if tag_name.eql?(:check_box)
|
33
|
+
choice_field = check_box_without_bootstrap(attribute, options, value, nil)
|
34
|
+
choice_label = check_box_label(attribute, value, options, bootstrap)
|
35
|
+
else
|
36
|
+
choice_field = radio_button_without_bootstrap(attribute, value, options)
|
37
|
+
choice_label = radio_button_label(attribute, value, options, bootstrap)
|
40
38
|
end
|
41
|
-
end
|
42
39
|
|
43
|
-
|
44
|
-
classes = Array("form-check-input") << [bootstrap.additional_field_class || options[:class]]
|
45
|
-
classes << "is-invalid" if is_invalid?(attribute)
|
46
|
-
classes.flatten.compact
|
40
|
+
choice_field + choice_label
|
47
41
|
end
|
42
|
+
end
|
48
43
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
[:check_box, :radio_button].each do |tag_name|
|
45
|
+
define_method("build_#{tag_name}_html") do |attribute, value, bootstrap, options|
|
46
|
+
tag.div(class: choice_wrapper_classes(bootstrap)) do
|
47
|
+
concat(send("bootstrap_#{tag_name}", attribute, value, options, bootstrap))
|
48
|
+
concat(help_text(attribute, bootstrap))
|
49
|
+
concat(generate_error(attribute)) if is_invalid?(attribute)
|
50
|
+
end
|
55
51
|
end
|
52
|
+
end
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
[:check_box, :radio_button].each do |tag_name|
|
55
|
+
define_method("build_wrapped_#{tag_name}_html") do |bootstrap, tag_html|
|
56
|
+
tag.div(**field_wrapper_options(bootstrap)) do
|
57
|
+
if bootstrap.layout_horizontal?
|
58
|
+
tag.div(class: choice_container_classes(bootstrap)) { tag_html }
|
59
|
+
else
|
60
|
+
tag_html
|
61
|
+
end
|
62
|
+
end
|
61
63
|
end
|
64
|
+
end
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
66
|
+
def choice_classes(attribute, bootstrap, options)
|
67
|
+
classes = Array("form-check-input") << [bootstrap.additional_field_class || options[:class]]
|
68
|
+
classes << "is-invalid" if is_invalid?(attribute)
|
69
|
+
classes.flatten.compact
|
70
|
+
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
def choice_label_classes(attribute, bootstrap, options)
|
73
|
+
classes = Array("form-check-label") << bootstrap.additional_label_class
|
74
|
+
classes << "required" if is_field_required?(attribute, options)
|
75
|
+
classes << "is-invalid" if is_invalid?(attribute)
|
76
|
+
classes << bootstrap.hide_class if bootstrap.hide_label?
|
77
|
+
classes.flatten.compact
|
78
|
+
end
|
79
|
+
|
80
|
+
def choice_container_classes(bootstrap)
|
81
|
+
classes = Array(bootstrap.field_col_wrapper_class)
|
82
|
+
classes << field_offset_class(bootstrap.label_col_wrapper_class)
|
83
|
+
classes.flatten.compact
|
84
|
+
end
|
85
|
+
|
86
|
+
def choice_wrapper_classes(bootstrap)
|
87
|
+
classes = Array("form-check")
|
88
|
+
classes << "form-check-inline" if bootstrap.inline?
|
89
|
+
classes << "form-switch" if bootstrap.switch?
|
90
|
+
classes.flatten.compact
|
91
|
+
end
|
74
92
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
:bootstrap_check_box, :bootstrap_radio_button,
|
79
|
-
:collection_input_checked?
|
93
|
+
def collection_input_checked?(checked, obj, input_value)
|
94
|
+
checked == input_value || Array(checked).try(:include?, input_value) ||
|
95
|
+
checked == obj || Array(checked).try(:include?, obj)
|
80
96
|
end
|
81
97
|
end
|
82
98
|
end
|
@@ -7,59 +7,69 @@ module RailsBootstrapForm
|
|
7
7
|
module Errors
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
def is_invalid?(attribute)
|
12
|
-
(attribute && object.respond_to?(:errors) && object.errors[attribute].any?) ||
|
13
|
-
has_association_error?(attribute)
|
14
|
-
end
|
10
|
+
private
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# end
|
12
|
+
def is_invalid?(attribute)
|
13
|
+
(attribute && object.respond_to?(:errors) && has_attribute_error?(attribute)) ||
|
14
|
+
has_association_error?(attribute)
|
15
|
+
end
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
def generate_error(attribute)
|
18
|
+
if is_invalid?(attribute)
|
19
|
+
error_text = error_messages(attribute)
|
20
|
+
error_klass = "invalid-feedback"
|
26
21
|
|
27
|
-
|
28
|
-
end
|
22
|
+
tag.div(error_text, class: error_klass)
|
29
23
|
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def error_messages(attribute)
|
27
|
+
messages = Array(attribute_error_messages(attribute))
|
28
|
+
messages << associated_error_messages(attribute)
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
messages.flatten.to_sentence
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_attribute_error?(attribute)
|
34
|
+
attribute_error_messages(attribute).any?
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
37
|
+
def has_association_error?(attribute)
|
38
|
+
object.class.try(:reflections)&.any? do |association_name, association|
|
39
|
+
has_errors_on_association?(attribute, association_name)
|
38
40
|
end
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
|
43
|
+
def has_errors_on_association?(attribute, association_name)
|
44
|
+
return false unless is_belongs_to_association?(object.class.reflections[association_name])
|
45
|
+
return false unless is_association_same?(attribute, object.class.reflections[association_name])
|
42
46
|
|
43
|
-
|
44
|
-
|
45
|
-
next unless is_association_same?(attribute, association)
|
47
|
+
object.errors[association_name].any?
|
48
|
+
end
|
46
49
|
|
47
|
-
|
48
|
-
|
50
|
+
def attribute_error_messages(attribute)
|
51
|
+
object.errors[attribute]
|
52
|
+
end
|
49
53
|
|
50
|
-
|
51
|
-
|
54
|
+
def associated_error_messages(attribute)
|
55
|
+
error_messages = []
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
57
|
+
object.class.try(:reflections)&.each do |association_name, association|
|
58
|
+
next unless is_belongs_to_association?(association)
|
59
|
+
next unless is_association_same?(attribute, association)
|
56
60
|
|
57
|
-
|
58
|
-
(association.foreign_key == attribute.to_s)
|
61
|
+
error_messages << object.errors[association_name]
|
59
62
|
end
|
60
63
|
|
61
|
-
|
62
|
-
|
64
|
+
error_messages
|
65
|
+
end
|
66
|
+
|
67
|
+
def is_belongs_to_association?(association)
|
68
|
+
association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
69
|
+
end
|
70
|
+
|
71
|
+
def is_association_same?(attribute, association)
|
72
|
+
(association.foreign_key == attribute.to_s)
|
63
73
|
end
|
64
74
|
end
|
65
75
|
end
|
@@ -7,46 +7,43 @@ module RailsBootstrapForm
|
|
7
7
|
module HelpText
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
def help_text(attribute, bootstrap)
|
12
|
-
return if bootstrap.help_text == false
|
10
|
+
private
|
13
11
|
|
14
|
-
|
12
|
+
def help_text(attribute, bootstrap)
|
13
|
+
return if bootstrap.help_text == false
|
15
14
|
|
16
|
-
|
17
|
-
end
|
15
|
+
help_text = (bootstrap.help_text || scoped_help_text(attribute))
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
object.respond_to?(:klass) && object.klass.is_a?(ActiveModel::Naming)
|
22
|
-
object.klass
|
23
|
-
else
|
24
|
-
object.class
|
25
|
-
end
|
26
|
-
end
|
17
|
+
tag.div(help_text, class: "form-text text-muted") if help_text.present?
|
18
|
+
end
|
27
19
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
20
|
+
def object_class
|
21
|
+
if !object.class.is_a?(ActiveModel::Naming) &&
|
22
|
+
object.respond_to?(:klass) && object.klass.is_a?(ActiveModel::Naming)
|
23
|
+
object.klass
|
24
|
+
else
|
25
|
+
object.class
|
34
26
|
end
|
27
|
+
end
|
35
28
|
|
36
|
-
|
37
|
-
|
29
|
+
def partial_scope
|
30
|
+
if object_class.respond_to?(:model_name)
|
31
|
+
object_class.model_name.name
|
32
|
+
else
|
33
|
+
object_class.name
|
34
|
+
end
|
35
|
+
end
|
38
36
|
|
39
|
-
|
37
|
+
def scoped_help_text(attribute)
|
38
|
+
translation_scope = "activerecord.help_texts.#{partial_scope.underscore}"
|
40
39
|
|
41
|
-
|
42
|
-
end
|
40
|
+
help_text = translated_help_text(attribute, translation_scope).presence
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
end
|
42
|
+
help_text
|
43
|
+
end
|
47
44
|
|
48
|
-
|
49
|
-
|
45
|
+
def translated_help_text(attribute, scope)
|
46
|
+
ActiveSupport::SafeBuffer.new(I18n.t(attribute, scope: scope, default: ""))
|
50
47
|
end
|
51
48
|
end
|
52
49
|
end
|
@@ -7,47 +7,44 @@ module RailsBootstrapForm
|
|
7
7
|
module Labels
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
def draw_label(attribute, options, bootstrap)
|
12
|
-
unless bootstrap.skip_label? && !bootstrap.floating?
|
13
|
-
label_options = {
|
14
|
-
class: label_classes(attribute, options, bootstrap)
|
15
|
-
}
|
16
|
-
label_options[:for] = options[:id] if options[:id].present?
|
17
|
-
label_text = label_text(attribute, bootstrap)
|
18
|
-
|
19
|
-
label(attribute, label_text, label_options)
|
20
|
-
end
|
21
|
-
end
|
10
|
+
private
|
22
11
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
classes.flatten.compact
|
31
|
-
end
|
12
|
+
def draw_label(attribute, options, bootstrap)
|
13
|
+
unless bootstrap.skip_label? && !bootstrap.floating?
|
14
|
+
label_options = {
|
15
|
+
class: label_classes(attribute, options, bootstrap)
|
16
|
+
}
|
17
|
+
label_options[:for] = options[:id] if options[:id].present?
|
18
|
+
label_text = label_text(attribute, bootstrap)
|
32
19
|
|
33
|
-
|
34
|
-
if bootstrap.layout_horizontal?
|
35
|
-
[bootstrap.label_col_class, bootstrap.label_col_wrapper_class]
|
36
|
-
else
|
37
|
-
bootstrap.label_class
|
38
|
-
end
|
20
|
+
label(attribute, label_text, label_options)
|
39
21
|
end
|
22
|
+
end
|
40
23
|
|
41
|
-
|
42
|
-
|
43
|
-
|
24
|
+
def label_classes(attribute, options, bootstrap)
|
25
|
+
classes = []
|
26
|
+
classes << label_layout_classes(bootstrap)
|
27
|
+
classes << bootstrap.additional_label_class
|
28
|
+
classes << bootstrap.hide_class if hide_class_required?(bootstrap)
|
29
|
+
classes << "required" if is_field_required?(attribute, options)
|
30
|
+
classes << "is-invalid" if is_invalid?(attribute)
|
31
|
+
classes.flatten.compact
|
32
|
+
end
|
44
33
|
|
45
|
-
|
46
|
-
|
34
|
+
def label_layout_classes(bootstrap)
|
35
|
+
if bootstrap.layout_horizontal?
|
36
|
+
[bootstrap.label_col_class, bootstrap.label_col_wrapper_class]
|
37
|
+
else
|
38
|
+
bootstrap.label_class
|
47
39
|
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def label_text(attribute, bootstrap)
|
43
|
+
bootstrap.label_text || object&.class.try(:human_attribute_name, attribute)
|
44
|
+
end
|
48
45
|
|
49
|
-
|
50
|
-
|
46
|
+
def hide_class_required?(bootstrap)
|
47
|
+
bootstrap.hide_label? || (bootstrap.layout_inline? && !bootstrap.floating?)
|
51
48
|
end
|
52
49
|
end
|
53
50
|
end
|
@@ -7,57 +7,61 @@ module RailsBootstrapForm
|
|
7
7
|
module RequiredField
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
11
|
-
def is_field_required?(attribute, options)
|
12
|
-
return false unless attribute
|
10
|
+
private
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
def is_field_required?(attribute, options)
|
13
|
+
return false unless attribute
|
14
|
+
|
15
|
+
if options.key?(:required)
|
16
|
+
options[:required]
|
17
|
+
else
|
18
|
+
is_attribute_required?(attribute)
|
19
19
|
end
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
def required_field_options(attribute, options)
|
23
|
+
required = is_field_required?(attribute, options)
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
25
|
+
{}.tap do |option|
|
26
|
+
option[:aria] = {required: true} if required
|
27
|
+
option[:required] = required
|
28
28
|
end
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
def is_attribute_required?(attribute)
|
32
|
+
return false unless attribute
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
target = target_object(object)
|
35
|
+
return false unless target.respond_to?(:validators_on)
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
37
|
+
has_presence_validator?(target_validators(target, attribute)) || is_required_association?(target, attribute)
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
def target_object(object)
|
41
|
+
object.instance_of?(Class) ? object : object.class
|
42
|
+
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
target_validators.include?(ActiveRecord::Validations::PresenceValidator))
|
48
|
-
end
|
44
|
+
def target_validators(target, attribute)
|
45
|
+
target.validators_on(attribute).map(&:class)
|
46
|
+
end
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
def has_presence_validator?(target_validators)
|
49
|
+
target_validators.include?(ActiveModel::Validations::PresenceValidator) ||
|
50
|
+
(defined?(ActiveRecord::Validations::PresenceValidator) &&
|
51
|
+
target_validators.include?(ActiveRecord::Validations::PresenceValidator))
|
52
|
+
end
|
54
53
|
|
55
|
-
|
56
|
-
|
54
|
+
def is_required_association?(target, attribute)
|
55
|
+
target.try(:reflections)&.find do |name, reflection|
|
56
|
+
required_association?(reflection, attribute)
|
57
57
|
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def required_association?(reflection, attribute)
|
61
|
+
return false unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
|
62
|
+
return false unless reflection.foreign_key == attribute.to_s
|
58
63
|
|
59
|
-
|
60
|
-
:has_presence_validator?, :is_required_association?
|
64
|
+
has_presence_validator?(target_validators(reflection.active_record, reflection.name))
|
61
65
|
end
|
62
66
|
end
|
63
67
|
end
|
@@ -6,58 +6,55 @@ module RailsBootstrapForm
|
|
6
6
|
module InputGroupBuilder
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
-
|
10
|
-
def input_group_wrapper(attribute, bootstrap, &block)
|
11
|
-
input = capture(&block) || ActiveSupport::SafeBuffer.new
|
9
|
+
private
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
append = attach_input(bootstrap, :append)
|
11
|
+
def input_group_wrapper(attribute, bootstrap, &block)
|
12
|
+
input = capture(&block) || ActiveSupport::SafeBuffer.new
|
16
13
|
|
17
|
-
|
18
|
-
|
14
|
+
if input_group_required?(bootstrap)
|
15
|
+
prepend = attach_input(bootstrap, :prepend)
|
16
|
+
append = attach_input(bootstrap, :append)
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
input += generate_error(attribute)
|
23
|
-
end
|
18
|
+
input = prepend + input + append
|
19
|
+
input += generate_error(attribute)
|
24
20
|
|
25
|
-
input
|
21
|
+
input = tag.div(input, class: input_group_classes(attribute, bootstrap))
|
22
|
+
else
|
23
|
+
input += generate_error(attribute)
|
26
24
|
end
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
if is_size_valid?(bootstrap)
|
31
|
-
classes << "input-group-#{bootstrap.size}"
|
32
|
-
end
|
33
|
-
# Require `has-validation` class if field has errors.
|
34
|
-
classes << "has-validation" if is_invalid?(attribute)
|
35
|
-
classes.flatten.compact
|
36
|
-
end
|
26
|
+
input
|
27
|
+
end
|
37
28
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
29
|
+
def input_group_classes(attribute, bootstrap)
|
30
|
+
classes = Array("input-group") << bootstrap.additional_input_group_class
|
31
|
+
if is_size_valid?(bootstrap)
|
32
|
+
classes << "input-group-#{bootstrap.size}"
|
33
|
+
end
|
34
|
+
# Require `has-validation` class if field has errors.
|
35
|
+
classes << "has-validation" if is_invalid?(attribute)
|
36
|
+
classes.flatten.compact
|
37
|
+
end
|
42
38
|
|
43
|
-
|
39
|
+
def attach_input(bootstrap, key)
|
40
|
+
tags = [*bootstrap.send(key)].map do |item|
|
41
|
+
input_group_content(item)
|
44
42
|
end
|
45
43
|
|
46
|
-
|
47
|
-
|
44
|
+
ActiveSupport::SafeBuffer.new(tags.join)
|
45
|
+
end
|
48
46
|
|
49
|
-
|
50
|
-
|
47
|
+
def input_group_content(content)
|
48
|
+
return content if /button|submit/.match?(content)
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
bootstrap.prepend,
|
55
|
-
bootstrap.append
|
56
|
-
].any?(&:present?)
|
57
|
-
end
|
50
|
+
tag.span(content.html_safe, class: "input-group-text")
|
51
|
+
end
|
58
52
|
|
59
|
-
|
60
|
-
|
53
|
+
def input_group_required?(bootstrap)
|
54
|
+
[
|
55
|
+
bootstrap.prepend,
|
56
|
+
bootstrap.append
|
57
|
+
].any?(&:present?)
|
61
58
|
end
|
62
59
|
end
|
63
60
|
end
|
@@ -12,22 +12,8 @@ module RailsBootstrapForm
|
|
12
12
|
inputs = ActiveSupport::SafeBuffer.new
|
13
13
|
|
14
14
|
collection.each do |object|
|
15
|
-
|
16
|
-
|
17
|
-
input_options = {
|
18
|
-
bootstrap: {
|
19
|
-
label_text: text_method.respond_to?(:call) ? text_method.call(object) : object.send(text_method),
|
20
|
-
inline: (bootstrap.inline? || bootstrap.layout_inline?)
|
21
|
-
},
|
22
|
-
required: false,
|
23
|
-
id: sanitized_tag_name(attribute, value)
|
24
|
-
}.deep_merge!(options)
|
25
|
-
|
26
|
-
if (checked = input_options[:checked])
|
27
|
-
input_options[:checked] = collection_input_checked?(checked, object, value)
|
28
|
-
end
|
29
|
-
|
30
|
-
input_value = value_method.respond_to?(:call) ? value_method.call(object) : value
|
15
|
+
input_options = build_input_options(object, attribute, value_method, text_method, bootstrap, options)
|
16
|
+
input_value = resolve_input_value(object, value_method)
|
31
17
|
|
32
18
|
inputs << yield(attribute, input_value, input_options)
|
33
19
|
end
|
@@ -66,6 +52,36 @@ module RailsBootstrapForm
|
|
66
52
|
end
|
67
53
|
end
|
68
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def build_input_options(object, attribute, value_method, text_method, bootstrap, options)
|
59
|
+
input_options = {
|
60
|
+
bootstrap: {
|
61
|
+
label_text: resolve_label_text(text_method, object),
|
62
|
+
inline: (bootstrap.inline? || bootstrap.layout_inline?)
|
63
|
+
},
|
64
|
+
required: false,
|
65
|
+
id: sanitized_tag_name(attribute, object.send(value_method))
|
66
|
+
}.deep_merge!(options)
|
67
|
+
|
68
|
+
input_options[:checked] = resolve_checked_option(input_options[:checked], object, value_method)
|
69
|
+
|
70
|
+
input_options
|
71
|
+
end
|
72
|
+
|
73
|
+
def resolve_label_text(text_method, object)
|
74
|
+
text_method.respond_to?(:call) ? text_method.call(object) : object.send(text_method)
|
75
|
+
end
|
76
|
+
|
77
|
+
def resolve_input_value(object, value_method)
|
78
|
+
value_method.respond_to?(:call) ? value_method.call(object) : object.send(value_method)
|
79
|
+
end
|
80
|
+
|
81
|
+
def resolve_checked_option(checked_option, object, value_method)
|
82
|
+
return collection_input_checked?(checked_option, object, object.send(value_method)) if checked_option
|
83
|
+
false
|
84
|
+
end
|
69
85
|
end
|
70
86
|
end
|
71
87
|
end
|
@@ -12,20 +12,10 @@ module RailsBootstrapForm
|
|
12
12
|
bootstrap = bootstrap_form_options.scoped(options.delete(:bootstrap))
|
13
13
|
return super if bootstrap.disabled?
|
14
14
|
|
15
|
-
check_box_html =
|
16
|
-
concat(bootstrap_check_box(attribute, checked_value, options, bootstrap))
|
17
|
-
concat(help_text(attribute, bootstrap))
|
18
|
-
concat(generate_error(attribute)) if is_invalid?(attribute)
|
19
|
-
end
|
15
|
+
check_box_html = build_check_box_html(attribute, checked_value, bootstrap, options)
|
20
16
|
|
21
17
|
if bootstrap.wrapper
|
22
|
-
|
23
|
-
if bootstrap.layout_horizontal?
|
24
|
-
tag.div(class: choice_container_classes(bootstrap)) { check_box_html }
|
25
|
-
else
|
26
|
-
check_box_html
|
27
|
-
end
|
28
|
-
end
|
18
|
+
build_wrapped_check_box_html(bootstrap, check_box_html)
|
29
19
|
else
|
30
20
|
check_box_html
|
31
21
|
end
|
@@ -12,20 +12,10 @@ module RailsBootstrapForm
|
|
12
12
|
bootstrap = bootstrap_form_options.scoped(options.delete(:bootstrap))
|
13
13
|
return super if bootstrap.disabled?
|
14
14
|
|
15
|
-
radio_button_html =
|
16
|
-
concat(bootstrap_radio_button(attribute, value, options, bootstrap))
|
17
|
-
concat(help_text(attribute, bootstrap))
|
18
|
-
concat(generate_error(attribute)) if is_invalid?(attribute)
|
19
|
-
end
|
15
|
+
radio_button_html = build_radio_button_html(attribute, value, bootstrap, options)
|
20
16
|
|
21
17
|
if bootstrap.wrapper
|
22
|
-
|
23
|
-
if bootstrap.layout_horizontal?
|
24
|
-
tag.div(class: choice_container_classes(bootstrap)) { radio_button_html }
|
25
|
-
else
|
26
|
-
radio_button_html
|
27
|
-
end
|
28
|
-
end
|
18
|
+
build_wrapped_radio_button_html(bootstrap, radio_button_html)
|
29
19
|
else
|
30
20
|
radio_button_html
|
31
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_bootstrap_form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harshal LADHE (shivam091)
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: generator_spec
|
@@ -60,6 +60,7 @@ executables: []
|
|
60
60
|
extensions: []
|
61
61
|
extra_rdoc_files: []
|
62
62
|
files:
|
63
|
+
- ".codeclimate.yml"
|
63
64
|
- ".rspec"
|
64
65
|
- CHANGELOG.md
|
65
66
|
- CODE_OF_CONDUCT.md
|