hot-glue 0.6.9.2 → 0.6.11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e1a2a4647618d6a657137799251c71e0f28dbc4023d2c870286fe9103971d88
4
- data.tar.gz: 50bd3438275045b1c97a7f0834eb8e7e2a646381f5795134929cd79f35c78190
3
+ metadata.gz: a9b9cdb44bde02376148703d050944c0c7cc169b42349b021539098afd8532bf
4
+ data.tar.gz: e4baffd1a1d3d4cf01b811c850b02827c803f7d1188a5b5166f40b11730a0821
5
5
  SHA512:
6
- metadata.gz: d115b6cee913161d39e9b345c994764fae30a4bda922396c5cac953d8918607844761082014d112c305d8e4ce1635d0c74a1481e5179cc6c07dcfbd954c6dcfa
7
- data.tar.gz: 566eddd287147279e2defed46ac941c1674002c35e54535feabae2b81975a54415990d464a1725af7c51fd59a4375d98d2a9d2145a515e16daf86920346b593a
6
+ metadata.gz: f298a2685d19ebc5a27c16d72cb05806a74bdf3ffbe15b7f85e799164e0d4c56ae1605d950a0843749a3db840c957d1f8a30feb27d68b276656a277a3dd21d53
7
+ data.tar.gz: e5984cc18282e0b5b99f5992d764395dc9964c1a81b08c6848d9f498564e04e8623275fd40b4c67ca2759d400fe3d4e952ea4db61aada50a63f7fab3ef67a6d9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.6.9.1)
4
+ hot-glue (0.6.10)
5
5
  ffaker (~> 2.16)
6
6
  kaminari (~> 1.2)
7
7
  rails (> 5.1)
@@ -139,7 +139,7 @@ GEM
139
139
  mini_mime (1.1.2)
140
140
  mini_portile2 (2.8.4)
141
141
  minitest (5.16.3)
142
- net-imap (0.5.1)
142
+ net-imap (0.5.4)
143
143
  date
144
144
  net-protocol
145
145
  net-pop (0.1.2)
@@ -235,7 +235,7 @@ GEM
235
235
  stimulus-rails (1.1.1)
236
236
  railties (>= 6.0.0)
237
237
  thor (1.2.1)
238
- timeout (0.4.2)
238
+ timeout (0.4.3)
239
239
  turbo-rails (1.3.2)
240
240
  actionpack (>= 6.0.0)
241
241
  activejob (>= 6.0.0)
data/README.md CHANGED
@@ -413,34 +413,6 @@ Then, finally the @charge will be loaded
413
413
  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.
414
414
 
415
415
 
416
- #### Optionalized Nested Parents
417
-
418
- 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.
419
-
420
- This is an advanced feature. To use, **make duplicative routes to the same controller**. You can only use this feature with Gd controller.
421
-
422
- 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**.
423
- ```
424
- namespace :admin
425
- resources :users do
426
- resources :invoices
427
- end
428
- resources :invoices
429
- end
430
- ```
431
-
432
- Even though we have two routes pointed to **invoices**, both will go to the same controller (`app/controllers/admin/invoices_controller.rb`)
433
-
434
- ```
435
- ./bin/rails generate hot_glue:scaffold User --namespace=admin --gd
436
- ./bin/rails generate hot_glue:scaffold Invoice --namespace=admin --gd --nested=~users
437
- ```
438
- Notice for the Invoice build, the parent user is *optionalized* (not 'optional'-- optionalized: to be made so it can be made optional).
439
-
440
- 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.
441
-
442
-
443
-
444
416
  ### `--auth=`
445
417
 
446
418
  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.
@@ -1015,6 +987,13 @@ Omits controller.
1015
987
 
1016
988
  Omits list views.
1017
989
 
990
+ `--new-button-position` (above, below; default: above)
991
+ Show the new button above or below the list.
992
+
993
+ `--downnest-shows-headings` (default: false)
994
+ Show headings above downnested portals.
995
+
996
+
1018
997
  ### `--big-edit`
1019
998
 
1020
999
  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`.
@@ -1134,7 +1113,16 @@ Use `before` to make the labels come before or `after` to make them come after.
1134
1113
 
1135
1114
  Omits the heading of column names that appears above the 1st row of data.
1136
1115
 
1116
+ ### `--include-object-names`
1117
+
1118
+ When you are "Editing X" we specify that X is a ___ (author, book, room, etc)
1119
+
1120
+ e.g. "Editing author Edgar Allan Poe" vs "Editing Edgar Allan Poe"
1121
+
1122
+ Can also be specified globally in `config/hot_glue.yml`
1123
+
1137
1124
 
1125
+ ### Code insertions
1138
1126
 
1139
1127
  `--code-before-create`
1140
1128
  `--code-after-create`
@@ -1454,7 +1442,7 @@ Always:
1454
1442
 
1455
1443
  Don't include this last line in your factory code.
1456
1444
 
1457
- ## Nav Templates
1445
+ ## Nav Templates and `--no-nav-menu`
1458
1446
  At the namespace level, you can have a file called `_nav.html.erb` to create tabbed bootstrap nav
1459
1447
 
1460
1448
  To create the file for the first time (at each namespace), start by running
@@ -1478,6 +1466,10 @@ Once the file is present, any further builds in this namespace will:
1478
1466
  ```
