hot-glue 0.6.22 → 0.6.24

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.
data/README.md CHANGED
@@ -321,16 +321,7 @@ 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
-
324
+ About these docs: The options (take an argument) flags (do not take an argument) have been merged together. Flags will be listed with no trailing `=`
334
325
 
335
326
  ### `--god` or `--gd`
336
327
 
@@ -346,167 +337,103 @@ end
346
337
 
347
338
  ```
348
339
 
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
-
340
+ ### `--namespace=`
401
341
 
402
- ### `--big-edit`
342
+ pass `--namespace=` as an option to denote a namespace to apply to the Rails path helpers
403
343
 
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
344
 
406
- The user will be taken to a full-screen edit page instead of an edit-in-place interaction.
345
+ `./bin/rails generate hot_glue:scaffold Thing --namespace=dashboard`
407
346
 
408
- When using `--big-edit`, any downnested portals will be displayed on the edit page instead of on the list page.
347
+ This produces several views at `app/views/dashboard/things/` and a controller at`app/controllers/dashboard/things_controller.rb`
409
348
 
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.
349
+ The controller looks like so:
411
350
 
412
- Likewise, the controller's `update` action always redirects instead of using Turbo.
351
+ ```
352
+ class Dashboard::ThingsController < ApplicationController
353
+ before_action :authenticate_user!
354
+ before_action :load_thing, only: [:show, :edit, :update, :destroy]
355
+ def load_thing
356
+ @thing = current_user.things.find(params[:id])
357
+ end
358
+ ...
359
+ end
413
360
 
361
+ ```
414
362
 
415
- ### `--display-list-after-update`
363
+ ### `--auth=`
416
364
 
417
- After an update-in-place normally only the edit view is swapped out for the show view of the record you just edited.
365
+ By default, it will be assumed you have a `current_user` for your user authentication. This will be treated as the "authentication root" for the "poor man's auth" explained above.
418
366
 
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).
367
+ The poor man's auth presumes that object graphs have only one natural way to traverse them (that is, one primary way to traverse them), and that all relationships infer that a set of things or their descendants are granted access to "me" for reading, writing, updating, and deleting.
420
368
 
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.
369
+ Of course this is a sloppy way to do access control, and can easily leave open endpoints your real users shouldn't have access to.
422
370
 
423
- ### `--with-turbo-streams`
371
+ When you display anything built with the scaffolding, Hot Glue assumes the `current_user` will have `has_many` association that matches the pluralized name of the scaffold. In the case of nesting, we will automatically find the nested objects first, then continue down the nest chain to find the target object. This is how Hot Glue assumes all object are 'anchored' to the logged-in user. (As explained in the `--nested` section.)
424
372
 
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.
373
+ If you use Devise, you probably already have a `current_user` method available in your controllers. If you don't use Devise, you can implement it in your ApplicationController.
426
374
 
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.
375
+ If you use a different object other than "User" for authentication, override using the `auth` option.
428
376
 
429
- This happens using two interconnected mechanisms:
377
+ `./bin/rails generate hot_glue:scaffold Thing --auth=current_account`
430
378
 
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
379
+ You will note that in this example it is presumed that the Account object will have an association for `things`
432
380
 
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.
381
+ It is also presumed that when viewing their own dashboard of things, the user will want to see ALL of their associated things.
434
382
 
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.
383
+ If you supply nesting (see below), your nest chain will automatically begin with your auth root object (see nesting)
436
384
 
437
385
 
438
- ### `--no-list-label`
439
- Omits list LABEL itself above the list. (Do not confuse with the list heading which contains the field labels.)
386
+ ### `--auth_identifier=`
440
387
 
441
- Note that list labels may be automatically omitted on downnested scaffolds.
388
+ Your controller will call a method authenticate_ (AUTH IDENTIFIER) bang, like:
442
389
 
390
+ `authenticate_user!`
443
391
 
444
- ### `--no-list-heading`
392
+ Before all of the controller actions. If you leave this blank, it will default to using the variable name supplied by auth with "current_" stripped away.
393
+ (This is setup for devise.)
445
394
 
446
- Omits the heading of column names that appears above the 1st row of data.
395
+ Be sure to implement the following method in your ApplicationController or some other method. Here's a quick example using Devise. You will note in the code below, user_signed_in? is implemented when you add Devise methods to your User table.
447
396
 
448
- ### `--include-object-names`
397
+ As well, the `after_sign_in_path_for(user)` here is a hook for Devise also that provides you with after login redirect to the page where the user first intended to go.
449
398
 
450
- When you are "Editing X" we specify that X is a ___ (author, book, room, etc)
399
+ ```
400
+ def authenticate_user!
401
+ if ! user_signed_in?
402
+ session['user_return_to'] = request.path
403
+ redirect_to new_user_registration_path
404
+ end
405
+ end
451
406
 
452
- e.g. "Editing author Edgar Allan Poe" vs "Editing Edgar Allan Poe"
407
+ def after_sign_in_path_for(user)
408
+ session['user_return_to'] || account_url(user)
409
+ end
410
+ ```
453
411
 
454
- Can also be specified globally in `config/hot_glue.yml`
455
412
 
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
413
+ The default (do not pass `auth_identifier=`) will match the `auth` (So if you use 'account' as the auth, `authenticate_account!` will get invoked from your generated controller; the default is always 'user', so you can leave both auth and auth_identifier off if you want 'user')
458
414
 
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
415
 
464
- This will append the file `_nav.html.erb` to the views folder at `views/xyz`. To begin, this file contains only the following:
416
+ `./bin/rails generate hot_glue:scaffold Thing --auth=current_account --auth_identifier=login`
465
417
 
418
+
419
+ In this example, the controller produced with:
466
420
  ```
467
- <ul class='nav nav-tabs'>
468
- </ul>
421
+ before_action :authenticate_login!
469
422
  ```
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).
423
+ However, the object graph anchors would continue to start from current_account. That is,
475
424
  ```
476
- <%= render partial: "owner/nav", locals: {nav: "things"} %>
425
+ @thing = current_account.things.find(params[:id])
477
426
  ```
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
-
484
- ## Options With Arguments
485
-
486
- All options begin with two dashes (`--`) and a followed by an `=` and a value
487
-
488
- ### `--namespace=`
489
-
490
- pass `--namespace=` as an option to denote a namespace to apply to the Rails path helpers
491
-
492
427
 
493
- `./bin/rails generate hot_glue:scaffold Thing --namespace=dashboard`
428
+ Use empty string to **turn this method off**:
429
+
430
+ `./bin/rails generate hot_glue:scaffold Thing --auth=current_account --auth_identifier=''`
494
431
 
495
- This produces several views at `app/views/dashboard/things/` and a controller at`app/controllers/dashboard/things_controller.rb`
432
+ In this case a controller would be generated that would have NO before_action to authenticate the account, but it would still treat the current_account as the auth root for the purpose of loading the objects.
496
433
 
497
- The controller looks like so:
434
+ Please note that this example would produce non-functional code, so you would need to manually fix your controllers to make sure `current_account` is available to the controller.
498
435
 
499
- ```
500
- class Dashboard::ThingsController < ApplicationController
501
- before_action :authenticate_user!
502
- before_action :load_thing, only: [:show, :edit, :update, :destroy]
503
- def load_thing
504
- @thing = current_user.things.find(params[:id])
505
- end
506
- ...
507
- end
508
436
 
