view_partial_form_builder 0.1.0 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +120 -37
- data/lib/view_partial_form_builder/form_builder.rb +81 -30
- data/lib/view_partial_form_builder/html_attributes.rb +8 -0
- data/lib/view_partial_form_builder/{lookup_context.rb → lookup_override.rb} +9 -22
- data/lib/view_partial_form_builder/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d1550e43fb981483d25dedf89ab64478b5dd2c5803b4aa7345ae6d7efba4fe2
|
4
|
+
data.tar.gz: d8bb992d8d959d2288c761d764275af9e470081145884b6bfcff7b7dda3fdc45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 121978b8280e71e3cf114291cf4874949c9323f7b909ab7cd93602f1a3fb00b143c2028ed1188623c8b926efe8b3a27e67005947599d66b8e1cb33c98d8c589e
|
7
|
+
data.tar.gz: 9e56d40123f55fac7ee53b3bd3e087ccc35bdf3ca709485cad0d636fb41f7f684744dbc068d7af13bcdc6aa246e9707373c557f792d86a1d3fee1cd76468e4f0
|
data/README.md
CHANGED
@@ -87,7 +87,7 @@ generating HTML through Rails' helpers:
|
|
87
87
|
<%# app/views/form_builder/_button.html.erb %>
|
88
88
|
|
89
89
|
<div class="button-wrapper">
|
90
|
-
<%= form.button(
|
90
|
+
<%= form.button(value, options, &block) %>
|
91
91
|
</div>
|
92
92
|
```
|
93
93
|
|
@@ -116,31 +116,8 @@ In addition, each view partial receives:
|
|
116
116
|
* `form` - a reference to the instance of `ViewPartialFormBuilder`, which is a
|
117
117
|
descendant of [`ActionView::Helpers::FormBuilder`][FormBuilder]
|
118
118
|
|
119
|
-
* `arguments` - an Array containing the arguments the helper received, in the
|
120
|
-
order they were received. This can be useful to pass to the view partial's
|
121
|
-
helper by [splatting them][splat] out.
|
122
|
-
|
123
119
|
* `&block` - a callable, `yield`-able block if the helper method was passed one
|
124
120
|
|
125
|
-
In cases when a [`ActionView::Helpers::FormBuilder` helper
|
126
|
-
method][FormBuilder]'s last arguments are options (either `Hash` instances or
|
127
|
-
[keyword arguments][]), they're omitted from the `arguments` array.
|
128
|
-
|
129
|
-
If you want to pass-through all arguments, options, and block parameters, you
|
130
|
-
can splat them out:
|
131
|
-
|
132
|
-
```html+erb
|
133
|
-
<%# app/views/form_builder/_label.html.erb %>
|
134
|
-
|
135
|
-
<%= form.label(*arguments, **options, &block) %>
|
136
|
-
```
|
137
|
-
|
138
|
-
```html+erb
|
139
|
-
<%# app/views/form_builder/_select.html.erb %>
|
140
|
-
|
141
|
-
<%= form.select(*arguments, **html_options, &block) %>
|
142
|
-
```
|
143
|
-
|
144
121
|
#### Handling DOMTokenList attributes
|
145
122
|
|
146
123
|
An [HTML element's `class` attribute][mdn-class] is treated by browsers as a
|
@@ -158,16 +135,12 @@ When rendering a field's DOMTokenList-backed attributes (like `class` or
|
|
158
135
|
controllers][stimulus-controller]), transforming and combining singular `String`
|
159
136
|
instances into lists of token can be very useful.
|
160
137
|
|
161
|
-
To simplify those scenarios, a partial's template-local optional attributes are
|
162
|
-
made available with the `#merge_token_lists` method.
|
163
|
-
|
164
138
|
These optional attributes are available through the `options` or `html_options`
|
165
139
|
partial-local variables. Their name will depend on the partial's corresponding
|
166
140
|
[`ActionView::Helpers::FormBuilder`][FormBuilder] interface.
|
167
141
|
|
168
|
-
|
169
|
-
|
170
|
-
Given call to `form.text_field` and a corresponding partial declaration:
|
142
|
+
To "merge" attributes together, you can combine Ruby's `String` interpolation
|
143
|
+
and `Hash#delete`:
|
171
144
|
|
172
145
|
```html+erb
|
173
146
|
<%# app/views/users/new.html.erb %>
|
@@ -177,7 +150,11 @@ Given call to `form.text_field` and a corresponding partial declaration:
|
|
177
150
|
|
178
151
|
<# app/views/form_builder/_text_field.html.erb %>
|
179
152
|
|
180
|
-
<%= form.text_field(
|
153
|
+
<%= form.text_field(
|
154
|
+
method,
|
155
|
+
class: "text-field #{options.delete(:class)}",
|
156
|
+
**options
|
157
|
+
) %>
|
181
158
|
```
|
182
159
|
|
183
160
|
The resulting HTML `<input>` element will merge have its [`class`
|
@@ -192,7 +169,6 @@ values:
|
|
192
169
|
[button]: https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-button
|
193
170
|
[local_assigns]: https://api.rubyonrails.org/classes/ActionView/Template.html#method-i-local_assigns
|
194
171
|
[splat]: https://ruby-doc.org/core-2.2.0/doc/syntax/calling_methods_rdoc.html#label-Array+to+Arguments+Conversion
|
195
|
-
[keyword arguments]: https://thoughtbot.com/blog/ruby-2-keyword-arguments
|
196
172
|
[mdn-class]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class
|
197
173
|
[DOMTokenList]: https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList
|
198
174
|
[classList]: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
|
@@ -210,7 +186,7 @@ block-local `form` variable:
|
|
210
186
|
```erb
|
211
187
|
<%# app/views/users/form_builder/_email_field.html.erb %>
|
212
188
|
|
213
|
-
<%= form.default.email_field(
|
189
|
+
<%= form.default.email_field(method, options) %>
|
214
190
|
```
|
215
191
|
|
216
192
|
When passing a `model:` or `scope:` to calls to [`form_with`][form_with],
|
@@ -225,21 +201,128 @@ For example, when calling `form_with(model: User.new)`, a partial declared in
|
|
225
201
|
<%# app/views/users/form_builder/_password_field.html.erb %>
|
226
202
|
|
227
203
|
<div class="password-field-wrapper">
|
228
|
-
<%= form.password_field(
|
204
|
+
<%= form.password_field(method, options) %>
|
229
205
|
</div>
|
230
206
|
```
|
231
207
|
|
232
|
-
If you'd like to render a specific partial for a field,
|
233
|
-
|
208
|
+
If you'd like to render a specific partial for a field, make sure that you pass
|
209
|
+
along the `form:` (along with any other partial-local variables) as part of the
|
210
|
+
`render` call's `locals:` option:
|
211
|
+
|
234
212
|
|
235
213
|
```erb
|
236
214
|
<%# app/views/users/new.html.erb %>
|
237
215
|
|
238
216
|
<%= form_with(model: User.new) do |form| %>
|
239
|
-
<%=
|
217
|
+
<%= render("emails/my_special_email_field", {
|
218
|
+
form: form,
|
219
|
+
method: :email,
|
220
|
+
options: { class: "user-email" },
|
221
|
+
) %>
|
240
222
|
<% end %>
|
223
|
+
|
224
|
+
<%# app/views/emails/_my_special_email_field.html.erb %>
|
225
|
+
|
226
|
+
<%= form.email_field(
|
227
|
+
method,
|
228
|
+
class: "my-special-email #{options.delete(:class)},
|
229
|
+
**options
|
230
|
+
) %>
|
241
231
|
```
|
242
232
|
|
233
|
+
#### Composing partials
|
234
|
+
|
235
|
+
Layering partials on top of one another can be useful to share foundational
|
236
|
+
styles and configuration across your fields. For instance, consider an
|
237
|
+
administrative interface that shares styles with a consumer facing site, but has
|
238
|
+
additional bells and whistles.
|
239
|
+
|
240
|
+
Declare the consumer facing inputs (in this example, `<input type="search">`):
|
241
|
+
|
242
|
+
```html+erb
|
243
|
+
<%# app/views/form_builder/_search_field.html.erb %>
|
244
|
+
|
245
|
+
<%= form.search_field(
|
246
|
+
method,
|
247
|
+
class: "
|
248
|
+
search-field
|
249
|
+
#{options.delete(:class}
|
250
|
+
",
|
251
|
+
"data-controller": "
|
252
|
+
input->search#executeQuery
|
253
|
+
#{options.delete(:"data-controller")}
|
254
|
+
",
|
255
|
+
**options
|
256
|
+
) %>
|
257
|
+
```
|
258
|
+
|
259
|
+
Then, declare the administrative interface's inputs, in terms of overriding the
|
260
|
+
foundation built by the more general definitions:
|
261
|
+
|
262
|
+
```html+erb
|
263
|
+
<%# app/views/admin/form_builder/_search_field.html.erb %>
|
264
|
+
|
265
|
+
<%= form.search_field(
|
266
|
+
method,
|
267
|
+
class: "
|
268
|
+
search-field--admin
|
269
|
+
#{options.delete(:class}
|
270
|
+
",
|
271
|
+
"data-controller": "
|
272
|
+
focus->admin-search#clearResults
|
273
|
+
#{options.delete(:"data-controller")}
|
274
|
+
",
|
275
|
+
) %>
|
276
|
+
```
|
277
|
+
|
278
|
+
The rendered `admin/form_builder/search_field` partial combines options and
|
279
|
+
arguments from both partials:
|
280
|
+
|
281
|
+
```html
|
282
|
+
<input
|
283
|
+
type="search"
|
284
|
+
class="
|
285
|
+
search-field
|
286
|
+
search-field--admin
|
287
|
+
"
|
288
|
+
data-controller="
|
289
|
+
input->search#executeQuery
|
290
|
+
focus->admin-search#clearResults
|
291
|
+
"
|
292
|
+
>
|
293
|
+
```
|
294
|
+
|
295
|
+
When constructing fields within a `form_with(model: ...)` block, partials will
|
296
|
+
use the `model:` instance's [`tableize`-d model name][tableize] to resolve
|
297
|
+
partials.
|
298
|
+
|
299
|
+
For example, `posts/form_builder/_text_field.html.erb` will be resolved ahead of
|
300
|
+
`form_builder/_text_field.html.erb`:
|
301
|
+
|
302
|
+
```html+erb
|
303
|
+
<%# app/views/posts/form_builder/_text_field.html.erb %>
|
304
|
+
|
305
|
+
<%= form.text_field(method, class: "post-text #{options.delete(:class)}", **options) %>
|
306
|
+
|
307
|
+
<%# app/views/application/form_builder/_text_field.html.erb %>
|
308
|
+
|
309
|
+
<%= form.text_field(method, class: "text #{options.delete(:class)}", **options) %>
|
310
|
+
```
|
311
|
+
|
312
|
+
The rendered `posts/form_builder/text_field` partial could combine options and
|
313
|
+
arguments from both partials:
|
314
|
+
|
315
|
+
```html
|
316
|
+
<input type="text" class="post-text text">
|
317
|
+
```
|
318
|
+
|
319
|
+
Models declared within modules will be delimited with `/`. For example,
|
320
|
+
`Special::Post` instances would first resolve partials within the
|
321
|
+
`app/views/special/posts/form_builder` directory, before falling back to
|
322
|
+
`app/views/application/form_builder`.
|
323
|
+
|
324
|
+
[tableize]: https://api.rubyonrails.org/classes/String.html#method-i-tableize
|
325
|
+
|
243
326
|
### Configuration
|
244
327
|
|
245
328
|
View partials lookup and resolution will be scoped to the
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "view_partial_form_builder/
|
1
|
+
require "view_partial_form_builder/lookup_override"
|
2
2
|
require "view_partial_form_builder/html_attributes"
|
3
3
|
|
4
4
|
module ViewPartialFormBuilder
|
@@ -14,9 +14,9 @@ module ViewPartialFormBuilder
|
|
14
14
|
@template,
|
15
15
|
options,
|
16
16
|
)
|
17
|
-
@
|
18
|
-
|
19
|
-
object_name: object_name,
|
17
|
+
@lookup_override = LookupOverride.new(
|
18
|
+
prefixes: @template.lookup_context.prefixes,
|
19
|
+
object_name: object&.model_name || object_name,
|
20
20
|
view_partial_directory: ViewPartialFormBuilder.view_partial_directory,
|
21
21
|
)
|
22
22
|
end
|
@@ -173,6 +173,17 @@ module ViewPartialFormBuilder
|
|
173
173
|
render_partial("time_zone_select", locals, fallback: -> { super })
|
174
174
|
end
|
175
175
|
|
176
|
+
def date_select(method, options = {}, **html_options)
|
177
|
+
locals = {
|
178
|
+
method: method,
|
179
|
+
options: options,
|
180
|
+
html_options: HtmlAttributes.new(html_options),
|
181
|
+
arguments: [method, options, html_options],
|
182
|
+
}
|
183
|
+
|
184
|
+
render_partial("date_select", locals, fallback: -> { super })
|
185
|
+
end
|
186
|
+
|
176
187
|
def hidden_field(method, **options)
|
177
188
|
@emitted_hidden_id = true if method == :id
|
178
189
|
|
@@ -247,50 +258,90 @@ module ViewPartialFormBuilder
|
|
247
258
|
|
248
259
|
private
|
249
260
|
|
250
|
-
attr_reader :lookup_context
|
251
|
-
|
252
261
|
def render_partial(field, locals, fallback:, &block)
|
253
|
-
return fallback.call if about_to_recurse_infinitely?(field)
|
254
|
-
|
255
262
|
options = locals.fetch(:options, {})
|
256
263
|
partial_override = options.delete(:partial)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
264
|
+
options_as_locals = objectify_options(options)
|
265
|
+
locals = DeprecatedHash.new(
|
266
|
+
options_as_locals.merge(locals, form: self),
|
267
|
+
deprecated_keys: options_as_locals.keys + [:arguments],
|
268
|
+
)
|
269
|
+
|
270
|
+
partial = if partial_override.present?
|
271
|
+
ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder").warn(<<~WARNING)
|
272
|
+
Providing a `partial:` option for a form field is deprecated.
|
273
|
+
WARNING
|
274
|
+
|
275
|
+
find_partial(partial_override, locals, prefixes: [])
|
276
|
+
else
|
277
|
+
find_partial(field, locals, prefixes: prefixes_after(field))
|
278
|
+
end
|
279
|
+
|
280
|
+
if partial.nil? || about_to_recurse_infinitely?(partial)
|
281
|
+
fallback.call
|
282
|
+
else
|
283
|
+
partial.render(@template, locals, &block)
|
267
284
|
end
|
268
285
|
end
|
269
286
|
|
270
|
-
def
|
287
|
+
def find_partial(template_name, locals, prefixes:)
|
271
288
|
template_is_partial = true
|
272
289
|
|
273
|
-
lookup_context.
|
290
|
+
@template.lookup_context.find_all(
|
274
291
|
template_name,
|
275
|
-
|
292
|
+
prefixes,
|
276
293
|
template_is_partial,
|
277
|
-
|
294
|
+
locals.keys,
|
295
|
+
).first.tap do |partial|
|
296
|
+
root_directory = ViewPartialFormBuilder.view_partial_directory
|
297
|
+
|
298
|
+
if partial&.virtual_path == "#{root_directory}/_#{template_name}"
|
299
|
+
ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder").warn(<<~WARNING.strip)
|
300
|
+
Declare root-level partials in app/views/application/#{root_directory}/, not app/views/#{root_directory}/.
|
301
|
+
WARNING
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def prefixes_after(template_name)
|
307
|
+
prefixes = @lookup_override.prefixes
|
308
|
+
current_prefix = current_virtual_path.delete_suffix("/_#{template_name}")
|
309
|
+
|
310
|
+
if prefixes.include?(current_prefix)
|
311
|
+
prefixes.from(prefixes.index(current_prefix).to_i + 1)
|
312
|
+
else
|
313
|
+
prefixes
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def about_to_recurse_infinitely?(partial)
|
318
|
+
partial.virtual_path == current_virtual_path
|
278
319
|
end
|
279
320
|
|
280
|
-
def
|
281
|
-
if
|
282
|
-
|
321
|
+
def current_virtual_path
|
322
|
+
if (current_template = @template.instance_values["current_template"])
|
323
|
+
current_template.virtual_path.to_s
|
283
324
|
else
|
284
|
-
@template.
|
325
|
+
@template.instance_values["virtual_path"].to_s
|
285
326
|
end
|
286
327
|
end
|
328
|
+
end
|
287
329
|
|
288
|
-
|
289
|
-
|
290
|
-
|
330
|
+
class DeprecatedHash < Hash
|
331
|
+
def initialize(hash, deprecated_keys: [])
|
332
|
+
super()
|
333
|
+
merge!(hash)
|
334
|
+
@deprecated_keys = deprecated_keys
|
335
|
+
end
|
291
336
|
|
292
|
-
|
337
|
+
def [](key)
|
338
|
+
if @deprecated_keys.include?(key)
|
339
|
+
ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder").warn <<~WARNING
|
340
|
+
Accessing `#{key}` from partials is deprecated.
|
341
|
+
WARNING
|
293
342
|
end
|
343
|
+
|
344
|
+
super
|
294
345
|
end
|
295
346
|
end
|
296
347
|
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module ViewPartialFormBuilder
|
2
2
|
class HtmlAttributes
|
3
|
+
deprecate merge_token_lists: <<~'WARNING', deprecator: ActiveSupport::Deprecation.new("0.2.0", "ViewPartialFormBuilder")
|
4
|
+
|
5
|
+
As an alternative, merge arguments through String interpolation:
|
6
|
+
|
7
|
+
<%= form.label(method, class: "label #{options.delete(:class)}", **options) %>
|
8
|
+
|
9
|
+
WARNING
|
10
|
+
|
3
11
|
def initialize(**attributes)
|
4
12
|
@attributes = attributes
|
5
13
|
end
|
@@ -1,29 +1,12 @@
|
|
1
1
|
module ViewPartialFormBuilder
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
@object_name = object_name.to_s.pluralize
|
7
|
-
@overridden_context = overridden_context
|
8
|
-
@prefixes = overridden_context.prefixes.dup.freeze
|
2
|
+
class LookupOverride
|
3
|
+
def initialize(prefixes:, object_name:, view_partial_directory:)
|
4
|
+
@object_name = object_name.to_s.tableize
|
5
|
+
@prefixes = prefixes
|
9
6
|
@view_partial_directory = view_partial_directory
|
10
7
|
end
|
11
8
|
|
12
|
-
def
|
13
|
-
previous_prefixes = overridden_context.prefixes
|
14
|
-
|
15
|
-
overridden_context.prefixes = prefix_overrides
|
16
|
-
|
17
|
-
yield
|
18
|
-
ensure
|
19
|
-
overridden_context.prefixes = previous_prefixes
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
attr_reader :overridden_context, :object_name, :view_partial_directory
|
25
|
-
|
26
|
-
def prefix_overrides
|
9
|
+
def prefixes
|
27
10
|
*overridden_prefixes, root_prefix = @prefixes.dup
|
28
11
|
|
29
12
|
prefixes = [
|
@@ -49,5 +32,9 @@ module ViewPartialFormBuilder
|
|
49
32
|
|
50
33
|
prefixes.uniq
|
51
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :object_name, :view_partial_directory
|
52
39
|
end
|
53
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_partial_form_builder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Doyle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -94,7 +94,7 @@ files:
|
|
94
94
|
- lib/view_partial_form_builder/engine.rb
|
95
95
|
- lib/view_partial_form_builder/form_builder.rb
|
96
96
|
- lib/view_partial_form_builder/html_attributes.rb
|
97
|
-
- lib/view_partial_form_builder/
|
97
|
+
- lib/view_partial_form_builder/lookup_override.rb
|
98
98
|
- lib/view_partial_form_builder/version.rb
|
99
99
|
homepage: https://github.com/seanpdoyle/view_partial_form_builder
|
100
100
|
licenses:
|