1479
1467
  (In this example `owner/` is the namespace and `things` is the name of the scaffold being built)
1480
1468
 
1469
+ To suppress this behavior, add `--no-nav-menu` to the build command and the _nav template will not be touched.
1470
+
1471
+
1472
+
1481
1473
  ## Automatic Base Controller
1482
1474
 
1483
1475
  Hot Glue will copy a file named `base_controller.rb` to the same folder where it tries to create any controller (so to the namespace), unless such a file exists there already.
@@ -1609,6 +1601,76 @@ puts the value into the search box and the id into a hidden field.
1609
1601
 
1610
1602
  You need to making a selection *and* click "Save" to update the record.
1611
1603
 
1604
+ The typeahead itself can be both namespaced and nested. (Remember, all controllers are generated at the namespace.)
1605
+
1606
+ Pay close attention to a nested typeahead: When generating the typeahead scaffold use both `--namespace=aaa` and `--nested=bbb/ccc`
1607
+
1608
+ In this example, the typeahead controller will operate at a namespace of `aaa` with two parents: `bbb` and `ccc`
1609
+
1610
+ Combined with `--auth-identifier`, you can load only objects that are related from the `ccc` thing that gets loaded off the `bbb` thing that gets loaded off the current_user.
1611
+
1612
+ This scopes the list returned by the typeahead.
1613
+
1614
+ You need to specify this twice: Once when specifying the typehead scaffold, and also any place in a regular scaffold that uses typeahead:
1615
+
1616
+ For example, assuming we have a reciprocal has_many through for Accounts & Users
1617
+
1618
+ user.rb
1619
+ ```
1620
+ has_many :account_users
1621
+ has_many :accounts, through: :account_users
1622
+ ```
1623
+
1624
+
1625
+ account.rb
1626
+ ```
1627
+ has_many :account_users
1628
+ has_many :users, through: :account_users
1629
+ ```
1630
+
1631
+
1632
+ `bin/rails generate hot_glue:scaffold Member --auth='current_user' --auth-identifier='user' --auth-identifier=user --modify='user_id{typeahead}[account]'`
1633
+
1634
+ in our routes.rb file, we have
1635
+ ```
1636
+ namespace :account_dashboard do
1637
+ resources :accounts do
1638
+ resources :users_typeahead
1639
+ resources :rooms do
1640
+ resources :members
1641
+ end
1642
+ end
1643
+ end
1644
+ ```
1645
+
1646
+ Notice that the scaffold with the references to users is 2 levels deep: accounts -> rooms -> members (and is also in a namespace)
1647
+
1648
+ Notice also that the *users typeahead* operates only one level deep in the same namespace.
1649
+
1650
+ This means that to find users within the search, the essential piece of information is the account, because we want to scope the result set to the users belong to that account.
1651
+
1652
+ • The account must belong to the current_user (notice the account uses nested account provided by the route)
1653
+ • Any users found by the typeahead must belong to the account
1654
+
1655
+ ```
1656
+ def account
1657
+ @account ||= current_user.accounts.find(params[:account_id])
1658
+ end
1659
+
1660
+ def index
1661
+ authorize User, :typeahead?
1662
+ query = params[:query]
1663
+ @typeahead_identifier = params[:typeahead_identifier]
1664
+ @users = account.users.where("LOWER(email) LIKE ? ", "%#{query.downcase}%").limit(10)
1665
+ render layout: false
1666
+ end
1667
+ ```
1668
+
1669
+
1670
+
1671
+ --
1672
+
1673
+
1612
1674
  ### TinyMCE
1613
1675
  1. `bundle add tinymce-rails` to add it to your Gemfile
1614
1676
 
@@ -1686,10 +1748,55 @@ These automatic pickups for partials are detected at buildtime. This means that
1686
1748
 
1687
1749
 
1688
1750
  # VERSION HISTORY
1751
+
1752
+
1753
+ #### 2025-01-28 v0.6.11
1754
+
1755
+ • Typeahead now can use --auth, --auth-identifier, --namespace, and --nested
1756
+ • Works similar to how same flags work on scaffold generator. (Notice that the typeahead generator is a completely separate generator).
1757
+ • When using nested, your results are scoped the parent objects in the nest chain (which is the last one)
1758
+
1759
+ e.g.
1760
+ `bin/rails generate hot_glue:scaffold Member --auth='current_user' --auth-identifier='user' --auth-identifier=user --modify='user_id{typeahead}[account]'`
1761
+
1762
+ here the user_id field on members will use a typeahead that is built in the namespace `account_dashboard` under 1 parent: `account`
1763
+
1764
+ This would be used in conjunction with a typeahead built using the same namespace and nesting that matches:
1765
+
1766
+ ```
1767
+ bin/rails generate hot_glue:typeahead User --namespace='account_dashboard' --nested='account' --auth-identifier='user' --auth='current_user'
1768
+ ```
1769
+
1770
+ (See 'typeahead' section for details)
1771
+
1772
+ `--include-object-names`
1773
+
1774
+ When you are "Editing X" we specify that X is a ___ (author, book, room, etc)
1775
+
1776
+ e.g. "Editing author Edgar Allan Poe" vs "Editing Edgar Allan Poe"
1777
+
1778
+ Can also be specified globally in `config/hot_glue.yml`
1779
+
1780
+
1781
+ `--new-button-position` (above, below; default: above)
1782
+ Show the new button above or below the list.
1783
+
1784
+ `--downnest-shows-headings` (default: false)
1785
+ Show headings above downnested portals.
1786
+
1787
+
1788
+ #### 2024-12-25 v0.6.10
1789
+ • adds `--no-nav-menu` option to supress writing to the _nav template
1790
+ • the _nav template itself can now end with either .html.erb or .erb
1791
+ • Removing feature: optionalized nested params (this was a bad idea)
1792
+ • enum partials now correctly render within a namespace
1793
+ • fixes for detecting missing belongs_to relationships
1794
+ • fixes to post-create and post-update parental reloads
1795
+
1796
+
1689
1797
  #### 2024-12-17 v0.6.9.2
