hot-glue 0.6.19 → 0.6.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e7085c942cab4dc377c75180d546b57f6ca76bdb3c96f3b2a837d1fdd887b0f
4
- data.tar.gz: 04fd50131e53977f25c8817c7fd7bd33e34cb0aa784c995394c94302491b1426
3
+ metadata.gz: bce1267ee3ccc16a7f32068e1f4faa0aafab073d1f1af91744f02c7e63e2b40c
4
+ data.tar.gz: f87d7c1cecc3cd30ceff3d2176d82cc6e51ff62245ba54e02ffd8487cb214d75
5
5
  SHA512:
6
- metadata.gz: fa984faeba62c9303c5ae0c64b32143e054289380ffdfecf2f0d457c7b2c2d69912b69901d78ee6fff75f1e6997ed19e86a4cc00a8c048341fbf2505e687a984
7
- data.tar.gz: 052122bbbc7d15c134653efc4da2b74df8400447eccae13d395627bd5d79b2a616f612c97ac995f07b563a8589c9609406320562616f7efe637b94d7289a5e8a
6
+ metadata.gz: b3740aff61a08504a01237aa822ff3e92ed09afc91666d4398e400bf8cede65d74d86dddd950e664ff155de69413707fd5b5460b31c0504428df50c6c18af8d5
7
+ data.tar.gz: 503b69ea3a5abe6074fcc0bc987db37c1efb88e38cbbb79ecf1fb5ff8a58fda01609469b4da0554c42ded090ef1c896e9dc00c759f1fa31fc31ed34b4ccbea7d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.6.18)
4
+ hot-glue (0.6.19)
5
5
  ffaker (~> 2.16)
6
6
  kaminari (~> 1.2)
7
7
  rails (> 5.1)
data/README.md CHANGED
@@ -321,6 +321,166 @@ TitleCase class name of the thing you want to build a scaffolding for.
321
321
 
322
322
  (note: Your `Thing` object must `belong_to` an authenticated `User` or alternatively you must create a Gd controller, see below.)
323
323
 
324
+
325
+ ## FLAGS (Options with no values)
326
+ These options (flags) also uses `--` syntax but do not take any values. (Notice no equal sign.) Everything is assumed (default) to be false unless specified.
327
+
328
+
329
+ ### `--stacked-downnesting`
330
+
331
+ This puts the downnested portals on top of one another (stacked top to bottom) instead of side-by-side (left to right). This is useful if you have a lot of downnested portals and you want to keep the page from getting too wide.
332
+
333
+
334
+
335
+ ### `--god` or `--gd`
336
+
337
+ Use this flag to create controllers with no root authentication. You can still use an auth_identifier, which can be useful for a meta-leval authentication to the controller.
338
+
339
+ For example, FOR ADMIN CONTROLLERS ONLY, supply a auth_identifier and use `--god` flag.
340
+
341
+ In Gd mode, the objects are loaded directly from the base class (these controllers have full access)
342
+ ```
343
+ def load_thing
344
+ @thing = Thing.find(params[:id])
345
+ end
346
+
347
+ ```
348
+
349
+ ### `--button-icons` (default is no icons)
350
+ You can specify this either as builder flag or as a config setting (in `config/hot_glue.yml`)
351
+ Use `font-awesome` for Font Awesome or `none` for no icons.
352
+
353
+
354
+ ### `--specs-only`
355
+
356
+ Produces ONLY the controller spec file, nothing else.
357
+
358
+
359
+ ### `--no-specs`
360
+
361
+ Produces all the files except the spec file.
362
+
363
+
364
+ ### `--no-paginate` (default: false)
365
+
366
+ Omits pagination. (All list views have pagination by default.)
367
+
368
+ ### `--paginate-per-page-selector` (default: false)
369
+
370
+ Show a small drop-down below the list to let the user choose 10, 25, or 100 results per page.
371
+
372
+
373
+ ### `--no-list`
374
+
375
+ Omits list action. Only makes sense to use this if want to create a view where you only want the create button or to navigate to the update screen alternative ways. (The new/create still appears, as well the edit, update & destroy actions are still created even though there is no natural way to navigate to them.)
376
+
377
+
378
+
379
+ ### `--no-create`
380
+
381
+ Omits new & create actions.
382
+
383
+ ### `--no-delete`
384
+
385
+ Omits delete button & destroy action.
386
+
387
+ ### `--no-controller`
388
+
389
+ Omits controller.
390
+
391
+ ### `--no-list`
392
+
393
+ Omits list views.
394
+
395
+ `--new-button-position` (above, below; default: above)
396
+ Show the new button above or below the list.
397
+
398
+ `--downnest-shows-headings` (default: false)
399
+ Show headings above downnested portals.
400
+
401
+
402
+ ### `--big-edit`
403
+
404
+ If you do not want inline editing of your list items but instead want to fallback to full-page style behavior for your edit views, use `--big-edit`.
405
+
406
+ The user will be taken to a full-screen edit page instead of an edit-in-place interaction.
407
+
408
+ When using `--big-edit`, any downnested portals will be displayed on the edit page instead of on the list page.
409
+
410
+ Big edit makes all edit and magic button operations happen using `'data-turbo': false`, fully reloading the page and submitting HTML requests instead of TURBO_STREAM requests.
411
+
412
+ Likewise, the controller's `update` action always redirects instead of using Turbo.
413
+
414
+
415
+ ### `--display-list-after-update`
416
+
417
+ After an update-in-place normally only the edit view is swapped out for the show view of the record you just edited.
418
+
419
+ Sometimes you might want to redisplay the entire list after you make an update (for example, if your action removes that record from the result set).
420
+
421
+ To do this, use flag `--display-list-after-update`. The update will behave like delete and re-fetch all the records in the result and tell Turbo to swap out the entire list.
422
+
423
+ ### `--with-turbo-streams`
424
+
425
+ If and only if you specify `--with-turbo-streams`, your views will contain `turbo_stream_from` directives. Whereas your views will always contain `turbo_frame_tags` (whether or not this flag is specified) and will use the Turbo stream replacement mechanism for non-idempotent actions (create & update). This flag just brings the magic of live-reload to the scaffold interfaces themselves.
426
+
427
+ **_To test_**: Open the same interface in two separate browser windows. Make an edit in one window and watch your edit appear in the other window instantly.
428
+
429
+ This happens using two interconnected mechanisms:
430
+
431
+ 1) by default, all Hot Glue scaffold is wrapped in `turbo_frame_tag`s. The id of these tags is your namespace + the Rails dom_id(...). That means all Hot Glue scaffold is namespaced to the namespaces you use and won't collide with other turbo_frame_tag you might be using elsewhere
432
+
433
+ 2) by appending **model callbacks**, we can automatically broadcast updates to the users who are using the Hot Glue scaffold. The model callbacks (after_update_commit and after_destroy_commit) get appended automatically to the top of your model file. Each model callback targets the scaffold being built (so just this scaffold), using its namespace, and renders the line partial (or destroys the content in the case of delete) from the scaffolding.
434
+
435
+ please note that *creating* and *deleting* do not yet have a full & complete implementation: Your pages won't re-render the pages being viewed cross-peer (that is, between two users using the app at the same time) if the insertion or deletion causes the pagination to be off for another user.
436
+
437
+
438
+ ### `--no-list-label`
439
+ Omits list LABEL itself above the list. (Do not confuse with the list heading which contains the field labels.)
440
+
441
+ Note that list labels may be automatically omitted on downnested scaffolds.
442
+
443
+
444
+ ### `--no-list-heading`
445
+
446
+ Omits the heading of column names that appears above the 1st row of data.
447
+
448
+ ### `--include-object-names`
449
+
450
+ When you are "Editing X" we specify that X is a ___ (author, book, room, etc)
451
+
452
+ e.g. "Editing author Edgar Allan Poe" vs "Editing Edgar Allan Poe"
453
+
454
+ Can also be specified globally in `config/hot_glue.yml`
455
+
456
+ ## Nav Templates and `--no-nav-menu`
457
+ At the namespace level, you can have a file called `_nav.html.erb` to create tabbed bootstrap nav
458
+
459
+ To create the file for the first time (at each namespace), start by running
460
+ ```
461
+ bin/rails generate hot_glue:nav_template --namespace=xyz
462
+ ```
463
+
464
+ This will append the file `_nav.html.erb` to the views folder at `views/xyz`. To begin, this file contains only the following:
465
+
466
+ ```
467
+ <ul class='nav nav-tabs'>
468
+ </ul>
469
+ ```
470
+
471
+ Once the file is present, any further builds in this namespace will:
472
+
473
+ 1) Append to this `_nav.html.erb` file, adding a tab for the new built scaffold
474
+ 2) On the list view of the scaffold being built, it will include a render to the _nav partial, passing the name of the currently-viewed thing as the local variable `nav` (this is how the nav template knows which tab to make active).
475
+ ```
476
+ <%= render partial: "owner/nav", locals: {nav: "things"} %>
477
+ ```
478
+ (In this example `owner/` is the namespace and `things` is the name of the scaffold being built)
479
+
480
+ To suppress this behavior, add `--no-nav-menu` to the build command and the _nav template will not be touched.
481
+
482
+
483
+
324
484
  ## Options With Arguments
