hot-glue 0.4.8.1 → 0.4.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -2
- data/Gemfile.lock +1 -1
- data/README.md +173 -118
- data/lib/generators/hot_glue/install_generator.rb +1 -1
- data/lib/generators/hot_glue/layout/builder.rb +19 -10
- data/lib/generators/hot_glue/markup_templates/erb.rb +4 -5
- data/lib/generators/hot_glue/scaffold_generator.rb +71 -24
- data/lib/generators/hot_glue/templates/controller.rb.erb +13 -8
- data/lib/generators/hot_glue/templates/erb/_list.erb +15 -11
- data/lib/generators/hot_glue/templates/erb/_new_button.erb +1 -1
- data/lib/generators/hot_glue/templates/erb/_new_form.erb +1 -1
- data/lib/generators/hot_glue/templates/erb/_show.erb +1 -1
- data/lib/generators/hot_glue/templates/erb/edit.erb +1 -1
- data/lib/hotglue/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b96fed7ffa4bfccbe2dbe0d22e4dc784a70304aebf50e0ef672b2fb30c6f4641
|
4
|
+
data.tar.gz: bb0398bf9529ad73c69c7703209a5888081ba0a629376a82880b726d5d74faa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5064b5d5432f0f7f518b70b61a21b7d98a0182efd73a7e8784009f1e8d381edfa5f64cd6222214aefaebc1d9d30fd4adb4be870bf854fc2e8896d7bc77e66120
|
7
|
+
data.tar.gz: 6d88a66a54966f7179f4c041d61670fb799c6bcdddc5a0fc5b3d984011ebc3cabbb5b8713fa05fa534b68a1f07f912f8c1ad8b955be3d0f1281618e2b04f3429
|
data/.github/FUNDING.yml
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
custom: ["https://
|
2
|
-
"https://jfb.teachable.com/p/hot-glue-in-depth-tutorial?utm_source=github.com&utm_campaign=github_hot_glue_repo_funding_link"]
|
1
|
+
custom: ["https://jfbcodes.com/p/hot-glue-in-depth-tutorial?utm_source=github.com&utm_campaign=github_hot_glue_repo_funding_link"]
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,32 +3,34 @@
|
|
3
3
|
|
4
4
|
Hot Glue is a Rails scaffold builder for the Turbo era. It is an evolution of the admin-interface style scaffolding systems of the 2010s ([activeadmin](https://github.com/activeadmin/activeadmin), [rails_admin](https://github.com/sferik/rails_admin), and [active_scaffold](https://github.com/activescaffold/active_scaffold)).
|
5
5
|
|
6
|
-
Using Turbo-Rails and Hotwire (default in Rails 7) you get a lightning-fast out-of-the-box CRUD
|
6
|
+
Using Turbo-Rails and Hotwire (default in Rails 7) you get a lightning-fast out-of-the-box CRUD-building experience.
|
7
7
|
|
8
|
-
Every page displays only a list view: new and edit operations happen as 'edit-in-place'
|
8
|
+
Every page displays only a list view: new and edit operations happen as 'edit-in-place,' so the user never leaves the page.
|
9
9
|
|
10
|
-
Because all page navigation is Turbo's
|
10
|
+
Because all page navigation is Turbo's responsibility, everything plugs & plays nicely into a Turbo-backed Rails app.
|
11
11
|
|
12
|
-
Alternatively, you can use this tool to create a Turbo-backed *section* of your Rails app--
|
12
|
+
Alternatively, you can use this tool to create a Turbo-backed *section* of your Rails app -- such as an admin interface -- while still treating the rest of the Rails app as an API or building out other features by hand.
|
13
13
|
|
14
|
-
It will read your relationships and field types to generate your code for you, leaving you with a 'sourdough starter' to work from. If you modify the generated code, you're on your own if you want to preserve your changes and also re-generate
|
14
|
+
It will read your relationships and field types to generate your code for you, leaving you with a 'sourdough starter' to work from. If you modify the generated code, you're on your own if you want to preserve your changes and also re-generate scaffolding after adding fields.
|
15
15
|
|
16
16
|
By default, it generates code that gives users full control over objects they 'own' and by default it spits out functionality giving access to all fields.
|
17
17
|
|
18
|
-
Hot Glue generates functionality that
|
18
|
+
Hot Glue generates functionality that is quick and dirty. It lets you be crafty. As with a real glue gun, use it with caution.
|
19
19
|
|
20
20
|
* Build plug-and-play scaffolding mixing generated ERB with the power of Hotwire and Turbo-Rails
|
21
|
-
* Everything edits-in-place (unless you use `--big-edit
|
22
|
-
* Automatically
|
23
|
-
* Excellent for CREATE-READ-UPDATE-DELETE (CRUD), lists with pagination
|
21
|
+
* Everything edits-in-place (unless you use `--big-edit`)
|
22
|
+
* Automatically reads your models (make them AND migrate your database before building your scaffolding!)
|
23
|
+
* Excellent for CREATE-READ-UPDATE-DELETE (CRUD), lists with pagination
|
24
24
|
* Great for prototyping, but you should learn Rails fundamentals first.
|
25
25
|
* 'Packaged' with Devise, Kaminari, Rspec, FontAwesome
|
26
26
|
* Create system specs automatically along with the generated code.
|
27
27
|
* Nest your routes model-by-model for built-in poor man's authentication.
|
28
28
|
* Throw the scaffolding away when your app is ready to graduate to its next phase.
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
|
31
|
+
# Get Hot Glue
|
32
|
+
## [Licence Only Option Now Available](https://heliosdev.shop/p/hot-glue/?utm_source=github.com&utm_campaign=github_hot_glue_readme_page) **only $50 USD!**
|
33
|
+
## [GET THE COURSE TODAY (includes Licence)](https://jfbcodes.com/courses/hot-glue-in-depth-tutorial/?utm_source=github.com&utm_campaign=github_hot_glue_readme_page) **only $60 USD!**
|
32
34
|
|
33
35
|
| | |
|
34
36
|
| ------------- | ------------- |
|
@@ -62,10 +64,13 @@ There are two ways to create new apps on Rails 7: With or without ImportMap. The
|
|
62
64
|
|
63
65
|
`rails new --css=bootstrap --javascript=webpack --database=postgresql`
|
64
66
|
|
65
|
-
Confirm that both Stimulus and Turbo are working.
|
66
|
-
|
67
|
+
Confirm that both Stimulus and Turbo are working.
|
68
|
+
|
69
|
+
**If using JSBundling, make sure to use the new `bin/dev rails` instead of the old `rails server` or else your Webpack will not compile.**
|
70
|
+
|
71
|
+
For the quick step-by-step guide to help you confirm that both Stimulus and Turbo are working for your new JSBundling-Rails/CSSBunlding-Rails setup [see this post](https://jasonfleetwoodboldt.com/courses/stepping-up-rails/rails-7-new-app-with-js-bundling-css-bundling/).
|
67
72
|
|
68
|
-
(Note that Bootstrap is optional for Hot Glue. Here, I am just showing you the default
|
73
|
+
(Note that Bootstrap is optional for Hot Glue. Here, I am just showing you the default installation for simplicity.)
|
69
74
|
|
70
75
|
For the old method of installing Bootstrap [see this post](https://jasonfleetwoodboldt.com/courses/stepping-up-rails/rails-7-bootstrap/)
|
71
76
|
|
@@ -87,26 +92,20 @@ gem 'ffaker'
|
|
87
92
|
## 3. HOTGLUE INSTALLER
|
88
93
|
Add `gem 'hot-glue'` to your Gemfile & `bundle install`
|
89
94
|
|
90
|
-
Purchase a license at https://heliosdev.shop/hot-glue
|
95
|
+
Purchase a license at https://heliosdev.shop/p/hot-glue
|
91
96
|
|
92
97
|
During in installation, you MUST supply a `--layout` flag.
|
93
98
|
|
94
|
-
### `--layout` flag (
|
95
|
-
Here you will set up and install Hot Glue for the first time.
|
99
|
+
### `--layout` flag (only two options: `hotglue` or `bootstrap`; default is `bootstrap`)
|
100
|
+
Here you will set up and install Hot Glue for the first time.
|
96
101
|
|
97
|
-
|
102
|
+
It will install a config file that will save two preferences: layout (`hotglue` or `bootstrap`)
|
98
103
|
|
99
|
-
|
104
|
+
The installer will create `config/hot_glue.yml`.
|
100
105
|
|
101
106
|
|
102
|
-
|
103
|
-
|
104
|
-
`.scaffold-container`,
|
105
|
-
`.scaffold-list`,
|
106
|
-
`.scaffold-row`, and
|
107
|
-
`.scaffold-cell`
|
108
|
-
|
109
|
-
During the installation, if your `--layout` flag is left unspecified or set to `hotglue` you must also pass `--theme` flag.
|
107
|
+
### `--theme` flag
|
108
|
+
During the installation, **if** your `--layout` flag is set to `hotglue` you must also pass `--theme` flag.
|
110
109
|
|
111
110
|
the themes are:
|
112
111
|
• like_mountain_view (Google)
|
@@ -114,25 +113,17 @@ the themes are:
|
|
114
113
|
• like_bootstrap (bootstrap 4 copy)
|
115
114
|
• dark_knight (_The Dark Night_ (2008) inspired)
|
116
115
|
• like_cupertino (modern Apple-UX inspired)
|
117
|
-
• gradeschool (spiral bound/lined notebook inspired)
|
118
|
-
|
119
|
-
Please note that the scaffold is ** built with different market up for boostrap**, so you cannot switch between the Bootstrap and Hotglue layouts without rebuilding the scaffold.
|
120
|
-
|
121
|
-
(On the other hand, if you build within the Hotglue layout, all of the Hotglue theme files CAN be swapped out without rebuilding the scaffold.)
|
122
116
|
|
123
|
-
The themes are just SCSS files installed into app/assets/stylesheets. You can tweak or modify or remove them after they get installed.
|
124
117
|
|
118
|
+
### `--markup` flag (NOTE: haml and slim are no longer supported at this time)
|
125
119
|
|
126
|
-
|
120
|
+
default is `erb`. IMPORTANT: As of right now, HAML and SLIM are not currently supported so the only option is also the default `erb`.
|
127
121
|
|
128
|
-
default is `erb`. IMPORTANT: As of right now, I am only supporting & building against ERB. HAML and SLIM are not currently supported.
|
129
122
|
|
130
|
-
|
131
|
-
## 3. RUN HOT-GLUE INSTALL:
|
132
123
|
### example installing ERB using Bootstrap layout:
|
133
124
|
`rails generate hot_glue:install --markup=erb --layout=bootstrap`
|
134
125
|
|
135
|
-
### Example installing
|
126
|
+
### Example installing using Hot Glue layout and the 'like_mountain_view' (Gmail-inspired) theme:
|
136
127
|
`rails generate hot_glue:install --markup=erb --layout=hotglue --theme=like_mountain_view`
|
137
128
|
|
138
129
|
The Hot Glue installer did several things for you in this step. Examine the git diffs or see 'Hot Glue Installer Notes' below.
|
@@ -152,11 +143,11 @@ https://github.com/FortAwesome/font-awesome-sass
|
|
152
143
|
|
153
144
|
Add to your Gemfile
|
154
145
|
|
155
|
-
As of
|
146
|
+
As of now, Devise for Rails 7 is still not released so you must use **main branch**, like so:
|
156
147
|
|
157
148
|
`gem 'devise', branch: 'main', git: 'https://github.com/heartcombo/devise.git'`
|
158
149
|
|
159
|
-
(If you are on Rails 6, you must do ALL of the steps in the Legacy Setup steps. Be sure not to skip **Legacy Step #5**
|
150
|
+
(If you are on Rails 6, you must do ALL of the steps in the Legacy Setup steps. Be sure not to skip **Legacy Step #5** below)
|
160
151
|
|
161
152
|
For Rails 7, be sure you are on the main branch of devise above and your logins should work. (The previously necessary step of disabling turbo shown in Legacy Step #5 is no longer needed. )
|
162
153
|
|
@@ -217,7 +208,7 @@ this may not have been automatically applied by the installer.
|
|
217
208
|
#### Hot Glue switched Capybara from RACK-TEST to HEADLESS CHROME
|
218
209
|
|
219
210
|
- By default Capybara is installed with :rack_test as its driver.
|
220
|
-
- This does not support Javascript
|
211
|
+
- This does not support Javascript. Hot Glue is not targeted for fallback browsers.
|
221
212
|
- From the [Capybara docs](https://github.com/teamcapybara/capybara#drivers):
|
222
213
|
```
|
223
214
|
By default, Capybara uses the :rack_test driver, which is fast but limited: it does not support JavaScript
|
@@ -312,7 +303,7 @@ end
|
|
312
303
|
|
313
304
|
### `--nested=`
|
314
305
|
|
315
|
-
This object is nested within another tree of objects, and there is a nested route in your `routes.rb` file. When specifying the parent(s), be sure to use **singular case**.
|
306
|
+
This object is nested within another tree of objects, and there is a nested route in your `routes.rb` file with the specified parent controllers above this controller. When specifying the parent(s), be sure to use **singular case**.
|
316
307
|
|
317
308
|
#### Example #1: One-level Nesting
|
318
309
|
Invoice `has_many :lines` and a Line `belongs_to :invoice`
|
@@ -373,16 +364,44 @@ Then, finally the @charge will be loaded
|
|
373
364
|
|
374
365
|
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.
|
375
366
|
|
367
|
+
|
368
|
+
#### Optionalized Nested Parents
|
369
|
+
|
370
|
+
Add `~` in front of any nested parameter (any parent in the `--nested` list) you want to make optional. This creates a two-headed controller: It can operate with or without that optionalized parameter.
|
371
|
+
|
372
|
+
This is an advanced feature. To use, **make duplicative routes to the same controller**. You can only use this feature with Gd controller.
|
373
|
+
|
374
|
+
Specify your controller *twice* in your routes.rb. Then, in your `--nested` setting, add `~` to any nested parent you want to **make optional**. "Make optional" means the controller will behave as-if it exists in two places: once, at the normal nest level. Then the same controller will 'exist' again one-level up in your routes. **If the route has sub-routes, you'll need to re-specify the entire subtree also**.
|
375
|
+
```
|
376
|
+
namespace :admin
|
377
|
+
resources :users do
|
378
|
+
resources :invoices
|
379
|
+
end
|
380
|
+
resources :invoices
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
384
|
+
Even though we have two routes pointed to **invoices**, both will go to the same controller (`app/controllers/admin/invoices_controller.rb`)
|
385
|
+
|
386
|
+
```
|
387
|
+
rails generate hot_glue:scaffold User --namespace=admin --gd
|
388
|
+
rails generate hot_glue:scaffold Invoice --namespace=admin --gd --nested=~users
|
389
|
+
```
|
390
|
+
Notice for the Invoice build, the parent user is *optionalized* (not 'optional'-- optionalized: to be made so it can be made optional).
|
391
|
+
|
392
|
+
The Invoices controller, which is a Gd controller, will load the User if a user is specified in the route (`/admin/users/:user_id/invoices/`). It will ALSO work at `/admin/invoices` and will switch back into loading directly from the base class when routed without the parent user.
|
393
|
+
|
394
|
+
|
376
395
|
|
377
396
|
### `--auth=`
|
378
397
|
|
379
398
|
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.
|
380
399
|
|
381
|
-
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.
|
400
|
+
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.
|
382
401
|
|
383
402
|
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.
|
384
403
|
|
385
|
-
When you display anything built with the scaffolding,
|
404
|
+
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.)
|
386
405
|
|
387
406
|
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.
|
388
407
|
|
@@ -396,28 +415,6 @@ It is also presumed that when viewing their own dashboard of things, the user wi
|
|
396
415
|
|
397
416
|
If you supply nesting (see below), your nest chain will automatically begin with your auth root object (see nesting)
|
398
417
|
|
399
|
-
#### Optionalized Nested Parents
|
400
|
-
|
401
|
-
Add `~` in front of any nested parameter (any parent in the `--nest` list) you want to make optional. This creates a two-headed controller: It can operate with or without that optionalized parameter.
|
402
|
-
|
403
|
-
This is an advanced feature is to use two duplicitous routes to the same controller. You can only use this feature with Gd controller. To use, specify your controller *twice* in your routes.rb. Then, in your `--nest` setting, add `~` to any nested parent you want to **make optional**. "Make optional" means the controller will behave as-if it exists in two places: once, at the normal nest level. Then the same controller will 'exist' again one-level up in your routes. **If the route has sub-routes, you'll need to re-specify the entire subtree also**.
|
404
|
-
```
|
405
|
-
namespace :admin
|
406
|
-
resources :users do
|
407
|
-
resources :invoices
|
408
|
-
end
|
409
|
-
resources :invoices
|
410
|
-
end
|
411
|
-
```
|
412
|
-
Hot Glue will build the scaffolding once for users and once again for invoice. Even though we have two routes pointed to invoices,
|
413
|
-
```
|
414
|
-
rails generate hot_glue:scaffold User --namespace=admin --gd --downnest=invoices
|
415
|
-
rails generate hot_glue:scaffold Invoice --namespace=admin --gd --nest=~users
|
416
|
-
```
|
417
|
-
Notice for the Invoice build, the parent user is *optionalized* (not 'optional'-- optionalized: to be made so it can be made optional). The Invoices controller, which is a Gd controller, will load the User if a user is specified in the route (`/admin/users/:user_id/invoices/`). It will ALSO work at `/admin/invoices` and will switch back into loading directly from the base class when routed this way.
|
418
|
-
|
419
|
-
|
420
|
-
|
421
418
|
|
422
419
|
### `--auth_identifier=`
|
423
420
|
|
@@ -450,6 +447,8 @@ The default (do not pass `auth_identifier=`) will match the `auth` (So if you us
|
|
450
447
|
|
451
448
|
|
452
449
|
`rails generate hot_glue:scaffold Thing --auth=current_account --auth_identifier=login`
|
450
|
+
|
451
|
+
|
453
452
|
In this example, the controller produced with:
|
454
453
|
```
|
455
454
|
before_action :authenticate_login!
|
@@ -460,11 +459,12 @@ However, the object graph anchors would continue to start from current_account.
|
|
460
459
|
```
|
461
460
|
|
462
461
|
Use empty string to **turn this method off**:
|
462
|
+
|
463
463
|
`rails generate hot_glue:scaffold Thing --auth=current_account --auth_identifier=''`
|
464
464
|
|
465
465
|
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.
|
466
466
|
|
467
|
-
Please note that this example would
|
467
|
+
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.
|
468
468
|
|
469
469
|
|
470
470
|
### `--plural=`
|
@@ -475,14 +475,14 @@ You don't need this if the pluralized version is just + "s" of the singular vers
|
|
475
475
|
### `--exclude=`
|
476
476
|
(separate field names by COMMA)
|
477
477
|
|
478
|
-
By default, all fields are included unless they are on the exclude list. (The default
|
478
|
+
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`.)
|
479
479
|
|
480
|
-
If you specify
|
480
|
+
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.)
|
481
481
|
|
482
482
|
|
483
483
|
`rails generate hot_glue:scaffold Account --exclude=password`
|
484
484
|
|
485
|
-
(The default excluded list is:
|
485
|
+
(The default excluded list is: If you want to edit any fields with the same name, you must use the include flag instead.)
|
486
486
|
|
487
487
|
|
488
488
|
### `--include=`
|
@@ -494,46 +494,50 @@ If you specify an include list, it will be treated as a whitelist: no fields wil
|
|
494
494
|
|
495
495
|
You may not specify both include and exclude.
|
496
496
|
|
497
|
-
Include setting is affected by both specified grouping and
|
497
|
+
Include setting is affected by both specified grouping mode and smart layouts, explained below.
|
498
498
|
|
499
499
|
|
500
500
|
#### Specified Grouping Mode
|
501
501
|
|
502
502
|
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.
|
503
503
|
|
504
|
-
(
|
504
|
+
(Smart layouts, below, achieves the same effect but automatically group your fields into a smart number of columns.)
|
505
505
|
|
506
506
|
If you want to group up fields together into columns, use a COLON (`:`) character to specify columns.
|
507
507
|
|
508
508
|
Your input **may** have a COLON at the end of it, but otherwise your columns will made **flush left**.
|
509
509
|
|
510
|
-
Without specified grouping (and not using smart layout), no
|
511
|
-
|
512
|
-
|
513
|
-
With a trailing colon you would be specifying the grouping. You're telling Hot Glue to make the two fields into column #1. (There is no other column.)
|
514
|
-
`--include=api_id,api_key:`
|
510
|
+
Without specified grouping (and not using smart layout), no grouping will happen. So these two fields would display in two (small, 1-column Bootstrap) columns:
|
511
|
+
|
512
|
+
`--include=first,last`
|
515
513
|
|
514
|
+
With a **trailing colon** you switch Hot Glue into specified grouping mode. You're telling Hot Glue to make the two fields into column #1. (There is no other column.)
|
515
|
+
`--include=first,last:`
|
516
516
|
|
517
|
-
|
518
|
-
`--include=name:api_id,api_key`
|
517
|
+
Hot Glue also happens to know that, for example, when you say "one column" you really want _one visual_ column made up of the available Bootstrap columns. For example, with 1 child portal (4) + the default edit/create buttons (2), you would have 6 remaining bootstrap columns (2+4+6=12). With 6 remaining Bootstrap columns Hot Glue will make 1 _visual colum_ into a 6-column Bootstrap column.
|
519
518
|
|
519
|
+
If, for example, you wanted to put the `email` field into column #1 and then the `first` and `last` into column #2, you would use:
|
520
|
+
`--include=email:first,last`
|
520
521
|
|
522
|
+
Assuming we have the same number of columns as the above example (6), Hot Glue knows that you now have 2 _visual columns_. It then gives each visual column 3-colum bootstrap columns, which makes your layout into a 3+3+4+2=12 Bootstrap layout.
|
521
523
|
|
522
|
-
Specifying any colon in your include syntax switches the builder into specified grouping mode
|
524
|
+
**Specifying any colon in your include syntax switches the builder into specified grouping mode.**
|
525
|
+
|
526
|
+
The effect will be that the fields will be stacked together into nicely fit columns. (This will look confusing if your end-user is expecting an Excel-like interface.)
|
523
527
|
|
524
|
-
With
|
528
|
+
With Hot Glue in specified grouping or smart layout mode, it automatically attempts to fit everything into Bootstrap 12-columns.
|
525
529
|
|
526
|
-
Using Bootstrap with neither specified grouping nor smart layouts may make 12 columns, which will produce strange
|
530
|
+
Using Bootstrap with neither specified grouping nor smart layouts may make more than 12 columns, which will produce strange results. (Bootstrap is not designed to work with, for example, a 13-column layout.)
|
527
531
|
|
528
|
-
You should typically either specify your grouping or use smart layouts when building with Bootstrap, but if your use case does not fit the stacking feature you can specify neither and then you may have deal with the over-stuffed layouts as explained
|
532
|
+
You should typically either specify your grouping or use smart layouts when building with Bootstrap, but if your use case does not fit the stacking feature you can specify neither flag and then you may then have to deal with the over-stuffed layouts as explained.
|
529
533
|
|
530
534
|
|
531
535
|
|
532
|
-
### `--smart-layout`
|
536
|
+
### `--smart-layout` (also known as automatic grouping)
|
533
537
|
|
534
538
|
Smart layouts are like specified grouping but Hot Glue does the work of figuring out how many fields you want in each column.
|
535
539
|
|
536
|
-
It will concatinate your fields into groups that will fit into the
|
540
|
+
It will concatinate your fields into groups that will fit into the Bootstrap's 12-column grid.
|
537
541
|
|
538
542
|
The effect will be that the fields will be stacked together into nicely fit columns.
|
539
543
|
|
@@ -541,7 +545,7 @@ The effect will be that the fields will be stacked together into nicely fit colu
|
|
541
545
|
|
542
546
|
**If your customer is used to Excel, this feature will confuse them.**
|
543
547
|
|
544
|
-
Also, this feature will **probably not** be supported by the SORTING (not yet implemented
|
548
|
+
Also, this feature will **probably not** be supported by the SORTING (not yet implemented). (You will be forced to choose between the two which I think makes sense.)
|
545
549
|
|
546
550
|
The layout builder works from right-to-left and starts with 12, the number of Bootstrap's columns.
|
547
551
|
|
@@ -553,7 +557,7 @@ If you're keeping track, that means we may have used 6 to 8 out of our Bootstrap
|
|
553
557
|
|
554
558
|
If we have 2 downnested portals and only the default buttons, that uses 10 out of 12 Bootstrap columns, leaving only 2 bootstrap columns for the fields.
|
555
559
|
|
556
|
-
|
560
|
+
The layout builder takes the number of columns remaining and then distributes the feilds 'evenly' among them. However, note that order specified translates to up-to-down within the column, and then left-to-right across the columns, like so:
|
557
561
|
|
558
562
|
A D G
|
559
563
|
|
@@ -563,7 +567,7 @@ C F I
|
|
563
567
|
|
564
568
|
This is what would happen if 9 fields, specified in the order A,B,C,D,E,F,G,H,I, were distributed across 3 columns.
|
565
569
|
|
566
|
-
(If you had a number of fields that wasn't easily divisible by the number of columns, it would leave the final column a few fields short of the others.)
|
570
|
+
(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.)
|
567
571
|
|
568
572
|
|
569
573
|
|
@@ -622,7 +626,11 @@ Automatically create subviews down your object tree. This should be the name of
|
|
622
626
|
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.
|
623
627
|
|
624
628
|
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).
|
625
|
-
|
629
|
+
|
630
|
+
Can now be created with more space (wider) by adding a `+` to the end of the downnest name
|
631
|
+
- e.g. `--downnest=abc+,xyz`
|
632
|
+
|
633
|
+
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
|
626
634
|
|
627
635
|
|
628
636
|
|
@@ -660,32 +668,41 @@ Omits pagination. (All list views have pagination by default.)
|
|
660
668
|
|
661
669
|
### `--no-list`
|
662
670
|
|
663
|
-
Omits list action. Only makes sense to use this if
|
671
|
+
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.)
|
672
|
+
|
664
673
|
|
674
|
+
### `--no-list-label`
|
665
675
|
|
666
|
-
|
676
|
+
Omits list LABEL itself above the list. (Do not confuse with the field labels.)
|
667
677
|
|
668
|
-
|
678
|
+
(Note that on a per model basis, you can also globally omit the label or set a unique label value using
|
679
|
+
`@@table_label_singular` and `@@table_label_plural` on your model objects.)
|
680
|
+
|
681
|
+
Note that list labels may be automatically omitted on downnested scaffolds.
|
682
|
+
|
683
|
+
### `--no-list-heading`
|
684
|
+
|
685
|
+
Omits the heading of column names that appears above the 1st row of data.
|
669
686
|
|
670
687
|
### `--no-create`
|
671
688
|
|
672
|
-
Omits create
|
689
|
+
Omits new & create actions.
|
673
690
|
|
674
691
|
### `--no-delete`
|
675
692
|
|
676
|
-
Omits delete action.
|
693
|
+
Omits delete button & destroy action.
|
677
694
|
|
678
695
|
### `--big-edit`
|
679
696
|
|
680
|
-
If you do not want inline editing of your list items but instead to fall back to full page style behavior for your edit views, use `--big-edit`. Turbo still handles the page interactions, but the user is taken to a full-screen edit page instead of an edit-in-place interaction.
|
697
|
+
If you do not want inline editing of your list items but instead want to fall back to full page style behavior for your edit views, use `--big-edit`. Turbo still handles the page interactions, but the user is taken to a full-screen edit page instead of an edit-in-place interaction.
|
681
698
|
|
682
|
-
### `--display-list-after-update`
|
699
|
+
### `--display-list-after-update`
|
683
700
|
|
684
701
|
After an update-in-place normally only the edit view is swapped out for the show view of the record you just edited.
|
685
702
|
|
686
703
|
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).
|
687
704
|
|
688
|
-
To do this, use flag `--
|
705
|
+
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.
|
689
706
|
|
690
707
|
|
691
708
|
|
@@ -695,12 +712,25 @@ To do this, use flag `--display_list_after_update`. The update will behave like
|
|
695
712
|
|
696
713
|
HotGlue will copy a file named base_controller.rb to the same folder where it tries to create any controller, unless such a file exists there already.
|
697
714
|
|
698
|
-
|
715
|
+
The created controller will always have this base controller as its subclass. You are encouraged to implement functionality common to the *namespace* (shared between the controllers in the namespace) using this technique.
|
716
|
+
|
717
|
+
## Special Table Labels
|
718
|
+
|
719
|
+
If your object is very wordy (like MyGreatHook) and you want it to display in the UI as something shorter,
|
720
|
+
add `@@table_label_plural = "Hooks"` and `@@table_label_singular = "Hook"`.
|
721
|
+
|
722
|
+
Hot Glue will use this as the **list heading** and **New record label**, respectively. This affects only the UI only.
|
723
|
+
|
724
|
+
You can also set these to `nil` to omit the labels completely.
|
725
|
+
|
726
|
+
Child portals have the headings omitted automatically (there is a heading identifying them already on the parent view where they get included), or you can use the `--no-list-heading` on any specific build.
|
727
|
+
|
699
728
|
|
700
729
|
## Field Types Supported
|
701
730
|
|
702
731
|
- Integers that don't end with `_id`: displayed as input fields with type="number"
|
703
|
-
- Integers that do end with `_id` will be treated automatically as associations. You should have a Rails association defined. (Hot Glue will warn you if it can't find one.)
|
732
|
+
- Foreign keys: Integers that do end with `_id` will be treated automatically as associations. You should have a Rails association defined. (Hot Glue will warn you if it can't find one.)
|
733
|
+
- Note: if your foreign key has a nonusual class name, it should be using the `class_name:` in the model definition
|
704
734
|
- String: displayed as small input box
|
705
735
|
- Text: displayed as large textarea
|
706
736
|
- Float: displayed as input box
|
@@ -709,32 +739,57 @@ Obviously, the created controller will always have this base controller as its s
|
|
709
739
|
- Time: displayed as HTML5 time picker
|
710
740
|
- Boolean: displayed radio buttons yes/ no
|
711
741
|
- Enum - displayed as a drop-down list (defined the enum values on your model). For Rails 6 see https://jasonfleetwoodboldt.com/courses/stepping-up-rails/enumerated-types-in-rails-and-postgres/
|
742
|
+
- AFAIK, you must specify the enum definition both in your model and also in your database migration for both Rails 6 + Rails 7
|
712
743
|
|
713
744
|
|
714
745
|
# VERSION HISTORY
|
715
746
|
|
747
|
+
#### 2022-02-14 - v0.4.9
|
748
|
+
• Fixed issue building models with namespaced class names (e.g. `Fruits::Apple` and `Fruits::Bannana`).
|
749
|
+
You can now build against these kinds of models natively (be sure to pass the full model name, with double-colon syntax).
|
750
|
+
|
751
|
+
• Also fixes issues when associations themselves were namespaced models.
|
716
752
|
|
753
|
+
• The N+1 Killer (!!!)
|
754
|
+
- N+1 queries for _any association_ built by the list are now automagically killed by the list controllers.
|
755
|
+
- Thanks to the work done back in Rails 5.2, Rails smartly uses either two queries to glob up the data OR one query with a LEFT OUTER JOIN if that's faster. You don't have to think about it. **Thanks Rails 5.2!**
|
756
|
+
- Hot Glue now adds `.includes(...)` if it is including an association when it loads the list view to eliminate the N+1s
|
757
|
+
- Bullet is still the best way to identify, find, & fix your N+1 queries is still highly recommended. https://github.com/flyerhzm/bullet
|
758
|
+
|
759
|
+
• Downnest children (portals) can now be created with more space (made wider) by adding one or more `+` to the end of the downnest name, denoting add 1 bootstrap column.
|
760
|
+
- e.g. `--downnest=abc+,xyz`
|
761
|
+
|
762
|
+
The "Abc" portal will take up 5 bootstrap columns and the Xyz portal will take up 4 columns. (++ to indicate 6, `+++` for 7, etc)
|
763
|
+
|
764
|
+
• Now that you can expand the downnest child portal width, you can easily give them too much width taking up more than 12 bootstrap columns.
|
765
|
+
The builder stops you from building if you have taken up too many bootstrap columns, are in _either_ **Smart Layout mode** or **Specified Grouping mode**.
|
766
|
+
However, this check is not in place if you are in neither mode, in which case you should watch out for building more than 12 bootstrap columns.
|
767
|
+
|
768
|
+
• To give a special label to a model, add `@@table_label_plural = "The Things"` and `@@table_label_singular = "The Thing"`.
|
769
|
+
This model will now have a list heading of "The Things" instead of its usual name.
|
770
|
+
For 'create buttons' and the 'New' screen, the builder will use the singular setting.
|
771
|
+
This affects all builds against that model and affects the UI (display) only.
|
772
|
+
All route names, table names, and variables are unaffected by this.
|
773
|
+
|
774
|
+
• You can also use `@@table_label_plural = nil` to set these tables to **omit** the label headings, or use the new flag...
|
775
|
+
|
776
|
+
• `--no-list-heading` (defaults to false but note several conditions when list headings are now hidden)
|
777
|
+
|
778
|
+
Omits the list heading. Note that the listing heading is omitted:
|
779
|
+
1) if there is no list,
|
780
|
+
2) if you set the `--no-list-heading` flag,
|
781
|
+
3) if the model has `@@table_label_plural = nil`, or
|
782
|
+
4) if you are constructing a nested child portal with only non-optionalized parents.
|
783
|
+
|
784
|
+
|
785
|
+
|
786
|
+
#### 2022-02-09 - v0.4.8.1 - Issue with Installer for v0.4.8
|
787
|
+
- There was an issue for the installer for v0.4.8. This new version v0.4.8.1 correts it.
|
717
788
|
|
718
789
|
|
719
790
|
#### 2022-02-07 - v0.4.8 Optionalized Nested Parents
|
720
791
|
- optionalized nested parents. to use add `~` in front of any nested parameter you want to make optional
|
721
792
|
|
722
|
-
- This is an advanced feature is to use two duplicitous routes to the same controller. You can only use this feature with Gd controller. To use, specify your controller *twice* in your routes.rb. Then, in your `--nest` setting, add `~` to any nested parent you want to **make optional**. "Make optional" means the controller will behave as-if it exists in two places: once, at the normal nest level. Then the same controller will 'exist' again one-level up in your routes. **If the route has sub-routes, you'll need to re-specify the entire subtree also**.
|
723
|
-
```
|
724
|
-
namespace :admin
|
725
|
-
resources :users do
|
726
|
-
resources :invoices
|
727
|
-
end
|
728
|
-
resoures :invoices
|
729
|
-
end
|
730
|
-
```
|
731
|
-
We will build the scaffolding once for users and once again for invoice. Even though we have two routes pointed to invoices,
|
732
|
-
```
|
733
|
-
rails generate hot_glue:scaffold User --namespace=admin --gd --downnest=invoices
|
734
|
-
rails generate hot_glue:scaffold Invoice --namespace=admin --gd --nest=~users
|
735
|
-
```
|
736
|
-
- Notice for the Invoice build, the parent user is *optionalized* (not 'optional'-- optionalized: to be made so it can be made optional). The Invoices controller, which is a Gd controller, will load the User if a user is specified in the route (`/admin/users/:user_id/invoices/`). It will ALSO work at `/admin/invoices` and will switch back into loading directly from the base class when routed this way.
|
737
|
-
- fixes to specified grouping mode-- the columns you specify now grab up the remaining bootstrap columns to fill space
|
738
793
|
|
739
794
|
#### 2022-01-26 - v0.4.7 `--nest` has been renamed to `--nested`; please use `--nested` moving forward
|
740
795
|
- `--alt-controller-name` feature from the last release has been removed, I have something better coming soon
|
@@ -851,8 +906,8 @@ COVERGE=on rake spec
|
|
851
906
|
--
|
852
907
|
--
|
853
908
|
|
854
|
-
Test coverage as of 2022-02-
|
855
|
-
|
856
|
-
![Screen Shot 2022-02-
|
909
|
+
Test coverage as of 2022-02-14 (v0.4.9)
|
910
|
+
|
911
|
+
![Screen Shot 2022-02-14 at 8 33 29 PM](https://user-images.githubusercontent.com/59002/153975911-30fa9c84-c8d8-49e7-bd5c-e2b958d6f10e.png)
|
857
912
|
|
858
913
|
|
@@ -38,7 +38,7 @@ module HotGlue
|
|
38
38
|
require 'open-uri'
|
39
39
|
|
40
40
|
# ask HeliosDev.shop if this email is good
|
41
|
-
stream = URI.open("https://heliosdev.shop/check_licenses/hot-glue
|
41
|
+
stream = URI.open("https://heliosdev.shop/check_licenses/hot-glue?email=#{license_email}")
|
42
42
|
resp = JSON.parse(stream.read)
|
43
43
|
|
44
44
|
if resp['status'] == 'success'
|
@@ -4,13 +4,13 @@ module HotGlue
|
|
4
4
|
module Layout
|
5
5
|
class Builder
|
6
6
|
attr_reader :include_setting,
|
7
|
-
:
|
7
|
+
:downnest_object,
|
8
8
|
:buttons_width, :columns,
|
9
9
|
:smart_layout, :specified_grouping_mode
|
10
10
|
|
11
11
|
def initialize(params)
|
12
12
|
@include_setting = params[:include_setting]
|
13
|
-
@
|
13
|
+
@downnest_object = params[:downnest_object]
|
14
14
|
|
15
15
|
@buttons_width = params[:buttons_width]
|
16
16
|
|
@@ -32,23 +32,28 @@ module HotGlue
|
|
32
32
|
buttons: { size: @buttons_width}
|
33
33
|
}
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
end
|
35
|
+
# downnest_object.each do |child, size|
|
36
|
+
# layout_object[:portals][child] = {size: size}
|
37
|
+
# end
|
38
38
|
|
39
39
|
# smart layout: 2 columns per field; 4 column for EACH downnested portals, 2 column for buttons
|
40
|
-
how_many_downnest =
|
40
|
+
how_many_downnest = downnest_object.size
|
41
|
+
|
42
|
+
bootstrap_columns = (12 - @buttons_width )
|
43
|
+
|
44
|
+
bootstrap_columns = bootstrap_columns - (downnest_object.collect{|k,v| v}.sum)
|
41
45
|
|
42
|
-
bootstrap_columns = (12-@buttons_width )
|
43
|
-
bootstrap_columns = bootstrap_columns - (how_many_downnest*4)
|
44
46
|
available_columns = (bootstrap_columns / 2).floor # bascially turns the 12-column grid into a 6-column grid
|
45
47
|
|
46
48
|
if available_columns < 0
|
47
49
|
raise "Cannot build layout with #{how_many_downnest} downnested portals"
|
48
50
|
end
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
downnest_children_width = []
|
53
|
+
|
54
|
+
downnest_object.each do |child, size|
|
55
|
+
layout_object[:portals][child] = {size: size}
|
56
|
+
end
|
52
57
|
|
53
58
|
|
54
59
|
if smart_layout
|
@@ -56,6 +61,10 @@ module HotGlue
|
|
56
61
|
#
|
57
62
|
|
58
63
|
if columns.size > available_columns
|
64
|
+
if available_columns == 0
|
65
|
+
raise "Oopps... No available columns in SMART LAYOUT"
|
66
|
+
end
|
67
|
+
|
59
68
|
each_col_can_have = (columns.size.to_f / available_columns.to_f).round
|
60
69
|
|
61
70
|
layout_object[:columns][:container] = (0..available_columns-1).collect { |x|
|
@@ -94,9 +94,9 @@ module HotGlue
|
|
94
94
|
end
|
95
95
|
|
96
96
|
is_owner = col == ownership_field
|
97
|
-
|
97
|
+
assoc_class_name = assoc.active_record.name
|
98
|
+
display_column = HotGlue.derrive_reference_name(assoc_class_name)
|
98
99
|
|
99
|
-
# TODO: add is_owner && check if this nested arg is optional
|
100
100
|
(is_owner ? "<% unless @#{assoc_name} %>\n" : "") +
|
101
101
|
" <%= f.collection_select(:#{col}, #{assoc.class_name}.all, :id, :#{display_column}, {prompt: true, selected: @#{singular}.#{col} }, class: 'form-control') %>\n" +
|
102
102
|
(is_owner ? "<% else %>\n <%= @#{assoc_name}.#{display_column} %>" : "") +
|
@@ -209,9 +209,8 @@ module HotGlue
|
|
209
209
|
exit
|
210
210
|
# raise(HotGlue::Error,exit_message)
|
211
211
|
end
|
212
|
-
|
213
|
-
display_column = HotGlue.derrive_reference_name(
|
214
|
-
|
212
|
+
assoc_class_name = assoc.active_record.name
|
213
|
+
display_column = HotGlue.derrive_reference_name(assoc_class_name)
|
215
214
|
"<%= #{singular}.#{assoc.name.to_s}.try(:#{display_column}) || '<span class=\"content alert-danger\">MISSING</span>'.html_safe %>"
|
216
215
|
|
217
216
|
else
|
@@ -12,6 +12,15 @@ module HotGlue
|
|
12
12
|
class Error < StandardError
|
13
13
|
end
|
14
14
|
|
15
|
+
def self.construct_downnest_object(input)
|
16
|
+
res = input.split(",").map { |child|
|
17
|
+
child_name = child.gsub("+","")
|
18
|
+
extra_size = child.count("+")
|
19
|
+
{child_name => 4+extra_size}
|
20
|
+
}
|
21
|
+
Hash[*res.collect{|hash| hash.collect{|key,value| [key,value].flatten}.flatten}.flatten]
|
22
|
+
end
|
23
|
+
|
15
24
|
def self.optionalized_ternary(params)
|
16
25
|
namespace = params[:namespace] || nil
|
17
26
|
target = params[:target]
|
@@ -116,7 +125,9 @@ module HotGlue
|
|
116
125
|
class_option :smart_layout, type: :boolean, default: false
|
117
126
|
class_option :markup, type: :string, default: nil # deprecated -- use in app config instead
|
118
127
|
class_option :layout, type: :string, default: nil # if used here it will override what is in the config
|
119
|
-
class_option :
|
128
|
+
class_option :no_list_label, type: :boolean, default: false
|
129
|
+
class_option :no_list_heading, type: :boolean, default: false
|
130
|
+
|
120
131
|
class_option :before_list_labels, type: :boolean, default: false
|
121
132
|
|
122
133
|
def initialize(*meta_args)
|
@@ -191,6 +202,12 @@ module HotGlue
|
|
191
202
|
|
192
203
|
|
193
204
|
@singular = args.first.tableize.singularize # should be in form hello_world
|
205
|
+
|
206
|
+
|
207
|
+
if @singular.include?("/")
|
208
|
+
@singular = @singular.split("/").last
|
209
|
+
end
|
210
|
+
|
194
211
|
@plural = options['plural'] || @singular + "s" # supply to override; leave blank to use default
|
195
212
|
@namespace = options['namespace'] || nil
|
196
213
|
|
@@ -217,9 +234,10 @@ module HotGlue
|
|
217
234
|
|
218
235
|
@nested = (!options['nested'].empty? && options['nested']) || nil
|
219
236
|
|
220
|
-
@singular_class =
|
221
|
-
@exclude_fields = []
|
237
|
+
@singular_class = args.first # note this is the full class name with a model namespace
|
222
238
|
|
239
|
+
|
240
|
+
@exclude_fields = []
|
223
241
|
@exclude_fields += options['exclude'].split(",").collect(&:to_sym)
|
224
242
|
|
225
243
|
if !options['include'].empty?
|
@@ -246,7 +264,9 @@ module HotGlue
|
|
246
264
|
|
247
265
|
@no_edit = options['no_edit'] || false
|
248
266
|
@no_list = options['no_list'] || false
|
249
|
-
@
|
267
|
+
@no_list_label = options['no_list_label'] || false
|
268
|
+
@no_list_heading = options['no_list_heading'] || false
|
269
|
+
|
250
270
|
@display_list_after_update = options['display_list_after_update'] || false
|
251
271
|
@smart_layout = options['smart_layout']
|
252
272
|
|
@@ -258,13 +278,13 @@ module HotGlue
|
|
258
278
|
@container_name = @layout == "hotglue" ? "scaffold-container" : "container-fluid"
|
259
279
|
@downnest = options['downnest'] || false
|
260
280
|
|
261
|
-
@downnest_children = []
|
262
|
-
|
281
|
+
@downnest_children = [] # TODO: defactor @downnest_children in favor of downnest_object
|
282
|
+
@downnest_object = {}
|
263
283
|
if @downnest
|
264
|
-
@downnest_children = @downnest.split(",")
|
284
|
+
@downnest_children = @downnest.split(",").map{|child| child.gsub("+","")}
|
285
|
+
@downnest_object = HotGlue.construct_downnest_object(@downnest)
|
265
286
|
end
|
266
287
|
|
267
|
-
|
268
288
|
if @god
|
269
289
|
@auth = nil
|
270
290
|
end
|
@@ -364,7 +384,7 @@ module HotGlue
|
|
364
384
|
|
365
385
|
builder = HotGlue::Layout::Builder.new({
|
366
386
|
include_setting: options['include'],
|
367
|
-
|
387
|
+
downnest_object: @downnest_object,
|
368
388
|
buttons_width: buttons_width,
|
369
389
|
columns: @columns,
|
370
390
|
smart_layout: @smart_layout
|
@@ -372,6 +392,7 @@ module HotGlue
|
|
372
392
|
@layout_object = builder.construct
|
373
393
|
|
374
394
|
@menu_file_exists = true if @nested_args.none? && File.exists?("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_menu.#{@markup}")
|
395
|
+
|
375
396
|
end
|
376
397
|
|
377
398
|
def identify_object_owner
|
@@ -379,6 +400,7 @@ module HotGlue
|
|
379
400
|
|
380
401
|
if !@object_owner_sym.empty?
|
381
402
|
auth_assoc_field = auth_assoc + "_id" unless @god
|
403
|
+
|
382
404
|
assoc = eval("#{singular_class}.reflect_on_association(:#{@object_owner_sym})")
|
383
405
|
|
384
406
|
if assoc
|
@@ -422,6 +444,7 @@ module HotGlue
|
|
422
444
|
@columns = @the_object.columns.map(&:name).map(&:to_sym).reject{|field| !@include_fields.include?(field) }
|
423
445
|
end
|
424
446
|
|
447
|
+
@associations = []
|
425
448
|
@columns.each do |col|
|
426
449
|
if col.to_s.starts_with?("_")
|
427
450
|
@show_only << col
|
@@ -431,11 +454,10 @@ module HotGlue
|
|
431
454
|
if col.to_s.ends_with?("_id")
|
432
455
|
# guess the association name label
|
433
456
|
assoc_name = col.to_s.gsub("_id","")
|
434
|
-
assoc = eval("#{singular_class}.reflect_on_association(:#{assoc_name})")
|
435
457
|
|
436
458
|
|
437
459
|
begin
|
438
|
-
eval(
|
460
|
+
assoc_model = eval("#{singular_class}.reflect_on_association(:#{assoc_name}).active_record")
|
439
461
|
rescue NameError => e
|
440
462
|
exit_message = "*** Oops: The model #{singular_class} is missing an association for :#{assoc_name} or the model #{assoc_name.titlecase} doesn't exist. TODO: Please implement a model for #{assoc_name.titlecase}; your model #{singular_class.titlecase} should belong_to :#{assoc_name}. To make a controller that can read all records, specify with --god."
|
441
463
|
puts exit_message
|
@@ -443,20 +465,21 @@ module HotGlue
|
|
443
465
|
end
|
444
466
|
|
445
467
|
|
446
|
-
if
|
468
|
+
if assoc_model.nil?
|
447
469
|
exit_message = "*** Oops. on the #{singular_class} object, there doesn't seem to be an association called '#{assoc_name}'"
|
448
470
|
puts exit_message
|
449
471
|
raise(HotGlue::Error,exit_message)
|
450
472
|
end
|
451
473
|
|
452
|
-
|
474
|
+
@associations << assoc_name.to_sym
|
475
|
+
assoc_class = eval(assoc_model.name)
|
453
476
|
name_list = [:name, :to_label, :full_name, :display_name, :email]
|
454
477
|
if name_list.collect{ |field|
|
455
478
|
assoc_class.column_names.include?(field.to_s) || assoc_class.instance_methods.include?(field)
|
456
479
|
}.any?
|
457
480
|
# do nothing here
|
458
481
|
else
|
459
|
-
exit_message = "*** Oops: Missing a label for
|
482
|
+
exit_message = "\n*** Oops: Missing a label for `#{assoc_class}`. Can't find any column to use as the display label for the #{assoc_name} association on the #{singular_class} model. TODO: Please implement just one of: \n1) name, \n2) to_label, \n3) full_name, \n4) display_name \n5) email \nYou can implement any of these directly on your`#{assoc_class}` model (can be database fields or model methods) or alias them to field you want to use as your display label. Then RERUN THIS GENERATOR. (Field used will be chosen based on rank here.)"
|
460
483
|
raise(HotGlue::Error,exit_message)
|
461
484
|
end
|
462
485
|
end
|
@@ -512,16 +535,12 @@ module HotGlue
|
|
512
535
|
end
|
513
536
|
|
514
537
|
def list_column_headings
|
515
|
-
if @
|
538
|
+
if @layout == "bootstrap"
|
539
|
+
column_width = @layout_object[:columns][:size_each]
|
540
|
+
col_identifier = "col-md-#{column_width}"
|
541
|
+
elsif @layout == "hotglue"
|
516
542
|
column_width = each_col * @columns.count
|
517
|
-
|
518
|
-
column_width = 0
|
519
|
-
end
|
520
|
-
|
521
|
-
if !@smart_layout
|
522
|
-
col_identifier = @layout == "hotglue" ? "scaffold-cell" : "col-md-1"
|
523
|
-
else
|
524
|
-
col_identifier = @layout == "hotglue" ? "scaffold-cell" : "col-md-2"
|
543
|
+
col_identifier = "scaffold-cell"
|
525
544
|
end
|
526
545
|
|
527
546
|
@template_builder.list_column_headings(
|
@@ -586,6 +605,10 @@ module HotGlue
|
|
586
605
|
@singular
|
587
606
|
end
|
588
607
|
|
608
|
+
def singular_class_name
|
609
|
+
@singular_class
|
610
|
+
end
|
611
|
+
|
589
612
|
def plural_name
|
590
613
|
plural
|
591
614
|
end
|
@@ -900,6 +923,22 @@ module HotGlue
|
|
900
923
|
@each_col ||= each_col
|
901
924
|
end
|
902
925
|
|
926
|
+
def list_label
|
927
|
+
if(eval("#{class_name}.class_variable_defined?(:@@table_label_plural)"))
|
928
|
+
eval("#{class_name}.class_variable_get(:@@table_label_plural)")
|
929
|
+
else
|
930
|
+
plural.gsub("_", " ").upcase
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
def thing_label
|
935
|
+
if(eval("#{class_name}.class_variable_defined?(:@@table_label_singular)"))
|
936
|
+
eval("#{class_name}.class_variable_get(:@@table_label_singular)")
|
937
|
+
else
|
938
|
+
singular.gsub("_", " ").upcase
|
939
|
+
end
|
940
|
+
end
|
941
|
+
|
903
942
|
def each_col
|
904
943
|
return col_width if @columns.count == 0
|
905
944
|
(col_width/(@columns.count)).to_i
|
@@ -1030,6 +1069,15 @@ module HotGlue
|
|
1030
1069
|
end
|
1031
1070
|
end
|
1032
1071
|
|
1072
|
+
|
1073
|
+
def n_plus_one_includes
|
1074
|
+
if @associations.any?
|
1075
|
+
".includes(" + @associations.map{|x| ":#{x.to_s}"}.join(", ") + ")"
|
1076
|
+
else
|
1077
|
+
""
|
1078
|
+
end
|
1079
|
+
end
|
1080
|
+
|
1033
1081
|
def nested_for_turbo_nested_constructor(top_level = true)
|
1034
1082
|
instance_symbol = "@" if top_level
|
1035
1083
|
instance_symbol = "" if !top_level
|
@@ -1052,7 +1100,6 @@ module HotGlue
|
|
1052
1100
|
end
|
1053
1101
|
end
|
1054
1102
|
|
1055
|
-
|
1056
1103
|
private # thor does something fancy like sending the class all of its own methods during some strange run sequence
|
1057
1104
|
# does not like public methods
|
1058
1105
|
def cc_filename_with_extensions(name, file_format = format)
|
@@ -40,24 +40,29 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
40
40
|
@<%= @nested_set[0][:singular] %> ||= <%= root_object %> <% else %>
|
41
41
|
@<%= @nested_set[0][:singular] %> ||= <%= root_object %>.find(params[:<%= @nested_set[0][:singular] %>_id])<%= " if params.include?(:#{@nested_set[0][:singular]}_id)" if @nested_set[0][:optional] %> <% end %>
|
42
42
|
end
|
43
|
-
<% end %><% if any_nested? %><% nest_chain = [@nested_set[0][:singular]]; this_scope = @nested_set[0][:plural]; %>
|
44
|
-
|
45
|
-
|
43
|
+
<% end %><% if any_nested? %><% nest_chain = [@nested_set[0][:singular]]; this_scope = @nested_set[0][:plural]; %>
|
44
|
+
<% for index in 0..(@nested_set.count - 1) do
|
45
|
+
arg = @nested_set[index]
|
46
|
+
last_arg = (index == 0 ? nil : @nested_set[index-1])
|
47
|
+
|
48
|
+
this_scope = "#{nest_chain.last}.#{arg[:plural]}"
|
49
|
+
nest_chain << arg %>
|
46
50
|
def <%= arg[:singular] %>
|
47
|
-
@<%= arg[:singular] %> ||= (<%= this_scope %>.find(params[:<%= arg[:singular] %>_id])<%= " if params.include?(:#{
|
48
|
-
|
51
|
+
@<%= arg[:singular] %> ||= (<%= this_scope %>.find(params[:<%= arg[:singular] %>_id])<%= " if params.include?(:#{last_arg[:singular]}_id)" if last_arg && @god && last_arg[:optional] %>)
|
52
|
+
<% if @god && (last_arg[:optional] ) %>@<%= arg[:singular] %> ||= (<%= collect_objects[index-1] %>.find(params[:<%= arg[:singular] %>_id]) if params.include?(:<%= arg[:singular] %>_id) ) <% end %>
|
53
|
+
end<% end %> <% end %> <% if !@self_auth %>
|
49
54
|
|
50
55
|
def load_<%= singular_name %>
|
51
56
|
@<%= singular_name %> = (<%= object_scope.gsub("@",'') %>.find(params[:id])<%= " if params.include?(:#{@nested_set.last[:singular]}_id)" if @nested_set[0] && @nested_set[0][:optional] %>)<% if @nested_set[0] && @nested_set[0][:optional] %> || <%= class_name %>.find(params[:id])<% end %>
|
52
57
|
end
|
53
58
|
<% else %>
|
54
59
|
def load_<%= singular_name %>
|
55
|
-
@<%= singular_name %> = (<%= auth_object.gsub("@",'') %><%= " if params.include?(:#{@nested_set[0][:singular]}_id)" if @nested_set[0][:optional] %>)<% if @nested_set[0][:optional] %> || <%= class_name %>.find(params[:id])<% end %>
|
60
|
+
@<%= singular_name %> = (<%= auth_object.gsub("@",'') %><%= " if params.include?(:#{@nested_set[0][:singular]}_id)" if @nested_set.any? && @nested_set[0][:optional] %>)<% if @nested_set.any? && @nested_set[0][:optional] %> || <%= class_name %>.find(params[:id])<% end %>
|
56
61
|
end<% end %>
|
57
62
|
|
58
63
|
def load_all_<%= plural %> <% if !@self_auth %>
|
59
|
-
@<%= plural_name %> = ( <%= object_scope.gsub("@",'') %>.page(params[:page])<%= " if params.include?(:#{ @nested_set.last[:singular]}_id)" if @nested_set[0] && @nested_set[0][:optional] %>) <% if @nested_set[0] && @nested_set[0][:optional] %> || <%= class_name %>.all<% end %> <% else %>
|
60
|
-
@<%= plural_name %> = <%= class_name %>.where(id: <%= auth_object.gsub("@",'') %>.id) # returns iterable even though this <%= singular_name %> is
|
64
|
+
@<%= plural_name %> = ( <%= object_scope.gsub("@",'') %>.page(params[:page])<%= n_plus_one_includes %><%= " if params.include?(:#{ @nested_set.last[:singular]}_id)" if @nested_set.any? && @nested_set[0] && @nested_set[0][:optional] %>) <% if @nested_set[0] && @nested_set[0][:optional] %> || <%= class_name %>.all<% end %> <% else %>
|
65
|
+
@<%= plural_name %> = <%= class_name %>.where(id: <%= auth_object.gsub("@",'') %>.id)<%= n_plus_one_includes %> # returns iterable even though this <%= singular_name %> is only allowed access to themselves<% end %>
|
61
66
|
end
|
62
67
|
|
63
68
|
def index
|
@@ -1,24 +1,28 @@
|
|
1
1
|
<\%= turbo_frame_tag "<%= plural %>-list" <%= nested_for_turbo_id_list_constructor %> do %>
|
2
2
|
<div class="<%= @container_name %> scaffold-list">
|
3
|
-
<% unless @no_list || @
|
3
|
+
<% unless @no_list || @no_list_label || (@nested_set.any? && !@nested_set.collect{|x| x[:optional]}.any?) %>
|
4
|
+
<% unless list_label.nil? %><h4>
|
5
|
+
<%= list_label %>
|
6
|
+
</h4><% end %>
|
7
|
+
<% end %>
|
4
8
|
|
5
9
|
<% unless @no_create %><%= '<%= render partial: "' + ((@namespace+"/" if @namespace) || "") + @controller_build_folder + '/new_button", locals: {}' + @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}} : {})"}.join() + ' %\>'.gsub('\\',"") %><br /><% end %>
|
6
10
|
|
7
11
|
<% unless @no_list %>
|
8
|
-
<% unless @
|
12
|
+
<% unless @no_list_heading %>
|
9
13
|
<div class="row scaffold-heading-row">
|
10
14
|
<%= list_column_headings %>
|
11
|
-
<% if @
|
12
|
-
|
15
|
+
<% if @downnest_object.any? %>
|
16
|
+
<%# each_downnest_width = @downnest_object.count == 1 ? 40 : (60/@downnest_object.count).floor %>
|
13
17
|
<% downnest_column_style = @layout == "hotglue" ? 'style="flex-basis: ' + each_downnest_width.to_s + '%;' : "" %>
|
14
18
|
|
15
|
-
<% @
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
<% @downnest_object.each do |downnest,i| %>
|
20
|
+
<div class=" scaffold-col-heading<%= " col-sm-#{ @layout_object[:portals][downnest][:size] }" if @layout=="bootstrap" %>" <%= downnest_column_style %>>
|
21
|
+
<strong>
|
22
|
+
<%= downnest.titleize %>
|
23
|
+
</strong>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
22
26
|
<% end %>
|
23
27
|
|
24
28
|
<% button_column_style = @layout == "hotglue" ? 'style="flex-basis: 150px' : "" %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<\%= turbo_frame_tag "<%= singular %>-new" do %>
|
2
2
|
<div>
|
3
|
-
<\%= link_to "New <%=
|
3
|
+
<\%= link_to "New <%= thing_label %>", <%= new_path_name %>, disable_with: "Loading...", class: "new-<%= singular %>-button btn btn-primary pull-right <%= 'btn-sm' if @nested_args.any? %> " %>
|
4
4
|
</div>
|
5
5
|
<\% end %>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<\%= turbo_frame_tag "<%= singular %>-new" do %>
|
2
2
|
<h3>
|
3
|
-
New <%=
|
3
|
+
New <%= thing_label %>
|
4
4
|
</h3>
|
5
5
|
<\%= form_with model: <%= singular %>, url: <%= form_path_new_helper %>, method: :post do |f| \%>
|
6
6
|
<\%= render partial: "<%= namespace_with_slash + @controller_build_folder %>/form",
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<% if @downnest_children.any? %>
|
4
4
|
<% each_downnest_width = @downnest_children.count == 1 ? 33 : (53/@downnest_children.count).floor %>
|
5
5
|
|
6
|
-
<% @
|
6
|
+
<% @downnest_object.each do |downnest, size| %>
|
7
7
|
|
8
8
|
<% downnest_object = eval("#{singular_class}.reflect_on_association(:#{downnest})") %>
|
9
9
|
<% if downnest_object.nil?; raise "no relationship for downnested portal `#{downnest}` found on `#{singular_class}`; please check relationship for has_many :#{downnest}"; end; %>
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<\% end %>
|
10
10
|
|
11
11
|
<h2>Editing <\%= @<%= @singular %>.<%= display_class %> %></h2>
|
12
|
-
<\%= form_with model: <%= "@" + singular%>, url: <%= form_path_edit_helper %> do |f| %>
|
12
|
+
<\%= form_with model: <%= "@" + singular %>, url: <%= form_path_edit_helper %> do |f| %>
|
13
13
|
<\%= render partial: "form", locals: {:<%= singular %> => <%= "@" + singular%>, f: f}<%= @nested_set.collect{|arg| ".merge(@#{arg[:singular]} ? {#{arg[:singular]}: @#{arg[:singular]}} : {})" }.join %> \%>
|
14
14
|
<\% end %>
|
15
15
|
|
data/lib/hotglue/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot-glue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Fleetwood-Boldt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -125,7 +125,7 @@ files:
|
|
125
125
|
- lib/hot-glue.rb
|
126
126
|
- lib/hotglue/engine.rb
|
127
127
|
- lib/hotglue/version.rb
|
128
|
-
homepage: https://
|
128
|
+
homepage: https://heliosdev.shop/p/hot-glue?utm_source=rubygems.org&utm_campaign=rubygems_link
|
129
129
|
licenses:
|
130
130
|
- Commercial with free option
|
131
131
|
metadata:
|