comfy_bootstrap_form 4.0.0.beta1 → 4.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +12 -0
  3. data/.travis.yml +14 -0
  4. data/CONTRIBUTING.md +40 -0
  5. data/Gemfile +14 -0
  6. data/{MIT-LICENSE → LICENSE.md} +1 -1
  7. data/README.md +130 -495
  8. data/Rakefile +5 -15
  9. data/bootstrap_form.gemspec +25 -0
  10. data/demo/README.md +18 -0
  11. data/demo/Rakefile +6 -0
  12. data/demo/app/controllers/application_controller.rb +2 -0
  13. data/demo/app/controllers/bootstrap_controller.rb +12 -0
  14. data/demo/app/models/application_record.rb +3 -0
  15. data/demo/app/models/user.rb +3 -0
  16. data/demo/app/views/bootstrap/form.html.erb +82 -0
  17. data/demo/app/views/layouts/application.html.erb +43 -0
  18. data/demo/bin/bundle +3 -0
  19. data/{test/dummy → demo}/bin/rails +1 -1
  20. data/{test/dummy → demo}/bin/rake +0 -0
  21. data/demo/bin/setup +36 -0
  22. data/demo/bin/update +31 -0
  23. data/demo/bin/yarn +11 -0
  24. data/{test/dummy → demo}/config.ru +2 -1
  25. data/demo/config/application.rb +24 -0
  26. data/demo/config/boot.rb +5 -0
  27. data/{test/dummy → demo}/config/database.yml +8 -12
  28. data/demo/config/environment.rb +5 -0
  29. data/demo/config/environments/development.rb +60 -0
  30. data/{test/dummy → demo}/config/environments/test.rb +15 -20
  31. data/demo/config/initializers/application_controller_renderer.rb +8 -0
  32. data/demo/config/initializers/assets.rb +14 -0
  33. data/{test/dummy → demo}/config/initializers/backtrace_silencers.rb +0 -0
  34. data/demo/config/initializers/cookies_serializer.rb +5 -0
  35. data/{test/dummy → demo}/config/initializers/filter_parameter_logging.rb +0 -0
  36. data/{test/dummy → demo}/config/initializers/inflections.rb +0 -0
  37. data/{test/dummy → demo}/config/initializers/mime_types.rb +0 -1
  38. data/{test/dummy → demo}/config/initializers/wrap_parameters.rb +2 -2
  39. data/{test/dummy → demo}/config/locales/en.yml +10 -0
  40. data/demo/config/puma.rb +56 -0
  41. data/{test/dummy → demo}/config/routes.rb +2 -0
  42. data/demo/config/spring.rb +6 -0
  43. data/demo/config/storage.yml +35 -0
  44. data/demo/db/schema.rb +11 -0
  45. data/{test/dummy/public/favicon.ico → demo/log/.keep} +0 -0
  46. data/demo/package.json +5 -0
  47. data/demo/public/favicon.ico +0 -0
  48. data/lib/bootstrap_form.rb +2 -2
  49. data/lib/bootstrap_form/form_builder.rb +367 -346
  50. data/lib/bootstrap_form/version.rb +1 -1
  51. data/lib/bootstrap_form/view_helper.rb +33 -0
  52. data/lib/comfy_bootstrap_form.rb +1 -1
  53. data/test/bootstrap_form/fields_test.rb +304 -0
  54. data/test/bootstrap_form/fields_with_errors_test.rb +109 -0
  55. data/test/bootstrap_form/form_builder_test.rb +49 -0
  56. data/test/bootstrap_form/horizontal_form_test.rb +159 -0
  57. data/test/bootstrap_form/inline_form_test.rb +68 -0
  58. data/test/bootstrap_form/input_group_test.rb +53 -0
  59. data/test/bootstrap_form/radios_and_checkboxes_test.rb +208 -0
  60. data/test/bootstrap_form/submit_test.rb +59 -0
  61. data/test/bootstrap_form/view_helpers_test.rb +99 -0
  62. data/test/gemfiles/5.2.gemfile +13 -0
  63. data/test/test_helper.rb +31 -69
  64. metadata +71 -258
  65. data/app/assets/stylesheets/rails_bootstrap_forms.css +0 -10
  66. data/lib/bootstrap_form/aliasing.rb +0 -35
  67. data/lib/bootstrap_form/helper.rb +0 -36
  68. data/lib/bootstrap_form/helpers/bootstrap.rb +0 -94
  69. data/lib/bootstrap_form/helpers/nested_form.rb +0 -33
  70. data/test/bootstrap_checkbox_test.rb +0 -144
  71. data/test/bootstrap_fields_test.rb +0 -152
  72. data/test/bootstrap_form_group_test.rb +0 -313
  73. data/test/bootstrap_form_test.rb +0 -276
  74. data/test/bootstrap_other_components_test.rb +0 -86
  75. data/test/bootstrap_radio_button_test.rb +0 -124
  76. data/test/bootstrap_selects_test.rb +0 -160
  77. data/test/dummy/Gemfile +0 -45
  78. data/test/dummy/Gemfile.lock +0 -120
  79. data/test/dummy/README.rdoc +0 -28
  80. data/test/dummy/Rakefile +0 -10
  81. data/test/dummy/app/assets/javascripts/application.js +0 -16
  82. data/test/dummy/app/assets/stylesheets/application.css +0 -13
  83. data/test/dummy/app/controllers/application_controller.rb +0 -5
  84. data/test/dummy/app/helpers/application_helper.rb +0 -2
  85. data/test/dummy/app/models/address.rb +0 -3
  86. data/test/dummy/app/models/faux_user.rb +0 -9
  87. data/test/dummy/app/models/super_user.rb +0 -2
  88. data/test/dummy/app/models/user.rb +0 -9
  89. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  90. data/test/dummy/bin/bundle +0 -3
  91. data/test/dummy/config/application.rb +0 -23
  92. data/test/dummy/config/boot.rb +0 -4
  93. data/test/dummy/config/environment.rb +0 -5
  94. data/test/dummy/config/environments/development.rb +0 -29
  95. data/test/dummy/config/environments/production.rb +0 -80
  96. data/test/dummy/config/initializers/generic_migration.rb +0 -6
  97. data/test/dummy/config/initializers/secret_token.rb +0 -12
  98. data/test/dummy/config/initializers/session_store.rb +0 -3
  99. data/test/dummy/db/migrate/20130703191909_create_users.rb +0 -13
  100. data/test/dummy/db/migrate/20130703191937_create_addresses.rb +0 -13
  101. data/test/dummy/db/migrate/20130912171202_add_preferences_to_user.rb +0 -5
  102. data/test/dummy/db/migrate/20140327190145_add_terms_to_user.rb +0 -5
  103. data/test/dummy/db/migrate/20140922133133_add_type_to_users.rb +0 -5
  104. data/test/dummy/db/schema.rb +0 -38
  105. data/test/dummy/db/seeds.rb +0 -7
  106. data/test/dummy/db/test.sqlite3 +0 -0
  107. data/test/dummy/log/test.log +0 -18394
  108. data/test/dummy/public/404.html +0 -58
  109. data/test/dummy/public/422.html +0 -58
  110. data/test/dummy/public/500.html +0 -57
  111. data/test/dummy/public/robots.txt +0 -5
  112. data/test/dummy/test/fixtures/addresses.yml +0 -15
  113. data/test/dummy/test/fixtures/users.yml +0 -15
  114. data/test/dummy/test/models/address_test.rb +0 -7
  115. data/test/dummy/test/models/user_test.rb +0 -7
  116. data/test/dummy/test/test_helper.rb +0 -15
  117. data/test/special_form_class_models_test.rb +0 -43
