formula 1.1.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Gemfile +11 -0
- data/README.md +71 -0
- data/bin/rubocop +8 -0
- data/bin/test +5 -0
- data/lib/formula/config.rb +136 -0
- data/lib/formula/form_builder.rb +351 -0
- data/lib/formula/form_helper.rb +49 -0
- data/lib/formula/railtie.rb +3 -2
- data/lib/formula/version.rb +3 -1
- data/lib/formula.rb +12 -495
- metadata +37 -17
- data/LICENSE +0 -20
- data/README.rdoc +0 -72
- data/Rakefile +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8e753004d2065d2a0aa15c73ac8393b54de9ae40086f2f5da874e0cd1a0ccd8a
|
4
|
+
data.tar.gz: 21797db2580b4ae567942e4b3c36e2c86caed7645d58ecd4ba10cecb25449ad5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59d014229aa1940f2b0d08b263d6bc49ab9caa836b4e4b993bc3c9407b2544fd789e8a36f1e324a19f467d8895b23ab3d15fa14fd3af27c77b426abe25c908b4
|
7
|
+
data.tar.gz: cd47a5b0382b1bf898a2e0691c3234504e016ab29d96496290127903ae3bafe6140cedd651e554f3f2a626ecf43293d1cb4fc318ef833e236417cca9bd11b522
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Formula
|
2
|
+
|
3
|
+
Formula is a Rails form helper that generates awesome markup. The project lets users create semantically beautiful forms without introducing too much syntax.
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
The gem is tested with:
|
8
|
+
|
9
|
+
- Ruby 3.3
|
10
|
+
- Rails 7.2
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
```bash
|
15
|
+
gem install formula
|
16
|
+
```
|
17
|
+
|
18
|
+
## Examples
|
19
|
+
|
20
|
+
```erb
|
21
|
+
<%= formula_form_for @user do |f| %>
|
22
|
+
<%= f.input :email %>
|
23
|
+
<%= f.input :password %>
|
24
|
+
<%= f.button 'Save' %>
|
25
|
+
<% end %>
|
26
|
+
```
|
27
|
+
|
28
|
+
```erb
|
29
|
+
<%= formula_form_for @user do |f| %>
|
30
|
+
<%= f.input :email, label: "Email:", hint: "We promise never to bother you." %>
|
31
|
+
<%= f.input :password, label: "Password:", hint: "Must be at least six characters." %>
|
32
|
+
<%= f.button 'Save' %>
|
33
|
+
<% end %>
|
34
|
+
```
|
35
|
+
|
36
|
+
```erb
|
37
|
+
<%= formula_form_for @company do |f|
|
38
|
+
<%= f.input :url, container: { class: 'third' }, input: { class: 'fill' } %>
|
39
|
+
<%= f.input :phone, container: { class: 'third' }, input: { class: 'fill' } %>
|
40
|
+
<%= f.input :email, container: { class: 'third' }, input: { class: 'fill' } %>
|
41
|
+
<%= f.button 'Save', button: { class: 'fancy' } %>
|
42
|
+
<% end %>
|
43
|
+
```
|
44
|
+
|
45
|
+
```erb
|
46
|
+
<%= formula_form_for @user do |f| %>
|
47
|
+
<%= f.input :email, label: "Email:" %>
|
48
|
+
<%= f.input :password, label: "Password:" %>
|
49
|
+
<%= f.input :gender, label: 'Gender:', as: :select, choices: User::GENDERS %>
|
50
|
+
<%= formula_fields_for @user.payment do |payment_f| %>
|
51
|
+
<%= payment_f.input :credit_card_number, label: 'Number:' %>
|
52
|
+
<%= payment_f.input :credit_card_expiration, label: 'Expiration:' %>
|
53
|
+
<% end %>
|
54
|
+
<%= f.button 'Save', button: { class: 'fancy' } %>
|
55
|
+
<% end %>
|
56
|
+
```
|
57
|
+
|
58
|
+
```erb
|
59
|
+
<%= formula_form_for @user do |f| %>
|
60
|
+
<%= f.block :favourite %>
|
61
|
+
<% @favourites.each do |favourite| %>
|
62
|
+
...
|
63
|
+
<% end %>
|
64
|
+
<% end %>
|
65
|
+
<%= f.button 'Save', button: { class: 'fancy' } %>
|
66
|
+
<% end %>
|
67
|
+
```
|
68
|
+
|
69
|
+
## Copyright
|
70
|
+
|
71
|
+
Copyright (c) 2010 - 2024 Kevin Sylvestre. See LICENSE for details.
|
data/bin/rubocop
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
# explicit rubocop config increases performance slightly while avoiding config confusion.
|
6
|
+
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
|
7
|
+
|
8
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/bin/test
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formula
|
4
|
+
class Config
|
5
|
+
# @!attribute [rw] box_options
|
6
|
+
# @return [Hash]
|
7
|
+
attr_accessor :box_options
|
8
|
+
|
9
|
+
# @!attribute [rw] area_options
|
10
|
+
# @return [Hash]
|
11
|
+
attr_accessor :area_options
|
12
|
+
|
13
|
+
# @!attribute [rw] file_options
|
14
|
+
# @return [Hash]
|
15
|
+
attr_accessor :file_options
|
16
|
+
|
17
|
+
# @!attribute [rw] field_options
|
18
|
+
# @return [Hash]
|
19
|
+
attr_accessor :field_options
|
20
|
+
|
21
|
+
# @!attribute [rw] select_options
|
22
|
+
# @return [Hash]
|
23
|
+
attr_accessor :select_options
|
24
|
+
|
25
|
+
# @!attribute [rw] label_options
|
26
|
+
# @return [Hash]
|
27
|
+
attr_accessor :label_options
|
28
|
+
|
29
|
+
# Default class assigned to block (<div class="block">...</div>).
|
30
|
+
#
|
31
|
+
# @!attribute [rw] block_class
|
32
|
+
# @return [String]
|
33
|
+
attr_accessor :block_class
|
34
|
+
|
35
|
+
# Default class assigned to input (<div class="input">...</div>).
|
36
|
+
#
|
37
|
+
# @!attribute [rw] input_class
|
38
|
+
# @return [String]
|
39
|
+
attr_accessor :input_class
|
40
|
+
|
41
|
+
# Default class assigned to association (<div class="association">...</div>).
|
42
|
+
#
|
43
|
+
# @!attribute [rw] association_class
|
44
|
+
# @return [String]
|
45
|
+
attr_accessor :association_class
|
46
|
+
|
47
|
+
# Default class assigned to block with errors (<div class="block errors">...</div>).
|
48
|
+
#
|
49
|
+
# @!attribute [rw] block_error_class
|
50
|
+
# @return [String]
|
51
|
+
attr_accessor :block_error_class
|
52
|
+
|
53
|
+
# Default class assigned to input with errors (<div class="input errors">...</div>).
|
54
|
+
#
|
55
|
+
# @!attribute [rw] input_error_class
|
56
|
+
# @return [String]
|
57
|
+
attr_accessor :input_error_class
|
58
|
+
|
59
|
+
# Default class assigned to input with errors (<div class="association errors">...</div>).
|
60
|
+
#
|
61
|
+
# @!attribute [rw] association_error_class
|
62
|
+
# @return [String]
|
63
|
+
attr_accessor :association_error_class
|
64
|
+
|
65
|
+
# Default class assigned to error (<div class="error">...</div>).
|
66
|
+
#
|
67
|
+
# @!attribute [rw] error_class
|
68
|
+
# @return [String]
|
69
|
+
attr_accessor :error_class
|
70
|
+
|
71
|
+
# Default class assigned to hint (<div class="hint">...</div>).
|
72
|
+
#
|
73
|
+
# @!attribute [rw] hint_class
|
74
|
+
# @return [String]
|
75
|
+
attr_accessor :hint_class
|
76
|
+
|
77
|
+
# Default tag assigned to block (<div class="input">...</div>).
|
78
|
+
#
|
79
|
+
# @!attribute [rw] block_tag
|
80
|
+
# @return [Symbol]
|
81
|
+
attr_accessor :block_tag
|
82
|
+
|
83
|
+
# Default tag assigned to input (<div class="input">...</div>).
|
84
|
+
#
|
85
|
+
# @!attribute [rw] input_tag
|
86
|
+
# @return [Symbol]
|
87
|
+
attr_accessor :input_tag
|
88
|
+
|
89
|
+
# Default tag assigned to association (<div class="association">...</div>).
|
90
|
+
#
|
91
|
+
# @!attribute [rw] association_tag
|
92
|
+
# @return [Symbol]
|
93
|
+
attr_accessor :association_tag
|
94
|
+
|
95
|
+
# Default tag assigned to error (<div class="error">...</div>).
|
96
|
+
#
|
97
|
+
# @!attribute [rw] error_tag
|
98
|
+
# @return [Symbol]
|
99
|
+
attr_accessor :error_tag
|
100
|
+
|
101
|
+
# Default tag assigned to hint (<div class="hint">...</div>).
|
102
|
+
#
|
103
|
+
# @!attribute [rw] hint_tag
|
104
|
+
# @return [Symbol]
|
105
|
+
attr_accessor :hint_tag
|
106
|
+
|
107
|
+
# Default as is :string.
|
108
|
+
#
|
109
|
+
# @!attribute [rw] default_as
|
110
|
+
# @return [Symbol]
|
111
|
+
attr_accessor :default_as
|
112
|
+
|
113
|
+
def initialize
|
114
|
+
@box_options = {}
|
115
|
+
@area_options = {}
|
116
|
+
@file_options = {}
|
117
|
+
@field_options = {}
|
118
|
+
@select_options = {}
|
119
|
+
@label_options = {}
|
120
|
+
@block_class = 'block'
|
121
|
+
@input_class = 'input'
|
122
|
+
@association_class = 'association'
|
123
|
+
@block_error_class = 'errors'
|
124
|
+
@input_error_class = false
|
125
|
+
@association_error_class = false
|
126
|
+
@error_class = 'error'
|
127
|
+
@hint_class = 'hint'
|
128
|
+
@block_tag = :div
|
129
|
+
@input_tag = :div
|
130
|
+
@association_tag = :div
|
131
|
+
@error_tag = :div
|
132
|
+
@hint_tag = :div
|
133
|
+
@default_as = :string
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formula
|
4
|
+
class FormBuilder < ::ActionView::Helpers::FormBuilder
|
5
|
+
# Generate a form button.
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
#
|
9
|
+
# * :container - add custom options to the container
|
10
|
+
# * :button - add custom options to the button
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
#
|
14
|
+
# f.button(:name)
|
15
|
+
#
|
16
|
+
# Equivalent:
|
17
|
+
#
|
18
|
+
# <div class="block">
|
19
|
+
# <%= f.submit("Save")
|
20
|
+
# </div>
|
21
|
+
def button(value = nil, options = {})
|
22
|
+
options[:button] ||= {}
|
23
|
+
|
24
|
+
options[:container] ||= {}
|
25
|
+
options[:container][:class] = arrayorize(options[:container][:class]) << config.block_class
|
26
|
+
|
27
|
+
@template.content_tag(config.block_tag, options[:container]) do
|
28
|
+
submit value, options[:button]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Basic container generator for use with blocks.
|
33
|
+
#
|
34
|
+
# Options:
|
35
|
+
#
|
36
|
+
# * :hint - specify a hint to be displayed ('We promise not to spam you.', etc.)
|
37
|
+
# * :label - override the default label used ('Name:', 'URL:', etc.)
|
38
|
+
# * :error - override the default error used ('invalid', 'incorrect', etc.)
|
39
|
+
#
|
40
|
+
# Usage:
|
41
|
+
#
|
42
|
+
# f.block(:name, :label => "Name:", :hint => "Please use your full name.", :container => { :class => 'fill' }) do
|
43
|
+
# ...
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# Equivalent:
|
47
|
+
#
|
48
|
+
# <div class='block fill'>
|
49
|
+
# <%= f.label(:name, "Name:") %>
|
50
|
+
# ...
|
51
|
+
# <div class="hint">Please use your full name.</div>
|
52
|
+
# <div class="error">...</div>
|
53
|
+
# </div>
|
54
|
+
def block(method = nil, options = {}, &block)
|
55
|
+
options[:error] ||= error(method) if method
|
56
|
+
|
57
|
+
components = ''.html_safe
|
58
|
+
|
59
|
+
components << label(method, options[:label], config.label_options) if method
|
60
|
+
|
61
|
+
components << @template.capture(&block)
|
62
|
+
|
63
|
+
options[:container] ||= {}
|
64
|
+
options[:container][:class] = arrayorize(options[:container][:class]) << config.block_class << method
|
65
|
+
options[:container][:class] << config.block_error_class if config.block_error_class.present? and error?(method)
|
66
|
+
|
67
|
+
if options[:hint]
|
68
|
+
components << @template.content_tag(config.hint_tag, options[:hint],
|
69
|
+
class: config.hint_class)
|
70
|
+
end
|
71
|
+
if options[:error]
|
72
|
+
components << @template.content_tag(config.error_tag, options[:error],
|
73
|
+
class: config.error_class)
|
74
|
+
end
|
75
|
+
|
76
|
+
@template.content_tag(config.block_tag, options[:container]) do
|
77
|
+
components
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Generate a suitable form input for a given method by performing introspection on the type.
|
82
|
+
#
|
83
|
+
# Options:
|
84
|
+
#
|
85
|
+
# * :as - override the default type used (:url, :email, :phone, :password, :number, :text)
|
86
|
+
# * :label - override the default label used ('Name:', 'URL:', etc.)
|
87
|
+
# * :error - override the default error used ('invalid', 'incorrect', etc.)
|
88
|
+
# * :input - add custom options to the input ({ :class => 'goregous' }, etc.)
|
89
|
+
# * :container - add custom options to the container ({ :class => 'gorgeous' }, etc.)
|
90
|
+
#
|
91
|
+
# Usage:
|
92
|
+
#
|
93
|
+
# f.input(:name)
|
94
|
+
# f.input(:email)
|
95
|
+
# f.input(:password_a, :label => "Password", :hint => "It's a secret!", :container => { :class => "half" })
|
96
|
+
# f.input(:password_b, :label => "Password", :hint => "It's a secret!", :container => { :class => "half" })
|
97
|
+
#
|
98
|
+
# Equivalent:
|
99
|
+
#
|
100
|
+
# <div class="block name">
|
101
|
+
# <%= f.label(:name)
|
102
|
+
# <div class="input string"><%= f.text_field(:name)</div>
|
103
|
+
# <div class="error">...</div>
|
104
|
+
# </div>
|
105
|
+
# <div class="block email">
|
106
|
+
# <%= f.label(:email)
|
107
|
+
# <div class="input string"><%= f.email_field(:email)</div>
|
108
|
+
# <div class="error">...</div>
|
109
|
+
# </div>
|
110
|
+
# <div class="block half password_a">
|
111
|
+
# <div class="input">
|
112
|
+
# <%= f.label(:password_a, "Password")
|
113
|
+
# <%= f.password_field(:password_a)
|
114
|
+
# <div class="hint">It's a secret!</div>
|
115
|
+
# <div class="error">...</div>
|
116
|
+
# </div>
|
117
|
+
# </div>
|
118
|
+
# <div class="block half password_b">
|
119
|
+
# <div class="input">
|
120
|
+
# <%= f.label(:password_b, "Password")
|
121
|
+
# <%= f.password_field(:password_b)
|
122
|
+
# <div class="hint">It's a secret!</div>
|
123
|
+
# <div class="error">...</div>
|
124
|
+
# </div>
|
125
|
+
# </div>
|
126
|
+
def input(method, options = {})
|
127
|
+
options[:as] ||= as(method)
|
128
|
+
options[:input] ||= {}
|
129
|
+
|
130
|
+
return hidden_field method, options[:input] if options[:as] == :hidden
|
131
|
+
|
132
|
+
klass = [config.input_class, options[:as]]
|
133
|
+
klass << config.input_error_class if config.input_error_class.present? and error?(method)
|
134
|
+
|
135
|
+
block(method, options) do
|
136
|
+
@template.content_tag(config.input_tag, class: klass) do
|
137
|
+
case options[:as]
|
138
|
+
when :text then text_area method, config.area_options.merge(options[:input] || {})
|
139
|
+
when :file then file_field method, config.file_options.merge(options[:input] || {})
|
140
|
+
when :string then text_field method, config.field_options.merge(options[:input] || {})
|
141
|
+
when :password then password_field method, config.field_options.merge(options[:input] || {})
|
142
|
+
when :url then url_field method, config.field_options.merge(options[:input] || {})
|
143
|
+
when :email then email_field method, config.field_options.merge(options[:input] || {})
|
144
|
+
when :phone then phone_field method, config.field_options.merge(options[:input] || {})
|
145
|
+
when :number then number_field method, config.field_options.merge(options[:input] || {})
|
146
|
+
when :boolean then check_box method, config.box_options.merge(options[:input] || {})
|
147
|
+
when :country then country_select method, config.select_options.merge(options[:input] || {})
|
148
|
+
when :date then date_select method, config.select_options.merge(options[:input] || {}),
|
149
|
+
options[:input].delete(:html) || {}
|
150
|
+
when :time then time_select method, config.select_options.merge(options[:input] || {}),
|
151
|
+
options[:input].delete(:html) || {}
|
152
|
+
when :datetime then datetime_select method, config.select_options.merge(options[:input] || {}),
|
153
|
+
options[:input].delete(:html) || {}
|
154
|
+
when :select then select method, options[:choices],
|
155
|
+
config.select_options.merge(options[:input] || {}), options[:input].delete(:html) || {}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Generate a suitable form association for a given method by performing introspection on the type.
|
162
|
+
#
|
163
|
+
# Options:
|
164
|
+
#
|
165
|
+
# * :label - override the default label used ('Name:', 'URL:', etc.)
|
166
|
+
# * :error - override the default error used ('invalid', 'incorrect', etc.)
|
167
|
+
# * :association - add custom options to the input ({ :class => 'goregous' }, etc.)
|
168
|
+
# * :container - add custom options to the container ({ :class => 'gorgeous' }, etc.)
|
169
|
+
#
|
170
|
+
# Usage:
|
171
|
+
#
|
172
|
+
# f.association(:category, Category.all, :id, :name, :hint => "What do you do?")
|
173
|
+
# f.association(:category, Category.all, :id, :name, :association => { :prompt => "Category?" })
|
174
|
+
# f.association(:category, Category.all, :id, :name, :association => { :html => { :class => "category" } })
|
175
|
+
#
|
176
|
+
# Equivalent:
|
177
|
+
#
|
178
|
+
# <div>
|
179
|
+
# <div class="association category">
|
180
|
+
# <%= f.label(:category)
|
181
|
+
# <div class="association">
|
182
|
+
# <%= f.collection_select(:category, Category.all, :id, :name) %>
|
183
|
+
# </div>
|
184
|
+
# <div class="hint">What do you do?</div>
|
185
|
+
# <div class="error">...</div>
|
186
|
+
# </div>
|
187
|
+
# </div>
|
188
|
+
# <div>
|
189
|
+
# <div class="association category">
|
190
|
+
# <%= f.label(:category)
|
191
|
+
# <div class="association">
|
192
|
+
# <%= f.collection_select(:category, Category.all, :id, :name, { :prompt => "Category") } %>
|
193
|
+
# </div>
|
194
|
+
# <div class="error">...</div>
|
195
|
+
# </div>
|
196
|
+
# </div>
|
197
|
+
# <div>
|
198
|
+
# <div class="association category">
|
199
|
+
# <%= f.label(:category)
|
200
|
+
# <div class="association">
|
201
|
+
# <%= f.collection_select(:category, Category.all, :id, :name, {}, { :class => "category" } %>
|
202
|
+
# </div>
|
203
|
+
# <div class="error">...</div>
|
204
|
+
# </div>
|
205
|
+
# </div>
|
206
|
+
def association(method, collection, value, text, options = {})
|
207
|
+
options[:as] ||= :select
|
208
|
+
options[:association] ||= {}
|
209
|
+
|
210
|
+
klass = [config.association_class, options[:as]]
|
211
|
+
klass << config.association_error_class if config.association_error_class.present? and error?(method)
|
212
|
+
|
213
|
+
block(method, options) do
|
214
|
+
@template.content_tag(config.association_tag, class: klass) do
|
215
|
+
case options[:as]
|
216
|
+
when :select then collection_select :"#{method}_id", collection, value, text,
|
217
|
+
options[:association], options[:association].delete(:html) || {}
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def config
|
226
|
+
::Formula.config
|
227
|
+
end
|
228
|
+
|
229
|
+
# Introspection on the column to determine how to render a method. The method is used to
|
230
|
+
# identify a method type (if the method corresponds to a column)
|
231
|
+
#
|
232
|
+
# Returns:
|
233
|
+
#
|
234
|
+
# * :text - for columns of type 'text'
|
235
|
+
# * :string - for columns of type 'string'
|
236
|
+
# * :integer - for columns of type 'integer'
|
237
|
+
# * :float - for columns of type 'float'
|
238
|
+
# * :decimal - for columns of type 'decimal'
|
239
|
+
# * :datetime - for columns of type 'datetime'
|
240
|
+
# * :date - for columns of type 'date'
|
241
|
+
# * :time - for columns of type 'time'
|
242
|
+
# * nil - for unkown columns
|
243
|
+
def type(method)
|
244
|
+
return unless @object.respond_to?(:has_attribute?) && @object.has_attribute?(method)
|
245
|
+
|
246
|
+
column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
|
247
|
+
column.type if column
|
248
|
+
end
|
249
|
+
|
250
|
+
# Introspection on an association to determine if a method is a file. This
|
251
|
+
# is determined by the methods ability to respond to file methods.
|
252
|
+
def file?(method)
|
253
|
+
@files ||= {}
|
254
|
+
@files[method] ||= begin
|
255
|
+
file = @object.send(method) if @object && @object.respond_to?(method)
|
256
|
+
file.is_a?(::ActiveStorage::Attached)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Introspection on the field and method to determine how to render a method. The method is
|
261
|
+
# used to generate form element types.
|
262
|
+
#
|
263
|
+
# Returns:
|
264
|
+
#
|
265
|
+
# * :url - for columns containing 'url'
|
266
|
+
# * :email - for columns containing 'email'
|
267
|
+
# * :phone - for columns containing 'phone'
|
268
|
+
# * :password - for columns containing 'password'
|
269
|
+
# * :number - for integer, float or decimal columns
|
270
|
+
# * :datetime - for datetime or timestamp columns
|
271
|
+
# * :date - for date column
|
272
|
+
# * :time - for time column
|
273
|
+
# * :text - for time column
|
274
|
+
# * :string - for all other cases
|
275
|
+
def as(method)
|
276
|
+
case "#{method}"
|
277
|
+
when /url/ then return :url
|
278
|
+
when /email/ then return :email
|
279
|
+
when /phone/ then return :phone
|
280
|
+
when /password/ then return :password
|
281
|
+
end
|
282
|
+
|
283
|
+
case type(method)
|
284
|
+
when :string then return :string
|
285
|
+
when :integer then return :number
|
286
|
+
when :float then return :number
|
287
|
+
when :decimal then return :number
|
288
|
+
when :timestamp then return :datetime
|
289
|
+
when :datetime then return :datetime
|
290
|
+
when :date then return :date
|
291
|
+
when :time then return :time
|
292
|
+
when :text then return :text
|
293
|
+
end
|
294
|
+
|
295
|
+
return :file if file?(method)
|
296
|
+
|
297
|
+
config.default_as
|
298
|
+
end
|
299
|
+
|
300
|
+
# Generate error messages by combining all errors on an object into a comma seperated string
|
301
|
+
# representation.
|
302
|
+
def error(method)
|
303
|
+
errors = @object.errors[method] if @object
|
304
|
+
errors.to_sentence if errors.present?
|
305
|
+
end
|
306
|
+
|
307
|
+
# Identify if error message exists for a given method by checking for the presence of the object
|
308
|
+
# followed by the presence of errors.
|
309
|
+
def error?(method)
|
310
|
+
@object.present? and @object.errors[method].present?
|
311
|
+
end
|
312
|
+
|
313
|
+
# Create an array from a string, a symbol, or an undefined value. The default is to return
|
314
|
+
# the value and assume it has already is valid.
|
315
|
+
def arrayorize(value)
|
316
|
+
case value
|
317
|
+
when nil then []
|
318
|
+
when String then value.to_s.split
|
319
|
+
when Symbol then value.to_s.split
|
320
|
+
else value
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
public
|
325
|
+
|
326
|
+
# Generates a wrapper around fields_form with :builder set to FormBuilder.
|
327
|
+
#
|
328
|
+
# Supports:
|
329
|
+
#
|
330
|
+
# * f.formula_fields_for(@user.company)
|
331
|
+
# * f.fieldsula_for(@user.company)
|
332
|
+
#
|
333
|
+
# Equivalent:
|
334
|
+
#
|
335
|
+
# * f.fields_for(@user.company, builder: Formula::FormBuilder))
|
336
|
+
#
|
337
|
+
# Usage:
|
338
|
+
#
|
339
|
+
# <% f.formula_fields_for(@user.company) do |company_f| %>
|
340
|
+
# <%= company_f.input :url %>
|
341
|
+
# <%= company_f.input :phone %>
|
342
|
+
# <% end %>
|
343
|
+
def formula_fields_for(record_or_name_or_array, *args, &block)
|
344
|
+
options = args.extract_options!
|
345
|
+
options[:builder] ||= self.class
|
346
|
+
fields_for(record_or_name_or_array, *(args << options), &block)
|
347
|
+
end
|
348
|
+
|
349
|
+
alias fieldsula_for formula_fields_for
|
350
|
+
end
|
351
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formula
|
4
|
+
module FormHelper
|
5
|
+
# Generates a wrapper around form_for with :builder set to Formula::FormBuilder.
|
6
|
+
#
|
7
|
+
# Supports:
|
8
|
+
#
|
9
|
+
# * formula_form_for(@user)
|
10
|
+
#
|
11
|
+
# Equivalent:
|
12
|
+
#
|
13
|
+
# * form_for(@user, builder: Formula::FormBuilder))
|
14
|
+
#
|
15
|
+
# Usage:
|
16
|
+
#
|
17
|
+
# <% formula_form_for(@user) do |f| %>
|
18
|
+
# <%= f.input :email %>
|
19
|
+
# <%= f.input :password %>
|
20
|
+
# <% end %>
|
21
|
+
def formula_form_for(record_or_name_or_array, *args, &proc)
|
22
|
+
options = args.extract_options!
|
23
|
+
options[:builder] ||= ::Formula::FormBuilder
|
24
|
+
form_for(record_or_name_or_array, *(args << options), &proc)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generates a wrapper around fields_for with :builder set to Formula::FormBuilder.
|
28
|
+
#
|
29
|
+
# Supports:
|
30
|
+
#
|
31
|
+
# * f.formula_fields_for(@user.company)
|
32
|
+
#
|
33
|
+
# Equivalent:
|
34
|
+
#
|
35
|
+
# * f.fields_for(@user.company, builder: Formula::FormulaFormBuilder))
|
36
|
+
#
|
37
|
+
# Usage:
|
38
|
+
#
|
39
|
+
# <% f.formula_fields_for(@user.company) do |company_f| %>
|
40
|
+
# <%= company_f.input :url %>
|
41
|
+
# <%= company_f.input :phone %>
|
42
|
+
# <% end %>
|
43
|
+
def formula_fields_for(record_or_name_or_array, *args, &block)
|
44
|
+
options = args.extract_options!
|
45
|
+
options[:builder] ||= ::Formula::FormBuilder
|
46
|
+
fields_for(record_or_name_or_array, *(args << options), &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/formula/railtie.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'rails'
|
3
4
|
|
4
5
|
module Formula
|
5
6
|
class Railtie < Rails::Railtie
|
6
7
|
initializer 'formula.initialize' do
|
7
|
-
ActionView::Base.
|
8
|
+
ActionView::Base.include Formula::FormHelper
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
data/lib/formula/version.rb
CHANGED