1690
1798
  • adds alt_lookup to related_set_field.rb and fixes a variable passdown problem in edit.html.erb
1691
1799
 
1692
-
1693
1800
  #### 2024-12-16 v0.6.9.1
1694
1801
  • Fixes hardcoding in #new action
1695
1802
 
@@ -65,6 +65,7 @@ class FieldFactory
65
65
  attachment_data: generator.attachments[name.to_sym],
66
66
  sample_file_path: generator.sample_file_path,
67
67
  modify_as: generator.modify_as[name.to_sym] || nil,
68
+ plural: generator.plural,
68
69
  display_as: generator.display_as[name.to_sym] || nil,
69
70
  default_boolean_display: generator.default_boolean_display,
70
71
  namespace: generator.namespace_value,
@@ -5,12 +5,14 @@ class AssociationField < Field
5
5
 
6
6
  attr_accessor :assoc_name, :assoc_class, :assoc, :alt_lookup
7
7
 
8
- def initialize( alt_lookup: , class_name: , default_boolean_display:, display_as: ,
9
- name: , singular: ,
10
- update_show_only: ,
11
- hawk_keys: , auth: , sample_file_path:, ownership_field: ,
12
- attachment_data: nil , layout_strategy: , form_placeholder_labels: nil,
13
- form_labels_position:, modify_as: , self_auth: , namespace:, pundit: )
8
+ def initialize( alt_lookup: ,
9
+ class_name: ,
10
+ default_boolean_display:, display_as: ,
11
+ name: , singular: ,
12
+ update_show_only: ,
13
+ hawk_keys: , auth: , sample_file_path:, ownership_field: ,
14
+ attachment_data: nil , layout_strategy: , form_placeholder_labels: nil,
15
+ form_labels_position:, modify_as: , self_auth: , namespace:, pundit: , plural: )
14
16
  super
15
17
 
16
18
 
@@ -108,7 +110,15 @@ class AssociationField < Field
108
110
  # else
109
111
  # end
110
112
  elsif modify_as && modify_as[:typeahead]
111
- search_url = "#{namespace ? namespace + "_" : ""}#{assoc.class_name.downcase.pluralize}_typeahead_index_url"
113
+ search_url = "#{namespace ? namespace + "_" : ""}" +
114
+ modify_as[:nested].join("_") +
115
+ + "_#{assoc.class_name.downcase.pluralize}_typeahead_index_url"
116
+
117
+
118
+ if @modify_as[:nested].any?
119
+ search_url << "(" + modify_as[:nested].collect{|x| "#{x}"}.join(",") + ")"
120
+ end
121
+
112
122
  "<div class='typeahead typeahead--#{assoc.name}_id'
113
123
  data-controller='typeahead'
114
124
  data-typeahead-url-value='<%= #{search_url} %>'
@@ -2,6 +2,7 @@ class AttachmentField < Field
2
2
  attr_accessor :attachment_data