325
485
 
326
486
  All options begin with two dashes (`--`) and a followed by an `=` and a value
@@ -348,7 +508,6 @@ end
348
508
 
349
509
  ```
350
510
 
351
-
352
511
  ### `--nested=`
353
512
 
354
513
  This object is nested within another tree of objects, and there is a nested route in your `routes.rb` file with the specified parent controllers above this controller. When specifying the parent(s), be sure to use **singular case**.
@@ -771,8 +930,7 @@ Hot Glue gives you automatic field level access control if you create `*_able?`
771
930
  The `*_able?` method should return true or false depending on whether or not the field can be edited. No distinction is made between the `new` and `edit` contexts. You may check if the record is new using `new_record?`.
772
931
 
773
932
 
774
- **The `*_able?` method is used by Hot Glue only on the new and edit actions. You must incorporate it into the policy's `update?` method as in the example, or else no guard will check prevent the user doesn't pass a value to input it anyway.**
775
-
933
+ **The `*_able?` method is used by Hot Glue only on the new and edit actions and also affects the strong parameters, so you no longer need to incorporate it into your policy's `update?` method
776
934
 
777
935
  Add one `*_able?` method to the policy for each field you want to allow field-level access control.
778
936
 
@@ -781,12 +939,8 @@ Replace `*` with the name of the field you want under access control. Remember t
781
939
  When the method returns true, the field will be displayed to the user (and allowed) for editing.
782
940
  When the method returns false, the field will be displayed as read-only (viewable) to the user.
783
941
 
784
- Important: These special fields determine *only* display behavior (new and edit), not create and update.
785
-
786
- 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.
787
-
788
942
  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)
789
- `:pundit_default:` (all builds in that project will use Pundit)
943
+ `:pundit:` (all builds in that project will use Pundit)
790
944
 
791
945
 
792
946
  Here's an example `ThingPolicy` that would allow **editing the name field** only if:
@@ -808,14 +962,11 @@ class ThingPolicy < ApplicationPolicy
808
962
  end
809
963
 
810
964
  def update?
811
- if !@user.is_admin?
812
- return false
813
- elsif record.name_changed? && !name_able?
814
- record.errors.add(:name, "cannot be changed.")
815
- return false
816
- else
817
- return true
818
- end
965
+ !@user.is_admin?
966
+ end
967
+
968
+ def edit?
969
+ !@user.is_admin?
819
970
  end
820
971
 
821
972
  class Scope < Scope
@@ -828,12 +979,11 @@ class ThingPolicy < ApplicationPolicy
828
979
  end
829
980
  ```
830
981
 
831
-
832
982
  Because Hot Glue detects the `*_able?` methods at build time, if you add them to your policy, you will have to rebuild your scaffold.
833
983
 
834
984
 
835
- ### `--pundit-policy-override`
836
- if you use the flag `--pundit-policy-override` your controller operations will bypass the invisible (pundit provided) access control and use the pundit policy you specify.
985
+ ### `--pundit-policy-override=`
986
+ if you pass a custom pundit class to `--pundit-policy-override=` your controller operations will bypass the invisible (pundit provided) access control and use the pundit policy you specify.
837
987
 
838
988
  example
839
989
 