509
- ```
510
437
 
511
438
  ### `--nested=`
512
439
 
@@ -569,7 +496,7 @@ Then, finally the @charge will be loaded
569
496
 
570
497
  `@charge = @line.charges.find(params[:id])`
571
498
 
572
- This is "starfish access control" or "poor man's access control." It works when the current user has several things they can manage, and by extension can manage children of those things.
499
+ This is "starfish access control" or "poor man's access control." It works when the current user has several things they can manage, and by extension can manage children of those things.
573
500
 
574
501
 
575
502
  #### Example #3: Polymorphic Nesting
@@ -603,83 +530,139 @@ Notices the relationship from the parent to child is `rules` but from the child
603
530
 
604
531
 
605
532
 
606
- ### `--auth=`
533
+ ### `--downnest=`
607
534
 
608
- By default, it will be assumed you have a `current_user` for your user authentication. This will be treated as the "authentication root" for the "poor man's auth" explained above.
535
+ Automatically create subviews down your object tree. This should be the name of a has_many relationship based from the current object.
536
+ 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.
609
537
 
610
- The poor man's auth presumes that object graphs have only one natural way to traverse them (that is, one primary way to traverse them), and that all relationships infer that a set of things or their descendants are granted access to "me" for reading, writing, updating, and deleting.
538
+ The downnested child table (not to be confused with this object's `--nested` setting, where you are specifying this object's _parents_) is called a **child portal**. When you create a record in the child portal, the related record is automatically set to be owned by its parent (as specified by `--nested`). For an example, see the [v0.4.7 release notes](https://github.com/jasonfb/hot-glue/releases/tag/v0.4.7).
611
539
 
612
- Of course this is a sloppy way to do access control, and can easily leave open endpoints your real users shouldn't have access to.
540
+ Can now be created with more space (wider) by adding a `+` to the end of the downnest name
541
+ - e.g. `--downnest=abc+,xyz`
613
542
 
614
- When you display anything built with the scaffolding, Hot Glue assumes the `current_user` will have `has_many` association that matches the pluralized name of the scaffold. In the case of nesting, we will automatically find the nested objects first, then continue down the nest chain to find the target object. This is how Hot Glue assumes all object are 'anchored' to the logged-in user. (As explained in the `--nested` section.)
543
+ 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
615
544
 
616
- If you use Devise, you probably already have a `current_user` method available in your controllers. If you don't use Devise, you can implement it in your ApplicationController.
545
+ If you are nesting from a controller that uses big edit, your child portals do not display on the list page.
617
546
 
618
- If you use a different object other than "User" for authentication, override using the `auth` option.
547
+ Instead, they display on the edit page, and they display below the record using a Bootstrap tab nav that is automatically built for you.
619
548
 
620
- `./bin/rails generate hot_glue:scaffold Thing --auth=current_account`
621
549
 
622
- You will note that in this example it is presumed that the Account object will have an association for `things`
623
550
 
624
- It is also presumed that when viewing their own dashboard of things, the user will want to see ALL of their associated things.
551
+ #### Polymorphic Downnesting
625
552
 
626
- If you supply nesting (see below), your nest chain will automatically begin with your auth root object (see nesting)
553
+ Here, a `Blast` `has_many :rules, as: :ruleable`
627
554
 
555
+ The child object is named `Rule` but it can belong to a Blast or an Agent. (Agent also has a similar has_many for Rules)
628
556
 
629
- ### `--auth_identifier=`
557
+ `belongs_to :ruleable, polymorphic: true`
630
558
 
631
- Your controller will call a method authenticate_ (AUTH IDENTIFIER) bang, like:
559
+ We build the blast & agent controllers like so:
632
560
 
633
- `authenticate_user!`
561
+ bin/rails generate hot_glue:scaffold Blast --downnest='blast_rules(rules)'
562
+ bin/rails generate hot_glue:scaffold Agent --downnest='agent_rules(rules)'
634
563
 
635
- Before all of the controller actions. If you leave this blank, it will default to using the variable name supplied by auth with "current_" stripped away.
636
- (This is setup for devise.)
564
+ Notice that the relationship name is `rules` (not blast_rules), so what goes before the parenthesis is the controller name (with prefix)
565
+ What goes inside the controller name is the real relationship name.
637
566
 
638
- Be sure to implement the following method in your ApplicationController or some other method. Here's a quick example using Devise. You will note in the code below, user_signed_in? is implemented when you add Devise methods to your User table.
567
+ For the children, we can't build one controller for the Rule, instead we build one for the `AgentRules` and another for the `BlastRules`
639
568
 
640
- As well, the `after_sign_in_path_for(user)` here is a hook for Devise also that provides you with after login redirect to the page where the user first intended to go.
569
+ bin/rails generate hot_glue:scaffold Rule --nested='blast(ruleable)' --controller-prefix='Blast'
570
+ bin/rails generate hot_glue:scaffold Rule --nested='agent(ruleable)' --controller-prefix='Agent'
571
+
572
+ (I realize building one child controller for each type of polymorph is tedius, but this is the best solution I could come up with.)
573
+
574
+ As these are children, what goes into the `--netsed` setting inside the parentheses is the polymorphic name specified by `as:` when declaring the `belongs_to`
575
+
576
+ routes.rb
641
577
 
642
578
  ```
643
- def authenticate_user!
644
- if ! user_signed_in?
645
- session['user_return_to'] = request.path
646
- redirect_to new_user_registration_path
647
- end
648
- end
579
+ resources :agents do
580
+ resources :agent_rules
581
+ end
649
582
 
650
- def after_sign_in_path_for(user)
651
- session['user_return_to'] || account_url(user)
652
- end
583
+ resources :blasts do
584
+ resources :blast_rules
585
+ end
653
586
  ```
654
587
 
588
+ ### `--stacked-downnesting`
655
589
 
656
- The default (do not pass `auth_identifier=`) will match the `auth` (So if you use 'account' as the auth, `authenticate_account!` will get invoked from your generated controller; the default is always 'user', so you can leave both auth and auth_identifier off if you want 'user')
590
+ 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.
657
591
 
658
592
 
659
- `./bin/rails generate hot_glue:scaffold Thing --auth=current_account --auth_identifier=login`
660
593
 
661
-
662
- In this example, the controller produced with:
663
- ```
664
- before_action :authenticate_login!
665
- ```
666
- However, the object graph anchors would continue to start from current_account. That is,
667
- ```
668
- @thing = current_account.things.find(params[:id])
669
- ```
594
+ ### `--downnest-shows-headings` (default: false)
595
+ Show headings (the label of the list) above downnested portals.
670
596
 
671
- Use empty string to **turn this method off**:
672
-
673
- `./bin/rails generate hot_glue:scaffold Thing --auth=current_account --auth_identifier=''`
674
597
 
675
- In this case a controller would be generated that would have NO before_action to authenticate the account, but it would still treat the current_account as the auth root for the purpose of loading the objects.
676
598
 
677
- Please note that this example would produce non-functional code, so you would need to manually fix your controllers to make sure `current_account` is available to the controller.
599
+
600
+ ### `--record-scope=`
601
+
602
+ Record scope allows you to apply a model based scope for the controller being generated.
603
+ This is applied on top of all other scopes, searches, and modifiers applied to the built controller.
604
+
605
+ `bin/rails :generate hot_glue:scaffold Order --record-scope='.is_open'`
606
+
607
+ Be sure to use single quotes (`'`) and don't forget the dot (`.`) before your scope(s).
608
+
609
+ Make sure your Order model has a scope `is_open`, like so:
610
+
611
+ ```
612
+ scope :is_open, -> {where(state == 'open')}
613
+ ```
614
+
615
+ Now all records displayed through the generated controller_
616
+
617
+
618
+ ### `--big-edit`
619
+
620
+ 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`.
621
+
622
+ The user will be taken to a full-screen edit page instead of an edit-in-place interaction.
623
+
624
+ When using `--big-edit`, any downnested portals will be displayed on the edit page instead of on the list page.
625
+
626
+ 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.
627
+
628
+ Likewise, the controller's `update` action always redirects instead of using Turbo.
629
+
630
+
631
+ ### `--include=`
632
+ Separate field names by COMMA
633
+
634
+ If you specify an include list, it will be treated as a whitelist: no fields will be included unless specified on the include list.
635
+
636
+ `./bin/rails generate hot_glue:scaffold Account --include=first_name,last_name,company_name,created_at,kyc_verified_at`
637
+
638
+ You may not specify both include and exclude.
639
+
640
+ Include setting is affected by both specified grouping mode and smart layouts, explained below.
641
+
642
+
643
+ ### `--exclude=`
644
+ (separate field names by COMMA)
645
+
646
+ By default, all fields are included unless they are on the default exclude list. (The default exclude list is `id`, `created_at`, `updated_at`, `encrypted_password`, `reset_password_token`, `reset_password_sent_at`, `remember_created_at`, `confirmation_token`, `confirmed_at`, `confirmation_sent_at`, `unconfirmed_email`.)
647
+
648
+ If you specify any exclude list, those excluded **and** the default exclude list will be excluded. (If you need any of the fields on the default exclude list, you must use `--include` instead.)
649
+
650
+
651
+ `./bin/rails generate hot_glue:scaffold Account --exclude=password`
652
+
653
+
654
+ ### `--display-list-after-update`
655
+
656
+ After an update-in-place normally only the edit view is swapped out for the show view of the record you just edited.
657
+
658
+ 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).
659
+
660
+ 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.
678
661
 
679
662
 
680
663
  ### `--hawk=`
681
664
 
682
- Hawk a foreign key that is not the object's owner to within a specified scope.
665
+ Hawk a foreign key that is not the object's owner to within a specified scope.
683
666
 
684
667
  Assuming a Pet belong_to a :human, when building an Appointments scaffold,
685
668
  you can hawk the `pet_id` to the current human's pets. (Whoever is the authentication object.)
@@ -693,16 +676,32 @@ The short form looks like this. It presumes there is a 'pets' association from `
693
676
 
694
677
  This is covered in [Example #3 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
695
678
 
696
- To hawk to a scope that is not the currently authenticated user, use the long form with `{...}`
697
- to specify the scope. Be sure to note to add the association name itself, like `users`:
679
+ To hawk to a scope that is not the currently authenticated user, use the long form with `{...}`
680
+ to specify the scope. Be sure to note to add the association name itself, like `users`:
698
681
 
699
682
  `--hawk=user_id{current_user.family.users}`
700
683
 
701
- This would hawk the Appointment's `user_id` key to any users who are within the scope of the
702
- current_user's has_many association (so, for any other "my" family, would be `current_user.family.users`).
684
+ This would hawk the Appointment's `user_id` key to any users who are within the scope of the
685
+ current_user's has_many association (so, for any other "my" family, would be `current_user.family.users`).
703
686
 
704
687
  This is covered in [Example #4 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
705
688
 
689
+ ### `--with-turbo-streams`
690
+
691
+ 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.
692
+
693
+ **_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.
694
+
695
+ This happens using two interconnected mechanisms:
696
+
697
+ 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
698
+
699
+ 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.
700
+
701
+ 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.
702
+
703
+
704
+
706
705
 
707
706
  ### `--plural=`
708
707
 
@@ -712,7 +711,7 @@ Only use for non-standard plurlizations, and be sure to pass it as TitleCase (as
712
711
  An better alternative is to define the non-standard plurlizations globally in your app, which Hot Glue will respect.
713
712
 
714
713
  Make a file at `config/initializers/inflections.rb`
715
-
714
+
716
715
  Add new inflection rules using the following format:
717
716
  ```
718
717
  ActiveSupport::Inflector.inflections do |inflect|
@@ -722,32 +721,256 @@ end
722
721
  ```
723
722
 
724
723
 
724
+ ### `--ujs_syntax=true` (Default is set automatically based on whether you have turbo-rails installed)
725
725
 
726
+ If you are pre-Turbo (UJS), your delete buttons will come out like this:
727
+ `data: {'confirm': 'Are you sure you want to delete....?'}`
726
728
 
727
- ### `--exclude=`
728
- (separate field names by COMMA)
729
+ If you are Turbo (Rails 7 or Rails 6 with proactive Turbo-Rails install), your delete button will be:
730
+ `data: {'turbo-confirm': 'Are you sure you want to delete....?'}`
729
731
 
730
- By default, all fields are included unless they are on the default exclude list. (The default exclude list is `id`, `created_at`, `updated_at`, `encrypted_password`, `reset_password_token`, `reset_password_sent_at`, `remember_created_at`, `confirmation_token`, `confirmed_at`, `confirmation_sent_at`, `unconfirmed_email`.)
732
+ If you specify the flag, you preference will be used. If you leave the flag off, Hot Glue will detect the presence of Turbo-Rails in your app.
731
733
 
732
- If you specify any exclude list, those excluded **and** the default exclude list will be excluded. (If you need any of the fields on the default exclude list, you must use `--include` instead.)
734
+ **WARNING**: If you created a new Rails app since October 2021 and you have the yanked turbo-rails Gems on your local machine,
735
+ you will have some bugs with the delete buttons and also not be on the latest version of turbo-rails.
733
736
 
737
+ Make sure to uninstall the yanked 7.1.0 and 7.1.1 from your machine with `gem uninstall turbo-rails`
738
+ and also fix any Rails apps created since October 2021 by fixing the Gemfile. Details here:
739
+ https://stackoverflow.com/questions/70671324/new-rails-7-turbo-app-doesnt-show-the-data-turbo-confirm-alert-messages-dont-f
734
740
 
735
- `./bin/rails generate hot_glue:scaffold Account --exclude=password`
736
741
 
742
+ ### `--magic-buttons=`
743
+ If you pass a list of magic buttons (separated by commas), they will appear in the button area on your list.
737
744
 
738
- ### `--include=`
739
- Separate field names by COMMA
745
+ It will be assumed there will be corresponding bang methods on your models.
740
746
 
741
- If you specify an include list, it will be treated as a whitelist: no fields will be included unless specified on the include list.
747
+ The bang (`!`) methods can respond in one of four ways:
748
+
749
+ • With true, in which case a generic success message will be shown in the flash notice (“Approved” or “Rejected” in this case)
750
+
751
+ • With false, in which case a generic error message will be shown in the flash alert (“Could not approve…”)
752
+
753
+ • With a string, which will be assumed to be a “success” case, and will be passed to the front-end in the alert notice.
754
+
755
+ • Raise an ActiveRecord exception
756
+
757
+ This means you can be a somewhat lazy about your bang methods, but keep in mind the truth operator compares boolean true NOT any object is truth. So your return object must either be actually true (boolean), or an object that is string or string-like (responds to .to_s). Want to just say it didn’t work? Return false. Want to just say it was OK? Return true. Want to say it was successful but provide a more detailed response? Return a string.
758
+
759
+ Finally, you can raise an ActiveRecord error which will also get passed to the user in the flash alert area.
760
+
761
+ For more information see [Example 6 in the Tutorial](https://school.jfbcodes.com/8188)
762
+
763
+ You can also define methods on your model that have the same name as the button with `_able?` at the end.
764
+ (Prior to v0.6.20, these methods expected names with `able?` but no underscore.)
765
+
766
+ The button will be display as disabled if the method returns false.
767
+
768
+
769
+
770
+ ### `--related-sets=`
771
+
772
+ 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.
773
+
774
+ Consider the classic example of three tables: users, user_roles, and roles
775
+
776
+ User `has_many :user_roles` and `has_many :roles, through: :user_roles`
777
+ UserRole `belongs_to :user` and `belongs_to :role`
778
+ and Role `has_many :user_roles` and `has_many :user, through: :user_roles`
779
+
780
+ We'll generate a scaffold to edit the users table. A checkbox set of related roles will also appear to allow editing of roles. (In this example, the only field to be edited is the email field.)
781
+
782
+ ```
783
+ rails generate hot_glue:scaffold User --related-sets=roles --include=email,roles --gd
784
+ ```
785
+
786
+ Note this leaves open a privileged escalation attack (a security vulnerability).
787
+
788
+ 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)
789
+
790
+ • Remember you model should have `accepts_nested_attributes_for :roles, allow_destroy: true`
791
+
792
+ • If you are using an --include list (not auto-detect or smart layout), be sure to treat the tags as-if it was one field on your layout and insert it according to where you want it.
793
+
794
+ • Each related set can take two additional parameters: specify the label to use as the related label using curly braces `{`...`}`, and specify any hawk scope to be applied to the displayed list of associated objects
795
+ (like the --hawk, with the need to explicitly call `--hawk`) using `[`...`]`
796
+ Both parameters are optional. If the label field is unspecified, it will default to `label`.
797
+ If the 2nd parameter is unspecified, it will display all records in the related table, with the base class + `.all`
798
+
799
+ If the 1st parameter is left off, still use square braces `[...]` for the hawk.
800
+
801
+ Example:
802
+ `rails generate hot_glue:scaffold User --nested=company --related-sets=roles{name}[company.roles] --include=email,roles --gd`
803
+
804
+ This shows the related set of `roles` using the field named `name` on the role object to display its name. Only the roles associated with the current company via the `company.roles` association.
805
+ Notice that here `company` must be in scope, which can either be supplied by you in the base class, or in this example we have nested User within Company (so its nest path would be different than the example above), which would put `company` in the scope of the build.
806
+
807
+
808
+ ### `--factory-creation={ ... }`
809
+
810
+ The code you specify inside of `{` and `}` will be used to generate a new object. The factory should instantiate with any arguments (I suggest Ruby keyword arguments) and must provide a method that is the name of the thing.
811
+
812
+ You may use semi-colons to separate multiple lines of code.
813
+
814
+ For example, a user Factory might be called like so:
815
+
816
+ `./bin/rails generate hot_glue:scaffold User --factory-creation={factory = UserFactory.new(params: user_params)} --gd`
817
+
818
+ (Note we are relying on the `user_params` method provided by the controller.)
819
+
820
+ You must do one of two things:
821
+
822
+ 1) In the code you specify, set an instance variable `@user` to be the newly created thing. (Your code should contain something like `@thing = ` to trigger this option.)
823
+ 2) Make a local variable called `factory` **and** have a method of the name of the object (`user`) on a local variable called `factory` that your code created
824
+
825
+ (The code example above is the option for #2 because it does not contain `@user =`)
826
+
827
+ If using number #2, Hot Glue will append this to the code specified:
828
+ ```
829
+ @user = factory.user
830
+ ```
831
+
832
+
833
+ Here's a sample UserFactory that will create a new user only if one with a matching email address doesn't exist. (Otherwise, it will update the existing record.)
834
+ Your initialize method can take any params you need it to, and using this pattern your business logic is applied consistently throughout your app. (You must, of course, use your Factory everywhere else in your app too.)
835
+
836
+ ```
837
+ class UserFactory
838
+ attr_reader :user
839
+ attr_accessor :email
840
+
841
+ def initialize(params: {})
842
+ user = User.find_or_create_by(email: params[:email])
843
+
844
+ user.update(params)
845
+ if user.new_record?
846
+ # do special new user logic here, like sending an email
847
+ end
848
+ end
849
+ end
850
+ ```
851
+
852
+
853
+ be sure your factory code creates a local variable that follows this name
854
+
855
+ **<downcase association name>**_factory.<downcase association name>
856
+
857
+ Thus, your factory object must have a method of the same name as the factory being created which returns the thing that got created.
858
+ (It can do the creation either on instantiation or when calling that method)
859
+
860
+ For example, assuming the example from above, we are going to do the lookup ourselves inside of our own `AgentFactory` object.)
861
+
862
+ ```
863
+ factory = AgentFactory.new(find_or_create_by_email: agent_company_params[:__lookup_email],
864
+ params: modified_params)
865
+ ```
866
+
867
+ Here the new AgentFactory will receive any variables by keyword argument, and since you're specifying the calling code here, Hot Glue does not dictate your factory's setup.
868
+ However, two special variables are in scope which you can use in your calling code.
869
+
870
+ `*_params` (where * is the name of the thing you are building)
871
+ `modified_params`
872
+
873
+ Either one must be received by your factory for your factory to create data based off the inputted data.
874
+
875
+ Remember, `*_params` has the input params passed only the through the sanitizer, and modified_params has it passed through the timezone aware mechanism and other Hot Glue-specific defaults.
876
+
877
+ Always:
878
+ • In your factory calling code, assign the variable `factory = ` (do not use a different variable name),
879
+ • Write a factory object with a `new` method that received the paramters you are specifying in your calling code,
880
+ • Be sure your factory has an _instance method_ a method with the **same name** of the built object, which hot glue will call next:
881
+
882
+ `@agent = factory.agent`
883
+
884
+ Don't include this last line in your factory code.
885
+
886
+
887
+
888
+ ## On/off switches
889
+
890
+ ### `--specs-only`
891
+
892
+ Produces ONLY the controller spec file, nothing else.
893
+
894
+ ### `--no-specs`
895
+
896
+ Produces all the files except the spec file.
897
+
898
+ ### `--no-paginate` (default: false)
899
+
900
+ Omits pagination. (All list views have pagination by default.)
901
+
902
+ ### `--paginate-per-page-selector` (default: false)
903
+
904
+ Show a small drop-down below the list to let the user choose 10, 25, or 100 results per page.
905
+
906
+
907
+ ### `--no-list`
908
+
909
+ 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.)
910
+
911
+ ### `--no-create`
912
+
913
+ Omits new & create actions.
914
+
915
+ ### `--no-delete`
916
+
917
+ Omits delete button & destroy action.
918
+
919
+ ### `--no-controller`
920
+
921
+ Omits controller.
922
+
923
+ ### `--no-list`
924
+
925
+ Omits list views.
926
+
927
+
928
+
929
+ ### `--no-list-label`
930
+ Omits list LABEL itself above the list. (Do not confuse with the list heading which contains the field labels.)
931
+
932
+ Note that list labels may be automatically omitted on downnested scaffolds.
933
+
934
+
935
+ ### `--no-list-heading`
936
+
937
+ Omits the heading of column names that appears above the 1st row of data.
938
+
939
+
940
+
941
+
942
+
943
+ ## Nav Templates and `--no-nav-menu`
944
+ At the namespace level, you can have a file called `_nav.html.erb` to create tabbed bootstrap nav
945
+
946
+ To create the file for the first time (at each namespace), start by running
947
+ ```
948
+ bin/rails generate hot_glue:nav_template --namespace=xyz
949
+ ```
950
+
951
+ This will append the file `_nav.html.erb` to the views folder at `views/xyz`. To begin, this file contains only the following:
952
+
953
+ ```
954
+ <ul class='nav nav-tabs'>
955
+ </ul>
956
+ ```
957
+
958
+ Once the file is present, any further builds in this namespace will:
959
+
960
+ 1) Append to this `_nav.html.erb` file, adding a tab for the new built scaffold
961
+ 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).
962
+ ```
963
+ <%= render partial: "owner/nav", locals: {nav: "things"} %>
964
+ ```
965
+ (In this example `owner/` is the namespace and `things` is the name of the scaffold being built)
742
966
 
743
- `./bin/rails generate hot_glue:scaffold Account --include=first_name,last_name,company_name,created_at,kyc_verified_at`
967
+ ### `--no-nav-menu`
744
968
 
745
- You may not specify both include and exclude.
969
+ To suppress this behavior, add `--no-nav-menu` to the build command and the _nav template will not be touched.
746
970
 
747
- Include setting is affected by both specified grouping mode and smart layouts, explained below.
748
971
 
749
972
 
750
- #### Specified Grouping Mode
973
+ ## Layout & Manipulation features
751
974
 
752
975
  To specify grouped columns, separate COLUMNS by a COLON, then separate fields with commas. Specified groupings work like smart layouts (see below), except you drive which groupings make up the columns.
753
976
 
@@ -818,6 +1041,20 @@ This is what would happen if 9 fields, specified in the order A,B,C,D,E,F,G,H,I,
818
1041
  (If you had a number of fields that wasn't easily divisible by the number of columns, it would leave the final column one or a few fields short of the others.)
819
1042
 
820
1043
 
1044
+ ### `--new-button-position` (above, below; default: above)
1045
+ Show the new button above or below the list.
1046
+
1047
+
1048
+
1049
+ ### `--button-icons` (default is no icons)
1050
+ You can specify this either as builder flag or as a config setting (in `config/hot_glue.yml`)
1051
+ Use `font-awesome` for Font Awesome or `none` for no icons.
1052
+
1053
+ ### `--include-object-names`
1054
+ When you are "Editing X" we specify that X is a ___ (author, book, room, etc)
1055
+ e.g. "Editing author Edgar Allan Poe" vs "Editing Edgar Allan Poe"
1056
+ Can also be specified globally in `config/hot_glue.yml`
1057
+
821
1058
 
822
1059
  ### `--modify=field1{...},field2{...}`
823
1060
 
@@ -852,7 +1089,8 @@ Notice that each modifiers can be used with specific field types.
852
1089
  | tinymce | applies to text fields only, be sure to setup TineMCE globally | text fields only | | |
853
1090
  | typeahead | turns a foreign key (only) into a searchable typeahead field | foreign keys only | | |
854
1091
  | timezone | turns a string (varchar) into a drop down of timezones | foreign keys only | | |
855
- | none | special modifier for using badges |
1092
+ | include_blank | special modifier for association fields, adds include_blank to the created dropdown | |
1093
+ | none | special modifier for using badges |
856
1094
 
857
1095
  Except for "(truthy label)" and "(falsy label)" which use the special syntax, use the modifier _exactly_ as it is named.
858
1096
 
@@ -948,7 +1186,55 @@ When building a non-Gd controller with a `--alt-foreign-key-lookup`, if you don'
948
1186
 
949
1187
  To fix, either hawk the field or use with a factory creation pattern. This is because the associated object is on your graph but Hot Glue doesn't know how to securly load it without knowing its relationship to the current user. Use the `--hawk` mechanism to specify that relationship, and the lookup mechanism will integrate nicely.
950
1188
 
1189
+ ## Labels
1190
+
1191
+ ### `--label=`
1192
+
1193
+ 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`
1194
+
1195
+ If you specify anything explicitly, it will be used.
1196
+ If not, a specification that exists as `@@tabel_label_singular` from the Model will be used.
1197
+ If this does not exist, the Titleized (capitalized) version of the model name.
1198
+
1199
+ ### `--list-label-heading=`
1200
+ The plural of the list of things at the top of the list.
1201
+ If not, a specification that exists as `@@tabel_label_plural` from the Model will be used.
1202
+ If this does not exist, the UPCASE (all-uppercase) version of the model name.
1203
+
1204
+ ### `--new-button-label=`
1205
+ Overrides the button on the list that the user clicks onto to create a new record.
1206
+ (Default is to follow the same rules described in the `--label` option but with the word "New" prepended.)
1207
+
1208
+ ### `--new-form-heading=`
1209
+ The text at the top of the new form that appears when the new input entry is displayed.
1210
+ (Default follows the same rules described in the `--label` option but with the word "New" prepended.)
1211
+
1212
+
1213
+
1214
+ ### `--form-labels-position=` (default: `after`; options are **before**, **after**, and **omit**)
1215
+ By default form labels appear after the form inputs. To make them appear before or omit them, use this flag.
1216
+
1217
+ See also `--form-placeholder-labels` to use placeolder labels.
1218
+
1219
+
1220
+ ### `--form-placeholder-labels=` (default: false)
1221
+
1222
+ When set to true, fields, numbers, and text areas will have placeholder labels.
1223
+ Will not apply to dates, times, datetimes, dropdowns (enums + foreign keys), or booleans.
1224
+
1225
+ See also setting `--form-labels-position` to control position or omit normal labels.
1226
+
1227
+ ### `--inline-list-labels=` (before, after, omit; default: omit)
1228
+
1229
+ 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.
1230
+
1231
+ Because the labels are already in the heading, this is `omit` by default. (Use with `--no-list-heading` to omit the labels in the list heading.)
1232
+
1233
+ 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.
1234
+
1235
+
951
1236
 
1237
+ ## Access Control & Field Visibility Features
952
1238
 
953
1239
  ### `--pundit`
954
1240
  If you enable Pundit, your controllers will look for a Policy that matches the name of the thing being built.
@@ -1029,9 +1315,6 @@ If provided, the output code looks something like (in this example, showing the
1029
1315
  raise Pundit::NotAuthorizedError if ! UniqueInvoicePolicy.edit?
1030
1316
  ```
1031
1317
 
1032
-
1033
-
1034
-
1035
1318
  ### `--show-only=`
1036
1319
  (separate field names by COMMA)
1037
1320
 
@@ -1167,270 +1450,92 @@ export default class extends Controller {
1167
1450
  }
1168
1451
 
1169
1452
  formSubmit(event) {
1170
- this.rawSourceTarget.value = this.view.state.doc.toString();
1171
- }
1172
- }
1173
- ```
1174
-
1175
- Notice we are also using `--stimmify` to decorate the form with a Stimulus controller.
1176
-
1177
- 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.
1178
-
1179
- ### `--invisible=`
1180
- ### `--create-invisible=`
1181
- ### `--update-invisible=`
1182
- (two lists are maintained: create and update. any fields on the unnamed invisible list will be invisible on both create and update actions)
1183
-
1184
- If a field is on the invisible list, the policy will be checked for a `_able?` method.
1185
- If this method returns true, displayed like a normal editable field.
1186
-
1187
- If the policy doesn't allow editing, this field will be made invisible: completely removed from the form.
1188
-
1189
- 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.
1190
-
1191
- 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=`.)
1192
-
1193
-
1194
- Like show-only, note special behavior with pundit.
1195
-
1196
- 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,
1197
-
1198
- 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.
1199
-
1200
- 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.
1201
-
1202
- | | | Pundit | | |
1203
- |----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|
1204
- | 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. | | |
1205
- | 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. | | |
1206
- | 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. | | |
1207
- | 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. | | |
1208
- | Hidden | Not displayed or updatable if the field respond false to _able? or the Policy doesn't allow. | Unrelated to pundit Policy | | |
1209
- | | | | | |
1210
-
1211
-
1212
- Pundit calls the policy for every action, including the index action. In these cases it passes an association instead of a single record.
1213
-
1214
- 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.
1215
-
1216
- 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.
1217
-
1218
- 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).
1219
-
1220
- 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)`)
1221
-
1222
- 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.
1223
-
1224
-
1225
- 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.
1226
-
1227
- ```
1228
- class ThingPolicy < ApplicationPolicy
1229
- attr_reader :user, :thing
1230
-
1231
- def initialize(user, thing)
1232
- @user = user
1233
- @thing = thing
1234
- end
1235
-
1236
- def ccc_able?
1237
- if thing.is_a?(Thing) # a thing is not a Thing when it is an active relation of many things
1238
- !!thing.bbb # show or hide ccc based on whether or not bbb is true
1239
- else
1240
- current_user.is_admin? # show or hide the column heading for ccc based on whether or not the current user is an admin
1241
- end
1242
- end
1243
- # more policy method here ...
1244
- end
1245
- ```
1246
- Here, for all CRUD actions, the object is a thing, and so the editablility of ccc is dependent on bbb being true.
1247
- 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.
1248
-
1249
- 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.
1250
-
1251
-
1252
-
1253
- ### `--ujs_syntax=true` (Default is set automatically based on whether you have turbo-rails installed)
1254
-
1255
- If you are pre-Turbo (UJS), your delete buttons will come out like this:
1256
- `data: {'confirm': 'Are you sure you want to delete....?'}`
1257
-
1258
- If you are Turbo (Rails 7 or Rails 6 with proactive Turbo-Rails install), your delete button will be:
1259
- `data: {'turbo-confirm': 'Are you sure you want to delete....?'}`
1260
-
1261
- If you specify the flag, you preference will be used. If you leave the flag off, Hot Glue will detect the presence of Turbo-Rails in your app.
1262
-
1263
- **WARNING**: If you created a new Rails app since October 2021 and you have the yanked turbo-rails Gems on your local machine,
1264
- you will have some bugs with the delete buttons and also not be on the latest version of turbo-rails.
1265
-
1266
- Make sure to uninstall the yanked 7.1.0 and 7.1.1 from your machine with `gem uninstall turbo-rails`
1267
- and also fix any Rails apps created since October 2021 by fixing the Gemfile. Details here:
1268
- https://stackoverflow.com/questions/70671324/new-rails-7-turbo-app-doesnt-show-the-data-turbo-confirm-alert-messages-dont-f
1269
-
1270
-
1271
- ### `--magic-buttons=`
1272
- If you pass a list of magic buttons (separated by commas), they will appear in the button area on your list.
1273
-
1274
- It will be assumed there will be corresponding bang methods on your models.
1275
-
1276
- The bang (`!`) methods can respond in one of four ways:
1277
-
1278
- • With true, in which case a generic success message will be shown in the flash notice (“Approved” or “Rejected” in this case)
1279
-
1280
- • With false, in which case a generic error message will be shown in the flash alert (“Could not approve…”)
1281
-
1282
- • With a string, which will be assumed to be a “success” case, and will be passed to the front-end in the alert notice.
1283
-
1284
- • Raise an ActiveRecord exception
1285
-
1286
- This means you can be a somewhat lazy about your bang methods, but keep in mind the truth operator compares boolean true NOT any object is truth. So your return object must either be actually true (boolean), or an object that is string or string-like (responds to .to_s). Want to just say it didn’t work? Return false. Want to just say it was OK? Return true. Want to say it was successful but provide a more detailed response? Return a string.
1287
-
1288
- Finally, you can raise an ActiveRecord error which will also get passed to the user in the flash alert area.
1289
-
1290
- For more information see [Example 6 in the Tutorial](https://school.jfbcodes.com/8188)
1291
-
1292
- You can also define methods on your model that have the same name as the button with `_able?` at the end.
1293
- (Prior to v0.6.20, these methods expected names with `able?` but no underscore.)
1294
-
1295
- The button will be display as disabled if the method returns false.
1296
-
1297
-
1298
- ### `--downnest=`
1299
-
1300
- Automatically create subviews down your object tree. This should be the name of a has_many relationship based from the current object.
1301
- 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.
1302
-
1303
- The downnested child table (not to be confused with this object's `--nested` setting, where you are specifying this object's _parents_) is called a **child portal**. When you create a record in the child portal, the related record is automatically set to be owned by its parent (as specified by `--nested`). For an example, see the [v0.4.7 release notes](https://github.com/jasonfb/hot-glue/releases/tag/v0.4.7).
1304
-
1305
- Can now be created with more space (wider) by adding a `+` to the end of the downnest name
1306
- - e.g. `--downnest=abc+,xyz`
1307
-
1308
- 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
1309
-
1310
-
1311
- Polymorphic Downnesting
1312
-
1313
- Here, a `Blast` `has_many :rules, as: :ruleable`
1314
-
1315
- The child object is named `Rule` but it can belong to a Blast or an Agent. (Agent also has a similar has_many for Rules)
1316
-
1317
- `belongs_to :ruleable, polymorphic: true`
1318
-
1319
- We build the blast & agent controllers like so:
1320
-
1321
- bin/rails generate hot_glue:scaffold Blast --downnest='blast_rules(rules)'
1322
- bin/rails generate hot_glue:scaffold Agent --downnest='agent_rules(rules)'
1323
-
1324
- Notice that the relationship name is `rules` (not blast_rules), so what goes before the parenthesis is the controller name (with prefix)
1325
- What goes inside the controller name is the real relationship name.
1326
-
1327
- For the children, we can't build one controller for the Rule, instead we build one for the `AgentRules` and another for the `BlastRules`
1328
-
1329
- bin/rails generate hot_glue:scaffold Rule --nested='blast(ruleable)' --controller-prefix='Blast'
1330
- bin/rails generate hot_glue:scaffold Rule --nested='agent(ruleable)' --controller-prefix='Agent'
1331
-
1332
- (I realize building one child controller for each type of polymorph is tedius, but this is the best solution I could come up with.)
1333
-
1334
- As these are children, what goes into the `--netsed` setting inside the parentheses is the polymorphic name specified by `as:` when declaring the `belongs_to`
1335
-
1336
- routes.rb
1337
-
1338
- ```
1339
- resources :agents do
1340
- resources :agent_rules
1341
- end
1342
-
1343
- resources :blasts do
1344
- resources :blast_rules
1345
- end
1346
- ```
1347
-
1348
-
1349
-
1350
- ### `--record-scope=`
1351
-
1352
- Record scope allows you to apply a model based scope for the controller being generated.
1353
- This is applied on top of all other scopes, searches, and modifiers applied to the built controller.
1354
-
1355
- `bin/rails :generate hot_glue:scaffold Order --record-scope='.is_open'`
1356
-
1357
- Be sure to use single quotes (`'`) and don't forget the dot (`.`) before your scope(s).
1358
-
1359
- Make sure your Order model has a scope `is_open`, like so:
1360
-
1361
- ```
1362
- scope :is_open, -> {where(state == 'open')}
1453
+ this.rawSourceTarget.value = this.view.state.doc.toString();
1454
+ }
1455
+ }
1363
1456
  ```
1364
1457
 
1365
- Now all records displayed through the generated controller
1366
-
1458
+ Notice we are also using `--stimmify` to decorate the form with a Stimulus controller.
1367
1459
 
1460
+ 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.
1368
1461
 
1369
- ### `--related-sets=`
1370
1462
 
1371
- 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.
1463
+ ### `--invisible=`
1464
+ ### `--create-invisible=`
1465
+ ### `--update-invisible=`
1466
+ (two lists are maintained: create and update. any fields on the unnamed invisible list will be invisible on both create and update actions)
1372
1467
 
1373
- Consider the classic example of three tables: users, user_roles, and roles
1468
+ If a field is on the invisible list, the policy will be checked for a `_able?` method.
1469
+ If this method returns true, displayed like a normal editable field.
1374
1470
 
1375
- User `has_many :user_roles`; UserRole `belongs_to :user` and `belongs_to :role`; and Role `has_many :user_roles` and `has_many :user, through: :user_roles`
1471
+ If the policy doesn't allow editing, this field will be made invisible: completely removed from the form.
1376
1472
 
1377
- We'll generate a scaffold to edit the users table. A checkbox set of related roles will also appear to allow editing of roles. (In this example, the only field to be edited is the email field.)
1473
+ 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.
1378
1474
 
1379
- ```
1380
- rails generate hot_glue:scaffold User --related-sets=roles --include=email,roles --gd
1381
- ```
1475
+ 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=`.)
1382
1476
 
1383
- Note this leaves open a privileged escalation attack (a security vulnerability).
1384
1477
 
1385
- 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)
1478
+ Like show-only, note special behavior with pundit.
1386
1479
 
1480
+ 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,
1387
1481
 
1388
- ### `--label=`
1482
+ 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.
1389
1483
 
1390
- 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`
1484
+ 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.
1391
1485
 
1392
- If you specify anything explicitly, it will be used.
1393
- If not, a specification that exists as `@@tabel_label_singular` from the Model will be used.
1394
- If this does not exist, the Titleized (capitalized) version of the model name.
1486
+ | | | Pundit | | |
1487
+ |----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---|---|
1488
+ | 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. | | |
1489
+ | 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. | | |
1490
+ | 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. | | |
1491
+ | 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. | | |
1492
+ | Hidden | Not displayed or updatable if the field respond false to _able? or the Policy doesn't allow. | Unrelated to pundit Policy | | |
1493
+ | | | | | |
1395
1494
 
1396
- ### `--list-label-heading=`
1397
- The plural of the list of things at the top of the list.
1398
- If not, a specification that exists as `@@tabel_label_plural` from the Model will be used.
1399
- If this does not exist, the UPCASE (all-uppercase) version of the model name.
1400
1495
 
1401
- ### `--new-button-label=`
1402
- Overrides the button on the list that the user clicks onto to create a new record.
1403
- (Default is to follow the same rules described in the `--label` option but with the word "New" prepended.)
1496
+ Pundit calls the policy for every action, including the index action. In these cases it passes an association instead of a single record.
1404
1497
 
1405
- ### `--new-form-heading=`
1406
- The text at the top of the new form that appears when the new input entry is displayed.
1407
- (Default follows the same rules described in the `--label` option but with the word "New" prepended.)
1498
+ 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.
1408
1499
 
1409
- ## Field Labels
1500
+ 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.
1410
1501
 
1411
- ### `--form-labels-position=` (default: `after`; options are **before**, **after**, and **omit**)
1412
- By default form labels appear after the form inputs. To make them appear before or omit them, use this flag.
1502
+ 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).
1413
1503
 
1414
- See also `--form-placeholder-labels` to use placeolder labels.
1504
+ 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)`)
1415
1505
 
1506
+ 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.
1416
1507
 
1417
- ### `--form-placeholder-labels=` (default: false)
1418
1508
 
1419
- When set to true, fields, numbers, and text areas will have placeholder labels.
1420
- Will not apply to dates, times, datetimes, dropdowns (enums + foreign keys), or booleans.
1509
+ 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.
1421
1510
 
1422
- See also setting `--form-labels-position` to control position or omit normal labels.
1511
+ ```
1512
+ class ThingPolicy < ApplicationPolicy
1513
+ attr_reader :user, :thing
1423
1514
 
1424
- ### `--inline-list-labels=` (before, after, omit; default: omit)
1515
+ def initialize(user, thing)
1516
+ @user = user
1517
+ @thing = thing
1518
+ end
1425
1519
 
1426
- 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.
1520
+ def ccc_able?
1521
+ if thing.is_a?(Thing) # a thing is not a Thing when it is an active relation of many things
1522
+ !!thing.bbb # show or hide ccc based on whether or not bbb is true
1523
+ else
1524
+ current_user.is_admin? # show or hide the column heading for ccc based on whether or not the current user is an admin
1525
+ end
1526
+ end
1527
+ # more policy method here ...
1528
+ end
1529
+ ```
1530
+ Here, for all CRUD actions, the object is a thing, and so the editablility of ccc is dependent on bbb being true.
1531
+ 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.
1427
1532
 
1428
- Because the labels are already in the heading, this is `omit` by default. (Use with `--no-list-heading` to omit the labels in the list heading.)
1533
+ 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.
1429
1534
 
1430
- 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.
1431
1535
 
1432
1536
 
1433
- ### Code insertions
1537
+ ## Code Insertion Features
1538
+ '
1434
1539
  Insert some code into the `new`, `create` or `update` action actions.
1435
1540
 
1436
1541
  Wrapped in quotation marks when specified in the command line, and use semicolons to separate multiple lines of code.
@@ -1484,7 +1589,6 @@ Notice that a separate hook for code-after-create is also available, but that ha
1484
1589
  Using both together `--code-after-new='@email_template.created_by_user = current_user' --code-after-create='@email_template.do_something'`
1485
1590
 
1486
1591
 
1487
-
1488
1592
  ```
1489
1593
  def create
1490
1594
  ...
@@ -1499,13 +1603,13 @@ def create
1499
1603
  account.reload
1500
1604
  ...
1501
1605
  ```
1606
+ TODO: build a solution for inserting code only in the `new` action but NOT the create action
1502
1607
 
1503
1608
 
1504
1609
 
1505
- ### `--search=` (options: simple, set, false predicate, default: false)
1506
-
1610
+ ## Searching
1507
1611
 
1508
- #### Set Search
1612
+ ### `--search=` (options: simple, set, false predicate, default: false)
1509
1613
  If you specify `--search` to `set`, you will get a whole bar across the top of the list with search fields for each field.
1510
1614
  Within the set, the search query is **_combinative_** ("and"), so records matching all criteria are shown as the **result set.**
1511
1615
  For date pickers and time pickers, you need the additional Stimulus.
@@ -1558,69 +1662,60 @@ NOT IMPLEMENTED YET
1558
1662
  TODO: implement me
1559
1663
 
1560
1664
 
1561
- ### `--attachments=`
1562
-
1563
- #### ActiveStorage Quick Setup
1564
- (For complete docs, refer to https://guides.rubyonrails.org/active_storage_overview.html)
1565
1665
 
1566
- `brew install vips`
1567
- (for videos `brew install ffmpeg`)
1666
+ ### `--stimmify` or `--stimmify=xyz`
1568
1667
 
1569
- ```
1570
- bundle add image_processing
1571
- ./bin/rails active_storage:install
1572
- ./bin/rails db:migrate
1573
- ```
1574
-
1575
- Generate an images model:
1576
-
1577
- `./bin/rails generate model Images name:string`
1668
+ Automatically build the new and edit form with `data-controller='xyz'` to attach stimulus
1578
1669
 
1579
- add to `app/model/image.rb`, giving it a variant called thumb (Note: Hot Glue will fallback to using a variant called "thumb" if you use the shorthand syntax. If you use the long syntax, you can specify the variant to use for displaying the image)
1670
+ If you use the shorthand (specify no `=`) your stimulus controller's name will be inferred from the Singular form of the scaffolding being built, with dashes for underscores, and ending with `-form`
1580
1671
 
1581
- ```
1582
- has_one_attached :avatar do |attachable|
1583
- attachable.variant :thumb, resize_to_limit: [100, 100]
1584
- end
1585
- ```
1586
- Generate a Hot Glue scaffold with the attachment avatar appended to the field list (the shorthand syntax)
1587
1672
 
1588
- `./bin/rails generate hot_glue:scaffold Image --include='name:avatar' --gd --attachments='avatar'`
1673
+ `@singular.gsub("_", "-") + "-form"`
1589
1674
 
1590
- (Attachments behave like fields and follow the same layout rules used for fields, except that, unlike fields, when NOT using an --include list, they do not get automatically added. Thus, attachments are opt-in. You do not need to specify an attachment on the attachments list as ALSO being on the include list, but you can for the purpose of using the layout tricks discussed in Specified Grouping Mode to make the attachment appear on the layout where you want it to)
1675
+ (so a `BankAcccount` scaffold, with singular form `bank_account`, attaches Stimulus-to-html using
1676
+ `data-controller='bank-account-form'` using a Stimulis controller at `app/javascript/controllers/bank_account_form_controller.js` stimulus controller)
1591
1677
 
1592
- #### Caveats:
1593
- • If thumbnails aren't showing up, make sure you have
1594
1678
 
1595
- 1) installed vips, and
1596
- 2) used an image that supports ActiveStorage "variable" mechanism. The supported types are png, gif, jpg, pjpeg, tiff, bmp, vnd.adobe.photoshop, vnd.microsoft.icon, webp. see https://stackoverflow.com/a/61971660/3163663
1597
- To debug, make sure the object responds true to the variable? method.
1679
+ For example, `rails g hot_glue:scaffold Thing --stimmify` generates a form that looks like
1598
1680
 
1599
- If you use the shorthand syntax, Hot Glue looks for a variant "thumb" (what you see in the example above).
1681
+ ```
1682
+ <%= form_with model: thing,
1683
+ url: things_path,
1684
+ html: {
1685
+ 'data-controller': "thing-form"
1686
+ }
1687
+ %>
1688
+ ...
1689
+ ```
1600
1690
 
1601
- If it finds one, it will render thumbnails from the attachment variant `thumb`. To specify a variant name other than "thumb", use the first parameter below.
1691
+ `rails g hot_glue:scaffold Thing --stimmify=xyz` generates a form that looks like
1692
+ ```
1693
+ <%= form_with model: thing,
1694
+ url: things_path,
1695
+ html: {
1696
+ 'data-controller': "xyz-form"
1697
+ }
1698
+ %>
1699
+ ...
1700
+ ```
1602
1701
 
1603
- 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.
1702
+ Note that your fields also appended with `data-thing-target=abc` and also `data-thing-target=abcWrapper`
1604
1703
 
1605
- #### `--attachments=` Long form syntax with 1 parameter
1704
+ For a crash course on Stimulus, see
1705
+ https://jasonfleetwoodboldt.com/courses/rails-7-crash-course/rails-7-stimulus-js-basics-with-importmap-rails/
1606
1706
 
1607
- **--attachments='_attachment name_{_variant name_}'**
1707
+ **You must make the stimulus controller yourself!!**
1608
1708
 
1609
- What if your variant is called something other than `thumb`
1709
+ Create them with:
1610
1710
 
1611
- Use the long-form syntax specifying a variant name other than `thumb`. For example, `thumbnail`
1711
+ `rails g stimulus thing_form`
1612
1712
 
1613
- `./bin/rails generate hot_glue:scaffold Image --include='name:avatar' --gd --attachments='avatar{thumbnail}'`
1713
+ _Notice that Stimulus requires object registration when used in a Node environment._ For this reason, you should NOT create new files yourself by hand in the `app/javascript/controllers/` folder.
1614
1714
 
1615
- The model would look like this:
1616
- ```
1617
- has_one_attached :avatar do |attachable|
1618
- attachable.variant :thumbnail, resize_to_limit: [100, 100]
1619
- end
1620
- ```
1715
+ Instead, use the generator and the registry will be updated for you automatically.
1621
1716
 
1622
- 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.
1623
1717
 
1718
+ ## Attachments
1624
1719
 
1625
1720
  #### `--attachments=` Long form syntax with 1st and 2nd parameters
1626
1721
 
@@ -1634,7 +1729,7 @@ Note: You must have a string field called `orig_filename`. It does not need to b
1634
1729
 
1635
1730
  Note that the `orig_filename` is not part of the inputted parameters, it simply gets appended to the model **bypassing the Rails strong parameters mechanism**, which is why it is irrelevant if it is included in the field list and recommended that if you do include it, you make it show-only so as not to allow your users to edit or modify it.
1636
1731
 
1637
- 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.
1732
+ 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.
1638
1733
 
1639
1734
  #### `--attachments=` Long form syntax with 1st, 2nd, and 3rd parameters
1640
1735
 
@@ -1690,16 +1785,16 @@ config.active_storage.service = :amazon
1690
1785
 
1691
1786
 
1692
1787
  #### For Direct Upload Support
1693
- 1.
1788
+ 1.
1694
1789
  ```
1695
1790
  yarn add @rails/activestorage