3
3
  def initialize(alt_lookup:,
4
4
  attachment_data:,
5
+ plural:,
5
6
  auth:,
6
7
  class_name:,
7
8
  display_as:, singular:,
@@ -72,7 +72,7 @@ class EnumField < Field
72
72
  end
73
73
 
74
74
  def partial_render
75
- "<% if #{singular}.#{name} %><%= render partial: #{singular}.#{name}, locals: { #{singular}: #{singular} } %><% end %>"
75
+ "<% if #{singular}.#{name} %><%= render partial: \"#{namespace + "/" if namespace}#{plural}/\#{#{singular}.#{name}}\", locals: { #{singular}: #{singular} } %><% end %>"
76
76
  end
77
77
 
78
78
 
@@ -5,7 +5,7 @@ class Field
5
5
  :hawk_keys, :layout_strategy, :limit, :modify_as, :name, :object, :sample_file_path,
6
6
  :self_auth,
7
7
  :singular_class, :singular, :sql_type, :ownership_field,
8
- :update_show_only, :namespace, :pundit
8
+ :update_show_only, :namespace, :pundit, :plural
9
9
 
10
10
  def initialize(
11
11
  auth: ,
@@ -26,7 +26,8 @@ class Field
26
26
  update_show_only:,
27
27
  self_auth:,
28
28
  namespace:,
29
- pundit:
29
+ pundit: ,
30
+ plural:
30
31
  )
31
32
  @name = name
32
33
  @layout_strategy = layout_strategy
@@ -43,6 +44,7 @@ class Field
43
44
  @modify_as = modify_as
44
45
  @display_as = display_as
45
46
  @pundit = pundit
47
+ @plural = plural
46
48
 
47
49
  @self_auth = self_auth
48
50
  @default_boolean_display = default_boolean_display
@@ -26,9 +26,9 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
26
26
  :singular_class, :smart_layout, :stacked_downnesting,
27
27
  :update_show_only, :ownership_field,
28
28
  :layout_strategy, :form_placeholder_labels,
29
- :form_labels_position, :pundit,
29
+ :form_labels_position, :no_nav_menu, :pundit,
30
30
  :self_auth, :namespace_value, :record_scope, :related_sets,
31
- :search_clear_button, :search_autosearch
31
+ :search_clear_button, :search_autosearch, :include_object_names
32
32
  # important: using an attr_accessor called :namespace indirectly causes a conflict with Rails class_name method
33
33
  # so we use namespace_value instead
34
34
 
@@ -37,7 +37,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
37
37
  class_option :singular_class, type: :string, default: nil
38
38
  class_option :nest, type: :string, default: nil # DEPRECATED —— DO NOT USE
39
39
  class_option :nested, type: :string, default: ""
40
-
41
40
  class_option :namespace, type: :string, default: nil
42
41
  class_option :auth, type: :string, default: nil
43
42
  class_option :auth_identifier, type: :string, default: nil
@@ -45,7 +44,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
45
44
  class_option :include, type: :string, default: ""
46
45
  class_option :god, type: :boolean, default: false
47
46
  class_option :gd, type: :boolean, default: false # alias for god
48
-
49
47
  class_option :specs_only, type: :boolean, default: false
50
48
  class_option :no_specs, type: :boolean, default: false
51
49
  class_option :no_delete, type: :boolean, default: false
@@ -58,7 +56,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
58
56
  class_option :big_edit, type: :boolean, default: false
59
57
  class_option :show_only, type: :string, default: ""
60
58
  class_option :update_show_only, type: :string, default: ""
61
-
62
59
  class_option :ujs_syntax, type: :boolean, default: nil
63
60
  class_option :downnest, type: :string, default: nil
64
61
  class_option :magic_buttons, type: :string, default: nil
@@ -71,7 +68,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
71
68
  class_option :layout, type: :string, default: nil # if used here it will override what is in the config
72
69
  class_option :hawk, type: :string, default: nil
73
70
  class_option :with_turbo_streams, type: :boolean, default: false
74
-
75
71
  class_option :label, default: nil
76
72
  class_option :list_label_heading, default: nil
77
73
  class_option :new_button_label, default: nil
@@ -101,8 +97,13 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
101
97
  class_option :code_before_update, default: nil
102
98
  class_option :code_after_update, default: nil
103
99
  class_option :record_scope, default: nil
100
+ class_option :no_nav_menu, type: :boolean, default: false # suppress writing to _nav template
101
+ class_option :include_object_names, type: :boolean, default: false
102
+ class_option :new_button_position, type: :string, default: 'above'
103
+ class_option :downnest_shows_headings, type: :boolean, default: nil
104
104
 
105
105
 
106
+ # SEARCH OPTIONS
106
107
  class_option :search, default: nil # set or predicate
107
108
 
108
109
  # FOR THE SET SEARCH
@@ -111,11 +112,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
111
112
  # for the single-entry search box, they will be removed from the list specified above.
112
113
  class_option :search_query_fields, default: '' # comma separated list of fields to search by single-entry search term
113
114
  class_option :search_position, default: 'vertical' # choices are vertical or horizontal
114
-
115
-
116
115
  class_option :search_clear_button, default: false
117
- class_option :saerch_autosearch, default: false
118
-
116
+ class_option :search_autosearch, default: false
119
117
 
120
118
  # FOR THE PREDICATE SEARCH
121
119
  # TDB
@@ -264,7 +262,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
264
262
  elsif $2 == "tinymce"
265
263
  @modify_as[key.to_sym] = {tinymce: 1, badges: $3}
266
264
  elsif $2 == "typeahead"
267
- @modify_as[key.to_sym] = {typeahead: 1, badges: $3}
265
+ nested = $3.split("/")
266
+ @modify_as[key.to_sym] = {typeahead: 1, nested: nested}
268
267
  elsif $2 == "timezone"
269
268
  @modify_as[key.to_sym] = {timezone: 1, badges: $3}
270
269
  elsif $2 == "none"
@@ -341,8 +340,14 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
341
340
 
342
341
  @smart_layout = options['smart_layout']
343
342
  @record_scope = options['record_scope']
343
+ @downnest_shows_headings = options['downnest_shows_headings']
344
+ @new_button_position = options['new_button_position']
345
+
344
346
 
345
347
  @pundit = options['pundit']
348
+
349
+ @no_nav_menu = options['no_nav_menu']
350
+
346
351
  if @pundit.nil?
347
352
  @pundit = get_default_from_config(key: :pundit_default)
348
353
  end
@@ -362,6 +367,10 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
362
367
  @downnest_object = HotGlue.construct_downnest_object(@downnest)
363
368
  end
364
369
 
370
+ @include_object_names = options['include_object_names'] || get_default_from_config(key: :include_object_names)
371
+
372
+
373
+
365
374
  if @god
366
375
  # @auth = nil
367
376
  end
@@ -464,6 +473,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
464
473
  identify_object_owner
465
474
  setup_fields
466
475
 
476
+
467
477
  if (@columns - @show_only - (@ownership_field ? [@ownership_field.to_sym] : [])).empty?
468
478
  @no_field_form = true
469
479
  end
@@ -788,12 +798,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
788
798
  raise(HotGlue::Error, exit_message)
789
799
 
790
800
  else
791
- if eval(singular_class + ".reflect_on_association(:#{@object_owner_sym.to_s})").nil? && !eval(singular_class + ".reflect_on_association(:#{@object_owner_sym.to_s.singularize})").nil?
792
- exit_message = "*** Oops: you tried to nest #{singular_class} within a route for `#{@object_owner_sym}` but I can't find an association for this relationship. Did you mean `#{@object_owner_sym.to_s.singularize}` (singular) instead?"
793
- # else # NOTE: not reachable
794
- # exit_message = "*** Oops: Missing relationship from class #{singular_class} to :#{@object_owner_sym} maybe add `belongs_to :#{@object_owner_sym}` to #{singular_class}\n (If your user is called something else, pass with flag auth=current_X where X is the model for your auth object as lowercase. Also, be sure to implement current_X as a method on your controller. If you really don't want to implement a current_X on your controller and want me to check some other method for your current user, see the section in the docs for --auth-identifier flag). To make a controller that can read all records, specify with --god."
795
- end
796
-
801
+ exit_message = "When trying to nest #{singular_class} within #{@nested_set.last[:plural]}, check the #{singular_class} model for the #{@object_owner_sym} association: \n belongs_to :#{@object_owner_sym}"
797
802
  raise(HotGlue::Error, exit_message)
798
803
  end
799
804
  elsif @object_owner_sym && !@object_owner_eval.include?(".")
@@ -1011,6 +1016,10 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1011
1016
  }.join(",\n ")
1012
1017
  end
1013
1018
 
1019
+ def nest_path
1020
+ @nested_set.collect{| arg| arg[:singular] }.join("/") + "/" if @nested_set.any?
1021
+ end
1022
+
1014
1023
  def controller_class_name
1015
1024
  @controller_build_name
1016
1025
  end
@@ -1071,7 +1080,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1071
1080
  HotGlue.optionalized_ternary(namespace: @namespace,
1072
1081
  target: @controller_build_folder,
1073
1082
  nested_set: @nested_set,
1074
- with_params: true,
1083
+ with_params: false,
1075
1084
  top_level: false)
1076
1085
  end
1077
1086
 
@@ -1079,7 +1088,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1079
1088
  HotGlue.optionalized_ternary(namespace: @namespace,
1080
1089
  target: @singular,
1081
1090
  nested_set: @nested_set,
1082
- with_params: true,
1091
+ with_params: false,
1083
1092
  put_form: true,
1084
1093
  top_level: false)
1085
1094
  end
@@ -1088,7 +1097,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1088
1097
  HotGlue.optionalized_ternary(namespace: @namespace,
1089
1098
  target: @singular,
1090
1099
  nested_set: @nested_set,
1091
- with_params: true,
1100
+ with_params: false,
1092
1101
  put_form: true)
1093
1102
  end
1094
1103
 
@@ -1097,7 +1106,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1097
1106
  target: @singular,
1098
1107
  nested_set: @nested_set,
1099
1108
  modifier: "edit_",
1100
- with_params: true,
1109
+ with_params: false,
1101
1110
  put_form: true)
1102
1111
  end
1103
1112
 
@@ -1126,7 +1135,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1126
1135
  target: singular,
1127
1136
  nested_set: @nested_set,
1128
1137
  modifier: "new_",
1129
- with_params: true)
1138
+ with_params: false)
1130
1139
  end
1131
1140
 
1132
1141
  def nested_assignments
@@ -1224,7 +1233,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1224
1233
  path: HotGlue.optionalized_ternary( namespace: @namespace,
1225
1234
  target: @singular,
1226
1235
  nested_set: @nested_set,
1227
- with_params: true,
1236
+ with_params: false,
1228
1237
  put_form: true),
1229
1238
  big_edit: @big_edit,
1230
1239
  singular: singular,
@@ -1238,7 +1247,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1238
1247
  end
1239
1248
 
1240
1249
  def include_nav_template
1241
- File.exist?("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}")
1250
+ File.exist?("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}") ||
1251
+ File.exist?("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.#{@markup}")
1242
1252
  end
1243
1253
 
1244
1254
  def copy_view_files
@@ -1303,11 +1313,16 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1303
1313
  end
1304
1314
  end
1305
1315
 
1306
- def insert_into_nav_template
1307
- # how does this get called(?)
1308
- nav_file = "#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}"
1309
-
1316
+ def insert_into_nav_template # called from somewhere in the generator
1317
+ return if @no_nav_menu
1310
1318
  if include_nav_template
1319
+ if File.exist?("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}")
1320
+ nav_file = "#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}"
1321
+ elsif File.exist?("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.#{@markup}")
1322
+ nav_file = "#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.#{@markup}"
1323
+ end
1324
+
1325
+
1311
1326
  append_text = " <li class='nav-item'>
1312
1327
  <%= link_to '#{@list_label_heading.humanize}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>
1313
1328
  </li>"
@@ -1604,20 +1619,26 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1604
1619
 
1605
1620
  def post_action_parental_updates
1606
1621
  if @nested_set.any?
1607
- @nested_set.collect { |data|
1622
+ @nested_set.collect { |data|
1608
1623
  parent = data[:singular]
1609
- "@#{singular}.#{parent}.reload"
1610
- }
1624
+ "#{parent}.reload"
1625
+ }
1611
1626
  else
1612
1627
  []
1613
1628
  end
1614
1629
  end
1615
1630
 
1616
1631
  def turbo_parental_updates
1632
+ set_path = @nested_set.collect { |data| "#{data[:singular]}" }.join("/") + "/"
1633
+ puts "constructing for #{set_path}"
1617
1634
  @nested_set.collect { |data|
1618
- "<%= turbo_stream.replace \"__#{@namespace if @namespace}\#{dom_id(@#{data[:singular]})}\" do %>
1619
- <%= render partial: \"#{@namespace}/#{data[:plural]}/line\", locals: {#{data[:singular]}: @#{singular}.#{data[:singular]}.reload} %>
1635
+ res = "<%= turbo_stream.replace \"__#{@namespace if @namespace}\#{dom_id(@#{data[:singular]})}\" do %>
1636
+ <%= render partial: \"#{@namespace}/#{data[:plural]}/line\", locals: {#{@nested_set.collect { |d|
1637
+ (set_path.index(data[:singular] + "/") > set_path.index(d[:singular] + "/") || d[:singular] == data[:singular] ) ? "#{d[:singular]}: @#{d[:singular]}" : nil
1638
+ }.compact.join(", ")}} %>
1620
1639
  <% end %>"
1640
+
1641
+ res
1621
1642
  }.join("\n")
1622
1643
  end
1623
1644
  end
@@ -60,13 +60,13 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
60
60
  <% if !@self_auth %>
61
61
  def load_<%= singular_name %>
62
62
  <% if @nested_set[0] && @nested_set[0][:optional] %>if params.include?(:<%= @nested_set.last[:singular] %>_id)
63
- @<%= singular_name %> = <%= object_scope.gsub("@",'') %><%= @record_scope %>.find(params[:id])
64
- else <% end %>@<%= singular_name %> = <%= object_scope %><%= @record_scope %>.find(params[:id])<% if @nested_set[0] && @nested_set[0][:optional] %>
63
+ @<%= singular_name %> = <%= object_scope.gsub("@",'') %>.find(params[:id])
64
+ else <% end %>@<%= singular_name %> = <%= object_scope %>.find(params[:id])<% if @nested_set[0] && @nested_set[0][:optional] %>
65
65
  end<% end %>
66
66
  end
67
67
  <% else %>
68
68
  def load_<%= singular_name %>
69
- @<%= 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 %><%= @record_scope %>
69
+ @<%= 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 %>
70
70
  end<% end %>
71
71
  <% if @paginate_per_page_selector %>def per
72
72
  params[:per] || 10
@@ -115,7 +115,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
115
115
  <%= @code_before_create ? "\n " + @code_before_create.gsub(";", "\n") : "" %>
116
116
  if @<%= singular_name %>.save<%= @code_after_create ? ("\n " + @code_after_create.gsub(";", "\n")) : ""%>
117
117
  flash[:notice] = "Successfully created #{@<%= singular %>.<%= display_class %>}"
118
- <%= post_action_parental_updates.join("\n ") %>
118
+ <%= post_action_parental_updates.compact.join("\n ") %>
119
119
  load_all_<%= plural %>
120
120
  <% unless @display_edit_after_create %>render :create<% else %>redirect_to <%= HotGlue.optionalized_ternary(namespace: @namespace,
121
121
  top_level: true,
@@ -154,7 +154,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
154
154
  @action = 'edit'
155
155
  render :edit<% if @pundit %>
156
156
  rescue Pundit::NotAuthorizedError
157
- flash[:notice] = "Editing #{@<%= singular %>.<%= display_class %>} not authorized."
157
+ flash[:notice] = "Editing <%= singular + ' ' if @include_object_names %>#{@<%= singular %>.<%= display_class %>} not authorized."
158
158
  render :index <% end %>
159
159
  end
160
160
 
@@ -181,10 +181,11 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
181
181
  authorize @<%= singular_name %>
182
182
  <%= @code_before_update ? "\n " + @code_before_update.gsub(";", "\n") : "" %>
183
183
  @<%= singular_name %>.save
184
- <% else %>
184
+ <% else %>
185
185
  <%= @code_before_update ? "\n " + @code_before_update.gsub(";", "\n") : "" %>
186
186
  if @<%= singular_name %>.update(modified_params)
187
187
  <% end %>
188
+ <%= post_action_parental_updates.compact.join("\n ") %>
188
189
  <%= @code_after_update ? "\n " + @code_after_update.gsub(";", "\n") : "" %>
189
190
  <% if @display_list_after_update %> load_all_<%= plural %><% end %>
190
191
  flash[:notice] << "Saved #{@<%= singular %>.<%= display_class %>}"
@@ -3,7 +3,7 @@
3
3
  <\% if <%= singular %>.errors.any? %>
4
4
  <\%= render(partial: "<%= namespace_with_trailing_dash %>errors", locals: {resource: <%= singular %> }) %>
5
5
  <\% end %>
6
- <h2>Editing <\%= <%= singular %>.<%= display_class %> %></h2>
6
+ <h2>Editing <%= singular + " " if @include_object_names %><\%= <%= singular %>.<%= display_class %> %></h2>
7
7
  <\%= form_with model: <%= singular %>, url: <%= form_path_edit_helper %><%= ", html: {'data-turbo': false}" if @big_edit %> do |f| %>
8
8
  <\%= render partial: "<%= namespace_with_trailing_dash + @controller_build_folder + "/" %>form", locals: {:<%= singular %> => <%= singular %>, f: f}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> \%>
9
9
  <% if @edit_within_form_partial %><\%= render partial: "edit_within_form", locals: {f: f, <%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %>
@@ -7,7 +7,9 @@
7
7
  </h4><% end %>
8
8
  <% end %>
9
9
 
10
+ <% if @new_button_position == 'above' %>
10
11
  <% 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 %>
12
+ <% end %>
11
13
 
12
14
  <% unless @no_list %>
13
15
  <% unless @no_list_heading %>
@@ -66,5 +68,10 @@
66
68
  <% end %>
67
69
  <%= @no_paginate ? "" : paginate %>
68
70
  <% end %>
71
+
72
+
73
+ <% if @new_button_position == 'below' %>
74
+ <% 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 %>
75
+ <% end %>
69
76
  </div>
70
77
  <\% end %>
@@ -12,7 +12,12 @@
12
12
  <% downnest_object_name = eval("#{downnest_class}.table_name") %>
13
13
  <% downnest_style = @layout_strategy.downnest_style %>
14
14
  <% if !@stacked_downnesting %><div class="<%= @layout_strategy.downnest_portal_column_width(downnest) %> scaffold-downnest" <%= downnest_style %> ><% end %>
15
- <\%= render partial: "<%= namespace_with_trailing_dash %><%= downnest_object_name %>/list", locals: {
15
+ <% if @downnest_shows_headings %>
16
+ <h3>
17
+ <%= downnest_object_name.pluralize.humanize %>
18
+ </h3>
19
+ <% end %>
20
+ <\%= render partial: "<%= namespace_with_trailing_dash %><%= downnest_object_name %>/list", locals: {
16
21
  <%= @singular %>: <%= @singular %>,
17
22
  <%= downnest_object_name %>: <%= @singular %>.<%= downnest %>
18
23
  }
@@ -3,7 +3,9 @@
3
3
  <\%= render partial: "list", locals: {<%= plural %>: @<%= plural %>}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {nested_for: \"" + arg[:singular] + "-\#{@" + arg[:singular] + ".id}\"" + ", " + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> \%>
4
4
  <\% end %>
5
5
  <\% end %>
6
+ <!-- parental updated -->
6
7
  <%= turbo_parental_updates %>
8
+ <!-- errors -->
7
9
  <\%= turbo_stream.replace "<%= @namespace %>__<%= singular %>-new" do %>
8
10
  <\% if @<%= singular %>.errors.none? %>
9
11
  <\%= render partial: "new_button", locals: {}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {" + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> %>
@@ -14,13 +14,17 @@
14
14
  <% each_downnest_width = @downnest_children.count == 1 ? 33 : (53/@downnest_children.count).floor %>
15
15
  <% @downnest_object.each do |downnest, size| %>
16
16
  <div class="row">
17
- <div class="col-md-6">
17
+ <div class="col-md-<%= @big_edit ? 12 : 6 %>">
18
18
  <% downnest_object = eval("#{singular_class}.reflect_on_association(:#{downnest})") %>
19
19
  <% if downnest_object.nil?; raise "no relationship for downnested portal `#{downnest}` found on `#{singular_class}`; please check relationship for has_many :#{downnest}"; end; %>
20
20
  <% downnest_class = downnest_object.class_name %>
21
21
  <% downnest_object_name = eval("#{downnest_class}.table_name") %>
22
22
  <% downnest_style = @layout_strategy.downnest_style %>
23
-
23
+ <% if @downnest_shows_headings %>
24
+ <h3>
25
+ <%= downnest_class.humanize %>
26
+ </h3>
27
+ <% end %>
24
28
  <\%= render partial: "<%= namespace_with_trailing_dash %><%= downnest_object_name %>/list", locals: {<%= @singular %>: @<%= @singular %>, <%= downnest_object_name %>: @<%= @singular %>.<%= downnest %><% if @nested_set.any? %>, <%= @nested_set.collect{|x| "#{x[:singular]}: @#{x[:singular]}"}.join(", ") %>, nested_for: "<%= @nested_set.collect{|x| "#{x[:singular]}-" + "\#{" + "@#{x[:singular]}.id}"}.join("__") %>__<%= singular %>-#{@<%= @singular %>.id}" <% end %> } \%>
25
29
 
26
30
  </div>
@@ -1,10 +1,13 @@
1
1
  <% if !@display_list_after_update %><\%= turbo_stream.replace "<%= @namespace %>__#{dom_id(@<%= singular %>)}" do %>
2
- <\%= render partial: 'line', locals: {<%= singular %>: @<%= singular %> }<%= @nested_set.collect{|arg| ".merge(@#{arg[:singular]} ? {#{arg[:singular]}: @#{arg[:singular]}} : {})" }.join %> \%>
2
+ <\%= render partial: 'line', locals: {<%= singular %>: @<%= singular %>, <%= @nested_set.collect{|arg| " #{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %> } \%>
3
3
  <\% end %><% else %><\%= turbo_stream.replace "<%= plural %>-list" do %>
4
- <\%= render partial: '<%= list_path_partial %>', locals: {<%= plural %>: @<%= plural %>}<%= @nested_set.collect{|arg| ".merge(@#{arg[:singular]} ? {#{arg[:singular]}: @#{arg[:singular]}} : {})" }.join %> \%>
4
+ <\%= render partial: '<%= list_path_partial %>', locals: {<%= plural %>: @<%= plural %><%= @nested_set.collect{|arg| " #{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %>
5
+ \%>
5
6
  <\% end %>
6
7
  <% end %>
8
+ <!-- parental updated -->
7
9
  <%= turbo_parental_updates %>
10
+ <!-- flash notices -->
8
11
  <\%= turbo_stream.update "flash_notices" do %>
9
12
  <\%= render partial: "layouts/flash_notices", locals: {resource: @<%= singular %>} %>
10
13
  <\% end %>
@@ -4,12 +4,38 @@ class <%= ((@namespace.titleize.gsub(" ", "") + "::" if @namespace) || "") + @pl
4
4
  <% end %># <%= regenerate_me_code %><% if defined?(RuboCop) %>
5
5
  # rubocop:enable Layout/LineLength <% end %>
6
6
 
7
+
8
+ <% @nested_set.each do |nest| %>before_action :<%= nest[:singular] %>
9
+ def <%= nest[:singular] %>
10
+ @<%= nest[:singular] %> ||= current_user.accounts.find(params[:account_id])
11
+ end
12
+ <% end %>
13
+
14
+ <% nest_chain = [] %>
15
+ <% @nested_set.each { |arg|
16
+ if @auth_identifier == arg[:singular]
17
+ this_scope = auth_object
18
+ elsif nest_chain.empty?
19
+ this_scope = "#{@auth ? @auth : class_name}.#{arg[:plural]}"
20
+ else
21
+ this_scope = "#{nest_chain.last}.#{arg[:plural]}"
22
+ end
23
+ nest_chain << arg
24
+ }%>
25
+
7
26
  def index
8
27
  <% if @pundit %>authorize <%= @class_name %>, :typeahead? <% end %>
9
28
  query = params[:query]
10
29
  @typeahead_identifier = params[:typeahead_identifier]
11
- @<%= @plural %> = <%= @singular.titleize.gsub(" ", "") %>.where("<%= @search_by.collect{|search| "LOWER(#{search}) LIKE ?" }.join(" OR ") %>", <%= @search_by.collect{|search| "\"%\#{query.downcase}%\"" }.join(", ") %>).limit(10)
12
30
 
31
+
32
+ <% if @nested_set.none? %>
33
+ @<%= @plural %> = <%= @singular.titleize.gsub(" ", "") %>.where("<%= @search_by.collect{|search| "LOWER(#{search}) LIKE ?" }.join(" OR ") %>", <%= @search_by.collect{|search| "\"%\#{query.downcase}%\"" }.join(", ") %>).limit(10)
34
+ <% else %>
35
+ <% @nested_set.each do |arg| %>
36
+ @<%= @plural %> = <%= @nested_set.last[:singular] %>.<%= @plural %>.where("<%= @search_by.collect{|search| "LOWER(#{search}) LIKE ?" }.join(" OR ") %>", <%= @search_by.collect{|search| "\"%\#{query.downcase}%\"" }.join(", ") %>).limit(10)
37
+ <% end %>
38
+ <% end %>
13
39
  render layout: false
14
40
  end
15
41
  end
@@ -5,6 +5,10 @@ module HotGlue
5
5
  source_root File.expand_path('templates', __dir__)
6
6
  class_option :namespace, type: :string, default: nil
7
7
  class_option :search_by, type: :string, default: nil
8
+ class_option :nested, type: :string, default: nil
9
+ class_option :auth, type: :string, default: nil
10
+ class_option :auth_identifier, type: :string, default: nil
11
+
8
12
 
9
13
  include DefaultConfigLoader
10
14
  def filepath_prefix
@@ -28,6 +32,25 @@ module HotGlue
28
32
  @class_name = args.first
29
33
  @plural = args.first.tableize.pluralize
30
34
  @namespace = options['namespace']
35
+ @nested = options['nested'] if options['nested']
36
+
37
+ if !@nested.nil?
38
+ @nested_set = @nested.split("/").collect { |arg|
39
+ is_optional = arg.start_with?("~")
40
+ arg.gsub!("~", "")
41
+ {
42
+ singular: arg,
43
+ plural: arg.pluralize,
44
+ optional: is_optional
45
+ }
46
+ }
47
+ puts "NESTING: #{@nested_set}"
48
+ else
49
+ @nested_set = []
50
+ end
51
+
52
+ @auth = options['auth'] || "current_user"
53
+ @auth_identifier = options['auth_identifier'] || "user"
31
54
 
32
55
  if options['search_by']
33
56
  @search_by = options['search_by'].split(",")
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.6.9.2'
3
+ CURRENT = '0.6.11'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hot-glue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.9.2
4
+ version: 0.6.11
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: 2024-12-17 00:00:00.000000000 Z
11
+ date: 2025-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails