Rapid scaffold builder for Turbo-Rails and Hotwire. Get the tutorial now at:
--modify=status{partials}
Here, status
is an enum field on your table; you must use the exact string partials
when using this feature.
You're telling Hot Glue to build scaffold that will display the status
enum field as a partial.
It will look for a partial in the same build directory, whose name matches to the value of the enum. You will need to create a partial for each enum option that you have defined.
Remember when defining enums Rails will patch methods on your objects with the name of the enum types, so you must avoid namespace collisions with existing Ruby or Rails methods that are common to all objects -- like, for example, new
.
Before this feature, enums always rendered like this on the show page:
<%= domain.status %>
(or, if you use custom labels:)
<%= Domain.status_labels(domain.status)
After, you if you use the --modify
flag and modify the enum to 'partials', your show output will render:
<%= render partial: thing.status, locals: {thing: thing} %>
(In this example Thing
is the name of the model being built.)
Your form will also render the partial, but after the collection_select used to create the drop-down. (So it will render both the drop-down and the partial. This implementation has the drawback of no immediate switch between the partials when changing the drop-down, so it is not as responsive of an interaction design as it could be.)
Assuming your Thing enum is defined like so:
enum status: {abc: 'abc', dfg: 'dfg', hgk: 'hgk'}
You then would create three partials in the things
directory. Make sure to create a partial for each defined enum, or else your app will crash when it tries to render a record with that enum.
_abc.html.erb
_dfg.html.erb
_hgk.html.erb
If your enum is on the show-only list, then the drop-down does not appear (but the partial is rendered).
Proof-of-concept can be found here: https://github.com/hot-glue-for-rails/HGEnumWithPartials1
Remember to see the section marked 'A Note About Enums' for more about working with Rails 7 enums.
update
method)flash[:notice]
(in update
method`)edit
method itselfshow
method that redirects to the edit
(Hot Glue takes the opinionated stance that show routes should not really exist. This addition makes it so that when the user is on the show
route and re-loads the browse window, they don't see a route error)@
symbols using instance variables in _edit
partials and in the enum syntax--no-controller
and --no-list
--pundit
If you enable Pundit, your controllers will look for a Policy that matches the name of the thing being built.
Realtime Field-level Access
Hot Glue gives you automatic field level access control if you create *_able?
methods for each field you'd like to control on your Pundit policy.
Take note this is not what Pundit recommends in their documentation for field-level access control. Instead, it has been implemented this way to provide field-by-field user feedback to the user shown in red just like Rails validation errors are currently shown. (Remember, we're talking about an access control failure, which may involve conditions that look like what you'd typically see in Rails validations).
You can refine on a field-by-field and role-by-role basis, which users have access to edit which fields on the Hot Glue layout. For everyone else, they see the field as viewable only.
A user inputting a field they don't have access to edit is only hypothetical for Hot Glue, because the interface correctly shows the field non-editable for them anyway. But, if the interface was edited or simply hacked (your web user can add hidden fields using the DevTools to try to set the values they don't have access to), the backend policy will prevent the hacking attempt by applying the access control in the update?
method of the policy. You can set the object errors here when catching for access control errors, as you see in the example below.
The *_able?
method should return true if the field can be edited and false if the field should appear as viewable only (non-editable). No distinction is made between the create/edit contexts. You may check if the record is new using new_record?
. To remove the field completely, either remove it from your --include
list or add it to your --exclude
list, whichever you are using.
The *_able?
method is used by Hot Glue only on the new and edit actions, but you must incorporate it into the policy's update?
method as in the example, which will extend other operations since Hot Glue will use the policy to authorize the action
Add one *_able?
method to the policy for each field you want to allow field-level access control.
Replace *
with the name of the field you want under access control. Remember to include _id
for foreign keys. You do not need it for any field you don't want under access control.
When the method returns true, the field will be displayed to the user (and allowed) for editing. When the method returns false, the field will be displayed as read-only (viewable) to the user.
Important: These special fields determine only display behavior (new and edit), not create and update.
For create & update field-level access control, you must also implement the update?
method on the Policy. Notice how in the example policy below, the update?
method uses the name_able?
method when it is checking if the name field can be updated, tying the feature together.
You can set Pundit to be enabled globally on the whole project for every build in config/hot_glue.yml
(then you can leave off the --pundit
flag from the scaffold command)
:pundit_default:
(all builds in that project will use Pundit)
Here's an example ThingPolicy
that would allow editing the name field only if:
• the current user is an admin
• the sent_at date is nil (meaning it has not been sent yet)
For your policies, copy the initialize
method of both the outer class (ThingPolicy
) and the inner class (Scope
) exactly as shown below.
If you argue with this implementation and think that there should be a way to make a field invisible based on access control — for example, if you have two different user contexts (roles) with different concerns and you want to use the same Hot Glue controller — instead you should simply make different Hot Glue controllers for the different purposes, hiding or showing the fields applicable to the users in their contexts. A singular application-wide policy is used to enforce consistency across the app, but each area of the app (namespaced Hot Glue controller) has a different user context interaction and, therefore, which fields to include or exclude get defined based on that.
class ThingPolicy < ApplicationPolicy
def initialize(user, record)
@user = user
@record = record
end
def name_able?
@record.sent_at.nil?
end
def update?
if [email protected]_admin?
return false
elsif record.name_changed? && !name_able?
record.errors.add(:name, "cannot be changed.")
return false
else
return true
end
end
class Scope < Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
end
end
Because Hot Glue detects the *_able?
methods at build time, if you add them to your policy, you will have to rebuild your scaffold.
When mixing the show only, update show only, and Pundit features, notice that the show only and update show only will act to override whatever the policy might say, so fields specified in those settings will be shown as viewable (non-editable) even when the policy may allow it.
Remember, the show only list is specified using --show-only
and the update show only list is specified using --update-show-only
.
'Viewable' means it displays as view-only (not editable) even on the form. In this context, 'viewable' means 'read-only'. It does not mean 'visible'.
That's because when the field is not viewable, then it is editable or inputable. This may seem counter-intuitive for a standard interpretation of the word 'viewable,' but consider that Hot Glue has been carefully designed this way. If you do not want the field to appear at all, remove it using the exclude list or don't specify it in your include list.
If the field is being built, Hot Glue assumes your users want to see or edit it. Other special cases are beyond the scope of Hot Glue but can easily be added using direct customization of the code.
Without Pundit:
on new screen | on edit screen | |
---|---|---|
for a field on the show only list | viewable | viewable |
for a field on the update show only list | inputable | viewable |
for all other fields | inputable | inputable |
With Pundit:
on new screen | on edit screen | |
---|---|---|
for a field on the show only list | viewable | viewable |
for a field on the update show only list | check policy | viewable |
for all other fields | check policy | check policy |
Given a table generated with this schema:
rails generate model Thing abc:boolean dfg:boolean hij:boolean
• You can now use new flag --display-as
to determine how the booleans will be displayed: checkbox, radio, or switch
rails generate hot_glue:scaffold Thing --include=abc,dfg,hij--god --display-as='abc{checkbox},dfg{radio},hij{switch}'
You may specify a default default_boolean_display
in config/hot_glue.yml
, like so:
:default_boolean_display: 'radio'
(The options are checkbox, radio, or switch.) If none is given and no default is specified, legacy display behavior will be used (radio buttons)
You still use the --modify
flag to determine the truthy and falsy labels, which are also used as the truth and false when a boolean is displays as radio button, as shown in the dfg
field above. (switches and checkboxes simply display with the field label and do not use the truthy and falsy labels)
there are three ways Hot Glue deals with Datetime fields:
(1) with current users who have timezone
method (field or method)
(2) without current user timezone and no global timezone set for your app
(3) without current user timezone and global timezone set for your app
For #1, previously this method returned a time zone offset (integer). After v0.5.18, the preferred return value is a Timezone string, but legacy implementation returning offset values will continue to work.
Your user objects should have a field called timezone
which should be a string.
Note that daylight savings time is accounted for in this implementation.
For #2 (your user does not have a timezone field and you have not set the timezone globally), all datetimes will display in UTC timezone.
For #3 (your user does not have a timezone field but you have set the timezone globally), your datetimes will display in the Rails-specified timezone.
be sure to configure in config/application.rb
this:
config.time_zone = 'Eastern Time (US & Canada)'
This should be your business's default timezone.
(#93) fixes variables be more clear if they are TimeZone objects (https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html) or are UTC offset (integers -/+ from UTC)
• Nav templates (_nav.html.erb
) are now automatically appended to if they exist. Remember nav template live in the views folder at the root of the namespace, which is one folder up from whatever folder is being built.
If a file exists _nav.html.erb
, it will get appnded to with content like this:
<li class="nav-item">
<%= link_to "Domains", domains_path, class: "nav-link #{'active' if nav == 'domains'}" %>
</li>
This append to the _nav.html.erb
template happens in addition to the partial itself being included from the layout.
(Also only if it exists, so be sure create it before running the scaffold generators for the namespace. Of course, you only need to create it once for each namespace)
• To create a new _nav.html.erb
template use
bin/rails generate hot_glue:nav_template --namespace=xyz
Here, you give only the namespace. It will create an empty nav template:
<ul class='nav nav-tabs'>
</ul>
• Fixes to specs for datetimes
• Fixes way the flash notices where created that violated frozen string literal
--modify=field1{...},field2{...}
You can apply the modification to the viewable (non-edit) display of the field using the --modify
switch.
The currency syntax is --modify=cost{$},price{$}
Here, the cost
and price
fields will be displayed as wrapped in number_to_currency()
when displayed on the list view and when displayed as show-only.
You can also use a binary modifier, which can apply to booleans, datetimes, times, dates or anything else. When using the binary modify, a specific value is displayed if the field is truthy and another one is display if the field is falsy. You specify it using a pipe | character like so:
--modify=paid_at{paid|unpaid}
here, even though paid_at
is a datetime field, it will display as-if it is a binary— showing either the truthy label or the falsy label depending on if paid_at
is or is not null in the database. For all fields except booleans, this affects only the viewable output — what you see on the list page and on the edit page for show-only fields. For booleans, it affects those outputs as well as the normal edit output: The labels you specify are used as the labels for the radio buttons that the user can select.
You must separately specify these as show-only if you want them to be non-editable.
The available modifiers are:
modifier | what it does | can be used on | ||
---|---|---|---|---|
$ | wraps output in number_to_currency() |
float, integer | ||
truthy label|falsy label | specify a binary switch with a pipe (|) character if the value is truthy, it will display as "truthy label" if the value is falsy, it will display as "falsy label" | boolean, datetime, date, time | ||
For example
rails generate hot_glue:scafold Invoice --big-edit
rails generate hot_glue:scafold LineItem --nested=invioce
Whenever the line item is created, updated, or destroyed, the parent invoice record gets (edit action) re-rendered automatically. This happens for the big edit screen of the invoice.
Refactors fields into polymoric objects
Adds test coverage for Postgres Enums
Fixes tests on CI (and switches to CircleCI)
After you upgrade to 0.5.14 from a prior version, please re-install the global flash notice template with
rails generate hot_glue:flash_notices_install
The newer template will work with old Hot Glue scaffold (generated prior to 0.5.14) and new scaffold moving forward. Old scaffold you have previously built should be unaffected, but new scaffold will only display errors correctly if you re-install the flash notice template using the line above. (If you fail to re-install the flash notice template scaffold built on or after this version will be missing detailed error message for the model on the create/update failure case)
If you miss this step, your old Hot Glue flash notices template will not show the model error messages (but will continue to show the global alert & notice messages)
Scaffold built after this release will no longer contain an _errors
file previously copied into every build folder.
If you like Hot Glue, please consider purchasing my course here.