1696
1791
  ```
1697
1792
 
1698
- 2. You need a job runner like sidekiq or delayed_job. I recommend sidekiq. Make sure to have it
1793
+ 2. You need a job runner like sidekiq or delayed_job. I recommend sidekiq. Make sure to have it
1699
1794
 
1700
1795
  - Start sidekiq with `bundle exec sidekiq` while you are testing
1701
1796
 
1702
- 3. Install ActiveStorage JS using:
1797
+ 3. Install ActiveStorage JS using:
1703
1798
 
1704
1799
  `./bin/rails generate hot_glue:direct_upload_install`
1705
1800
 
@@ -1726,7 +1821,7 @@ This will 1) copy the dropzone_controller.js file into your app and 2) add the d
1726
1821
 
1727
1822
  ### Attach Stimulus JS Controllers to Your Forms with `--stimmify` or `--stimmify=xyz`
1728
1823
 
1729
- Automatically build the new and edit form with `data-controller='xyz'` to attach cooresponding stimulus controllers.
1824
+ Automatically build the new and edit form with `data-controller='xyz'` to attach cooresponding stimulus controllers.
1730
1825
 
1731
1826
  If you use the shorthand (specify no `=`) your stimulus controller's name will be inferred from the Singular form of the scaffolding beild built, with dashes for underscores, and ending with `-form`
1732
1827
 
@@ -1774,91 +1869,13 @@ https://jasonfleetwoodboldt.com/courses/rails-7-crash-course/rails-7-stimulus-js
1774
1869
 
1775
1870
 
1776
1871
 
1777
- ### `--factory-creation={ ... }`
1778
-
1779
- The code you specify inside of `{` and `}` will be used to generate a new object. The factory should instantiate with any arguments (I suggest Ruby keyword arguments) and must provide a method that is the name of the thing.
1780
-
1781
- You may use semi-colons to separate multiple lines of code.
1782
-
1783
- For example, a user Factory might be called like so:
1784
-
1785
- `./bin/rails generate hot_glue:scaffold User --factory-creation={factory = UserFactory.new(params: user_params)} --gd`
1786
-
1787
- (Note we are relying on the `user_params` method provided by the controller.)
1788
-
1789
- You must do one of two things:
1790
-
1791
- 1) In the code you specify, set an instance variable `@user` to be the newly created thing. (Your code should contain something like `@thing = ` to trigger this option.)
1792
- 2) Make a local variable called `factory` **and** have a method of the name of the object (`user`) on a local variable called `factory` that your code created
1793
-
1794
- (The code example above is the option for #2 because it does not contain `@user =`)
1795
-
1796
- If using number #2, Hot Glue will append this to the code specified:
1797
- ```
1798
- @user = factory.user
1799
- ```
1800
-
1801
-
1802
- Here's a sample UserFactory that will create a new user only if one with a matching email address doesn't exist. (Otherwise, it will update the existing record.)
1803
- Your initialize method can take any params you need it to, and using this pattern your business logic is applied consistently throughout your app. (You must, of course, use your Factory everywhere else in your app too.)
1804
-
1805
- ```
1806
- class UserFactory
1807
- attr_reader :user
1808
- attr_accessor :email
1809
-
1810
- def initialize(params: {})
1811
- user = User.find_or_create_by(email: params[:email])
1812
-
1813
- user.update(params)
1814
- if user.new_record?
1815
- # do special new user logic here, like sending an email
1816
- end
1817
- end
1818
- end
1819
- ```
1820
-
1821
-
1822
- be sure your factory code creates a local variable that follows this name
1823
-
1824
- **<downcase association name>**_factory.<downcase association name>
1825
-
1826
- Thus, your factory object must have a method of the same name as the factory being created which returns the thing that got created.
1827
- (It can do the creation either on instantiation or when calling that method)
1828
-
1829
- For example, assuming the example from above, we are going to do the lookup ourselves inside of our own `AgentFactory` object.)
1830
-
1831
- ```
1832
- factory = AgentFactory.new(find_or_create_by_email: agent_company_params[:__lookup_email],
1833
- params: modified_params)
1834
- ```
1835
-
1836
- Here the new AgentFactory will receive any variables by keyword argument, and since you're specifying the calling code here, Hot Glue does not dictate your factory's setup.
1837
- However, two special variables are in scope which you can use in your calling code.
1838
-
1839
- `*_params` (where * is the name of the thing you are building)
1840
- `modified_params`
1841
-
1842
- Either one must be received by your factory for your factory to create data based off the inputted data.
1843
-
1844
- Remember, `*_params` has the input params passed only the through the sanitizer, and modified_params has it passed through the timezone aware mechanism and other Hot Glue-specific defaults.
1845
-
1846
- Always:
1847
- • In your factory calling code, assign the variable `factory = ` (do not use a different variable name),
1848
- • Write a factory object with a `new` method that received the paramters you are specifying in your calling code,
1849
- • Be sure your factory has an _instance method_ a method with the **same name** of the built object, which hot glue will call next:
1850
-
1851
- `@agent = factory.agent`
1852
-
1853
- Don't include this last line in your factory code.
1854
-
1855
-
1856
- # SPECIAL FEATURES
1872
+ # SPECIAL FEATURES DISCUSSION
1873
+ This section discusses features that don't correspond to a single option or flag.
1857
1874
 
1858
1875
 
1859
1876
  ## "Thing" Label
1860
1877
 
1861
- Note that on a per model basis, you can also globally omit the label or set a unique label value using
1878
+ Note that on a per-model basis, you can also globally omit the label or set a unique label value using
1862
1879
  `@@table_label_singular` and `@@table_label_plural` on your model objects.
1863
1880
 
1864
1881
  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.
@@ -2144,16 +2161,96 @@ These automatic pickups for partials are detected at build time. This means that
2144
2161
 
2145
2162
 
2146
2163
  # VERSION HISTORY
2164
+ #### 2025-08-22 - v0.6.24
2165
+ `--related-sets` fixes issue with related sets due to ruby syntax
2166
+
2167
+ • Each related set can take two additional parameters: specify the label to use as the related label using curly braces `{`...`}`, and specify any hawk scope to be applied to the displayed list of associated objects
2168
+ (like the --hawk, with the need to explicitly call `--hawk`) using `[`...`]`
2169
+ Both parameters are optional. If the label field `{...}` is unspecified, it will default to `label`.
2170
+ If the 2nd parameter `[...]` (hawk) is unspecified, the widget will display all records in the related table, using the base class + `.all`
2171
+ If the 1st parameter `{...}` is left off, still use square braces `[...]` for the hawk.
2172
+
2173
+
2174
+ Example:
2175
+ `rails generate hot_glue:scaffold User --nested=company --related-sets=roles{name}[company.roles] --include=email,roles --gd`
2176
+
2177
+ This shows the related set of `roles` using the field named `name` on the role object to display its name.
2178
+
2179
+ Only displays the roles associated with the current company via the `company.roles` association.
2180
+
2181
+ Notice that here `company` must be in scope, which can either be supplied by you in the base class, or in this example we have nested User within Company (so its nest path would be different than the example above), which would put `company` in the scope of the build.
2182
+
2183
+
2184
+ #### 2025-08-15 - v.0.6.23
2185
+
2186
+
2187
+ • Lazy Lists: Important Breaking Change
2188
+
2189
+ All downnested portals now use Turbo's last list feature (frame with `src:` to load the subview via a separate request).
2190
+ The user sees "Loading" in the box as it is loading. (See `--downnest` and `--nested` sections.)
2191
+
2192
+ Unfortunately, this is a partially breaking change in that a parent & child should be rebuilt together on this version.
2193
+
2194
+ Whereas before the parent's edit template included the list and passed it the records to render immediately (in the same request)
2195
+
2196
+ ```
2197
+ <%= render partial: "agent_rules/list", locals: {agent: @agent, rules: @agent.rules} %>
2198
+
2199
+ ```
2200
+
2201
+ Now, we will render a new partial (from the child's build folder) at `lazy_list` (when the parent is rendering its children)
2202
+
2203
+ ```
2204
+ <%= render partial: "agent_rules/lazy_list", locals: {agent: @agent, ...
2205
+ ```
2206
+
2207
+ The `lazy_list` itself contains a turbo frame tag with an `src` set to the URL where the list can be loaded, appened with __lazy=1 to let the child controller know not to render the full layout.
2208
+
2209
+
2210
+ ```
2211
+ <%= tag.turbo_frame id: "__agents-list" + ((('__' + nested_for) if defined?(nested_for)) || ""),
2212
+ src: account_crusade_agents_path(account,crusade) + "?__lazy",
2213
+ loading: "lazy" do %>
2214
+
2215
+ ```
2216
+
2217
+ In the downnested controller, the children will now suppress the layout when loaded lazy
2218
+
2219
+ ```
2220
+ render layout: (params[:__lazy].present? ? false : true)
2221
+ ```
2222
+
2223
+ Just remember you must rebuild the parent if you rebuild a child, and you must rebuild ALL the children of any parent that is rebuilt.
2224
+
2225
+
2226
+ • Modify now has an `include_blank` option to add a blank option for associations
2227
+
2228
+ (Assuming the thing you are building has a `person_id` and `belongs_to :person, optional: true`)
2229
+
2230
+ `--modify=person_id{include_blank}`
2231
+
2232
+ Make sure your `belongs_to` association has `optional: true` or else you will get validation errors when you try to save a record with an empty association. Without this setting (by default), drop-downs show a blank line when the association is *not set*, but when the association is *set* no blank line is shown, making it impossible to unset or clear out an association once it has already been set.
2233
+
2234
+ • Fixes cancel button problems related to subviews (no longer necessary to load the edit of the parent in the lazy paradigm)
2235
+
2236
+ • Fixes double-entry redisplay problem (second create action would be saved but not show up in UI) due to malformed nested_for
2237
+
2238
+
2147
2239
 
2148
2240
 
2149
2241
  #### 2025-07-28 v0.6.22
2150
2242
 
2243
+
2244
+
2245
+ #### 2025-07-05 v0.6.21
2246
+ •Now use new code insertion `--code-after-new` for code that happens directly after `.new()` call. use semicolon (`;`) to create linebreaks; no reason why the factories should insert flash messages
2247
+
2151
2248
  `--phantom-create-params`
2152
2249
  These parameters get added in the strong parameters safelist for the create action
2153
2250
 
2154
2251
  You'll probably wnat to use this along with --code-after-create to check that phanton param
2155
2252
 
2156
- TODO: you have to tap these away yourself
2253
+ TODO: you have to tap these away yourself
2157
2254
  TODO: can they be tapped away automatically if not using a factory
2158
2255
 
2159
2256
  `--phantom-update-params`
@@ -2182,7 +2279,7 @@ See "Polymorphic downnesting" in the downnesting section for an example.
2182
2279
 
2183
2280
  • Removes more vestiges of optionalized nesting (which I had implemented 3 years ago!)
2184
2281
 
2185
- I no longer like optionalized nesting, and recommend against it.
2282
+ I no longer like optionalized nesting, and recommend against it.
2186
2283
 
2187
2284
  Nesting should always be part of the structure, and every route should operate firmly in its nest path.
2188
2285
 
@@ -2190,11 +2287,6 @@ Use new controller-prefix to make on-off exceptions or with polymorphic children
2190
2287
 
2191
2288
  • Fixes for localized datetime & time inputs
2192
2289
 
2193
-
2194
-
2195
- #### 2025-07-05 v0.6.21
2196
- •Now use new code insertion `--code-after-new` for code that happens directly after `.new()` call. use semicolon (`;`) to create linebreaks; no reason why the factories should insert flash messages
2197
-
2198
2290
  • removes duplicitous flash messages in factory context
2199
2291
 
2200
2292
  • adds documentation for `--code-before-create`, `--code-after-create`, `--code-before-update`, `--code-after-update` (these features were already implemented)
@@ -2229,7 +2321,7 @@ probably want about 3 or 4 columns. When you have more than 5 it's difficult to
2229
2321
  #### 2025-06-10 v0.6.19
2230
2322
 
2231
2323
  • Fixes magic button output behavior to correctly show the string returned by the bang menthod
2232
- • Fixes internal syntax of modify_as for modified fields; note default is now 'Yes' and 'No' for radio buttons
2324
+ • Fixes internal syntax of modify for modified fields; note default is now 'Yes' and 'No' for radio buttons
2233
2325
  • fixes destroy behavior with explicit .destroy! and ActiveRecord::RecordNotDestroyed; documents previously undocumented 4th parameter for attachment input
2234
2326
 
2235
2327
 
@@ -2266,7 +2358,10 @@ Automatically build the new and edit form with `data-controller='xyz'` to attach
2266
2358
 
2267
2359
  If you use the shorthand (specify no `=`) your stimulus controller's name will be inferred from the Singular form of the scaffolding beild built, with dashes for underscores, and ending with `-form`
2268
2360
 
2269
- (For example, `rails g hot_glue:scaffold Thing --stimmy` generates a form that looks like
2361
+ Notice that under the hood we are doing this `@singular.gsub("_", "-") + "-form"` so a scaffold built for `bank_account` will become a `BankAccountForm` stimulus controller
2362
+
2363
+
2364
+ For example, `rails g hot_glue:scaffold Thing --stimmy` generates a form that looks like.
2270
2365
 
2271
2366
  ```
2272
2367
  <%= form_with model: thing,
@@ -2281,7 +2376,7 @@ If you use the shorthand (specify no `=`) your stimulus controller's name will b
2281
2376
 
2282
2377
  Note that your fields also appended with `data-thing-target=abc` and also `data-thing-target=abcWrapper`
2283
2378
 
2284
- See section "Attach Stimulus JS Controllers to Your Forms with `--stimmify` or `--stimmify=xyz`"
2379
+
2285
2380
 
2286
2381
  For a crash course on Stimulus, see
2287
2382
  https://jasonfleetwoodboldt.com/courses/rails-7-crash-course/rails-7-stimulus-js-basics-with-importmap-rails/