@@ -1,4 +1,4 @@
1
- Dummy::Application.configure do
1
+ Rails.application.configure do
2
2
  # Settings specified here will take precedence over those in config/application.rb.
3
3
 
4
4
  # The test environment is used exclusively to run your application's
@@ -12,23 +12,11 @@ Dummy::Application.configure do
12
12
  # preloads Rails for running tests, you may have to set it to true.
13
13
  config.eager_load = false
14
14
 
15
- version = Rails.version.to_f
16
-
17
- # Configure static asset server for tests with Cache-Control for performance.
18
- if version < 4.2
19
- config.serve_static_assets = true
20
- elsif version < 5
21
- config.serve_static_files = true
22
- end
23
-
24
- if version < 5
25
- config.static_cache_control = "public, max-age=3600"
26
- else
27
- config.public_file_server.enabled = true
28
- config.public_file_server.headers = {
29
- "Cache-Control" => "public, max-age=3600"
30
- }
31
- end
15
+ # Configure public file server for tests with Cache-Control for performance.
16
+ config.public_file_server.enabled = true
17
+ config.public_file_server.headers = {
18
+ 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19
+ }
32
20
 
33
21
  # Show full error reports and disable caching.
34
22
  config.consider_all_requests_local = true
@@ -40,6 +28,13 @@ Dummy::Application.configure do
40
28
  # Disable request forgery protection in test environment.
41
29
  config.action_controller.allow_forgery_protection = false
42
30
 
31
+ # Store uploaded files on the local file system in a temporary directory
32
+ if config.respond_to?(:active_storage)
33
+ config.active_storage.service = :test
34
+ end
35
+
36
+ config.action_mailer.perform_caching = false
37
+
43
38
  # Tell Action Mailer not to deliver emails to the real world.
44
39
  # The :test delivery method accumulates sent emails in the
45
40
  # ActionMailer::Base.deliveries array.
@@ -48,6 +43,6 @@ Dummy::Application.configure do
48
43
  # Print deprecation notices to the stderr.
49
44
  config.active_support.deprecation = :stderr
50
45
 
51
- # In Rails 5, the default value of this option will change from `:sorted` to `:random`.
52
- config.active_support.test_order = :sorted
46
+ # Raises error for missing translations
47
+ # config.action_view.raise_on_missing_translations = true
53
48
  end
@@ -0,0 +1,8 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # ActiveSupport::Reloader.to_prepare do
4
+ # ApplicationController.renderer.defaults.merge!(
5
+ # http_host: 'example.org',
6
+ # https: false
7
+ # )
8
+ # end
@@ -0,0 +1,14 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Version of your assets, change this if you want to expire all your assets.
4
+ Rails.application.config.assets.version = '1.0'
5
+
6
+ # Add additional assets to the asset load path.
7
+ # Rails.application.config.assets.paths << Emoji.images_path
8
+ # Add Yarn node_modules folder to the asset load path.
9
+ Rails.application.config.assets.paths << Rails.root.join('node_modules')
10
+
11
+ # Precompile additional assets.
12
+ # application.js, application.css, and all non-JS/CSS in the app/assets
13
+ # folder are already added.
14
+ # Rails.application.config.assets.precompile += %w( admin.js admin.css )
@@ -0,0 +1,5 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Specify a serializer for the signed and encrypted cookie jars.
4
+ # Valid options are :json, :marshal, and :hybrid.
5
+ Rails.application.config.action_dispatch.cookies_serializer = :json
@@ -2,4 +2,3 @@
2
2
 
3
3
  # Add new mime types for use in respond_to blocks:
4
4
  # Mime::Type.register "text/richtext", :rtf
5
- # Mime::Type.register_alias "text/html", :iphone
@@ -5,10 +5,10 @@
5
5
 
6
6
  # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
7
  ActiveSupport.on_load(:action_controller) do
8
- wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
8
+ wrap_parameters format: [:json]
9
9
  end
10
10
 
11
11
  # To enable root element in JSON for ActiveRecord objects.
12
12
  # ActiveSupport.on_load(:active_record) do
13
- # self.include_root_in_json = true
13
+ # self.include_root_in_json = true
14
14
  # end
@@ -16,6 +16,16 @@
16
16
  #
17
17
  # This would use the information in config/locales/es.yml.
18
18
  #
19
+ # The following keys must be escaped otherwise they will not be retrieved by
20
+ # the default I18n backend:
21
+ #
22
+ # true, false, on, off, yes, no
23
+ #
24
+ # Instead, surround them with single quotes.
25
+ #
26
+ # en:
27
+ # 'true': 'foo'
28
+ #
19
29
  # To learn more, please read the Rails Internationalization guide
20
30
  # available at http://guides.rubyonrails.org/i18n.html.
21
31
 
@@ -0,0 +1,56 @@
1
+ # Puma can serve each request in a thread from an internal thread pool.
2
+ # The `threads` method setting takes two numbers: a minimum and maximum.
3
+ # Any libraries that use thread pools should be configured to match
4
+ # the maximum value specified for Puma. Default is set to 5 threads for minimum
5
+ # and maximum; this matches the default thread size of Active Record.
6
+ #
7
+ threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8
+ threads threads_count, threads_count
9
+
10
+ # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11
+ #
12
+ port ENV.fetch("PORT") { 3000 }
13
+
14
+ # Specifies the `environment` that Puma will run in.
15
+ #
16
+ environment ENV.fetch("RAILS_ENV") { "development" }
17
+
18
+ # Specifies the number of `workers` to boot in clustered mode.
19
+ # Workers are forked webserver processes. If using threads and workers together
20
+ # the concurrency of the application would be max `threads` * `workers`.
21
+ # Workers do not work on JRuby or Windows (both of which do not support
22
+ # processes).
23
+ #
24
+ # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25
+
26
+ # Use the `preload_app!` method when specifying a `workers` number.
27
+ # This directive tells Puma to first boot the application and load code
28
+ # before forking the application. This takes advantage of Copy On Write
29
+ # process behavior so workers use less memory. If you use this option
30
+ # you need to make sure to reconnect any threads in the `on_worker_boot`
31
+ # block.
32
+ #
33
+ # preload_app!
34
+
35
+ # If you are preloading your application and using Active Record, it's
36
+ # recommended that you close any connections to the database before workers
37
+ # are forked to prevent connection leakage.
38
+ #
39
+ # before_fork do
40
+ # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
41
+ # end
42
+
43
+ # The code in the `on_worker_boot` will be called if you are using
44
+ # clustered mode by specifying a number of `workers`. After each worker
45
+ # process is booted, this block will be run. If you are using the `preload_app!`
46
+ # option, you will want to use this block to reconnect to any threads
47
+ # or connections that may have been created at application boot, as Ruby
48
+ # cannot share connections between processes.
49
+ #
50
+ # on_worker_boot do
51
+ # ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
52
+ # end
53
+ #
54
+
55
+ # Allow puma to be restarted by `rails restart` command.
56
+ plugin :tmp_restart
@@ -1,3 +1,5 @@
1
1
  Dummy::Application.routes.draw do
2
2
  resources :users
3
+
4
+ root to: "bootstrap#form"
3
5
  end
@@ -0,0 +1,6 @@
1
+ %w[
2
+ .ruby-version
3
+ .rbenv-vars
4
+ tmp/restart.txt
5
+ tmp/caching-dev.txt
6
+ ].each { |path| Spring.watch(path) }
@@ -0,0 +1,35 @@
1
+ test:
2
+ service: Disk
3
+ root: <%= Rails.root.join("tmp/storage") %>
4
+
5
+ local:
6
+ service: Disk
7
+ root: <%= Rails.root.join("storage") %>
8
+
9
+ # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10
+ # amazon:
11
+ # service: S3
12
+ # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13
+ # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14
+ # region: us-east-1
15
+ # bucket: your_own_bucket
16
+
17
+ # Remember not to checkin your GCS keyfile to a repository
18
+ # google:
19
+ # service: GCS
20
+ # project: your_project
21
+ # keyfile: <%= Rails.root.join("path/to/gcs.keyfile") %>
22
+ # bucket: your_own_bucket
23
+
24
+ # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25
+ # microsoft:
26
+ # service: AzureStorage
27
+ # path: your_azure_storage_path
28
+ # storage_account_name: your_account_name
29
+ # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
30
+ # container: your_container_name
31
+
32
+ # mirror:
33
+ # service: Mirror
34
+ # primary: local
35
+ # mirrors: [ amazon, google, microsoft ]
@@ -0,0 +1,11 @@
1
+ ActiveRecord::Schema.define(version: 1) do
2
+
3
+ create_table :users, force: :cascade do |t|
4
+ t.string :email
5
+ t.string :password
6
+ t.boolean :terms, default: false
7
+ t.string :test
8
+ t.timestamps
9
+ end
10
+
11
+ end
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "dummy",
3
+ "private": true,
4
+ "dependencies": {}
5
+ }
File without changes
@@ -1,5 +1,5 @@
1
1
  require 'bootstrap_form/form_builder'
2
- require 'bootstrap_form/helper'
2
+ require 'bootstrap_form/view_helper'
3
3
 
4
4
  module BootstrapForm
5
5
  module Rails
@@ -9,5 +9,5 @@ module BootstrapForm
9
9
  end
10
10
 
11
11
  ActiveSupport.on_load(:action_view) do
12
- include BootstrapForm::Helper
12
+ include BootstrapForm::ViewHelper
13
13
  end
@@ -1,460 +1,481 @@
1
- require_relative 'aliasing'
2
- require_relative 'helpers/bootstrap'
3
-
4
1
  module BootstrapForm
5
2
  class FormBuilder < ActionView::Helpers::FormBuilder
6
- extend BootstrapForm::Aliasing
7
- include BootstrapForm::Helpers::Bootstrap
8
-
9
- attr_reader :layout, :label_col, :control_col, :has_error, :inline_errors, :label_errors, :acts_like_form_tag
10
3
 
11
- FIELD_HELPERS = %w{color_field date_field datetime_field datetime_local_field
12
- email_field month_field number_field password_field phone_field
13
- range_field search_field telephone_field text_area text_field time_field
14
- url_field week_field}
4
+ FIELD_HELPERS = %w[
5
+ color_field file_field phone_field text_field
6
+ date_field month_field range_field time_field
7
+ datetime_field number_field search_field url_field
8
+ email_field password_field text_area week_field
9
+ ].freeze
10
+
11
+ # Container for bootstrap specific form builder options. It controls options
12
+ # that define form layout and grid sizing.
13
+ class BootstrapOptions
14
+
15
+ attr_reader :layout,
16
+ :label_col_class,
17
+ :control_col_class,
18
+ :label_align_class,
19
+ :inline_margin_class
20
+
21
+ def initialize(options = {})
22
+ @layout = options[:layout] || "default"
23
+ @label_col_class = options[:label_col_class] || "col-sm-2"
24
+ @control_col_class = options[:control_col_class] || "col-sm-10"
25
+ @label_align_class = options[:label_align_class] || "text-sm-right"
26
+ @inline_margin_class = options[:inline_margin_class] || "mr-sm-2"
27
+ end
15
28
 
16
- DATE_SELECT_HELPERS = %w{date_select time_select datetime_select}
29
+ def horizontal?
30
+ @layout.to_s == "horizontal"
31
+ end
17
32
 
18
- delegate :content_tag, :capture, :concat, to: :@template
33
+ def inline?
34
+ @layout.to_s == "inline"
35
+ end
19
36
 
20
- def initialize(object_name, object, template, options)
21
- @layout = options[:layout]
22
- @label_col = options[:label_col] || default_label_col
23
- @control_col = options[:control_col] || default_control_col
24
- @label_errors = options[:label_errors] || false
25
- @inline_errors = if options[:inline_errors].nil?
26
- @label_errors != true
27
- else
28
- options[:inline_errors] != false
37
+ def offset_col_class
38
+ label_col_class.sub(/\Acol-(\w+)-(\d+)\z/, 'offset-\1-\2')
29
39
  end
30
- @acts_like_form_tag = options[:acts_like_form_tag]
31
40
 
32
- super
33
41
  end
34
42
 
35
- FIELD_HELPERS.each do |method_name|
36
- with_method_name = "#{method_name}_with_bootstrap"
37
- without_method_name = "#{method_name}_without_bootstrap"
43
+ delegate :content_tag, :capture, :concat, to: :@template
38
44
 
39
- define_method(with_method_name) do |name, options = {}|
40
- form_group_builder(name, options) do
41
- prepend_and_append_input(options) do
42
- send(without_method_name, name, options)
43
- end
44
- end
45
- end
45
+ attr_accessor :bootstrap
46
46
 
47
- bootstrap_method_alias method_name
47
+ def initialize(object_name, object, template, options)
48
+ @bootstrap = BootstrapOptions.new(options.delete(:bootstrap) || {})
49
+ super(object_name, object, template, options)
48
50
  end
49
51
 
50
- DATE_SELECT_HELPERS.each do |method_name|
51
- with_method_name = "#{method_name}_with_bootstrap"
52
- without_method_name = "#{method_name}_without_bootstrap"
53
-
54
- define_method(with_method_name) do |name, options = {}, html_options = {}|
55
- form_group_builder(name, options, html_options) do
56
- content_tag(:div, send(without_method_name, name, options, html_options), class: control_specific_class(method_name))
52
+ # Overriding default methods to forward everything to field_helper
53
+ FIELD_HELPERS.each do |field_helper|
54
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
55
+ def #{field_helper}(method, options = {})
56
+ field_helper(method, options) do
57
+ super(method, options)
58
+ end
57
59
  end
58
- end
59
-
60
- bootstrap_method_alias method_name
60
+ RUBY_EVAL
61
61
  end
62
62
 
63
- def file_field_with_bootstrap(name, options = {})
64
- form_group_builder(name, options.reverse_merge(control_class: nil)) do
65
- file_field_without_bootstrap(name, options)
63
+ # Wrapper for select helper. Boostrap options are sent via html_options hash:
64
+ #
65
+ # select :choices, ["a", "b"], {}, bootstrap: {label: {text: "Custom"}}
66
+ #
67
+ def select(method, choices = nil, options = {}, html_options = {}, &block)
68
+ bootstrap_options = (html_options.delete(:bootstrap) || {})
69
+ draw_form_group(bootstrap_options, method, html_options) do
70
+ super(method, choices, options, html_options, &block)
66
71
  end
67
72
  end
68
73
 
69
- bootstrap_method_alias :file_field
74
+ # Wrapper around checkbox. Example usage:
75
+ #
76
+ # checkbox :agree, bootstrap: {label: {text: "Do you agree?"}}
77
+ #
78
+ def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
79
+ bootstrap_options = options.delete(:bootstrap) || {}
80
+ bootstrap_label_options = bootstrap_options[:label] || {}
70
81
 
71
- if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new("4.1.0")
72
- def select_with_bootstrap(method, choices = nil, options = {}, html_options = {}, &block)
73
- form_group_builder(method, options, html_options) do
74
- prepend_and_append_input(options) do
75
- select_without_bootstrap(method, choices, options, html_options, &block)
76
- end
77
- end
82
+ help_text = draw_help(bootstrap_options[:help])
83
+ errors = draw_errors(method)
84
+
85
+ add_css_class!(options, "form-check-input")
86
+ add_css_class!(options, "is-invalid") if errors.present?
87
+
88
+ label_text = nil
89
+ if (custom_text = bootstrap_label_options[:text]).present?
90
+ label_text = custom_text
78
91
  end
79
- else
80
- def select_with_bootstrap(method, choices, options = {}, html_options = {})
81
- form_group_builder(method, options, html_options) do
82
- prepend_and_append_input(options) do
83
- select_without_bootstrap(method, choices, options, html_options)
92
+
93
+ fieldset_css_class = "form-group"
94
+ fieldset_css_class << " row" if bootstrap.horizontal?
95
+ fieldset_css_class << " #{bootstrap.inline_margin_class}" if bootstrap.inline?
96
+
97
+ content_tag(:fieldset, class: fieldset_css_class) do
98
+ draw_control_column(offset: true) do
99
+ content_tag(:div, class: "form-check") do
100
+ concat super(method, options, checked_value, unchecked_value)
101
+ concat label(method, label_text, class: "form-check-label")
102
+ concat errors if errors.present?
103
+ concat help_text if help_text.present?
84
104
  end
85
105
  end
86
106
  end
87
107
  end
88
108
 
89
- bootstrap_method_alias :select
90
-
91
- def collection_select_with_bootstrap(method, collection, value_method, text_method, options = {}, html_options = {})
92
- form_group_builder(method, options, html_options) do
93
- collection_select_without_bootstrap(method, collection, value_method, text_method, options, html_options)
109
+ # Helper to generate multiple radio buttons. Example usage:
110
+ #
111
+ # collection_radio_buttons :choices, ["a", "b"], :to_s, :to_s %>
112
+ # collection_radio_buttons :choices, [["a", "Label A"], ["b", "Label B"]], :first, :second
113
+ # collection_radio_buttons :choices, Choice.all, :id, :label
114
+ #
115
+ # Takes bootstrap options:
116
+ # inline: true - to render inputs inline
117
+ # label: {text: "Custom"} - to specify a label
118
+ # label: {hide: true} - to not render label at all
119
+ #
120
+ def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {})
121
+ bootstrap_options = (options.delete(:bootstrap) || {})
122
+
123
+ args = [bootstrap_options, method, collection, value_method, text_method, options, html_options]
124
+ draw_choices(*args) do |m, v, opts|
125
+ radio_button(m, v, opts)
94
126
  end
95
127
  end
96
128
 
97
- bootstrap_method_alias :collection_select
129
+ # Helper to generate multiple checkboxes. Same options as for radio buttons.
130
+ # Example usage:
131
+ #
132
+ # collection_check_boxes :choices, Choice.all, :id, :label
133
+ #
134
+ def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {})
135
+ bootstrap_options = (options.delete(:bootstrap) || {})
98
136
 
