template_form 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +178 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/template_form.rb +24 -0
- data/lib/template_form/base_input.rb +85 -0
- data/lib/template_form/checkbox_input.rb +7 -0
- data/lib/template_form/date_input.rb +5 -0
- data/lib/template_form/errors.rb +7 -0
- data/lib/template_form/file_input.rb +7 -0
- data/lib/template_form/form_builder.rb +59 -0
- data/lib/template_form/form_helper.rb +31 -0
- data/lib/template_form/grouped_select_input.rb +76 -0
- data/lib/template_form/select_input.rb +87 -0
- data/lib/template_form/templates/bulma/select_input.html.erb +31 -0
- data/lib/template_form/templates/bulma/text_input.html.erb +23 -0
- data/lib/template_form/templates/bulma/textarea_input.html.erb +24 -0
- data/lib/template_form/templates/bulma_horizontal/checkbox_input.html.erb +29 -0
- data/lib/template_form/templates/bulma_horizontal/date_input.html.erb +35 -0
- data/lib/template_form/templates/bulma_horizontal/grouped_select_input.html.erb +39 -0
- data/lib/template_form/templates/bulma_horizontal/select_input.html.erb +37 -0
- data/lib/template_form/templates/bulma_horizontal/text_input.html.erb +29 -0
- data/lib/template_form/templates/bulma_horizontal/textarea_input.html.erb +29 -0
- data/lib/template_form/text_input.rb +7 -0
- data/lib/template_form/textarea_input.rb +7 -0
- data/lib/template_form/version.rb +3 -0
- data/template_form.gemspec +30 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: db33072169a06cf9c413c7344703d7f0ecec5a370e837e3d3fe3b75f72af3db4
|
4
|
+
data.tar.gz: 8eab8b70bae6bf35de91d7de20400381fb10512a05e0687fca726b784e3ecbf4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4b49e409abf1686d12586aa8aa2b4f709a6feb36a888f6cd00f26d7577702a26aad076dc606ff7e4363f4e9ed3fb8fe550eb379ea72137c4ba729c5d7f794322
|
7
|
+
data.tar.gz: 438cb38f9938e9787efbc5d47da96d1aad7c554268f659c819752122cdad6ae85f2b356fcea62999b36a8e86dd6c670c872822396833bdbafdccb8b98f10fb74
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Andy Stewart
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# Template Form - a Rails form builder where you bring your own HTML
|
2
|
+
|
3
|
+
Template Form makes defining the HTML your form inputs generate as simple as possible: you just write it out. Most other form builders, e.g. Simple Form, add a layer of indirection and make you use their own DSL.
|
4
|
+
|
5
|
+
Templates are grouped so you can for example define both vertical and horizontal forms, or Bootstrap and Tailwind forms, in the same Rails app.
|
6
|
+
|
7
|
+
Right now Template Form includes templates for [Bulma](https://bulma.io/documentation/form/) but in time it will include templates for all the popular CSS frameworks.
|
8
|
+
|
9
|
+
Template Form works with all template engines supported by [Tilt](https://github.com/rtomayko/tilt).
|
10
|
+
|
11
|
+
|
12
|
+
### Installation
|
13
|
+
|
14
|
+
Add it to your Gemfile:
|
15
|
+
|
16
|
+
```
|
17
|
+
gem 'template_form'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then `bundle install`.
|
21
|
+
|
22
|
+
|
23
|
+
### Example
|
24
|
+
|
25
|
+
Let's say you use Bulma. Here's what your view might look like:
|
26
|
+
|
27
|
+
```erb
|
28
|
+
<%= template_form_with model: Post.new do |f| %>
|
29
|
+
<%= f.input :title %>
|
30
|
+
<%= f.input :body %>
|
31
|
+
<% end %>
|
32
|
+
```
|
33
|
+
|
34
|
+
This will be rendered as:
|
35
|
+
|
36
|
+
```html
|
37
|
+
<form action="/users" method="post" data-remote="true">
|
38
|
+
|
39
|
+
<div class="field">
|
40
|
+
<label class="label" for="post_title">Title</label>
|
41
|
+
<div class="control">
|
42
|
+
<input class="input" type="text" name="post[title]" id="post_title">
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div class="field">
|
47
|
+
<label class="label" for="post_body">Title</label>
|
48
|
+
<div class="control">
|
49
|
+
<textarea class="textarea" name="post[body]" id="post_body"></textarea>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
|
53
|
+
</form>
|
54
|
+
```
|
55
|
+
|
56
|
+
Now let's say you want a horizontal form instead. The only change you need to make is to specify the form type:
|
57
|
+
|
58
|
+
```diff
|
59
|
+
- <%= template_form_with model: Post.new do |f| %>
|
60
|
+
+ <%= template_form_with model: Post.new, form_type: :bulma_horizontal do |f| %>
|
61
|
+
```
|
62
|
+
|
63
|
+
And then you will get this HTML instead:
|
64
|
+
|
65
|
+
|
66
|
+
```html
|
67
|
+
<form action="/users" method="post" data-remote="true">
|
68
|
+
|
69
|
+
<div class="field is-horizontal">
|
70
|
+
<div class="field-label is-normal">
|
71
|
+
<label class="label" for="post_title">Title</label>
|
72
|
+
</div>
|
73
|
+
<div class="field-body">
|
74
|
+
<div class="field">
|
75
|
+
<div class="control">
|
76
|
+
<input class="input" type="text" name="post[title]" id="post_title">
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
|
82
|
+
<div class="field is-horizontal">
|
83
|
+
<div class="field-label is-normal">
|
84
|
+
<label class="label" for="post_body">Body</label>
|
85
|
+
</div>
|
86
|
+
<div class="field-body">
|
87
|
+
<div class="field">
|
88
|
+
<div class="control">
|
89
|
+
<textarea class="textarea" name="post[body]" id="post_body"></textarea>
|
90
|
+
</div>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
|
95
|
+
</form>
|
96
|
+
```
|
97
|
+
|
98
|
+
|
99
|
+
### Defining your templates
|
100
|
+
|
101
|
+
Your templates live at `/app/forms/<FORM TYPE>/` where `<FORM TYPE>` is a name that makes sense to you, and the value that you use with the `:form_type` option to `#template_form_with`.
|
102
|
+
|
103
|
+
They should have these names:
|
104
|
+
|
105
|
+
- `text_input.html.erb`
|
106
|
+
- `textarea_input.html.erb`
|
107
|
+
- `select_input.html.erb`
|
108
|
+
- `grouped_select_input.html.erb`
|
109
|
+
- `checkbox_input.html.erb`
|
110
|
+
- `date_input.html.erb`
|
111
|
+
|
112
|
+
Use whatever extension goes with your template engine.
|
113
|
+
|
114
|
+
Inside each template you can use the normal Rails form helpers. Here's the template for Bulma's [text input](https://github.com/airblade/template_form/blob/master/lib/template_form/templates/bulma/text_input.html.erb):
|
115
|
+
|
116
|
+
```erb
|
117
|
+
<div class="field <%= options.delete(:field_class) %>">
|
118
|
+
|
119
|
+
<% if has_label %>
|
120
|
+
<%- label_options[:class] << ' label' %>
|
121
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
122
|
+
<%= label attribute_name, label_text, label_options %>
|
123
|
+
<% end %>
|
124
|
+
|
125
|
+
<div class="control <%= options.delete(:control_class) %>">
|
126
|
+
<%- options[:class] << ' input' %>
|
127
|
+
<%- options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
128
|
+
<%= text_field attribute_name, options %>
|
129
|
+
</div>
|
130
|
+
|
131
|
+
<%- if hint_text.present? %>
|
132
|
+
<p class="help"><%= hint_text %></p>
|
133
|
+
<%- end %>
|
134
|
+
|
135
|
+
<%- if errors[attribute_name].present? %>
|
136
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
137
|
+
<%- end %>
|
138
|
+
|
139
|
+
</div>
|
140
|
+
```
|
141
|
+
|
142
|
+
The methods available inside the template are defined in the corresponding [models](https://github.com/airblade/template_form/blob/master/lib/template_form/)' `#render` method. For example, here is the [text input](https://github.com/airblade/template_form/blob/c80b445a5a50e836635fd1bdf010d32f49902604/lib/template_form/base_input.rb#L28-L42)'s:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
def render
|
146
|
+
template.render(
|
147
|
+
builder,
|
148
|
+
attribute_name: attribute_name,
|
149
|
+
|
150
|
+
has_label: has_label,
|
151
|
+
label_text: label_text,
|
152
|
+
label_options: label_options,
|
153
|
+
|
154
|
+
hint_text: hint_text,
|
155
|
+
|
156
|
+
options: options,
|
157
|
+
errors: errors
|
158
|
+
).html_safe
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
### Attribute types and form inputs
|
163
|
+
|
164
|
+
Template Form looks up the type for the attribute, falling back to `:string` if it can't find one. Then it uses the corresponding form input.
|
165
|
+
|
166
|
+
Attribute type | Form input
|
167
|
+
--|--
|
168
|
+
`:string` | `TextInput`
|
169
|
+
`:text` | `TextareaInput`
|
170
|
+
`:date` | `DateInput`
|
171
|
+
`:boolean` | `CheckboxInput`
|
172
|
+
`:file` | `FileInput`
|
173
|
+
`:select` | `SelectInput`
|
174
|
+
`:grouped_select` | `GroupedSelectInput`
|
175
|
+
|
176
|
+
You can specify a type to use with `:as`, e.g. `<%= f.input :reason, as: :text %>`.
|
177
|
+
|
178
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "template_form"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'template_form/version'
|
2
|
+
require 'template_form/form_helper'
|
3
|
+
|
4
|
+
module TemplateForm
|
5
|
+
|
6
|
+
def self.field_error_proc
|
7
|
+
noop_field_error_proc
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.form_type
|
11
|
+
@form_type || :bulma
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.form_type=(val)
|
15
|
+
@form_type = val
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.noop_field_error_proc
|
21
|
+
Proc.new { |html_tag, instance| html_tag }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module TemplateForm
|
4
|
+
class BaseInput
|
5
|
+
|
6
|
+
def initialize(builder, attribute_name, options)
|
7
|
+
@builder = builder
|
8
|
+
@attribute_name = attribute_name
|
9
|
+
|
10
|
+
@form_type = options.delete(:form_type) || builder.form_type
|
11
|
+
|
12
|
+
# Use the `:label` option to override the default label text.
|
13
|
+
# Use `label: false` to indicate no label should be shown (check `has_label` in the template).
|
14
|
+
@has_label = !(options.has_key?(:label) && options[:label] == false)
|
15
|
+
@label_text = options.delete(:label) || ''
|
16
|
+
@label_options = Hash.new { |h,k| h[k] = '' }.update(options.delete(:label_options) || {})
|
17
|
+
|
18
|
+
@hint_text = options.delete(:hint) || ''
|
19
|
+
|
20
|
+
data_attributes = (options.delete(:data) || {}).transform_keys { |k| "data-#{k}" }
|
21
|
+
|
22
|
+
@options = options
|
23
|
+
@options.merge! data_attributes
|
24
|
+
@options[:class] ||= ''
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def render
|
29
|
+
template.render(
|
30
|
+
builder,
|
31
|
+
attribute_name: attribute_name,
|
32
|
+
|
33
|
+
has_label: has_label,
|
34
|
+
label_text: label_text,
|
35
|
+
label_options: label_options,
|
36
|
+
|
37
|
+
hint_text: hint_text,
|
38
|
+
|
39
|
+
options: options,
|
40
|
+
errors: errors
|
41
|
+
).html_safe
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader *%i[
|
48
|
+
form_type
|
49
|
+
builder
|
50
|
+
attribute_name
|
51
|
+
|
52
|
+
has_label
|
53
|
+
label_text
|
54
|
+
label_options
|
55
|
+
|
56
|
+
hint_text
|
57
|
+
|
58
|
+
options
|
59
|
+
]
|
60
|
+
|
61
|
+
def errors
|
62
|
+
builder.object ? builder.object.errors : {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def template
|
66
|
+
Tilt.new template_file
|
67
|
+
end
|
68
|
+
|
69
|
+
def template_file
|
70
|
+
name = self.class.name.demodulize.underscore # TemplateForm::TextInput -> text_input
|
71
|
+
f = paths(name).map { |p| Pathname.glob(p).first }.detect &:itself
|
72
|
+
f or raise TemplateForm::MissingTemplateError, "No template found at: #{paths(name).join ', '}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def paths(name)
|
76
|
+
locations.map { |p| p / form_type.to_s / "#{name}.html.*" }
|
77
|
+
end
|
78
|
+
|
79
|
+
def locations
|
80
|
+
[ Pathname.new("#{Rails.root}/app/forms"),
|
81
|
+
Pathname.new("#{__dir__}/templates") ]
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'action_pack'
|
2
|
+
require 'action_view'
|
3
|
+
require 'template_form/errors'
|
4
|
+
require 'template_form/base_input'
|
5
|
+
require 'template_form/checkbox_input'
|
6
|
+
require 'template_form/grouped_select_input'
|
7
|
+
require 'template_form/select_input'
|
8
|
+
require 'template_form/text_input'
|
9
|
+
require 'template_form/textarea_input'
|
10
|
+
require 'template_form/date_input'
|
11
|
+
require 'template_form/file_input'
|
12
|
+
|
13
|
+
module TemplateForm
|
14
|
+
class FormBuilder < ::ActionView::Helpers::FormBuilder
|
15
|
+
|
16
|
+
attr_reader :form_type
|
17
|
+
|
18
|
+
def initialize(*)
|
19
|
+
super
|
20
|
+
# Do not remove `:form_type` from `options`. It has to stay so
|
21
|
+
# when `fields_for()` instantiates a new form builder for each
|
22
|
+
# associated record, the second and subsequent ones have the
|
23
|
+
# correct form type.
|
24
|
+
@form_type = options[:form_type] || TemplateForm.form_type
|
25
|
+
end
|
26
|
+
|
27
|
+
def input(attribute_name, options = {})
|
28
|
+
attribute_type = options.delete(:as) ||
|
29
|
+
(@object.respond_to?(:type_for_attribute) && @object.type_for_attribute(attribute_name).type) ||
|
30
|
+
:string
|
31
|
+
|
32
|
+
options[:type] ||= 'password' if attribute_name.match(/password/)
|
33
|
+
|
34
|
+
input_for(attribute_type).new(self, attribute_name, options).render
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def hidden(attribute_name, options = {})
|
39
|
+
hidden_field attribute_name, options
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def input_for(attribute_type)
|
46
|
+
case attribute_type
|
47
|
+
when :string then TextInput
|
48
|
+
when :text then TextareaInput
|
49
|
+
when :date then DateInput
|
50
|
+
when :boolean then CheckboxInput
|
51
|
+
when :file then FileInput
|
52
|
+
when :select then SelectInput
|
53
|
+
when :grouped_select then GroupedSelectInput
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'template_form/form_builder'
|
2
|
+
|
3
|
+
module TemplateForm
|
4
|
+
module FormHelper
|
5
|
+
|
6
|
+
def template_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
|
7
|
+
options[:builder] = TemplateForm::FormBuilder
|
8
|
+
|
9
|
+
with_template_form_field_error_proc do
|
10
|
+
form_with model: model, scope: scope, url: url, format: format, **options, &block
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def with_template_form_field_error_proc
|
17
|
+
default_field_error_proc = ActionView::Base.field_error_proc
|
18
|
+
begin
|
19
|
+
ActionView::Base.field_error_proc = TemplateForm.field_error_proc
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
ActionView::Base.field_error_proc = default_field_error_proc
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ActiveSupport.on_load(:action_view) do
|
30
|
+
include TemplateForm::FormHelper
|
31
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module TemplateForm
|
4
|
+
class GroupedSelectInput < BaseInput
|
5
|
+
|
6
|
+
OPTION_KEYS = %i[ include_blank prompt index disabled selected ]
|
7
|
+
|
8
|
+
def initialize(builder, attribute_name, options)
|
9
|
+
@builder = builder
|
10
|
+
@attribute_name = attribute_name
|
11
|
+
|
12
|
+
@form_type = options.delete(:form_type) || builder.form_type
|
13
|
+
|
14
|
+
@collection = options.delete(:collection).to_a
|
15
|
+
|
16
|
+
# Use the `:label` option to override the default label text.
|
17
|
+
# Use `label: false` to indicate no label should be shown (check `has_label` in the template).
|
18
|
+
@has_label = !(options.has_key?(:label) && options[:label] == false)
|
19
|
+
@label_text = options.delete(:label) || ''
|
20
|
+
@label_options = Hash.new { |h,k| h[k] = '' }.update(options.delete(:label_options) || {})
|
21
|
+
|
22
|
+
@hint_text = options.delete(:hint) || ''
|
23
|
+
|
24
|
+
@group_method = options.delete(:group_method) || :last
|
25
|
+
@group_label_method = options.delete(:group_label_method) || :first
|
26
|
+
@option_key_method = options.delete :option_key_method
|
27
|
+
@option_value_method = options.delete :option_value_method
|
28
|
+
|
29
|
+
data_attributes = (options.delete(:data) || {}).transform_keys { |k| "data-#{k}" }
|
30
|
+
|
31
|
+
@options = options.select { |k,_| OPTION_KEYS.include? k }
|
32
|
+
|
33
|
+
@html_options = options.reject { |k,_| OPTION_KEYS.include? k }
|
34
|
+
@html_options.merge! data_attributes
|
35
|
+
@html_options[:class] ||= ''
|
36
|
+
end
|
37
|
+
|
38
|
+
def render
|
39
|
+
template.render(
|
40
|
+
builder,
|
41
|
+
attribute_name: attribute_name,
|
42
|
+
collection: collection,
|
43
|
+
|
44
|
+
has_label: has_label,
|
45
|
+
label_text: label_text,
|
46
|
+
label_options: label_options,
|
47
|
+
|
48
|
+
hint_text: hint_text,
|
49
|
+
|
50
|
+
options: options,
|
51
|
+
html_options: html_options,
|
52
|
+
|
53
|
+
errors: errors,
|
54
|
+
|
55
|
+
group_method: group_method,
|
56
|
+
group_label_method: group_label_method,
|
57
|
+
option_key_method: option_key_method,
|
58
|
+
option_value_method: option_value_method
|
59
|
+
).html_safe
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
attr_reader *%i[
|
65
|
+
collection
|
66
|
+
|
67
|
+
html_options
|
68
|
+
|
69
|
+
group_method
|
70
|
+
group_label_method
|
71
|
+
option_key_method
|
72
|
+
option_value_method
|
73
|
+
]
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module TemplateForm
|
4
|
+
class SelectInput < BaseInput
|
5
|
+
|
6
|
+
OPTION_KEYS = %i[ include_blank prompt index disabled selected ]
|
7
|
+
|
8
|
+
def initialize(builder, attribute_name, options)
|
9
|
+
@builder = builder
|
10
|
+
@attribute_name = attribute_name
|
11
|
+
|
12
|
+
@form_type = options.delete(:form_type) || builder.form_type
|
13
|
+
|
14
|
+
@collection = options.delete(:collection).to_a
|
15
|
+
|
16
|
+
# Use the `:label` option to override the default label text.
|
17
|
+
# Use `label: false` to indicate no label should be shown (check `has_label` in the template).
|
18
|
+
@has_label = !(options.has_key?(:label) && options[:label] == false)
|
19
|
+
@label_text = options.delete(:label) || ''
|
20
|
+
@label_options = Hash.new { |h,k| h[k] = '' }.update(options.delete(:label_options) || {})
|
21
|
+
|
22
|
+
@hint_text = options.delete(:hint) || ''
|
23
|
+
|
24
|
+
@value_method = options.delete(:value_method) || default_value_method
|
25
|
+
@text_method = options.delete(:text_method) || default_text_method
|
26
|
+
|
27
|
+
data_attributes = (options.delete(:data) || {}).transform_keys { |k| "data-#{k}" }
|
28
|
+
|
29
|
+
@options = options.select { |k,_| OPTION_KEYS.include? k }
|
30
|
+
|
31
|
+
@html_options = options.reject { |k,_| OPTION_KEYS.include? k }
|
32
|
+
@html_options.merge! data_attributes
|
33
|
+
@html_options[:class] ||= ''
|
34
|
+
end
|
35
|
+
|
36
|
+
def render
|
37
|
+
template.render(
|
38
|
+
builder,
|
39
|
+
attribute_name: attribute_name,
|
40
|
+
collection: collection,
|
41
|
+
|
42
|
+
has_label: has_label,
|
43
|
+
label_text: label_text,
|
44
|
+
label_options: label_options,
|
45
|
+
|
46
|
+
hint_text: hint_text,
|
47
|
+
|
48
|
+
options: options,
|
49
|
+
html_options: html_options,
|
50
|
+
|
51
|
+
errors: errors,
|
52
|
+
|
53
|
+
value_method: value_method,
|
54
|
+
text_method: text_method
|
55
|
+
).html_safe
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
attr_reader *%i[
|
61
|
+
collection
|
62
|
+
|
63
|
+
html_options
|
64
|
+
|
65
|
+
value_method
|
66
|
+
text_method
|
67
|
+
]
|
68
|
+
|
69
|
+
def default_text_method
|
70
|
+
case collection.first
|
71
|
+
when String then :to_s
|
72
|
+
when Array then :first
|
73
|
+
when nil then :to_s # it won't be called because collection is empty
|
74
|
+
else raise NotImplementedError, "first collection element is a #{collection.first.class.name}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_value_method
|
79
|
+
case collection.first
|
80
|
+
when String then :to_s
|
81
|
+
when Array then :last
|
82
|
+
when nil then :to_s # it won't be called because collection is empty
|
83
|
+
else raise NotImplementedError, "first collection element is a #{collection.first.class.name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<div class="field <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<%- label_options[:class] << ' label' %>
|
5
|
+
<%- label_options[:class] << ' required' if html_options.delete(:required) %>
|
6
|
+
<%= label attribute_name, label_text, label_options %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<div class="control <%= options.delete(:control_class) %>">
|
10
|
+
<%- html_options[:class] << ' select' %>
|
11
|
+
<%- html_options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
12
|
+
<div class="<%= html_options[:class] %>">
|
13
|
+
<%= collection_select(
|
14
|
+
attribute_name,
|
15
|
+
collection,
|
16
|
+
value_method,
|
17
|
+
text_method,
|
18
|
+
options,
|
19
|
+
html_options) %>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<%- if hint_text.present? %>
|
24
|
+
<p class="help"><%= hint_text %></p>
|
25
|
+
<%- end %>
|
26
|
+
|
27
|
+
<%- if errors[attribute_name].present? %>
|
28
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
29
|
+
<%- end %>
|
30
|
+
|
31
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div class="field <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<%- label_options[:class] << ' label' %>
|
5
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
6
|
+
<%= label attribute_name, label_text, label_options %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<div class="control <%= options.delete(:control_class) %>">
|
10
|
+
<%- options[:class] << ' input' %>
|
11
|
+
<%- options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
12
|
+
<%= text_field attribute_name, options %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<%- if hint_text.present? %>
|
16
|
+
<p class="help"><%= hint_text %></p>
|
17
|
+
<%- end %>
|
18
|
+
|
19
|
+
<%- if errors[attribute_name].present? %>
|
20
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
21
|
+
<%- end %>
|
22
|
+
|
23
|
+
</div>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div class="field <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<%- label_options[:class] << ' label' %>
|
5
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
6
|
+
<%= label attribute_name, label_text, label_options %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<div class="control">
|
10
|
+
<%- options[:class] << ' textarea' %>
|
11
|
+
<%- options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
12
|
+
<%= text_area attribute_name, options %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<%- if hint_text.present? %>
|
16
|
+
<p class="help"><%= hint_text %></p>
|
17
|
+
<%- end %>
|
18
|
+
|
19
|
+
<%- if errors[attribute_name].present? %>
|
20
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
21
|
+
<%- end %>
|
22
|
+
|
23
|
+
</div>
|
24
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="field is-horizontal <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<div class="field-label">
|
5
|
+
<%- label_options[:class] << ' label' %>
|
6
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
7
|
+
<%= label attribute_name, label_text, label_options %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class="field-body">
|
12
|
+
<div class="field">
|
13
|
+
<div class="control <%= options.delete(:control_class) %>">
|
14
|
+
<label class="checkbox">
|
15
|
+
<%= check_box attribute_name, options %>
|
16
|
+
</label>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<%- if hint_text.present? %>
|
20
|
+
<p class="help"><%= hint_text %></p>
|
21
|
+
<%- end %>
|
22
|
+
|
23
|
+
<%- if errors[attribute_name].present? %>
|
24
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
25
|
+
<%- end %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
</div>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div class="field is-horizontal <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<div class="field-label <%= options.delete(:field_label_class) || 'is-normal' %>">
|
5
|
+
<%- label_options[:class] << ' label' %>
|
6
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
7
|
+
<%= label attribute_name, label_text, label_options %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class="field-body">
|
12
|
+
<%#
|
13
|
+
Use an extra nested field so the help text appears where expected
|
14
|
+
rather than as another addon.
|
15
|
+
https://github.com/jgthms/bulma/issues/640#issuecomment-415231212
|
16
|
+
%>
|
17
|
+
<div class="field">
|
18
|
+
<div class="control <%= options.delete(:control_class) %>">
|
19
|
+
<%- options[:class] << ' input' %>
|
20
|
+
<%- options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
21
|
+
<%= text_field attribute_name, options %>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<%- if hint_text.present? %>
|
25
|
+
<p class="help"><%= hint_text %></p>
|
26
|
+
<%- end %>
|
27
|
+
|
28
|
+
<%- if errors[attribute_name].present? %>
|
29
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
30
|
+
<%- end %>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
</div>
|
35
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<div class="field is-horizontal <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<div class="field-label <%= options.delete(:field_label_class) || 'is-normal' %>">
|
5
|
+
<%- label_options[:class] << ' label' %>
|
6
|
+
<%- label_options[:class] << ' required' if html_options.delete(:required) %>
|
7
|
+
<%= label attribute_name, label_text, label_options %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class="field-body">
|
12
|
+
<div class="field <%= options.delete(:field_body_field_class) %>">
|
13
|
+
<div class="control <%= options.delete(:control_class) %>">
|
14
|
+
<%- html_options[:class] << ' select' %>
|
15
|
+
<%- html_options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
16
|
+
<div class="<%= html_options[:class] %>">
|
17
|
+
<%= grouped_collection_select(
|
18
|
+
attribute_name,
|
19
|
+
collection,
|
20
|
+
group_method,
|
21
|
+
group_label_method,
|
22
|
+
option_key_method,
|
23
|
+
option_value_method,
|
24
|
+
options,
|
25
|
+
html_options) %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<%- if hint_text.present? %>
|
30
|
+
<p class="help"><%= hint_text %></p>
|
31
|
+
<%- end %>
|
32
|
+
|
33
|
+
<%- if errors[attribute_name].present? %>
|
34
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
35
|
+
<%- end %>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
</div>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<div class="field is-horizontal <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<div class="field-label <%= options.delete(:field_label_class) || 'is-normal' %>">
|
5
|
+
<%- label_options[:class] << ' label' %>
|
6
|
+
<%- label_options[:class] << ' required' if html_options.delete(:required) %>
|
7
|
+
<%= label attribute_name, label_text, label_options %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class="field-body">
|
12
|
+
<div class="field <%= options.delete(:field_body_field_class) %>">
|
13
|
+
<div class="control <%= options.delete(:control_class) %>">
|
14
|
+
<%- html_options[:class] << ' select' %>
|
15
|
+
<%- html_options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
16
|
+
<div class="<%= html_options[:class] %>">
|
17
|
+
<%= collection_select(
|
18
|
+
attribute_name,
|
19
|
+
collection,
|
20
|
+
value_method,
|
21
|
+
text_method,
|
22
|
+
options,
|
23
|
+
html_options) %>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<%- if hint_text.present? %>
|
28
|
+
<p class="help"><%= hint_text %></p>
|
29
|
+
<%- end %>
|
30
|
+
|
31
|
+
<%- if errors[attribute_name].present? %>
|
32
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
33
|
+
<%- end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
</div>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="field is-horizontal <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<div class="field-label <%= options.delete(:field_label_class) || 'is-normal' %>">
|
5
|
+
<%- label_options[:class] << ' label' %>
|
6
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
7
|
+
<%= label attribute_name, label_text, label_options %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class="field-body">
|
12
|
+
<div class="field">
|
13
|
+
<div class="control <%= options.delete(:control_class) %>">
|
14
|
+
<%- options[:class] << ' input' %>
|
15
|
+
<%- options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
16
|
+
<%= text_field attribute_name, options %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<%- if hint_text.present? %>
|
20
|
+
<p class="help"><%= hint_text %></p>
|
21
|
+
<%- end %>
|
22
|
+
|
23
|
+
<%- if errors[attribute_name].present? %>
|
24
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
25
|
+
<%- end %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
</div>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="field is-horizontal <%= options.delete(:field_class) %>">
|
2
|
+
|
3
|
+
<% if has_label %>
|
4
|
+
<div class="field-label <%= options.delete(:field_label_class) || 'is-normal' %>">
|
5
|
+
<%- label_options[:class] << ' label' %>
|
6
|
+
<%- label_options[:class] << ' required' if options.delete(:required) %>
|
7
|
+
<%= label attribute_name, label_text, label_options %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<div class="field-body">
|
12
|
+
<div class="field">
|
13
|
+
<div class="control <%= options.delete(:control_class) %>">
|
14
|
+
<%- options[:class] << ' textarea' %>
|
15
|
+
<%- options[:class] << ' is-danger' if errors[attribute_name].present? %>
|
16
|
+
<%= text_area attribute_name, options %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<%- if hint_text.present? %>
|
20
|
+
<p class="help"><%= hint_text %></p>
|
21
|
+
<%- end %>
|
22
|
+
|
23
|
+
<%- if errors[attribute_name].present? %>
|
24
|
+
<p class="help is-danger"><%= errors.full_messages_for(attribute_name).to_sentence %></p>
|
25
|
+
<%- end %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
</div>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'template_form/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "template_form"
|
8
|
+
spec.version = TemplateForm::VERSION
|
9
|
+
spec.authors = ['Andy Stewart']
|
10
|
+
spec.email = ['boss@airbladesoftware.com']
|
11
|
+
|
12
|
+
spec.summary = 'A template-based form builder for Rails.'
|
13
|
+
spec.description = 'A template-based form builder for Rails.'
|
14
|
+
spec.homepage = 'https://github.com/airblade/template_form'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency 'actionpack', '> 5.0'
|
25
|
+
spec.add_dependency 'tilt', '~> 2.0'
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: template_form
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andy Stewart
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-09-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tilt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.0'
|
83
|
+
description: A template-based form builder for Rails.
|
84
|
+
email:
|
85
|
+
- boss@airbladesoftware.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- bin/console
|
97
|
+
- bin/setup
|
98
|
+
- lib/template_form.rb
|
99
|
+
- lib/template_form/base_input.rb
|
100
|
+
- lib/template_form/checkbox_input.rb
|
101
|
+
- lib/template_form/date_input.rb
|
102
|
+
- lib/template_form/errors.rb
|
103
|
+
- lib/template_form/file_input.rb
|
104
|
+
- lib/template_form/form_builder.rb
|
105
|
+
- lib/template_form/form_helper.rb
|
106
|
+
- lib/template_form/grouped_select_input.rb
|
107
|
+
- lib/template_form/select_input.rb
|
108
|
+
- lib/template_form/templates/bulma/select_input.html.erb
|
109
|
+
- lib/template_form/templates/bulma/text_input.html.erb
|
110
|
+
- lib/template_form/templates/bulma/textarea_input.html.erb
|
111
|
+
- lib/template_form/templates/bulma_horizontal/checkbox_input.html.erb
|
112
|
+
- lib/template_form/templates/bulma_horizontal/date_input.html.erb
|
113
|
+
- lib/template_form/templates/bulma_horizontal/grouped_select_input.html.erb
|
114
|
+
- lib/template_form/templates/bulma_horizontal/select_input.html.erb
|
115
|
+
- lib/template_form/templates/bulma_horizontal/text_input.html.erb
|
116
|
+
- lib/template_form/templates/bulma_horizontal/textarea_input.html.erb
|
117
|
+
- lib/template_form/text_input.rb
|
118
|
+
- lib/template_form/textarea_input.rb
|
119
|
+
- lib/template_form/version.rb
|
120
|
+
- template_form.gemspec
|
121
|
+
homepage: https://github.com/airblade/template_form
|
122
|
+
licenses:
|
123
|
+
- MIT
|
124
|
+
metadata: {}
|
125
|
+
post_install_message:
|
126
|
+
rdoc_options: []
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
requirements: []
|
140
|
+
rubygems_version: 3.1.2
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: A template-based form builder for Rails.
|
144
|
+
test_files: []
|