rails_bootstrap_form 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/demo/.byebug_history +4 -0
  4. data/demo/app/controllers/users_controller.rb +70 -0
  5. data/demo/app/models/address.rb +10 -0
  6. data/demo/app/models/country.rb +17 -0
  7. data/demo/app/models/fruit.rb +14 -0
  8. data/demo/app/models/skill.rb +21 -0
  9. data/demo/app/models/user.rb +20 -0
  10. data/demo/app/models/user_skill.rb +8 -0
  11. data/demo/app/views/users/_form.html.erb +5 -0
  12. data/demo/app/views/users/_form_without_bootstrap_helpers.html.erb +49 -0
  13. data/demo/app/views/users/_vertical_form.html.erb +33 -0
  14. data/demo/app/views/users/edit.html.erb +1 -0
  15. data/demo/app/views/users/index.html.erb +42 -0
  16. data/demo/app/views/users/new.html.erb +1 -0
  17. data/demo/config/locales/en.rb +36 -3
  18. data/demo/config/routes.rb +3 -0
  19. data/demo/db/migrate/20230514054308_create_countries.rb +12 -0
  20. data/demo/db/migrate/20230514054851_create_fruits.rb +12 -0
  21. data/demo/db/migrate/20230514055237_create_users.rb +21 -0
  22. data/demo/db/migrate/20230514055840_create_addresses.rb +17 -0
  23. data/demo/db/migrate/20230514060556_create_skills.rb +12 -0
  24. data/demo/db/migrate/20230514061100_create_user_skills.rb +13 -0
  25. data/demo/db/schema.rb +69 -0
  26. data/demo/db/seeds.rb +12 -0
  27. data/lib/rails_bootstrap_form/action_view_extensions/bootstrap_form_helper.rb +54 -0
  28. data/lib/rails_bootstrap_form/bootstrap_form_builder.rb +29 -0
  29. data/lib/rails_bootstrap_form/bootstrap_form_options.rb +90 -0
  30. data/lib/rails_bootstrap_form/components/help_text.rb +53 -0
  31. data/lib/rails_bootstrap_form/components.rb +13 -0
  32. data/lib/rails_bootstrap_form/field_wrapper_builder.rb +51 -0
  33. data/lib/rails_bootstrap_form/inputs.rb +43 -0
  34. data/lib/rails_bootstrap_form/version.rb +1 -1
  35. data/lib/rails_bootstrap_form.rb +15 -0
  36. metadata +29 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2ee7dc67b6fc5b01acdcdda6bb86a228a3aaffa3279e4f13b512d806483a972
4
- data.tar.gz: 86387e4cf163ee56a24793b8ec4ac185b6faccd4862faeca3a652fd4b89144aa
3
+ metadata.gz: 1e721e3897b40c0457a2cd5cfdd2d70be38f43b1c707f103e3799ea03a9c398d
4
+ data.tar.gz: baef33d0746182ad54d2dd1b0ca0b4e827bb1ff4ad70762fca8c44d4826f94c8
5
5
  SHA512:
6
- metadata.gz: 05f729ca9e103345b2f8d0dc95f0ed9e5a9e442c690a6a1b885162d9c31ae3b77fdd321d48aa8af7b23358e4e5bce8747e005bf88f6b2988e53ce5a97f71ea80
7
- data.tar.gz: 9cb29fb1b803ca07e28c033c1ece4b3116593c4438c646aef8f4c1b2b2ba7e2c1768b27086334cea66b2acf71cd9e20ccf6a4f91548beb90e8edaafb5e2f6985
6
+ metadata.gz: 8f1916dfbb809f724be629beeff70e6e7ca287967bf769dc9d5d3501f3ee00beb641ca73966661d33d3293972090667390672a4d444c32318f5a9ca74863822a
7
+ data.tar.gz: 6357308e7ddd7df6667d9a36a96cd506039526c258438bfa19583cc898f36221ffff2f96c4dcdb06f34fb75b39c9fbebfd3a9ced43ab5257f5323b61921bd8e9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails_bootstrap_form (0.1.1)
4
+ rails_bootstrap_form (0.2.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -0,0 +1,4 @@
1
+ c
2
+ @bootstrap_form_options
3
+ c
4
+ @bootstrap_form_options
@@ -0,0 +1,70 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class UsersController < ApplicationController
6
+
7
+ before_action :find_user, except: [:index, :new, :create]
8
+
9
+ def index
10
+ @users = ::User.all
11
+ end
12
+
13
+ def new
14
+ @user = ::User.new
15
+ end
16
+
17
+ def create
18
+ @user = ::User.new(user_params)
19
+ if @user.save
20
+ redirect_to users_path
21
+ else
22
+ render :new, status: :unprocessable_entity
23
+ end
24
+ end
25
+
26
+ def edit
27
+ end
28
+
29
+ def update
30
+ if @user.update(user_params)
31
+ redirect_to users_path
32
+ else
33
+ render :edit, status: :unprocessable_entity
34
+ end
35
+ end
36
+
37
+ def destroy
38
+ @user.destroy
39
+ redirect_to users_path
40
+ end
41
+
42
+ private
43
+
44
+ def user_params
45
+ params.require(:user).permit(
46
+ :name,
47
+ :email,
48
+ :password,
49
+ :mobile_number,
50
+ :birth_date,
51
+ :terms,
52
+ :excellence,
53
+ :blog_url,
54
+ :fruit_id,
55
+ :favorite_color,
56
+ skill_ids: [],
57
+ address_attributes: [
58
+ :street,
59
+ :state,
60
+ :city,
61
+ :postal_code,
62
+ :country_id,
63
+ ]
64
+ )
65
+ end
66
+
67
+ def find_user
68
+ @user = ::User.find(params.fetch(:id))
69
+ end
70
+ end
@@ -0,0 +1,10 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class Address < ApplicationRecord
6
+ belongs_to :user
7
+ belongs_to :country
8
+
9
+ delegate :name, to: :country, prefix: true
10
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class Country < ApplicationRecord
6
+ DEFAULT_OPTIONS = [
7
+ "India",
8
+ "Ireland",
9
+ "United States",
10
+ "United Kingdom",
11
+ "Spain",
12
+ "France",
13
+ "Canada"
14
+ ].freeze
15
+
16
+ has_many :addresses, dependent: :restrict_with_exception
17
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class Fruit < ApplicationRecord
6
+ DEFAULT_OPTIONS = [
7
+ "Mango",
8
+ "Apple",
9
+ "Orange",
10
+ "Watermelon"
11
+ ].freeze
12
+
13
+ has_many :users, dependent: :restrict_with_exception
14
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class Skill < ApplicationRecord
6
+ DEFAULT_OPTIONS = [
7
+ "Communication",
8
+ "Problem Solving",
9
+ "Leadership",
10
+ "Writing",
11
+ "Creativity",
12
+ "Time Management",
13
+ "Team Work",
14
+ "Negotiation",
15
+ "Decision Making",
16
+ "Management"
17
+ ].freeze
18
+
19
+ has_many :user_skills, dependent: :restrict_with_exception
20
+ has_many :users, through: :user_skills, source: :user
21
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class User < ApplicationRecord
6
+ has_one :address, dependent: :destroy
7
+
8
+ has_many :user_skills, dependent: :destroy
9
+ has_many :skills, through: :user_skills, source: :skill
10
+
11
+ belongs_to :fruit
12
+
13
+ delegate :name, to: :fruit, prefix: true
14
+
15
+ accepts_nested_attributes_for :address, update_only: true
16
+
17
+ def address
18
+ super.presence || build_address
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class UserSkill < ApplicationRecord
6
+ belongs_to :user
7
+ belongs_to :skill
8
+ end
@@ -0,0 +1,5 @@
1
+ <% if false %>
2
+ <%= render partial: "users/form_without_bootstrap_helpers" %>
3
+ <% end %>
4
+
5
+ <%= render partial: "users/vertical_form" %>
@@ -0,0 +1,49 @@
1
+ <div class="card card-primary my-3">
2
+ <div class="card-header fw-bold">
3
+ Profile Form (Without RailsBootstrapForm)
4
+ </div>
5
+ <div class="card-body">
6
+ <%= form_for @user do |form| %>
7
+ <%= form.text_field :name, autocomplete: "new-name" %>
8
+ <%= form.email_field :email %>
9
+ <%= form.password_field :password, autocomplete: "new-password" %>
10
+ <%= form.phone_field :mobile_number %>
11
+ <%= form.date_field :birth_date %>
12
+ <%= form.check_box :terms %>
13
+ <%= form.range_field :excellence %>
14
+ <%= form.url_field :blog_url %>
15
+ <%= form.url_field :favorite_color %>
16
+ <%= form.select :fruit_id, options_for_select(::Fruit.pluck(:name, :id), form.object.fruit_id), include_blank: "Select Favorite Fruit" %>
17
+ <%= form.collection_check_boxes :skill_ids, ::Skill.all, :id, :name do |b| %>
18
+ <%= b.check_box + b.text %>
19
+ <% end %>
20
+ <%= form.fields_for :address, include_id: false do |address_form| %>
21
+ <%= address_form.text_area :street %>
22
+ <%= address_form.text_field :state %>
23
+ <%= address_form.text_field :city %>
24
+ <%= address_form.text_field :postal_code %>
25
+ <%= address_form.select :country_id, options_for_select(::Country.pluck(:name, :id), address_form.object.country_id), include_blank: "Select Country" %>
26
+ <% end %>
27
+ <div class="mt-3">
28
+ <%= form.submit "Register", class: "btn btn-primary" %>
29
+ <%= link_to "Cancel", users_path, class: "btn btn-secondary" %>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="card card-primary">
36
+ <div class="card-header fw-bold">
37
+ Login Form (Without RailsBootstrapForm)
38
+ </div>
39
+ <div class="card-body">
40
+ <%= form_for @user do |form| %>
41
+ <%= form.email_field :email %>
42
+ <%= form.password_field :password, autocomplete: "new-password" %>
43
+ <div class="mt-3">
44
+ <%= form.submit "Login", class: "btn btn-primary" %>
45
+ <%= link_to "Cancel", users_path, class: "btn btn-secondary" %>
46
+ </div>
47
+ <% end %>
48
+ </div>
49
+ </div>
@@ -0,0 +1,33 @@
1
+ <div class="card card-primary my-3">
2
+ <div class="card-header fw-bold">
3
+ Profile Form (Vertical layout)
4
+ </div>
5
+ <div class="card-body">
6
+ <%= bootstrap_form_for @user, bootstrap_form: {} do |form| %>
7
+ <%= form.text_field :name, autocomplete: "new-name", required: true, bootstrap_form: {} %>
8
+ <%= form.text_field :email, autocomplete: "new-email", bootstrap_form: {} %>
9
+ <%= form.text_field :password, autocomplete: "new-password" %>
10
+ <%= form.phone_field :mobile_number %>
11
+ <%= form.date_field :birth_date %>
12
+ <%= form.check_box :terms %>
13
+ <%= form.range_field :excellence %>
14
+ <%= form.url_field :blog_url %>
15
+ <%= form.url_field :favorite_color %>
16
+ <%= form.select :fruit_id, options_for_select(::Fruit.pluck(:name, :id), form.object.fruit_id), include_blank: "Select Favorite Fruit" %>
17
+ <%= form.collection_check_boxes :skill_ids, ::Skill.all, :id, :name do |b| %>
18
+ <%= b.check_box + b.text %>
19
+ <% end %>
20
+ <%= form.fields_for :address, include_id: false do |address_form| %>
21
+ <%= address_form.text_area :street %>
22
+ <%= address_form.text_field :state %>
23
+ <%= address_form.text_field :city %>
24
+ <%= address_form.text_field :postal_code %>
25
+ <%= address_form.select :country_id, options_for_select(::Country.pluck(:name, :id), address_form.object.country_id), {include_blank: "Select Country", bootstrap: {}} %>
26
+ <% end %>
27
+ <div class="mt-3">
28
+ <%= form.submit "Register", class: "btn btn-primary" %>
29
+ <%= link_to "Cancel", users_path, class: "btn btn-secondary" %>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
@@ -0,0 +1 @@
1
+ <%= render partial: "form" %>
@@ -0,0 +1,42 @@
1
+ <div class="card card-primary">
2
+ <div class="card-header">
3
+ <div class="text-end">
4
+ <%= link_to "New user", new_user_path, class: "btn btn-primary" %>
5
+ </div>
6
+ </div>
7
+ <div class="card-body">
8
+ <div class="table-responsive mt-3">
9
+ <table class="table table-bordered table-stripped">
10
+ <thead>
11
+ <tr>
12
+ <th scope="col">#</th>
13
+ <th scope="col">Name</th>
14
+ <th scope="col">Email</th>
15
+ <th scope="col">Mobile number</th>
16
+ <th scope="col">Favorite Fruit</th>
17
+ <th scope="col">Country</th>
18
+ <th scope="col" width="15%">Actions</th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <% @users.each.with_index(1) do |user, index| %>
23
+ <tr style="vertical-align: middle;">
24
+ <td><%= index %></td>
25
+ <td><%= user.name %></td>
26
+ <td><%= user.email %></td>
27
+ <td><%= user.mobile_number %></td>
28
+ <td><%= user.fruit_name %></td>
29
+ <td><%= user.address.country_name %></td>
30
+ <td>
31
+ <%= link_to "Edit", edit_user_path(user), class: "btn btn-sm btn-secondary" %>
32
+ <div class="d-inline-block">
33
+ <%= button_to "Delete", user, class: "btn btn-sm btn-danger", method: :delete %>
34
+ </div>
35
+ </td>
36
+ </tr>
37
+ <% end %>
38
+ </tbody>
39
+ </table>
40
+ </div>
41
+ </div>
42
+ </div>
@@ -0,0 +1 @@
1
+ <%= render partial: "form" %>
@@ -6,11 +6,44 @@
6
6
  en: {
7
7
  activerecord: {
8
8
  attributes: {
9
-
9
+ country: {
10
+ name: "Name",
11
+ },
12
+ fruit: {
13
+ name: "Name",
14
+ },
15
+ skill: {
16
+ name: "Name",
17
+ },
18
+ user: {
19
+ name: "Name",
20
+ email: "Email address",
21
+ password: "Password",
22
+ password_confirmation: "Confirm password",
23
+ mobile_number: "Mobile number",
24
+ blog_url: "Blog URL",
25
+ birth_date: "Birth date",
26
+ fruit_id: "Favorite fruit",
27
+ terms: "I accept terms and conditions",
28
+ excellence: "Excellence",
29
+ },
30
+ user_skill: {
31
+ user_id: "User",
32
+ skill_id: "Skill"
33
+ },
34
+ address: {
35
+ street: "Street",
36
+ state: "State",
37
+ city: "City",
38
+ postal_code: "Postal code",
39
+ country_id: "Country"
40
+ },
10
41
  },
11
42
  help_texts: {
12
-
43
+ user: {
44
+ email: "Please use official email address"
45
+ }
13
46
  },
14
- },
47
+ }
15
48
  }
16
49
  }
@@ -3,4 +3,7 @@
3
3
  # -*- warn_indent: true -*-
4
4
 
5
5
  Rails.application.routes.draw do
6
+ root to: "users#index"
7
+
8
+ resources :users
6
9
  end
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class CreateCountries < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table :countries do |t|
8
+ t.string :name
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class CreateFruits < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table :fruits do |t|
8
+ t.string :name
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class CreateUsers < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table :users do |t|
8
+ t.string :name
9
+ t.string :email
10
+ t.string :password
11
+ t.string :mobile_number
12
+ t.string :blog_url
13
+ t.date :birth_date
14
+ t.references :fruit
15
+ t.boolean :terms
16
+ t.string :excellence
17
+ t.string :favorite_color
18
+ t.timestamps
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class CreateAddresses < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table :addresses, id: false do |t|
8
+ t.references :user, primary_key: true
9
+ t.references :country
10
+ t.string :street
11
+ t.string :city
12
+ t.string :state
13
+ t.string :postal_code
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class CreateSkills < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table :skills do |t|
8
+ t.string :name
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ class CreateUserSkills < ActiveRecord::Migration[7.0]
6
+ def change
7
+ create_table :user_skills do |t|
8
+ t.references :user
9
+ t.references :skill
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
data/demo/db/schema.rb ADDED
@@ -0,0 +1,69 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # This file is the source Rails uses to define your schema when running `bin/rails
6
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema[7.0].define(version: 2023_05_14_061100) do
14
+ create_table "addresses", primary_key: "user_id", force: :cascade do |t|
15
+ t.integer "country_id"
16
+ t.string "street"
17
+ t.string "city"
18
+ t.string "state"
19
+ t.string "postal_code"
20
+ t.datetime "created_at", null: false
21
+ t.datetime "updated_at", null: false
22
+ t.index ["country_id"], name: "index_addresses_on_country_id"
23
+ t.index ["user_id"], name: "index_addresses_on_user_id"
24
+ end
25
+
26
+ create_table "countries", force: :cascade do |t|
27
+ t.string "name"
28
+ t.datetime "created_at", null: false
29
+ t.datetime "updated_at", null: false
30
+ end
31
+
32
+ create_table "fruits", force: :cascade do |t|
33
+ t.string "name"
34
+ t.datetime "created_at", null: false
35
+ t.datetime "updated_at", null: false
36
+ end
37
+
38
+ create_table "skills", force: :cascade do |t|
39
+ t.string "name"
40
+ t.datetime "created_at", null: false
41
+ t.datetime "updated_at", null: false
42
+ end
43
+
44
+ create_table "user_skills", force: :cascade do |t|
45
+ t.integer "user_id"
46
+ t.integer "skill_id"
47
+ t.datetime "created_at", null: false
48
+ t.datetime "updated_at", null: false
49
+ t.index ["skill_id"], name: "index_user_skills_on_skill_id"
50
+ t.index ["user_id"], name: "index_user_skills_on_user_id"
51
+ end
52
+
53
+ create_table "users", force: :cascade do |t|
54
+ t.string "name"
55
+ t.string "email"
56
+ t.string "password"
57
+ t.string "mobile_number"
58
+ t.string "blog_url"
59
+ t.date "birth_date"
60
+ t.integer "fruit_id"
61
+ t.boolean "terms"
62
+ t.string "excellence"
63
+ t.string "favorite_color"
64
+ t.datetime "created_at", null: false
65
+ t.datetime "updated_at", null: false
66
+ t.index ["fruit_id"], name: "index_users_on_fruit_id"
67
+ end
68
+
69
+ end
data/demo/db/seeds.rb CHANGED
@@ -1,3 +1,15 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # -*- frozen_string_literal: true -*-
3
3
  # -*- warn_indent: true -*-
4
+
5
+ ::Country::DEFAULT_OPTIONS.each do |country|
6
+ ::Country.find_or_create_by(name: country)
7
+ end
8
+
9
+ ::Fruit::DEFAULT_OPTIONS.each do |fruit|
10
+ ::Fruit.find_or_create_by(name: fruit)
11
+ end
12
+
13
+ ::Skill::DEFAULT_OPTIONS.each do |skill|
14
+ ::Skill.find_or_create_by(name: skill)
15
+ end
@@ -0,0 +1,54 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ module ActionViewExtensions
7
+ # This module creates `RailsBootstrapForm` wrappers around the default form_with
8
+ # and form_for methods.
9
+ #
10
+ # Example:
11
+ #
12
+ # bootstrap_form_for @user do |f|
13
+ # f.text_field :name
14
+ # end
15
+ #
16
+ # Example:
17
+ #
18
+ # bootstrap_form_with model: @user do |f|
19
+ # f.text_field :name
20
+ # end
21
+ #
22
+ module BootstrapFormHelper
23
+ def bootstrap_form_for(record, options = {}, &block)
24
+ options.reverse_merge!(builder: RailsBootstrapForm::BootstrapFormBuilder)
25
+
26
+ supress_form_field_errors do
27
+ form_for(record, options, &block)
28
+ end
29
+ end
30
+
31
+ def bootstrap_form_with(options = {}, &block)
32
+ options.reverse_merge!(builder: RailsBootstrapForm::BootstrapFormBuilder)
33
+
34
+ supress_form_field_errors do
35
+ form_with(**options, &block)
36
+ end
37
+ end
38
+
39
+ def supress_form_field_errors
40
+ original_proc = ActionView::Base.field_error_proc
41
+ ActionView::Base.field_error_proc = RailsBootstrapForm.field_error_proc
42
+ yield
43
+ ensure
44
+ ActionView::Base.field_error_proc = original_proc
45
+ end
46
+
47
+ private :supress_form_field_errors
48
+ end
49
+ end
50
+ end
51
+
52
+ ActiveSupport.on_load(:action_view) do
53
+ include RailsBootstrapForm::ActionViewExtensions::BootstrapFormHelper
54
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ class BootstrapFormBuilder < ActionView::Helpers::FormBuilder
7
+
8
+ include RailsBootstrapForm::FieldWrapperBuilder
9
+ include RailsBootstrapForm::Components
10
+ include RailsBootstrapForm::Inputs
11
+
12
+ delegate :capture, :concat, :tag, to: :@template
13
+
14
+ attr_accessor :bootstrap_form_options
15
+
16
+ def initialize(object_name, object, template, options)
17
+ @bootstrap_form_options = RailsBootstrapForm::BootstrapFormOptions.new(options.delete(:bootstrap_form))
18
+ apply_default_form_options(options)
19
+ super(object_name, object, template, options)
20
+ end
21
+
22
+ def apply_default_form_options(options)
23
+ options[:html] ||= {}
24
+ options[:html].reverse_merge!(RailsBootstrapForm.config.default_form_attributes)
25
+ end
26
+
27
+ private :apply_default_form_options
28
+ end
29
+ end
@@ -0,0 +1,90 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ # Container for bootstrap specific form builder options. It controls options
7
+ # that define form layout, grid sizing, and few other configurable options.
8
+ # They are passed-in into form builder helper and field helpers via
9
+ # `:bootstrap_form` option.
10
+ #
11
+ # For example:
12
+ #
13
+ # bootstrap_form_with model: @user, bootstrap_form: {layout: :inline} do |f|
14
+ # f.text_field :email, bootstrap_form: {label: {text: "Your email"}}
15
+ # end
16
+ #
17
+ class BootstrapFormOptions
18
+
19
+ # Controls form and field layout. It can be "vertical, "horizontal", or "inline".
20
+ # It can also be "floating" if field should have floating labels.
21
+ attr_accessor :layout
22
+
23
+ # A CSS class that will be applied to all form fields and it can be overridden
24
+ # by the `BootstrapFormOptions` object. Default is `form-control`.
25
+ attr_accessor :field_class
26
+
27
+ # An additional CSS class that will be added along with the existing
28
+ # `field_class` of the field. Default is nil.
29
+ attr_accessor :additional_field_class
30
+
31
+ # Describes help text for the HTML field. Help text is automatically read
32
+ # from translation file. If you want to customize it, you can pass string.
33
+ # You can also set it false if you do not want help text displayed.
34
+ # Default is nil.
35
+ attr_accessor :help_text
36
+
37
+ def initialize(options = {})
38
+ set_defaults
39
+ set_bootstrap_form_options(options)
40
+ end
41
+
42
+ def horizontal?
43
+ @layout.to_s == "horizontal"
44
+ end
45
+
46
+ def inline?
47
+ @layout.to_s == "inline"
48
+ end
49
+
50
+ def vertical?
51
+ @layout.to_s == "vertical"
52
+ end
53
+
54
+ def floating?
55
+ @layout.to_s == "floating"
56
+ end
57
+
58
+ # This will return a copy of `BootstrapFormOptions` object with new options set
59
+ # that don't affect original object. This way we can have options specific
60
+ # to a given form field. For example, we can change grid just for one field:
61
+ #
62
+ # bootstrap_form_with model: @user do |f|
63
+ # f.text_field :email, bootstrap_form: {label_col_class: "col-md-6", control_col_class: "col-md-6"}
64
+ # f.password_field :password
65
+ # end
66
+ #
67
+ def scoped(options = {})
68
+ scope = clone
69
+ scope.set_bootstrap_form_options(options)
70
+ scope
71
+ end
72
+
73
+ def set_bootstrap_form_options(options)
74
+ options.is_a?(Hash) && options.each do |key, value|
75
+ public_send("#{key}=", value)
76
+ end
77
+ end
78
+
79
+ def set_defaults
80
+ @layout = "vertical"
81
+
82
+ @field_class = "form-control"
83
+ @additional_field_class = nil
84
+
85
+ @help_text = nil
86
+ end
87
+
88
+ private :set_defaults
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ module Components
7
+ module HelpText
8
+ extend ActiveSupport::Concern
9
+
10
+ def self.included(base_class)
11
+ def help_text(attribute, bootstrap_options)
12
+ return if bootstrap_options.help_text == false
13
+
14
+ help_text = (bootstrap_options.help_text || scoped_help_text(attribute))
15
+
16
+ tag.span(help_text, class: "form-text text-muted") if help_text.present?
17
+ end
18
+
19
+ def object_class
20
+ if !object.class.is_a?(ActiveModel::Naming) &&
21
+ object.respond_to?(:klass) && object.klass.is_a?(ActiveModel::Naming)
22
+ object.klass
23
+ else
24
+ object.class
25
+ end
26
+ end
27
+
28
+ def partial_scope
29
+ if object_class.respond_to?(:model_name)
30
+ object_class.model_name.name
31
+ else
32
+ object_class.name
33
+ end
34
+ end
35
+
36
+ def scoped_help_text(attribute)
37
+ translation_scope = "activerecord.help_texts.#{partial_scope.underscore}"
38
+
39
+ help_text = translated_help_text(attribute, translation_scope).presence
40
+
41
+ help_text
42
+ end
43
+
44
+ def translated_help_text(attribute, scope)
45
+ ActiveSupport::SafeBuffer.new(I18n.t(attribute, scope: scope, default: ""))
46
+ end
47
+
48
+ private :help_text, :partial_scope, :object_class, :scoped_help_text
49
+ :translated_help_text
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,13 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ module Components
7
+ extend ActiveSupport::Autoload
8
+
9
+ autoload :HelpText
10
+
11
+ include HelpText
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ module FieldWrapperBuilder
7
+ def field_wrapper_builder(attribute, options, html_options = nil, &block)
8
+ bootstrap_options = bootstrap_form_options.scoped(options.delete(:bootstrap_form))
9
+
10
+ field_options = field_css_options(attribute, bootstrap_options, options, html_options.try(:symbolize_keys!))
11
+
12
+ field_wrapper(attribute, bootstrap_options, field_options, &block)
13
+ end
14
+
15
+ def field_wrapper(attribute, bootstrap_options, options, &block)
16
+ help_text = help_text(attribute, bootstrap_options)
17
+
18
+ tag.div(class: field_wrapper_classes) do
19
+ concat(capture(&block))
20
+ concat(help_text)
21
+ end
22
+ end
23
+
24
+ def field_wrapper_classes
25
+ classes = [form_wrapper_default_class]
26
+ classes.flatten.compact
27
+ end
28
+
29
+ def field_wrapper_options
30
+ end
31
+
32
+ def form_wrapper_default_class
33
+ "mb-3"
34
+ end
35
+
36
+ def field_css_options(attribute, bootstrap_options, options, html_options)
37
+ css_options = (html_options || options)
38
+
39
+ field_classes = [
40
+ bootstrap_options.field_class,
41
+ bootstrap_options.additional_field_class
42
+ ]
43
+ css_options[:class] = field_classes.flatten.compact
44
+
45
+ css_options
46
+ end
47
+
48
+ private :field_wrapper, :field_wrapper_classes, :form_wrapper_default_class,
49
+ :field_css_options
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # -*- frozen_string_literal: true -*-
3
+ # -*- warn_indent: true -*-
4
+
5
+ module RailsBootstrapForm
6
+ module Inputs
7
+
8
+ FIELD_HELPERS = %i[
9
+ text_field
10
+ url_field
11
+ search_field
12
+ telephone_field
13
+ number_field
14
+ email_field
15
+ file_field
16
+ phone_field
17
+ password_field
18
+ text_area
19
+ date_field
20
+ time_field
21
+ datetime_field
22
+ datetime_local_field
23
+ month_field
24
+ week_field
25
+ ].freeze
26
+
27
+ FIELD_HELPERS.each do |field_tag_name|
28
+ define_method(field_tag_name) do |attribute, options = {}|
29
+ field_wrapper_builder(attribute, options) do
30
+ super(attribute, options)
31
+ end
32
+ end
33
+ end
34
+
35
+ def select(attribute, choices = nil, options = {}, html_options = {}, &block)
36
+ options = options.reverse_merge(bootstrap_form: {field_class: "form-select"})
37
+
38
+ field_wrapper_builder(attribute, options, html_options) do
39
+ super(attribute, choices, options, html_options, &block)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -3,6 +3,6 @@
3
3
  # -*- warn_indent: true -*-
4
4
 
5
5
  module RailsBootstrapForm
6
- VERSION = "0.1.1".freeze
6
+ VERSION = "0.2.1".freeze
7
7
  REQUIRED_RAILS_VERSION = "~> 7.0".freeze
8
8
  end
@@ -4,17 +4,24 @@
4
4
 
5
5
  require "action_view"
6
6
  require "action_pack"
7
+ require "rails_bootstrap_form/action_view_extensions/bootstrap_form_helper"
7
8
 
8
9
  module RailsBootstrapForm
9
10
  extend ActiveSupport::Autoload
10
11
 
11
12
  eager_autoload do
12
13
  autoload :Configuration
14
+ autoload :BootstrapFormOptions
15
+ autoload :BootstrapFormBuilder
16
+ autoload :FieldWrapperBuilder
17
+ autoload :Components
18
+ autoload :Inputs
13
19
  end
14
20
 
15
21
  class << self
16
22
  def eager_load!
17
23
  super
24
+ RailsBootstrapForm::Components.eager_load!
18
25
  end
19
26
 
20
27
  def config
@@ -25,6 +32,14 @@ module RailsBootstrapForm
25
32
  yield config
26
33
  end
27
34
  end
35
+
36
+ # Override `field_error_proc` to suppress errors coming from user defined
37
+ # `field_error_proc`.
38
+ mattr_accessor :field_error_proc
39
+ @@field_error_proc = proc do |html_tag, _instance_tag|
40
+ html_tag
41
+ end
42
+
28
43
  end
29
44
 
30
45
  require "rails_bootstrap_form/engine" if defined?(Rails)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_bootstrap_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harshal LADHE (shivam091)
@@ -41,6 +41,7 @@ files:
41
41
  - README.md
42
42
  - Rakefile
43
43
  - app/assets/stylesheets/rails_bootstrap_form.css
44
+ - demo/.byebug_history
44
45
  - demo/.ruby-version
45
46
  - demo/Rakefile
46
47
  - demo/app/assets/config/manifest.js
@@ -48,11 +49,24 @@ files:
48
49
  - demo/app/channels/application_cable/channel.rb
49
50
  - demo/app/channels/application_cable/connection.rb
50
51
  - demo/app/controllers/application_controller.rb
52
+ - demo/app/controllers/users_controller.rb
51
53
  - demo/app/helpers/application_helper.rb
52
54
  - demo/app/jobs/application_job.rb
53
55
  - demo/app/mailers/application_mailer.rb
56
+ - demo/app/models/address.rb
54
57
  - demo/app/models/application_record.rb
58
+ - demo/app/models/country.rb
59
+ - demo/app/models/fruit.rb
60
+ - demo/app/models/skill.rb
61
+ - demo/app/models/user.rb
62
+ - demo/app/models/user_skill.rb
55
63
  - demo/app/views/layouts/application.html.erb
64
+ - demo/app/views/users/_form.html.erb
65
+ - demo/app/views/users/_form_without_bootstrap_helpers.html.erb
66
+ - demo/app/views/users/_vertical_form.html.erb
67
+ - demo/app/views/users/edit.html.erb
68
+ - demo/app/views/users/index.html.erb
69
+ - demo/app/views/users/new.html.erb
56
70
  - demo/bin/bundle
57
71
  - demo/bin/rails
58
72
  - demo/bin/rake
@@ -76,6 +90,13 @@ files:
76
90
  - demo/config/puma.rb
77
91
  - demo/config/routes.rb
78
92
  - demo/config/storage.yml
93
+ - demo/db/migrate/20230514054308_create_countries.rb
94
+ - demo/db/migrate/20230514054851_create_fruits.rb
95
+ - demo/db/migrate/20230514055237_create_users.rb
96
+ - demo/db/migrate/20230514055840_create_addresses.rb
97
+ - demo/db/migrate/20230514060556_create_skills.rb
98
+ - demo/db/migrate/20230514061100_create_user_skills.rb
99
+ - demo/db/schema.rb
79
100
  - demo/db/seeds.rb
80
101
  - demo/public/favicon.ico
81
102
  - gemfiles/7.0.gemfile
@@ -83,8 +104,15 @@ files:
83
104
  - lib/generators/rails_bootstrap_form/install_generator.rb
84
105
  - lib/generators/rails_bootstrap_form/templates/install.rb
85
106
  - lib/rails_bootstrap_form.rb
107
+ - lib/rails_bootstrap_form/action_view_extensions/bootstrap_form_helper.rb
108
+ - lib/rails_bootstrap_form/bootstrap_form_builder.rb
109
+ - lib/rails_bootstrap_form/bootstrap_form_options.rb
110
+ - lib/rails_bootstrap_form/components.rb
111
+ - lib/rails_bootstrap_form/components/help_text.rb
86
112
  - lib/rails_bootstrap_form/configuration.rb
87
113
  - lib/rails_bootstrap_form/engine.rb
114
+ - lib/rails_bootstrap_form/field_wrapper_builder.rb
115
+ - lib/rails_bootstrap_form/inputs.rb
88
116
  - lib/rails_bootstrap_form/version.rb
89
117
  - sig/rails_bootstrap_form.rbs
90
118
  homepage: https://github.com/shivam091/rails_bootstrap_form