99
- def grouped_collection_select_with_bootstrap(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
100
- form_group_builder(method, options, html_options) do
101
- grouped_collection_select_without_bootstrap(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
137
+ content = "".html_safe
138
+ unless options[:include_hidden] == false
139
+ content << hidden_field(method, multiple: true, value: "")
102
140
  end
103
- end
104
-
105
- bootstrap_method_alias :grouped_collection_select
106
141
 
107
- def time_zone_select_with_bootstrap(method, priority_zones = nil, options = {}, html_options = {})
108
- form_group_builder(method, options, html_options) do
109
- time_zone_select_without_bootstrap(method, priority_zones, options, html_options)
142
+ args = [bootstrap_options, method, collection, value_method, text_method, options, html_options]
143
+ content << draw_choices(*args) do |m, v, opts|
144
+ opts[:multiple] = true
145
+ opts[:include_hidden] = false
146
+ ActionView::Helpers::FormBuilder.instance_method(:check_box).bind(self).call(m, opts, v)
110
147
  end
111
148
  end
112
149
 
113
- bootstrap_method_alias :time_zone_select
114
-
115
- def check_box_with_bootstrap(name, options = {}, checked_value = "1", unchecked_value = "0", &block)
116
- options = options.symbolize_keys!
117
- check_box_options = options.except(:label, :label_class, :help, :inline)
118
- check_box_options[:class] = ["form-check-input", check_box_options[:class]].compact.join(' ')
119
-
120
- html = check_box_without_bootstrap(name, check_box_options, checked_value, unchecked_value)
121
- label_content = block_given? ? capture(&block) : options[:label]
122
- html.concat(" ").concat(label_content || (object && object.class.human_attribute_name(name)) || name.to_s.humanize)
123
-
124
- label_name = name
125
- # label's `for` attribute needs to match checkbox tag's id,
126
- # IE sanitized value, IE
127
- # https://github.com/rails/rails/blob/c57e7239a8b82957bcb07534cb7c1a3dcef71864/actionview/lib/action_view/helpers/tags/base.rb#L116-L118
128
- if options[:multiple]
129
- label_name =
130
- "#{name}_#{checked_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase}"
150
+ # Bootstrap wrapper for readonly text field that is shown as plain text.
151
+ #
152
+ # plaintext(:value)
153
+ #
154
+ def plaintext(method, options = {})
155
+ bootstrap_options = (options.delete(:bootstrap) || {})
156
+ draw_form_group(bootstrap_options, method, options) do
157
+ remove_css_class!(options, "form-control")
158
+ add_css_class!(options, "form-control-plaintext")
159
+ options[:readonly] = true
160
+ ActionView::Helpers::FormBuilder.instance_method(:text_field).bind(self).call(method, options)
131
161
  end
162
+ end
132
163
 
133
- disabled_class = " disabled" if options[:disabled]
134
- label_class = options[:label_class]
135
-
136
- if options[:inline]
137
- label_class = " #{label_class}" if label_class
138
- label(label_name, html, class: "form-check-inline#{disabled_class}#{label_class}")
139
- else
140
- content_tag(:div, class: "form-check#{disabled_class}") do
141
- label(label_name, html, class: ["form-check-label", label_class].compact.join(" "))
164
+ # Add bootstrap formatted submit button. If you need to change its type or
165
+ # add another css class, you need to override all css classes like so:
166
+ #
167
+ # submit(class: "btn btn-info custom-class")
168
+ #
169
+ # You may add additional content that directly follows the button. Here's
170
+ # an example of a cancel link:
171
+ #
172
+ # submit do
173
+ # link_to("Cancel", "/", class: "btn btn-link")
174
+ # end
175
+ #
176
+ def submit(value = nil, options = {}, &block)
177
+ value, options = nil, value if value.is_a?(Hash)
178
+ add_css_class!(options, "btn")
179
+
180
+ form_group_class = "form-group"
181
+ form_group_class << " row" if bootstrap.horizontal?
182
+
183
+ content_tag(:div, class: form_group_class) do
184
+ draw_control_column(offset: true) do
185
+ out = super(value, options)
186
+ out << capture(&block) if block_given?
187
+ out
142
188
  end
143
189
  end
144
190
  end
145
191
 
146
- bootstrap_method_alias :check_box
192
+ # Same as submit button, only with btn-primary class added
193
+ def primary(value = nil, options = {}, &block)
194
+ add_css_class!(options, "btn-primary")
195
+ submit(value, options, &block)
196
+ end
147
197
 
148
- def radio_button_with_bootstrap(name, value, *args)
149
- options = args.extract_options!.symbolize_keys!
150
- args << options.except(:label, :label_class, :help, :inline)
198
+ # Helper method to put arbitrary content in markup that renders correctly
199
+ # for the Bootstrap form. Example:
200
+ #
201
+ # form_group bootstrap: {label: {text: "Label"}} do
202
+ # "Some content"
203
+ # end
204
+ #
205
+ def form_group(options = {}, &block)
206
+ bootstrap_options = options.delete(:bootstrap) || {}
207
+ bootstrap_label_options = bootstrap_options.delete(:label) || {}
151
208
 
152
- html = radio_button_without_bootstrap(name, value, *args) + " " + options[:label]
209
+ label_text = bootstrap_label_options[:text]
153
210
 
154
- disabled_class = " disabled" if options[:disabled]
155
- label_class = options[:label_class]
211
+ label = if label_text.present?
212
+ label_options = {}
213
+ add_css_class!(label_options, bootstrap_label_options[:class])
156
214
 
157
- if options[:inline]
158
- label_class = " #{label_class}" if label_class
159
- label(name, html, class: "radio-inline#{disabled_class}#{label_class}", value: value)
160
- else
161
- content_tag(:div, class: "radio#{disabled_class}") do
162
- label(name, html, value: value, class: label_class)
215
+ if bootstrap.horizontal?
216
+ add_css_class!(label_options, "col-form-label")
217
+ add_css_class!(label_options, bootstrap.label_col_class)
218
+ add_css_class!(label_options, bootstrap.label_align_class)
219
+ elsif bootstrap.inline?
220
+ add_css_class!(label_options, bootstrap.inline_margin_class)
163
221
  end
164
- end
165
- end
166
-
167
- bootstrap_method_alias :radio_button
168
222
 
169
- def collection_check_boxes_with_bootstrap(*args)
170
- html = inputs_collection(*args) do |name, value, options|
171
- options[:multiple] = true
172
- check_box(name, options, value, nil)
223
+ content_tag(:label, label_text, label_options)
173
224
  end
174
- hidden_field(args.first,{value: "", multiple: true}).concat(html)
175
- end
176
225
 
177
- bootstrap_method_alias :collection_check_boxes
226
+ form_group_class = "form-group"
227
+ form_group_class << " row" if bootstrap.horizontal?
228
+ form_group_class << " mr-sm-2" if bootstrap.inline?
178
229
 
179
- def collection_radio_buttons_with_bootstrap(*args)
180
- inputs_collection(*args) do |name, value, options|
181
- radio_button(name, value, options)
230
+ content_tag(:div, class: form_group_class) do
231
+ content = "".html_safe
232
+ content << label if label.present?
233
+ content << draw_control_column(offset: label.blank?) do
234
+ yield
235
+ end
182
236
  end
183
237
  end
184
238
 
185
- bootstrap_method_alias :collection_radio_buttons
186
-
187
- def check_boxes_collection(*args)
188
- warn "'BootstrapForm#check_boxes_collection' is deprecated, use 'BootstrapForm#collection_check_boxes' instead"
189
- collection_check_boxes(*args)
190
- end
191
-
192
- def radio_buttons_collection(*args)
193
- warn "'BootstrapForm#radio_buttons_collection' is deprecated, use 'BootstrapForm#collection_radio_buttons' instead"
194
- collection_radio_buttons(*args)
195
- end
196
-
197
- def form_group(*args, &block)
198
- options = args.extract_options!
199
- name = args.first
200
-
201
- options[:class] = ["form-group", options[:class]].compact.join(' ')
202
- options[:class] << " row" if get_group_layout(options[:layout]) == :horizontal
203
- options[:class] << " #{error_class}" if has_error?(name)
204
- options[:class] << " #{feedback_class}" if options[:icon]
205
-
206
- content_tag(:div, options.except(:id, :label, :help, :icon, :label_col, :control_col, :layout)) do
207
- label = generate_label(options[:id], name, options[:label], options[:label_col], options[:layout]) if options[:label]
208
- control = capture(&block).to_s
209
- control.concat(generate_help(name, options[:help]).to_s)
210
- control.concat(generate_icon(options[:icon])) if options[:icon]
211
-
212
- if get_group_layout(options[:layout]) == :horizontal
213
- control_class = options[:control_col] || control_col
214
- unless options[:label]
215
- control_offset = offset_col(options[:label_col] || @label_col)
216
- control_class = "#{control_class} #{control_offset}"
217
- end
218
- control = content_tag(:div, control, class: control_class)
219
- end
239
+ private
220
240
 
221
- concat(label).concat(control)
241
+ # Wrapper for all field helpers. Example usage:
242
+ #
243
+ # bootstrap_form_with model: @user do |form|
244
+ # form.text_field :name
245
+ # end
246
+ #
247
+ # Output of the `text_field` will be wrapped in Bootstrap markup
248
+ #
249
+ def field_helper(method, options, &block)
250
+ bootstrap_options = (options.delete(:bootstrap) || {})
251
+ draw_form_group(bootstrap_options, method, options) do
252
+ yield
222
253
  end
223
254
  end
224
255
 
225
- def fields_for_with_bootstrap(record_name, record_object = nil, fields_options = {}, &block)
226
- fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
227
- fields_options[:layout] ||= options[:layout]
228
- fields_options[:label_col] = fields_options[:label_col].present? ? "#{fields_options[:label_col]} #{label_class}" : options[:label_col]
229
- fields_options[:control_col] ||= options[:control_col]
230
- fields_options[:inline_errors] ||= options[:inline_errors]
231
- fields_options[:label_errors] ||= options[:label_errors]
232
- fields_for_without_bootstrap(record_name, record_object, fields_options, &block)
233
- end
256
+ # form group wrapper for input fields
257
+ def draw_form_group(bootstrap_options, method, options, &block)
258
+ label = draw_label(bootstrap_options, method)
259
+ errors = draw_errors(method)
234
260
 
235
- bootstrap_method_alias :fields_for
261
+ control = draw_control(bootstrap_options, errors, method, options) do
262
+ yield
263
+ end
236
264
 
237
- private
265
+ form_group_class = "form-group"
266
+ form_group_class << " row" if bootstrap.horizontal?
267
+ form_group_class << " mr-sm-2" if bootstrap.inline?
238
268
 
239
- def horizontal?
240
- layout == :horizontal
269
+ content_tag(:div, class: form_group_class) do
270
+ concat label
271
+ concat control
272
+ end
241
273
  end
242
274
 
243
- def get_group_layout(group_layout)
244
- group_layout || layout
245
- end
275
+ def draw_errors(method)
276
+ return unless (errors = object && object.errors[method]).present?
246
277
 
247
- def default_label_col
248
- "col-sm-2"
278
+ content_tag(:div, class: "invalid-feedback") do
279
+ errors.join(", ")
280
+ end
249
281
  end
250
282
 
251
- def offset_col(label_col)
252
- label_col.sub(/^col-(\w+)-(\d)$/, 'col-\1-offset-\2')
253
- end
283
+ # Renders label for a given field. Takes following bootstrap options:
284
+ #
285
+ # :text - replace default label text
286
+ # :class - css class on the label
287
+ # :hide - if `true` will render for screen readers only
288
+ #
289
+ # This is how those options can be passed in:
290
+ #
291
+ # text_field(:value, bootstrap: {label: {text: "Custom", class: "custom"}})
292
+ #
293
+ def draw_label(bootstrap_options, method)
294
+ bootstrap_label_options = bootstrap_options[:label] || {}
295
+
296
+ text = nil
297
+ options = {}
298
+
299
+ if (custom_text = bootstrap_label_options[:text]).present?
300
+ text = custom_text
301
+ end
254
302
 
255
- def default_control_col
256
- "col-sm-10"
257
- end
303
+ add_css_class!(options, bootstrap_label_options[:class])
304
+ add_css_class!(options, "sr-only") if bootstrap_label_options[:hide]
305
+ add_css_class!(options, bootstrap.inline_margin_class) if bootstrap.inline?
258
306
 
259
- def hide_class
260
- "sr-only" # still accessible for screen readers
261
- end
307
+ if bootstrap.horizontal?
308
+ add_css_class!(options, "col-form-label")
309
+ add_css_class!(options, bootstrap.label_col_class)
310
+ add_css_class!(options, bootstrap.label_align_class)
311
+ end
262
312
 
263
- def control_class
264
- "form-control"
313
+ label(method, text, options)
265
314
  end
266
315
 
267
- def label_class
268
- "form-control-label"
269
- end
316
+ # Renders control for a given field
317
+ def draw_control(bootstrap_options, errors, method, options, &block)
318
+ bootstrap_label_options = bootstrap_options[:label] || {}
270
319
 
271
- def error_class
272
- "has-danger"
273
- end
320
+ add_css_class!(options, "form-control")
321
+ add_css_class!(options, "is-invalid") if errors.present?
274
322
 
275
- def feedback_class
276
- "has-feedback"
277
- end
323
+ offset = !!bootstrap_label_options[:hide]
278
324
 
279
- def control_specific_class(method)
280
- "rails-bootstrap-forms-#{method.gsub(/_/, "-")}"
325
+ draw_control_column(offset: offset) do
326
+ draw_input_group(bootstrap_options, errors) do
327
+ yield
328
+ end
329
+ end
281
330
  end
282
331
 
283
- def has_error?(name)
284
- object.respond_to?(:errors) && !(name.nil? || object.errors[name].empty?)
332
+ # Wrapping in control in column wrapper
333
+ #
334
+ def draw_control_column(offset:, &block)
335
+ return yield unless bootstrap.horizontal?
336
+ css_class = "#{bootstrap.control_col_class}"
337
+ css_class << " #{bootstrap.offset_col_class}" if offset
338
+ content_tag(:div, class: css_class) do
339
+ yield
340
+ end
285
341
  end
286
342
 
287
- def required_attribute?(obj, attribute)
288
-
289
- return false unless obj and attribute
290
-
291
- target = (obj.class == Class) ? obj : obj.class
292
-
293
- target_validators = if target.respond_to? :validators_on
294
- target.validators_on(attribute).map(&:class)
295
- else
296
- []
297
- end
298
-
299
- has_presence_validator = target_validators.include?(
300
- ActiveModel::Validations::PresenceValidator)
301
-
302
- if defined? ActiveRecord::Validations::PresenceValidator
303
- has_presence_validator |= target_validators.include?(
304
- ActiveRecord::Validations::PresenceValidator)
343
+ # Wraps input field in input group container that allows prepending and
344
+ # appending text or html. Example:
345
+ #
346
+ # text_field(:value, bootstrap: {prepend: "$.$$"}})
347
+ # text_field(:value, bootstrap: {append: {html: "<button>Go</button>"}}})
348
+ #
349
+ def draw_input_group(bootstrap_options, errors, &block)
350
+ prepend_html = draw_input_group_content(bootstrap_options, :prepend)
351
+ append_html = draw_input_group_content(bootstrap_options, :append)
352
+
353
+ help_text = draw_help(bootstrap_options[:help])
354
+
355
+ # Not prepending or appending anything. Bail.
356
+ if prepend_html.blank? && append_html.blank?
357
+ content = capture(&block)
358
+ content << errors if errors.present?
359
+ content << help_text if help_text.present?
360
+ return content
305
361
  end
306
362
 
307
- has_presence_validator
308
- end
309
-
310
- def form_group_builder(method, options, html_options = nil)
311
- options.symbolize_keys!
312
- html_options.symbolize_keys! if html_options
313
-
314
- # Add control_class; allow it to be overridden by :control_class option
315
- css_options = html_options || options
316
- control_classes = css_options.delete(:control_class) { control_class }
317
- css_options[:class] = [control_classes, css_options[:class]].compact.join(" ")
318
- css_options[:class] << " is-invalid" if has_error?(method)
319
-
320
- options = convert_form_tag_options(method, options) if acts_like_form_tag
321
-
322
- wrapper_class = css_options.delete(:wrapper_class)
323
- wrapper_options = css_options.delete(:wrapper)
324
- help = options.delete(:help)
325
- icon = options.delete(:icon)
326
- label_col = options.delete(:label_col)
327
- control_col = options.delete(:control_col)
328
- layout = get_group_layout(options.delete(:layout))
329
- form_group_options = {
330
- id: options[:id],
331
- help: help,
332
- icon: icon,
333
- label_col: label_col,
334
- control_col: control_col,
335
- layout: layout,
336
- class: wrapper_class
337
- }
338
-
339
- if wrapper_options.is_a?(Hash)
340
- form_group_options.merge!(wrapper_options)
363
+ content_tag(:div, class: "input-group") do
364
+ concat prepend_html if prepend_html.present?
365
+ concat capture(&block)
366
+ concat append_html if append_html.present?
367
+ concat errors if errors.present?
368
+ concat help_text if help_text.present?
341
369
  end
370
+ end
342
371
 
343
- unless options.delete(:skip_label)
344
- if options[:label].is_a?(Hash)
345
- label_text = options[:label].delete(:text)
346
- label_class = options[:label].delete(:class)
347
- options.delete(:label)
348
- end
349
- label_class ||= options.delete(:label_class)
350
- label_class = hide_class if options.delete(:hide_label)
372
+ def draw_input_group_content(bootstrap_options, type)
373
+ value = bootstrap_options[type]
374
+ return unless value.present?
351
375
 
352
- if options[:label].is_a?(String)
353
- label_text ||= options.delete(:label)
376
+ content_tag(:div, class: "input-group-#{type}") do
377
+ if value.is_a?(Hash) && value[:html].present?
378
+ value[:html]
379
+ else
380
+ content_tag(:span, value, class: "input-group-text")
354
381
  end
355
-
356
- form_group_options.merge!(label: {
357
- text: label_text,
358
- class: label_class,
359
- skip_required: options.delete(:skip_required)
360
- })
361
- end
362
-
363
- form_group(method, form_group_options) do
364
- yield
365
382
  end
366
383
  end
367
384
 
368
- def convert_form_tag_options(method, options = {})
369
- options[:name] ||= method
370
- options[:id] ||= method
371
- options
385
+ # Drawing boostrap form field help text. Example usage:
386
+ #
387
+ # text_field(:value, bootstrap: {help: "help text"})
388
+ #
389
+ def draw_help(text)
390
+ return if text.blank?
391
+ content_tag(:small, text, class: "form-text text-muted")
372
392
  end
373
393
 
374
- def generate_label(id, name, options, custom_label_col, group_layout)
375
- options[:for] = id if acts_like_form_tag
376
- classes = [options[:class], label_class]
377
- classes << (custom_label_col || label_col) if get_group_layout(group_layout) == :horizontal
378
- unless options.delete(:skip_required)
379
- classes << "required" if required_attribute?(object, name)
380
- end
394
+ # Rendering of choices for checkboxes and radio buttons
395
+ def draw_choices(bootstrap_options, method, collection, value_method, text_method, options, html_options, &input)
396
+ add_css_class!(html_options, "form-check-input")
381
397
 
382
- options[:class] = classes.compact.join(" ")
398
+ draw_form_group_fieldset(bootstrap_options, method, html_options) do
383
399
 
384
- if label_errors && has_error?(name)
385
- error_messages = get_error_messages(name)
386
- label_text = (options[:text] || object.class.human_attribute_name(name)).to_s.concat(" #{error_messages}")
387
- label(name, label_text, options.except(:text))
388
- else
389
- label(name, options[:text], options.except(:text))
390
- end
400
+ form_check_css_class = "form-check"
401
+ form_check_css_class << " form-check-inline" if bootstrap_options[:inline]
391
402
 
392
- end
403
+ errors = draw_errors(method)
404
+ help_text = draw_help(bootstrap_options[:help])
393
405
 
394
- def generate_help(name, help_text)
395
- if has_error?(name) && inline_errors
396
- help_text = get_error_messages(name)
397
- help_klass = 'invalid-feedback'
398
- end
399
- return if help_text == false
406
+ add_css_class!(html_options, "is-invalid") if errors.present?
400
407
 
401
- help_klass ||= 'form-text text-muted'
402
- help_text ||= get_help_text_by_i18n_key(name)
408
+ content = "".html_safe
409
+ collection.each_with_index.map do |item, index|
410
+ item_value = item.send(value_method)
411
+ item_text = item.send(text_method)
403
412
 
404
- content_tag(:span, help_text, class: help_klass) if help_text.present?
405
- end
413
+ content << content_tag(:div, class: form_check_css_class) do
414
+ concat input.call(method, item_value, html_options)
415
+ concat label(method, item_text, value: item_value, class: "form-check-label")
416
+ if ((collection.count - 1) == index) && !bootstrap_options[:inline]
417
+ concat errors if errors.present?
418
+ concat help_text if help_text.present?
419
+ end
420
+ end
421
+ end
406
422
 
407
- def generate_icon(icon)
408
- content_tag(:span, "", class: "glyphicon glyphicon-#{icon} form-control-feedback")
409
- end
423
+ if bootstrap_options[:inline]
424
+ content << errors if errors.present?
425
+ content << help_text if help_text.present?
426
+ end
410
427
 
411
- def get_error_messages(name)
412
- object.errors[name].join(", ")
428
+ content
429
+ end
413
430
  end
414
431
 
415
- def inputs_collection(name, collection, value, text, options = {}, &block)
416
- form_group_builder(name, options) do
417
- inputs = ""
432
+ # Wrapper for collections of radio buttons and checkboxes
433
+ def draw_form_group_fieldset(bootstrap_options, method, options, &block)
434
+ bootstrap_label_options = bootstrap_options[:label] || {}
418
435
 
419
- collection.each do |obj|
420
- input_options = options.merge(label: text.respond_to?(:call) ? text.call(obj) : obj.send(text))
436
+ unless bootstrap_label_options[:hide]
437
+ label_text = bootstrap_label_options.delete(:text)
438
+ label_text ||= ActionView::Helpers::Tags::Label::LabelBuilder
439
+ .new(@template, @object_name.to_s, method, @object, nil).translation
421
440
 
422
- input_value = value.respond_to?(:call) ? value.call(obj) : obj.send(value)
423
- if checked = input_options[:checked]
424
- input_options[:checked] = checked == input_value ||
425
- Array(checked).try(:include?, input_value) ||
426
- checked == obj ||
427
- Array(checked).try(:include?, obj)
428
- end
441
+ add_css_class!(bootstrap_label_options, "col-form-label pt-0")
429
442
 
430
- input_options.delete(:class)
431
- inputs << block.call(name, input_value, input_options)
443
+ if bootstrap.horizontal?
444
+ add_css_class!(bootstrap_label_options, bootstrap.label_col_class)
445
+ add_css_class!(bootstrap_label_options, bootstrap.label_align_class)
432
446
  end
433
447
 
434
- inputs.html_safe
448
+ label = content_tag(:legend, bootstrap_label_options) do
449
+ label_text
450
+ end
435
451
  end
436
- end
437
452
 
438
- def get_help_text_by_i18n_key(name)
439
- if object
453
+ content_tag(:fieldset, class: "form-group") do
454
+ content = "".html_safe
455
+ content << label if label.present?
456
+ content << draw_control_column(offset: bootstrap_label_options[:hide]) do
457
+ yield
458
+ end
440
459
 
441
- if object.class.respond_to?(:model_name)
442
- # ActiveModel::Naming 3.X.X does not support .name; it is supported as of 4.X.X
443
- partial_scope = object.class.model_name.respond_to?(:name) ? object.class.model_name.name : object.class.model_name
460
+ if bootstrap.horizontal?
461
+ content_tag(:div, content, class: "row")
444
462
  else
445
- partial_scope = object.class.name
463
+ content
446
464
  end
447
-
448
- underscored_scope = "activerecord.help.#{partial_scope.underscore}"
449
- downcased_scope = "activerecord.help.#{partial_scope.downcase}"
450
- help_text = I18n.t(name, scope: underscored_scope, default: '').presence
451
- help_text ||= if text = I18n.t(name, scope: downcased_scope, default: '').presence
452
- warn "I18n key '#{downcased_scope}.#{name}' is deprecated, use '#{underscored_scope}.#{name}' instead"
453
- text
454
- end
455
- help_text
456
465
  end
457
466
  end
458
467
 
468
+ def add_css_class!(options, string)
469
+ css_class = [options[:class], string].compact.join(" ")
470
+ options[:class] = css_class if css_class.present?
471
+ end
472
+
473
+ def remove_css_class!(options, string)
474
+ css_class = options[:class].to_s.split(" ")
475
+ options[:class] = (css_class - [string]).compact.join(" ")
476
+ options.delete(:class) if options[:class].blank?
477
+ end
478
+
459
479
  end
480
+
460
481
  end