@@ -849,6 +999,8 @@ If provided, the output code looks something like (in this example, showing the
849
999
  ```
850
1000
 
851
1001
 
1002
+
1003
+
852
1004
  ### `--show-only=`
853
1005
  (separate field names by COMMA)
854
1006
 
@@ -860,7 +1012,7 @@ IMPORTANT: By default, all fields that begin with an underscore (`_`) are automa
860
1012
  This is for fields you want globally non-editable by users in your app. For example, a counter cache or other field set only by a backend mechanism.
861
1013
 
862
1014
 
863
- ### `--update-show-only`
1015
+ ### `--update-show-only=`
864
1016
  (separate field names by COMMA)
865
1017
 
866
1018
  • Make this field appear as viewable only for the edit action (and not allowed in the update action).
@@ -909,18 +1061,19 @@ Remember, if there's a corresponding `*_able?` method on the policy, it will be
909
1061
 
910
1062
  As shown in the method `name_able?` of the example ThingPolicy above, if this field on your policy returns true, the field will be editable. If it returns false, the field will be viewable (read-only).
911
1063
 
912
-
913
- ### `--hidden`
914
-
1064
+ ### `--hidden=` (affects both create + update actions)
1065
+ ### `--create-hidden=`
1066
+ ### `--update-hidden=`
1067
+ TODO: RENAME ME TO INVISIBLE
915
1068
  Separate list of fields.
916
1069
 
917
- These fields will be hidden from the form but will exist as hidden_field, and so the update will still work.
1070
+ These fields will exist on the create or update form exist as hidden_field, and so the update will still work._
918
1071
 
919
1072
 
920
1073
  EXAMPLE:
921
1074
 
922
1075
  ```
923
- bin/rails generate hot_glue:scaffold Wrapper --namespace='account_dashboard' --no-nav-menu --big-edit --smart-layout --stimmify --hidden=raw_source
1076
+ bin/rails generate hot_glue:scaffold Wrapper --namespace='account_dashboard' --no-nav-menu --big-edit --smart-layout --stimmify --invisible=raw_source
924
1077
  ```
925
1078
 
926
1079
  In the `wrappers` folder, I am using a special sticky partial `_edit_within_form.html.erb`, which contains code preserved from build-to-build and included in the form:
@@ -992,7 +1145,77 @@ Notice we are also using `--stimmify` to decorate the form with a Stimulus contr
992
1145
 
993
1146
  The code above uses Code Mirror to act as a code editor, which requires pulling the value off the hidden form element (putting it into the code mirror interface) and pushing it back into the hidden form element when the Submit button is clicked.
994
1147
 
1148
+ ### `--invisible=`
1149
+ ### `--create-invisible=`
1150
+ ### `--update-invisible=`
1151
+ (two lists are maintained: create and update. any fields on the unnamed invisible list will be invisible on both create and update actions)
1152
+
1153
+ If a field is on the invisible list, the policy will be checked for a `_able?` method.
1154
+ If this method returns true, displayed like a normal editable field.
1155
+
1156
+ If the policy doesn't allow editing, this field will be made invisible: completely removed from the form.
1157
+
1158
+ Like show only, these will check for `*_able?` methods on the object or Policy and will only display the field if the method returns true.
1159
+
1160
+ It will also block the field from being updated on the backend, so don't use this if you want to create a hidden_field tag but still allow the controller to update it. (For that, see `--hidden=`.)
1161
+
1162
+
1163
+ Like show-only, note special behavior with pundit.
1164
+
1165
+ A field can be marked invisible and show-only, in which case the invisible rule take prescedence when the access control is denied (field removed from form) but th show-only rule takes prescedance when the access control is granted,
1166
+
1167
+ Hidden can be used with invisible. In this case, the access control will be applied to the field. When editable, the hidden field output will be used.
1168
+
1169
+ Must be used with Pundit. Without pundit, see other alternatives: hidden fields, show only fields, or just removing the field completely by using the exclude list or leaving it off the include list.
1170
+
1171
+ | | | Pundit | | |
1172
+ |----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|
1173
+ | fields not on any of these other lists | Included as editable on both the create + update actions or as viewable if Policy access control returns false | Without pundit, everything is editable. With pundit, the Policy is checked for an `_able?` method for each field at build time. When this method returns false, the field is viewable. | | |
1174
+ | Show only | Viewable only (non-editable) on both create + edit/update overriding whatever Policy returns. | Without pundit, this field is viewable only. Overrides whatever policy returns. | | |
1175
+ | Update Show only | Viewable only on the update screen or if Pundit doesn't allow editing | Same as above but apply only to the update screen. | | |
1176
+ | Invisible | Displayed in the HTML output and received by the controller for create or update action but shown as a hidden_field on the HTML, invisible to the user. You should use this if you want to construct your own form input or set the value via Javascript | Cannot be used without Pundit. With pundit, fields editable via the policy are editable on the screen (unless they are also show-only, in which case they are visible) and non-editable via the policy are made invisible (completely removed) from the screen. | | |
1177
+ | Hidden | Not displayed or updatable if the field respond false to _able? or the Policy doesn't allow. | Unrelated to pundit Policy | | |
1178
+ | | | | | |
1179
+
1180
+
1181
+ Pundit calls the policy for every action, including the index action. In these cases it passes an association instead of a single record.
1182
+
1183
+ Normally, field level access control that applies for show only and invisible is affects each record, in the list view, new page, or edit page.
1184
+
1185
+ For that reason, your `_able?` methods may check the individual records, except in the case of the `index` action for which the pundit policy has no individual record.
1186
+
1187
+ For these special cases, you might want to hide the entire column itself when the `_able?` return false on the association call (not in the context any of any record).
1188
+
1189
+ You'd want to do this for a global switch, unrelated to the record, to show or hide the column itself. When using invisible fields, the column headings are check against the policy's `_able?` fields, too, but since this called on the list, the entire set of objects being returned by the list is passed to the policy. (For example `policy(@things)`)
1190
+
1191
+ This makes it impossible to make your access control depend solely on the record itself, so can be used only for context-based access control that is applied to the column headings.
1192
+
1193
+
1194
+ In a case like this, you'll want the `_able?` method on the policy to know if the object is a record or many records.
1195
+
1196
+ ```
1197
+ class ThingPolicy < ApplicationPolicy
1198
+ attr_reader :user, :thing
1199
+
1200
+ def initialize(user, thing)
1201
+ @user = user
1202
+ @thing = thing
1203
+ end
1204
+
1205
+ def ccc_able?
1206
+ if thing.is_a?(Thing) # a thing is not a Thing when it is an active relation of many things
1207
+ !!thing.bbb # show or hide ccc based on whether or not bbb is true
1208
+ else
1209
+ current_user.is_admin? # show or hide the column heading for ccc based on whether or not the current user is an admin
1210
+ end
1211
+ end
1212
+ # more policy method here ...
1213
+ end
1214
+ ```
1215
+ Here, for all CRUD actions, the object is a thing, and so the editablility of ccc is dependent on bbb being true.
1216
+ If thing is not a Thing, then it is active relation (so this applies to the column headings) and we show it only to admins.
995
1217
 
1218
+ Remember, since the `_able?` methods are not otherwise called during Pundit's index cycle, this applies only to the list column headings and has no bearing on create, update, read, delete for which the access control can be anything you want that is available to the Poilcy.
996
1219
 
997
1220
 
998
1221
 
@@ -1014,7 +1237,7 @@ and also fix any Rails apps created since October 2021 by fixing the Gemfile. De
1014
1237
  https://stackoverflow.com/questions/70671324/new-rails-7-turbo-app-doesnt-show-the-data-turbo-confirm-alert-messages-dont-f
1015
1238
 
1016
1239
 
1017
- ### `--magic-buttons`
1240
+ ### `--magic-buttons=`
1018
1241
  If you pass a list of magic buttons (separated by commas), they will appear in the button area on your list.
1019
1242
 
1020
1243
  It will be assumed there will be corresponding bang methods on your models.
@@ -1035,8 +1258,13 @@ Finally, you can raise an ActiveRecord error which will also get passed to the u
1035
1258
 
1036
1259
  For more information see [Example 6 in the Tutorial](https://school.jfbcodes.com/8188)
1037
1260
 
1261
+ You can also define methods on your model that have the same name as the button with `_able?` at the end.
1262
+ (Prior to v0.6.20, these methods expected names with `able?` but no underscore.)
1263
+
1264
+ The button will be display as disabled if the method returns false.
1038
1265
 
1039
- ### `--downnest`
1266
+
1267
+ ### `--downnest=`
1040
1268
 
1041
1269
  Automatically create subviews down your object tree. This should be the name of a has_many relationship based from the current object.
1042
1270
  You will need to build scaffolding with the same name for the related object as well. On the list view, the object you are currently building will be built with a sub-view list of the objects related from the given line.
@@ -1048,11 +1276,6 @@ Can now be created with more space (wider) by adding a `+` to the end of the dow
1048
1276
 
1049
1277
  The 'Abcs' portal will display as 5 bootstrap columns instead of the typical 4. (You may use multiple ++ to keep making it wider but the inverse with minus is not supported
1050
1278
 
1051
- ### `--stacked-downnesting`
1052
-
1053
- This puts the downnested portals on top of one another (stacked top to bottom) instead of side-by-side (left to right). This is useful if you have a lot of downnested portals and you want to keep the page from getting too wide.
1054
-
1055
-
1056
1279
 
1057
1280
  ### `--record-scope=`
1058
1281
 
@@ -1071,113 +1294,9 @@ scope :is_open, -> {where(state == 'open')}
1071
1294
 
1072
1295
  Now all records displayed through the generated controller
1073
1296
 
1074
- ## FLAGS (Options with no values)
1075
- These options (flags) also uses `--` syntax but do not take any values. Everything is assumed (default) to be false unless specified.
1076
-
1077
- ### `--god` or `--gd`
1078
-
1079
- Use this flag to create controllers with no root authentication. You can still use an auth_identifier, which can be useful for a meta-leval authentication to the controller.
1080
-
1081
- For example, FOR ADMIN CONTROLLERS ONLY, supply a auth_identifier and use `--god` flag.
1082
-
1083
- In Gd mode, the objects are loaded directly from the base class (these controllers have full access)
1084
- ```
1085
- def load_thing
1086
- @thing = Thing.find(params[:id])
1087
- end
1088
-
1089
- ```
1090
-
1091
- ### `--button-icons` (default is no icons)
1092
- You can specify this either as builder flag or as a config setting (in `config/hot_glue.yml`)
1093
- Use `font-awesome` for Font Awesome or `none` for no icons.
1094
-
1095
-
1096
- ### `--specs-only`
1097
-
1098
- Produces ONLY the controller spec file, nothing else.
1099
-
1100
-
1101
- ### `--no-specs`
1102
-
1103
- Produces all the files except the spec file.
1104
-
1105
-
1106
- ### `--no-paginate` (default: false)
1107
-
1108
- Omits pagination. (All list views have pagination by default.)
1109
-
1110
- ### `--paginate-per-page-selector` (default: false)
1111
-
1112
- Show a small drop-down below the list to let the user choose 10, 25, or 100 results per page.
1113
-
1114
-
1115
- ### `--no-list`
1116
-
1117
- Omits list action. Only makes sense to use this if want to create a view where you only want the create button or to navigate to the update screen alternative ways. (The new/create still appears, as well the edit, update & destroy actions are still created even though there is no natural way to navigate to them.)
1118
-
1119
1297
 
1120
1298
 
1121
- ### `--no-create`
1122
-
1123
- Omits new & create actions.
1124
-
1125
- ### `--no-delete`
1126
-
1127
- Omits delete button & destroy action.
1128
-
1129
- ### `--no-controller`
1130
-
1131
- Omits controller.
1132
-
1133
- ### `--no-list`
1134
-
1135
- Omits list views.
1136
-
1137
- `--new-button-position` (above, below; default: above)
1138
- Show the new button above or below the list.
1139
-
1140
- `--downnest-shows-headings` (default: false)
1141
- Show headings above downnested portals.
1142
-
1143
-
1144
- ### `--big-edit`
1145
-
1146
- If you do not want inline editing of your list items but instead want to fallback to full-page style behavior for your edit views, use `--big-edit`.
1147
-
1148
- The user will be taken to a full-screen edit page instead of an edit-in-place interaction.
1149
-
1150
- When using `--big-edit`, any downnested portals will be displayed on the edit page instead of on the list page.
1151
-
1152
- Big edit makes all edit and magic button operations happen using `'data-turbo': false`, fully reloading the page and submitting HTML requests instead of TURBO_STREAM requests.
1153
-
1154
- Likewise, the controller's `update` action always redirects instead of using Turbo.
1155
-
1156
-
1157
- ### `--display-list-after-update`
1158
-
1159
- After an update-in-place normally only the edit view is swapped out for the show view of the record you just edited.
1160
-
1161
- Sometimes you might want to redisplay the entire list after you make an update (for example, if your action removes that record from the result set).
1162
-
1163
- To do this, use flag `--display-list-after-update`. The update will behave like delete and re-fetch all the records in the result and tell Turbo to swap out the entire list.
1164
-
1165
- ### `--with-turbo-streams`
1166
-
1167
- If and only if you specify `--with-turbo-streams`, your views will contain `turbo_stream_from` directives. Whereas your views will always contain `turbo_frame_tags` (whether or not this flag is specified) and will use the Turbo stream replacement mechanism for non-idempotent actions (create & update). This flag just brings the magic of live-reload to the scaffold interfaces themselves.
1168
-
1169
- **_To test_**: Open the same interface in two separate browser windows. Make an edit in one window and watch your edit appear in the other window instantly.
1170
-
1171
- This happens using two interconnected mechanisms:
1172
-
1173
- 1) by default, all Hot Glue scaffold is wrapped in `turbo_frame_tag`s. The id of these tags is your namespace + the Rails dom_id(...). That means all Hot Glue scaffold is namespaced to the namespaces you use and won't collide with other turbo_frame_tag you might be using elsewhere
1174
-
1175
- 2) by appending **model callbacks**, we can automatically broadcast updates to the users who are using the Hot Glue scaffold. The model callbacks (after_update_commit and after_destroy_commit) get appended automatically to the top of your model file. Each model callback targets the scaffold being built (so just this scaffold), using its namespace, and renders the line partial (or destroys the content in the case of delete) from the scaffolding.
1176
-
1177
- please note that *creating* and *deleting* do not yet have a full & complete implementation: Your pages won't re-render the pages being viewed cross-peer (that is, between two users using the app at the same time) if the insertion or deletion causes the pagination to be off for another user.
1178
-
1179
-
1180
- ### `--related-sets`
1299
+ ### `--related-sets=`
1181
1300
 
1182
1301
  Used to show a checkbox set of related records. The relationship should be a `has_and_belongs_to_many` or a `has_many through:` from the object being built.
1183
1302
 
@@ -1196,58 +1315,43 @@ Note this leaves open a privileged escalation attack (a security vulnerability).
1196
1315
  To fix this, you'll need to use Pundit with special syntax designed for this purpose. Please see [Example #17 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
1197
1316
 
1198
1317
 
1199
- ## "Thing" Label
1200
-
1201
- Note that on a per model basis, you can also globally omit the label or set a unique label value using
1202
- `@@table_label_singular` and `@@table_label_plural` on your model objects.
1203
-
1204
- You have three options to specify labels explicitly with a string, and 1 option to specify a global name for which the words "Delete ___" and "New ___" will be added.
1205
-
1206
- If no `--label` is specified, it will be inferred to be the Capitalized version of the name of the thing you are building, with spaces for two or more words.
1207
-
1208
- ### `--label`
1318
+ ### `--label=`
1209
1319
 
1210
1320
  The general name of the thing, will be applied as "New ___" for the new button & form. Will be *pluralized* for list label heading, so if the word has a non-standard pluralization, be sure to specify it in `config/inflictions.rb`
1211
1321
 
1212
1322
  If you specify anything explicitly, it will be used.
1213
1323
  If not, a specification that exists as `@@tabel_label_singular` from the Model will be used.
1214
- If this does not exist, the Titleized (capitalized) version of the model name.
1324
+ If this does not exist, the Titleized (capitalized) version of the model name.
1215
1325
 
1216
- ### `--list-label-heading`
1326
+ ### `--list-label-heading=`
1217
1327
  The plural of the list of things at the top of the list.
1218
1328
  If not, a specification that exists as `@@tabel_label_plural` from the Model will be used.
1219
1329
  If this does not exist, the UPCASE (all-uppercase) version of the model name.
1220
1330
 
1221
- ### `--new-button-label`
1222
- The button on the list that the user clicks onto to create a new record.
1223
- (Follows same rules described in the `--label` option but with the word "New" prepended.)
1331
+ ### `--new-button-label=`
1332
+ Overrides the button on the list that the user clicks onto to create a new record.
1333
+ (Default is to follow the same rules described in the `--label` option but with the word "New" prepended.)
1224
1334
 
1225
- ### `--new-form-heading`
1335
+ ### `--new-form-heading=`
1226
1336
  The text at the top of the new form that appears when the new input entry is displayed.
1227
- (Follows same rules described in the `--label` option but with the word "New" prepended.)
1228
-
1229
- ### `--no-list-label`
1230
- Omits list LABEL itself above the list. (Do not confuse with the list heading which contains the field labels.)
1231
-
1232
- Note that list labels may be automatically omitted on downnested scaffolds.
1233
-
1337
+ (Default follows the same rules described in the `--label` option but with the word "New" prepended.)
1234
1338
 
1235
1339
  ## Field Labels
1236
1340
 
1237
- ### `--form-labels-position` (default: `after`; options are **before**, **after**, and **omit**)
1341
+ ### `--form-labels-position=` (default: `after`; options are **before**, **after**, and **omit**)
1238
1342
  By default form labels appear after the form inputs. To make them appear before or omit them, use this flag.
1239
1343
 
1240
1344
  See also `--form-placeholder-labels` to use placeolder labels.
1241
1345
 
1242
1346
 
1243
- ### `--form-placeholder-labels` (default: false)
1347
+ ### `--form-placeholder-labels=` (default: false)
1244
1348
 
1245
1349
  When set to true, fields, numbers, and text areas will have placeholder labels.
1246
1350
  Will not apply to dates, times, datetimes, dropdowns (enums + foreign keys), or booleans.
1247
1351
 
1248
1352
  See also setting `--form-labels-position` to control position or omit normal labels.
1249
1353
 
1250
- ### `--inline-list-labels` (before, after, omit; default: omit)
1354
+ ### `--inline-list-labels=` (before, after, omit; default: omit)
1251
1355
 
1252
1356
  Determines if field label will appear on the LIST VIEW. Note that because Hot Glue has no separate show route or page, this affects the `_show` template which is rendered as a partial from the LIST view.
1253
1357
 
@@ -1256,25 +1360,12 @@ Because the labels are already in the heading, this is `omit` by default. (Use w
1256
1360
  Use `before` to make the labels come before or `after` to make them come after. See Version 0.5.1 release notes for an example.
1257
1361
 
1258
1362
 
1259
- ### `--no-list-heading`
1260
-
1261
- Omits the heading of column names that appears above the 1st row of data.
1262
-
1263
- ### `--include-object-names`
1264
-
1265
- When you are "Editing X" we specify that X is a ___ (author, book, room, etc)
1266
-
1267
- e.g. "Editing author Edgar Allan Poe" vs "Editing Edgar Allan Poe"
1268
-
1269
- Can also be specified globally in `config/hot_glue.yml`
1270
-
1271
-
1272
1363
  ### Code insertions
1273
1364
 
1274
- `--code-before-create`
1275
- `--code-after-create`
1276
- `--code-before-update`
1277
- `--code-after-update`
1365
+ ### `--code-before-create=`
1366
+ ### `--code-after-create=`
1367
+ ### `--code-before-update=`
1368
+ ### `--code-after-update=`
1278
1369
 
1279
1370
  Insert some code into the `create` action or `update` actions.
1280
1371
  The **before code** is called _after authorization_ but _before saving_ (which creates the record, or fails validation).
@@ -1283,9 +1374,8 @@ Both should be wrapped in quotation marks when specified in the command line, an
1283
1374
  (Notice the funky indentation of the lines in the generated code. Adjust you input to get the indentation correct.)
1284
1375
 
1285
1376
 
1286
- ## Searching
1287
1377
 
1288
- ### `--search` (options: simple, set, false predicate, default: false)
1378
+ ### `--search=` (options: simple, set, false predicate, default: false)
1289
1379
 
1290
1380
 
1291
1381
  #### Set Search
@@ -1336,24 +1426,12 @@ Here's how you would add a search interface to Example #1 in the [Hot Glue Tutor
1336
1426
  bin/rails generate Book --include=name,author_id --search=set --search-fields=name,author_id
1337
1427
  ```
1338
1428
 
1339
-
1340
-
1341
-
1342
-
1343
- #### Predicate
1429
+ #### Predicate Search
1344
1430
  NOT IMPLEMENTED YET
1345
1431
  TODO: implement me
1346
1432
 
1347
1433
 
1348
-
1349
-
1350
-
1351
-
1352
-
1353
-
1354
- ## Special Features
1355
-
1356
- ### `--attachments`
1434
+ ### `--attachments=`
1357
1435
 
1358
1436
  #### ActiveStorage Quick Setup
1359
1437
  (For complete docs, refer to https://guides.rubyonrails.org/active_storage_overview.html)
@@ -1397,7 +1475,7 @@ If it finds one, it will render thumbnails from the attachment variant `thumb`.
1397
1475
 
1398
1476
  If using the shortform syntax and Hot Glue does **not find a variant** called `thumb` at the code generation time, it will build scaffolding without thumbnails.
1399
1477
 
1400
- #### `--attachments` Long form syntax with 1 parameter
1478
+ #### `--attachments=` Long form syntax with 1 parameter
1401
1479
 
1402
1480
  **--attachments='_attachment name_{_variant name_}'**
1403
1481
 
@@ -1417,7 +1495,7 @@ end
1417
1495
  If using the long-form syntax with 1 parameter and Hot Glue does not find the specified variant declared in your attachment, it will stop and raise an error.
1418
1496
 
1419
1497
 
1420
- #### `--attachments` Long form syntax with 1st and 2nd parameters
1498
+ #### `--attachments=` Long form syntax with 1st and 2nd parameters
1421
1499
 
1422
1500
  **--attachments='_attachment name_{_variant name_|_field for saving original filename_}'**
1423
1501
 
@@ -1431,7 +1509,7 @@ Note that the `orig_filename` is not part of the inputted parameters, it simply
1431
1509
 
1432
1510
  Note: The 1st and 2nd parameters may be left empty (use `||`) but the 3rd and 4th parameters must either be specified or the parameter must be left off.
1433
1511
 
1434
- #### `--attachments` Long form syntax with 1st, 2nd, and 3rd parameters
1512
+ #### `--attachments=` Long form syntax with 1st, 2nd, and 3rd parameters
1435
1513
 
1436
1514
  An optional 3rd parameter to the long-form syntax allows you to specify direct upload using the word "direct", which will add direct_upload: true to your f.file_field tags.
1437
1515
 
@@ -1443,7 +1521,7 @@ If you leave the 2nd parameter blank when using the 3rd parameter, it will defau
1443
1521
 
1444
1522
  `--attachments='avatar{thumbnail||direct}'`
1445
1523
 
1446
- #### `--attachments` Long form syntax with 4 parameters
1524
+ #### `--attachments=` Long form syntax with 4 parameters
1447
1525
 
1448
1526
  The final (4th) parameter should be `dropzone` to enable dropzone support for this attachment.
1449
1527
 
@@ -1647,32 +1725,18 @@ Always:
1647
1725
 
1648
1726
  Don't include this last line in your factory code.
1649
1727
 
1650
- ## Nav Templates and `--no-nav-menu`
1651
- At the namespace level, you can have a file called `_nav.html.erb` to create tabbed bootstrap nav
1652
1728
 
1653
- To create the file for the first time (at each namespace), start by running
1654
- ```
1655
- bin/rails generate hot_glue:nav_template --namespace=xyz
1656
- ```
1657
-
1658
- This will append the file `_nav.html.erb` to the views folder at `views/xyz`. To begin, this file contains only the following:
1729
+ # SPECIAL FEATURES
1659
1730
 
1660
- ```
1661
- <ul class='nav nav-tabs'>
1662
- </ul>
1663
- ```
1664
1731
 
1665
- Once the file is present, any further builds in this namespace will:
1732
+ ## "Thing" Label
1666
1733
 
1667
- 1) Append to this `_nav.html.erb` file, adding a tab for the new built scaffold
1668
- 2) On the list view of the scaffold being built, it will include a render to the _nav partial, passing the name of the currently-viewed thing as the local variable `nav` (this is how the nav template knows which tab to make active).
1669
- ```
1670
- <%= render partial: "owner/nav", locals: {nav: "things"} %>
1671
- ```
1672
- (In this example `owner/` is the namespace and `things` is the name of the scaffold being built)
1734
+ Note that on a per model basis, you can also globally omit the label or set a unique label value using
1735
+ `@@table_label_singular` and `@@table_label_plural` on your model objects.
1673
1736
 
1674
- To suppress this behavior, add `--no-nav-menu` to the build command and the _nav template will not be touched.
1737
+ You have three options to specify labels explicitly with a string, and 1 option to specify a global name for which the words "Delete ___" and "New ___" will be added.
1675
1738
 
1739
+ If no `--label` is specified, it will be inferred to be the Capitalized version of the name of the thing you are building, with spaces for two or more words.
1676
1740
 
1677
1741
 
1678
1742
  ## Automatic Base Controller
@@ -1715,7 +1779,7 @@ Child portals have the headings omitted automatically (there is a heading identi
1715
1779
 
1716
1780
 
1717
1781
 
1718
- # Note about enums
1782
+ ## Note about enums
1719
1783
 
1720
1784
  The Rails 7 enum implementation for Postgres is very slick but has a counter-intuitive facet.
1721
1785
 
@@ -1760,7 +1824,7 @@ Now, your labels will show up on the front-end as defined in the `_labels` ("Is
1760
1824
 
1761
1825
  You can modify an enum so that instead of a drop down list, it displays a partial view that you must build. See the [v0.5.23 Release notes](https://github.com/hot-glue-for-rails/hot-glue#2023-10-01---v0523) for details.
1762
1826
 
1763
- ### Validation Magic
1827
+ ## Validation Magic
1764
1828
 
1765
1829
  Use ActiveRecord validations or hooks to validate your data. Hot Glue will automatically display the errors in the UI.
1766
1830
 
@@ -1778,7 +1842,7 @@ end
1778
1842
 
1779
1843
  ```
1780
1844
 
1781
- ### Typeahead Foreign Keys
1845
+ ## Typeahead Foreign Keys
1782
1846
 
1783
1847
  Let's go back to the first Books & Authors example.
1784
1848
  assuming you have created
@@ -1876,7 +1940,7 @@ This means that to find users within the search, the essential piece of informat
1876
1940
  --
1877
1941
 
1878
1942
 
1879
- ### TinyMCE
1943
+ ## TinyMCE
1880
1944
  1. `bundle add tinymce-rails` to add it to your Gemfile
1881
1945
 
1882
1946
  2. Add this inside of your `<head>` tag (at the bottom is fine)
@@ -1954,6 +2018,25 @@ These automatic pickups for partials are detected at build time. This means that
1954
2018
 
1955
2019
  # VERSION HISTORY
1956
2020
 
2021
+
2022
+ #### 2025-06-13 v0.6.20
2023
+ Breaking changes:
2024
+ • the setting in your hot_glue.yml file for `:pundit_default:` has been renamed just `:pundit:`.
2025
+ If you fail to rename it in your config file, hot glue will not build if it finds the old config setting.
2026
+
2027
+ • previously magic methods had a ability method that was the name of the magic method + `able?` as one word.
2028
+ this has been renamed to `_able?` and you must fix your cooresponding magic method ability methods by adding the underscore
2029
+
2030
+
2031
+ New Features
2032
+ • adds new invisibilty feature with `--invisible`, `--create-invisible`, and `--update-invisible` see docs above
2033
+ • changes previous `--hidden` into `--hidden`, `--create-hidden`, and `--update-hidden`. use hidden to apply to both fields
2034
+ • restores functionality of layout builder to magically distribute bootstrap 12 columns
2035
+ (4 columns use rows of 3 col widths; 2 columns use 6, 3 uses 4, etc). WHen using specified grouping mode, you
2036
+ probably want about 3 or 4 columns. When you have more than 5 it's difficult to build layouts that look good.
2037
+
2038
+
2039
+
1957
2040
  #### 2025-06-10 v0.6.19
1958
2041
 
1959
2042
  • Fixes magic button output behavior to correctly show the string returned by the bang menthod
@@ -6,7 +6,7 @@ class Field
6
6
  :self_auth,
7
7
  :singular_class, :singular, :sql_type, :ownership_field,
8
8
  :update_show_only, :namespace, :pundit, :plural,
9
- :stimmify, :hidden, :attachment_data, :god
9
+ :stimmify, :hidden_create, :hidden_update, :attachment_data, :god
10
10
 
11
11
 
12
12
  def initialize(
@@ -33,7 +33,8 @@ class Field
33
33
  @default_boolean_display = scaffold.default_boolean_display
34
34
  @namespace = scaffold.namespace_value
35
35
  @stimmify = scaffold.stimmify
36
- @hidden = scaffold.hidden
36
+ @hidden_create = scaffold.hidden_create
37
+ @hidden_update = scaffold.hidden_update
37
38
  @attachment_data = scaffold.attachments[name.to_sym]
38
39
  @god = scaffold.god
39
40
 
@@ -127,15 +127,24 @@ module HotGlue
127
127
  # if user_layout_columns.size > available_columns
128
128
  # raise "Your include statement #{@include_setting } has #{user_layout_columns.size} columns, but I can only construct up to #{available_columns}"
129
129
  # end
130
+
131
+
132
+ columns_to_work_with = (12 - @buttons_width)
133
+
134
+ if columns_to_work_with < user_layout_columns.size
135
+ raise "Your include statement #{@include_setting } has #{user_layout_columns.size} columns, but I can only construct up to #{columns_to_work_with}"
136
+ end
137
+
138
+ target_col_size = columns_to_work_with / user_layout_columns.size
139
+ extra_columns = columns_to_work_with % user_layout_columns.size
140
+
141
+
130
142
  user_layout_columns.each_with_index do |column,i|
131
143
  layout_object[:columns][:container][i] = column.split(",").collect(&:to_sym)
132
-
133
- default_col_width = 1
134
- if extra_columns > 0
135
- default_col_width += 1
136
- extra_columns -= 1
144
+ layout_object[:columns][:bootstrap_column_width][i] = target_col_size
145
+ if i < extra_columns
146
+ layout_object[:columns][:bootstrap_column_width][i] += 1
137
147
  end
138
- layout_object[:columns][:bootstrap_column_width][i] = default_col_width
139
148
  end
140
149
 
141
150
  if user_layout_columns.size < layout_object[:columns][:container].size
@@ -11,7 +11,8 @@ module HotGlue
11
11
  :attachments, :show_only, :columns_map, :pundit, :related_sets,
12
12
  :search, :search_fields, :search_query_fields, :search_position,
13
13
  :form_path, :layout_object, :search_clear_button, :search_autosearch,
14
- :stimmify, :stimmify_camel, :hidden
14
+ :stimmify, :stimmify_camel, :hidden_create, :hidden_update, :invisible_create,
15
+ :invisible_update, :plural
15
16
 
16
17
 
17
18
  def initialize(singular:, singular_class: ,
@@ -23,7 +24,8 @@ module HotGlue
23
24
  update_show_only:, attachments: , columns_map:, pundit:, related_sets:,
24
25
  search:, search_fields:, search_query_fields: , search_position:,
25
26
  search_clear_button:, search_autosearch:, layout_object:,
26
- form_path: , stimmify: , stimmify_camel:, hidden: )
27
+ form_path: , stimmify: , stimmify_camel:, hidden_create:, hidden_update: ,
28
+ invisible_create:, invisible_update: , plural: )
27
29
 
28
30
 
29
31
  @form_path = form_path
@@ -34,7 +36,11 @@ module HotGlue
34
36
  @layout_object = layout_object
35
37
  @stimmify = stimmify
36
38
  @stimmify_camel = stimmify_camel
37
- @hidden = hidden
39
+ @hidden_create = hidden_create
40
+ @hidden_update = hidden_update
41
+ @invisible_create = invisible_create
42
+ @invisible_update = invisible_update
43
+ @plural = plural
38
44
 
39
45
  @singular = singular
40
46
  @singular_class = singular_class
@@ -75,7 +81,7 @@ module HotGlue
75
81
  (big_edit ? ", \"turbo\": false" : "") +
76
82
  "}} do |f| %>" +
77
83
  "<%= f.hidden_field :__#{button_name}, value: \"__#{button_name}\" %>" +
78
- "<%= f.submit '#{button_name.titleize}'.html_safe, disabled: (#{singular}.respond_to?(:#{button_name}able?) && ! #{singular}.#{button_name}able? ), class: '#{singular}-button #{@layout_strategy.button_applied_classes} #{@layout_strategy.magic_button_classes}' %>" +
84
+ "<%= f.submit '#{button_name.titleize}'.html_safe, disabled: (#{singular}.respond_to?(:#{button_name}_able?) && ! #{singular}.#{button_name}_able? ), class: '#{singular}-button #{@layout_strategy.button_applied_classes} #{@layout_strategy.magic_button_classes}' %>" +
79
85
  "<% end %>"
80
86
  }.join("\n")
81
87
  end
@@ -88,7 +94,18 @@ module HotGlue
88
94
 
89
95
  size = layout_object[:columns][:bootstrap_column_width][i]
90
96
  "<div class='#{layout_strategy.column_classes_for_column_headings(size)} hg-heading-row heading--#{singular}--#{column.join("-")}' " + col_style + ">" +
91
- column.map(&:to_s).map{|col_name| "#{col_name.humanize}"}.join("<br />") + "</div>"
97
+ column.map(&:to_s).map{|col_name|
98
+ the_output = "#{col_name.humanize}"
99
+ if invisible_update.include?(col_name.to_sym)
100
+ if_statements = []
101
+ if_statements << "false" if invisible_update.include?(col_name.to_sym)
102
+ # if_statements << "@action == 'new'" if invisible_create.include?(col_name.to_sym)
103
+ the_output = "<% if ( " + if_statements.join(" || ") + " || policy(#{@plural}).#{col_name}_able? ) %>" +
104
+ + the_output + "<% end %>"
105
+
106
+ end
107
+ the_output
108
+ }.join("<br />") + "</div>"
92
109
  }.join("\n")
93
110
  return result
94
111
  end
@@ -160,7 +177,6 @@ module HotGlue
160
177
  "<% if @action == 'edit' %>" + columns_map[col].form_show_only_output + "<% else %>" + columns_map[col].form_field_output + "<% end %>"
161
178
  elsif update_show_only.include?(col) && @pundit && eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym)
162
179
  "<% if @action == 'new' && policy(@#{singular}).#{col}_able? %>" + columns_map[col].form_field_output + "<% else %>" + columns_map[col].form_show_only_output + "<% end %>"
163
-
164
180
  # show only on the update action overrides any pundit policy
165
181
  elsif @pundit && eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym)
166
182
  "<% if policy(@#{singular}).#{col}_able? %>" + columns_map[col].form_field_output + "<% else %>" + columns_map[col].form_show_only_output + "<% end %>"
@@ -178,17 +194,35 @@ module HotGlue
178
194
  data_attr = " data-#{@stimmify}-target='#{col_target}Wrapper'"
179
195
  end
180
196
 
181
- unless hidden.include?(col.to_sym)
182
- add_spaces_each_line( "\n <span #{@tinymce_stimulus_controller}class='<%= \"alert alert-danger\" if #{singular}.errors.details.keys.include?(:#{field_error_name}) %>' #{data_attr} >\n" +
183
- add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
184
- + field_result +
185
- (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
186
- "\n </span>\n ", 2)
187
- else
188
- columns_map[col].hidden_output
197
+
198
+ the_output = add_spaces_each_line( "\n <span #{@tinymce_stimulus_controller}class='<%= \"alert alert-danger\" if #{singular}.errors.details.keys.include?(:#{field_error_name}) %>' #{data_attr} >\n" +
199
+ add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
200
+ + field_result +
201
+ (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
202
+ "\n </span>\n ", 2)
203
+
204
+
205
+ if hidden_create.include?(col.to_sym) || hidden_update.include?(col.to_sym)
206
+ if_statements = []
207
+ if_statements << "@action == 'edit'" if hidden_update.include?(col.to_sym)
208
+ if_statements << "@action == 'new'" if hidden_create.include?(col.to_sym)
209
+
210
+ the_output = "<% if " + if_statements.join(" || ") + " %>" +
211
+ columns_map[col].hidden_output + "<% else %>" + the_output + "<% end %>"
189
212
  end
190
213
 
214
+ if invisible_create.include?(col) || invisible_update.include?(col)
215
+ if_statements = []
216
+ if_statements << "@action == 'edit'" if invisible_update.include?(col.to_sym)
217
+ if_statements << "@action == 'new'" if invisible_create.include?(col.to_sym)
191
218
 
219
+ the_output = "<% if !(" + if_statements.join(" || ") + ") || policy(@#{singular}).#{col}_able? %>" +
220
+ + the_output + "<% end %>"
221
+ end
222
+
223
+
224
+
225
+ the_output
192
226
  }.join("") + "\n </div>"
193
227
  }.join("\n")
194
228
  return result
@@ -233,7 +267,21 @@ module HotGlue
233
267
 
234
268
  label = "<label class='small form-text text-muted'>#{col.to_s.humanize}</label>"
235
269
 
236
- "#{inline_list_labels == 'before' ? label + "<br/>" : ''}#{field_output}#{inline_list_labels == 'after' ? "<br/>" + label : ''}"
270
+ the_output = "#{inline_list_labels == 'before' ? label + "<br/>" : ''}#{field_output}#{inline_list_labels == 'after' ? "<br/>" + label : ''}"
271
+ if invisible_create.include?(col) || invisible_update.include?(col)
272
+ if_statements = []
273
+ if invisible_update.include?(col.to_sym) && invisible_create.include?(col.to_sym)
274
+ # elsif invisible_create.include?(col.to_sym)
275
+ # if_statements << "!(@action == 'new')"
276
+ else
277
+ if_statements << "@action == 'edit'"
278
+ end
279
+
280
+ if_statements << " policy(#{singular}).#{col}_able?"
281
+ the_output = "<% if " + if_statements.join(" || ") + " %>" +
282
+ + the_output + "<% end %>"
283
+ end
284
+ the_output
237
285
  }.join( "<br />") + "</div>"
238
286
  }.join("\n")
239
287
  return result
@@ -29,7 +29,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
29
29
  :form_labels_position, :no_nav_menu, :pundit,
30
30
  :self_auth, :namespace_value, :record_scope, :related_sets,
31
31
  :search_clear_button, :search_autosearch, :include_object_names,
32
- :stimmify, :stimmify_camel, :hidden
32
+ :stimmify, :stimmify_camel, :hidden_create, :hidden_update,
33
+ :invisible_create, :invisible_update
33
34
  # important: using an attr_accessor called :namespace indirectly causes a conflict with Rails class_name method
34
35
  # so we use namespace_value instead
35
36
 
@@ -58,6 +59,12 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
58
59
  class_option :show_only, type: :string, default: ""
59
60
  class_option :update_show_only, type: :string, default: ""
60
61
  class_option :hidden, type: :string, default: ""
62
+ class_option :hidden_create, type: :string, default: ""
63
+ class_option :hidden_update, type: :string, default: ""
64
+ class_option :invisible, type: :string, default: ""
65
+ class_option :invisible_create, type: :string, default: ""
66
+ class_option :invisible_update, type: :string, default: ""
67
+
61
68
  class_option :ujs_syntax, type: :boolean, default: nil
62
69
  class_option :downnest, type: :string, default: nil
63
70
  class_option :magic_buttons, type: :string, default: nil
@@ -231,11 +238,29 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
231
238
  puts "show only field #{@show_only}}"
232
239
  end
233
240
 
234
- @hidden = options['hidden'].split(",").collect(&:to_sym)
235
- if @hidden.any?
236
- puts "hidden fields #{@hidden}}"
241
+ @hidden_all = options['hidden'].split(",").collect(&:to_sym)
242
+ @hidden_create = options['hidden_create'].split(",").collect(&:to_sym)
243
+ @hidden_update = options['hidden_update'].split(",").collect(&:to_sym)
244
+ @hidden_update.concat(@hidden_all) if @hidden_all.any?
245
+ @hidden_create.concat(@hidden_all) if @hidden_all.any?
246
+ @hidden_create.uniq!
247
+ @hidden_update.uniq!
248
+
249
+ if @hidden_create.any? || @hidden_update.any? || @hidden_all.any?
250
+ puts "hidden update fields #{@hidden_update}}"
251
+ puts "hidden create fields #{@hidden_create}}"
237
252
  end
238
253
 
254
+
255
+ @invisible_all = options['invisible'].split(",").collect(&:to_sym)
256
+ @invisible_create = options['invisible_create'].split(",").collect(&:to_sym)
257
+ @invisible_update = options['invisible_update'].split(",").collect(&:to_sym)
258
+ @invisible_update.concat(@invisible_all) if @invisible_all.any?
259
+ @invisible_update.uniq!
260
+ @invisible_create.concat(@invisible_all) if @invisible_all.any?
261
+ @invisible_create.uniq!
262
+
263
+
239
264
  @modify_as = {}
240
265
  if !options['modify'].empty?
241
266
  modify_input = options['modify'].split(",")
@@ -302,6 +327,9 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
302
327
  @new_button_label = options['new_button_label'] || (eval("#{class_name}.class_variable_defined?(:@@table_label_singular)") ? "New " + eval("#{class_name}.class_variable_get(:@@table_label_singular)") : "New " + singular.gsub("_", " ").titleize)
303
328
  @new_form_heading = options['new_form_heading'] || "New #{@label}"
304
329
 
330
+ # @table_display_name_singular = (eval("#{class_name}.class_variable_defined?(:@@table_label_singular)") ? eval("#{class_name}.class_variable_get(:@@table_label_singular)") : singular.gsub("_", " ").titleize)
331
+ @table_display_name_plural = (eval("#{class_name}.class_variable_defined?(:@@table_label_plural)") ? eval("#{class_name}.class_variable_get(:@@table_label_plural)") : plural.gsub("_", " ").titleize)
332
+
305
333
  setup_hawk_keys
306
334
  @form_placeholder_labels = options['form_placeholder_labels'] # true or false
307
335
  @inline_list_labels = options['inline_list_labels'] || get_default_from_config(key: :inline_list_labels) || 'omit' # 'before','after','omit'
@@ -358,11 +386,19 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
358
386
 
359
387
  @no_nav_menu = options['no_nav_menu']
360
388
 
389
+ if get_default_from_config(key: :pundit_default)
390
+ raise "please note the config setting `pundit_default` has been renamed `pundit`. please update your hot_glue.yml file"
391
+ end
392
+
361
393
  if @pundit.nil?
362
- @pundit = get_default_from_config(key: :pundit_default)
394
+ @pundit = get_default_from_config(key: :pundit)
363
395
  end
364
396
 
365
397
 
398
+ if (@invisible_create + @invisible_update).any? && !@pundit
399
+ raise "you specified invisible fields without using Pundit. please remove the invisible fields or use --pundit"
400
+ end
401
+
366
402
  if options['include'].include?(":") && @smart_layout
367
403
  raise HotGlue::Error, "You specified both --smart-layout and also specified grouping mode (there is a : character in your field include list); you must remove the colon(s) from your --include tag or remove the --smart-layout option"
368
404
  end
@@ -653,6 +689,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
653
689
  update_show_only: @update_show_only,
654
690
  singular_class: singular_class,
655
691
  singular: singular,
692
+ plural: @plural,
656
693
  hawk_keys: @hawk_keys,
657
694
  ownership_field: @ownership_field,
658
695
  form_labels_position: @form_labels_position,
@@ -670,7 +707,10 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
670
707
  form_path: form_path_new_helper,
671
708
  stimmify: @stimmify,
672
709
  stimmify_camel: @stimmify_camel,
673
- hidden: @hidden
710
+ hidden_create: @hidden_create,
711
+ hidden_update: @hidden_update,
712
+ invisible_create: @invisible_create,
713
+ invisible_update: @invisible_update,
674
714
  )
675
715
  elsif @markup == "slim"
676
716
  raise(HotGlue::Error, "SLIM IS NOT IMPLEMENTED")
@@ -98,8 +98,8 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
98
98
  <% if @pundit && !@pundit_policy_override %>
99
99
  authorize @<%= singular %><% elsif @pundit && @pundit_policy_override %>
100
100
  skip_authorization
101
- raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.new?<% end %><% if @pundit %>
102
- @action = 'new'
101
+ raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.new?<% end %>
102
+ @action = 'new' <% if @pundit %>
103
103
  rescue Pundit::NotAuthorizedError
104
104
  flash[:alert] = 'You are not authorized to perform this action.'
105
105
  load_all_<%= plural %>
@@ -135,7 +135,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
135
135
  instance_last_item: true,
136
136
  put_form: true).gsub("(#{singular}", "(@#{singular}") %><% end %>
137
137
  else
138
- flash[:alert] = "Oops, your <%= singular_name %> could not be created. #{@hawk_alarm}"
138
+ flash[:alert] = "Oops, your <%= @label %> could not be created. #{@hawk_alarm}"
139
139
  @action = 'new'
140
140
  <% unless @display_edit_after_create %>render :create, status: :unprocessable_entity<% else %>render :new , status: :unprocessable_entity<% end %>
141
141
  end<% if @pundit %>
@@ -238,7 +238,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
238
238
  redirect_to <%= path_helper_plural(false) %>
239
239
  <% end %>
240
240
  else
241
- flash[:alert] = "<%= singular_name.titlecase %> could not be saved. #{@hawk_alarm}"
241
+ flash[:alert] = "<%= @label %> could not be saved. #{@hawk_alarm}"
242
242
  <%= @alt_lookups.collect{ |k,v|
243
243
  assoc = k.gsub("_id","")
244
244
  "@#{singular }.#{k} = #{class_name}.find(@#{singular }.id).person.id if @#{singular }.errors.include?(:#{assoc})"
@@ -260,9 +260,9 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
260
260
  raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.destroy?<% end %>
261
261
  begin
262
262
  @<%=singular_name%>.destroy!
263
- flash[:notice] = '<%= singular_name.titlecase %> successfully deleted'
263
+ flash[:notice] = '<%= @label %> successfully deleted'
264
264
  rescue ActiveRecord::RecordNotDestroyed => e
265
- flash[:alert] = '<%= singular_name.titlecase %> could not be deleted'
265
+ flash[:alert] = '<%= @label %> could not be deleted'
266
266
  end
267
267
  <%= post_action_parental_updates.join("\n ") %>
268
268
  load_all_<%= plural %><% if @pundit %>
@@ -284,12 +284,23 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
284
284
  end<% end %><% end %>
285
285
 
286
286
  def <%=singular_name%>_params
287
- params.require(:<%= testing_name %>).permit(<%= ((fields_filtered_for_strong_params - @show_only ) + @magic_buttons.collect{|x| "__#{x}"}).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>)
287
+ fields = <%= ((fields_filtered_for_strong_params - @show_only) + @magic_buttons.collect{|x| "__#{x}"}).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>
288
+ params.require(:<%= testing_name %>).permit(fields)
288
289
  end<% if @update_show_only %>
289
290
 
290
291
  <% unless @no_edit %>
291
292
  def update_<%=singular_name%>_params
292
- params.require(:<%= testing_name %>).permit(<%= ((fields_filtered_for_strong_params - @update_show_only) + @magic_buttons.collect{|x| "__#{x}"}).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>)
293
+ fields = <%= ((fields_filtered_for_strong_params - @update_show_only) + @magic_buttons.collect{|x| "__#{x}"}).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>
294
+ <%= (fields_filtered_for_strong_params - @update_show_only).collect{|col|
295
+ # TODO : fields not on show only also not invisible should be checked here
296
+ # for _able? methods and added only when able
297
+ if (@invisible_create.include?(col) || eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym))
298
+ "fields.delete :#{col} if !policy(@#{singular}).#{col}_able?"
299
+ else
300
+ nil
301
+ end
302
+ }.compact.join("\n ") %>
303
+ params.require(:<%= testing_name %>).permit(fields)
293
304
  end<% end %>
294
305
  <% end %>
295
306
 
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.6.19'
3
+ CURRENT = '0.6.20'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hot-glue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.19
4
+ version: 0.6.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Fleetwood-Boldt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-10 00:00:00.000000000 Z
11
+ date: 2025-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails