hot-glue 0.6.21.2 → 0.6.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f79c85d7b5413fe8f12306c5b46de1eedd68c6373ffbab5512a1a57ac22c0e30
4
- data.tar.gz: d151d9357396d201e1b8a25b59146b7f065b260d38b8f456ece7d798d2b09320
3
+ metadata.gz: 342a81dfd2a338859c929f72b0634b6458a8e6f07f037b6d7c22aba2c2259e9e
4
+ data.tar.gz: 138dab55e50cef822a4ae6c56c32dbb147d3941b65bb38e1a96c3eef1d11d6a5
5
5
  SHA512:
6
- metadata.gz: 0e9df1507995d80cc0efc229c29bca7e6e1d52e1a6be0870cbcee4d9cc42cfec85ddf2dd3ec8b710162eff4d800a04f847dcdbd8580220277642026a7e5dbc45
7
- data.tar.gz: 0271cd11a4848ef7044ac0310cf9064fff0defef82a2e4237c8caf34bb8e660b618f2da3c1f3565e6317b69c10aa3e6bc3e24366a088c4be5e756d6d25cada7e
6
+ metadata.gz: 29c0d4473cf36ebf0468b463d80ee19c98375b19085a21218f61d7986ac380b48b12acef8b229fa4884902c8d42f4efadc3668e280994c626f5ff6ddf8ae6d30
7
+ data.tar.gz: e851c22d5484a50a2135d8a6cf3a3e417c40c40af271880589a7513389b2513f9c3a4cef706657ceaf86a3d1204479726674f8feed0acf2b8accb429becf98b3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.6.21.1)
4
+ hot-glue (0.6.22)
5
5
  ffaker (~> 2.16)
6
6
  kaminari (~> 1.2)
7
7
  rails (> 5.1)
data/README.md CHANGED
@@ -572,6 +572,37 @@ Then, finally the @charge will be loaded
572
572
  This is "starfish access control" or "poor man's access control." It works when the current user has several things they can manage, and by extension can manage children of those things.
573
573
 
574
574
 
575
+ #### Example #3: Polymorphic Nesting
576
+ Use `(` and `)` to specify a non-standard upwards relationship from the child, as in the case of a polymorphic belongs_to
577
+
578
+ ```
579
+ class Blast
580
+ has_many :rules, as: :ruleable
581
+ end
582
+
583
+ class Rule
584
+ # ruleable_id
585
+ # ruleable_type
586
+
587
+ belongs_to :ruleable, polymorphic: true
588
+ end
589
+ ```
590
+
591
+ routes.rb
592
+ ```
593
+ resources :blasts do
594
+ resources :rules
595
+ end
596
+ ```
597
+
598
+ `rails generate hot_glue:scaffold Blast --downnest=rules`
599
+
600
+ `rails generate hot_glue:scaffold Rules --nested='blast(ruleable)'`
601
+
602
+ Notices the relationship from the parent to child is `rules` but from the child to parent, it is `ruleable` instead of `blast`
603
+
604
+
605
+
575
606
  ### `--auth=`
576
607
 
577
608
  By default, it will be assumed you have a `current_user` for your user authentication. This will be treated as the "authentication root" for the "poor man's auth" explained above.
@@ -821,7 +852,8 @@ Notice that each modifiers can be used with specific field types.
821
852
  | tinymce | applies to text fields only, be sure to setup TineMCE globally | text fields only | | |
822
853
  | typeahead | turns a foreign key (only) into a searchable typeahead field | foreign keys only | | |
823
854
  | timezone | turns a string (varchar) into a drop down of timezones | foreign keys only | | |
824
- | none | special modifier for using badges |
855
+ | include_blank | special modifier for association fields, adds include_blank to the created dropdown | |
856
+ | none | special modifier for using badges |
825
857
 
826
858
  Except for "(truthy label)" and "(falsy label)" which use the special syntax, use the modifier _exactly_ as it is named.
827
859
 
@@ -1277,6 +1309,45 @@ Can now be created with more space (wider) by adding a `+` to the end of the dow
1277
1309
  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
1278
1310
 
1279
1311
 
1312
+ Polymorphic Downnesting
1313
+
1314
+ Here, a `Blast` `has_many :rules, as: :ruleable`
1315
+
1316
+ The child object is named `Rule` but it can belong to a Blast or an Agent. (Agent also has a similar has_many for Rules)
1317
+
1318
+ `belongs_to :ruleable, polymorphic: true`
1319
+
1320
+ We build the blast & agent controllers like so:
1321
+
1322
+ bin/rails generate hot_glue:scaffold Blast --downnest='blast_rules(rules)'
1323
+ bin/rails generate hot_glue:scaffold Agent --downnest='agent_rules(rules)'
1324
+
1325
+ Notice that the relationship name is `rules` (not blast_rules), so what goes before the parenthesis is the controller name (with prefix)
1326
+ What goes inside the controller name is the real relationship name.
1327
+
1328
+ For the children, we can't build one controller for the Rule, instead we build one for the `AgentRules` and another for the `BlastRules`
1329
+
1330
+ bin/rails generate hot_glue:scaffold Rule --nested='blast(ruleable)' --controller-prefix='Blast'
1331
+ bin/rails generate hot_glue:scaffold Rule --nested='agent(ruleable)' --controller-prefix='Agent'
1332
+
1333
+ (I realize building one child controller for each type of polymorph is tedius, but this is the best solution I could come up with.)
1334
+
1335
+ As these are children, what goes into the `--netsed` setting inside the parentheses is the polymorphic name specified by `as:` when declaring the `belongs_to`
1336
+
1337
+ routes.rb
1338
+
1339
+ ```
1340
+ resources :agents do
1341
+ resources :agent_rules
1342
+ end
1343
+
1344
+ resources :blasts do
1345
+ resources :blast_rules
1346
+ end
1347
+ ```
1348
+
1349
+
1350
+
1280
1351
  ### `--record-scope=`
1281
1352
 
1282
1353
  Record scope allows you to apply a model based scope for the controller being generated.
@@ -1374,8 +1445,6 @@ called _after authorization_ but _before saving the new record_
1374
1445
  (which creates the record, or fails validation).
1375
1446
  Here you can do things like set default values, or modify the params before the record is saved.
1376
1447
 
1377
- #### `--code-after-create=`
1378
- is called after the record is saved (and thus has an id in the case of the create action).
1379
1448
 
1380
1449
  #### `--code-before-update=`
1381
1450
  is called in the `update` action _before_ it is saved.
@@ -1383,9 +1452,54 @@ is called in the `update` action _before_ it is saved.
1383
1452
  #### `--code-after-update=`
1384
1453
  is called in the `update` action _after_ it is saved.
1385
1454
 
1455
+
1456
+ #### `--code-after-create=`
1457
+ is called after a new record is saved (and thus has an id).
1458
+
1459
+ Here is where you will call operations that depend on the record having an id, like building child table records.
1460
+
1461
+ Notice that the next option is inserted in both `new` and `create`, making it the more suitable choice for setting default values.
1462
+
1463
+
1386
1464
  #### `--code-after-new=`
1387
- is called in the `new` after the .new() call
1465
+ is called in both the `new` and `create` actions _directly after the .new() call_
1388
1466
 
1467
+ This is a good place to set your created_by user id, like so
1468
+
1469
+ `--code-after-new='@email_template.created_by_user = current_user'`
1470
+
1471
+
1472
+
1473
+ ```
1474
+ def new
1475
+ @email_template = EmailTemplate.new(crusade: crusade)
1476
+ @email_template.created_by_user = current_user // <--- YOUR CUSTOM CODE via --code-after-new
1477
+
1478
+ authorize @email_template
1479
+ @action = 'new'
1480
+ ...
1481
+ ```
1482
+
1483
+ Notice that a separate hook for code-after-create is also available, but that happens after the save
1484
+
1485
+ Using both together `--code-after-new='@email_template.created_by_user = current_user' --code-after-create='@email_template.do_something'`
1486
+
1487
+
1488
+
1489
+ ```
1490
+ def create
1491
+ ...
1492
+ @email_template = EmailTemplate.new(modified_params)
1493
+ @email_template.created_by_user = current_user // <--- YOUR CUSTOM CODE via --code-after-new
1494
+
1495
+ authorize @email_template
1496
+
1497
+ if @email_template.save
1498
+ @email_template.do_something // <--- YOUR CUSTOM CODE via --code-after-create
1499
+ flash[:notice] = "Successfully created #{@email_template.name}"
1500
+ account.reload
1501
+ ...
1502
+ ```
1389
1503
 
1390
1504
 
1391
1505
 
@@ -2032,10 +2146,111 @@ These automatic pickups for partials are detected at build time. This means that
2032
2146
 
2033
2147
  # VERSION HISTORY
2034
2148
 
2149
+ #### 2025-08-15 - v.0.6.23
2150
+
2151
+ • Lazy Lists: Important Breaking Change
2152
+
2153
+ All downnested portals now use Turbo's last list feature (frame with `src:` to load the subview via a separate request).
2154
+ The user sees "Loading" in the box as it is loading. (See `--downnest` and `--nested` sections.)
2155
+
2156
+ Unfortunately, this is a partially breaking change in that a parent & child should be rebuilt together on this version.
2157
+
2158
+ Whereas before the parent's edit template included the list and passed it the records to render immediately (in the same request)
2159
+
2160
+ ```
2161
+ <%= render partial: "agent_rules/list", locals: {agent: @agent, rules: @agent.rules} %>
2162
+
2163
+ ```
2164
+
2165
+ Now, we will render a new partial (from the child's build folder) at `lazy_list` (when the parent is rendering its children)
2166
+
2167
+ ```
2168
+ <%= render partial: "agent_rules/lazy_list", locals: {agent: @agent, ...
2169
+ ```
2170
+
2171
+ The `lazy_list` itself contains a turbo frame tag with an `src` set to the URL where the list can be loaded, appened with __lazy=1 to let the child controller know not to render the full layout.
2172
+
2173
+
2174
+ ```
2175
+ <%= tag.turbo_frame id: "__agents-list" + ((('__' + nested_for) if defined?(nested_for)) || ""),
2176
+ src: account_crusade_agents_path(account,crusade) + "?__lazy",
2177
+ loading: "lazy" do %>
2178
+
2179
+ ```
2180
+
2181
+ In the downnested controller, the children will now suppress the layout when loaded lazy
2182
+
2183
+ ```
2184
+ render layout: (params[:__lazy].present? ? false : true)
2185
+ ```
2186
+
2187
+ Just remember you must rebuild the parent if you rebuild a child, and you must rebuild ALL the children of any parent that is rebuilt.
2188
+
2189
+
2190
+ • Modify now has an `include_blank` option to add a blank option for associations
2191
+
2192
+
2193
+ `--modify-as=person_id{include_blank}`
2194
+
2195
+ Make sure your `belongs_to` association has `optional: true` or else you will get validation errors when you try to save a record with an empty association.
2196
+
2197
+
2198
+ • Fixes cancel button problems related to subviews (no longer necessary to load the edit of the parent in the lazy paradigm)
2199
+
2200
+ • Fixes double-entry redisplay problem (second create action would be saved but not show up in UI) due to malformed nested_for
2201
+
2202
+
2203
+
2204
+
2205
+ #### 2025-07-28 v0.6.22
2206
+
2207
+
2035
2208
 
2036
2209
  #### 2025-07-05 v0.6.21
2037
2210
  •Now use new code insertion `--code-after-new` for code that happens directly after `.new()` call. use semicolon (`;`) to create linebreaks; no reason why the factories should insert flash messages
2038
2211
 
2212
+ `--phantom-create-params`
2213
+ These parameters get added in the strong parameters safelist for the create action
2214
+
2215
+ You'll probably wnat to use this along with --code-after-create to check that phanton param
2216
+
2217
+ TODO: you have to tap these away yourself
2218
+ TODO: can they be tapped away automatically if not using a factory
2219
+
2220
+ `--phantom-update-params`
2221
+ These parameters get added in the strong parameters safelist for the update action
2222
+
2223
+ `--controller-prefix`
2224
+
2225
+ Prefix the controller name, and the cooresponding route & path structure, with this prefix.
2226
+ For example, using `--controller-prefix='Open'` on a Document build will produce a controller
2227
+
2228
+ `OpenDocumentsController`
2229
+
2230
+ The controller will still treat the `Document` model as the thing it is building, just a different style of Document named with the prefix.
2231
+
2232
+ (To make this meaningful, you'll want to add a `--record-scope` or in some other way differentiate this controller based on its descriptive prefix)
2233
+
2234
+ • Polymorphic Downnesting
2235
+
2236
+ - `--nested` and `--downnest` can now both accept a param pass in parentheses `(`..`)` to use with polymorphism
2237
+
2238
+ See "Polymorphic downnesting" in the downnesting section for an example.
2239
+
2240
+ • Magic buttons no longer take up 2 bootstrap columns for each button
2241
+
2242
+ • Adds auto-disable to all Delete buttons; use with a `delete_able?` method on the model
2243
+
2244
+ • Removes more vestiges of optionalized nesting (which I had implemented 3 years ago!)
2245
+
2246
+ I no longer like optionalized nesting, and recommend against it.
2247
+
2248
+ Nesting should always be part of the structure, and every route should operate firmly in its nest path.
2249
+
2250
+ Use new controller-prefix to make on-off exceptions or with polymorphic children.
2251
+
2252
+ • Fixes for localized datetime & time inputs
2253
+
2039
2254
  • removes duplicitous flash messages in factory context
2040
2255
 
2041
2256
  • adds documentation for `--code-before-create`, `--code-after-create`, `--code-before-update`, `--code-after-update` (these features were already implemented)
@@ -9,7 +9,7 @@ module HotGlue
9
9
  current_timezone
10
10
 
11
11
  args = args.merge({class: 'form-control',
12
- type: 'datetime-local' })
12
+ type: 'datetime-local' })
13
13
 
14
14
  if !value.nil?
15
15
  args[:value] = date_to_current_timezone(value, current_timezone) + timezonize(current_timezone)
@@ -23,8 +23,8 @@ module HotGlue
23
23
  def date_field_localized(form_object, field_name, value, **args)
24
24
 
25
25
  form_object.text_field(field_name, args.merge({class: 'form-control',
26
- type: 'date',
27
- value: value }))
26
+ type: 'date',
27
+ value: value }))
28
28
  end
29
29
 
30
30
  def time_field_localized(form_object, field_name, value, **args )
@@ -69,18 +69,23 @@ module HotGlue
69
69
  end
70
70
 
71
71
  def is_dst_now?
72
- Time.now.utc.month > 3 && Time.now.month < 11 ||
73
- (Time.now.utc.month == 3 && Time.now.day >= (14 - Time.now.utc.wday)) ||
74
- (Time.now.utc.month == 11 && Time.now.utc.day < (7 - Time.now.utc.wday))
72
+ ActiveSupport::TimeZone['Eastern Time (US & Canada)'].now.dst?
73
+ end
74
+
75
+ def format_timezone_offset(hour, minute)
76
+ sign = hour < 0 ? "-" : "+"
77
+ hour_abs = hour.abs.to_s.rjust(2, '0')
78
+ minute_str = minute.to_s.rjust(2, '0')
79
+ "#{sign}#{hour_abs}#{minute_str}"
75
80
  end
76
81
 
77
82
  def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = {})
78
83
 
79
84
  use_timezone = if current_user_object.try(:timezone)
80
- (ActiveSupport::TimeZone[current_user_object.timezone])
81
- else
82
- Time.zone
83
- end
85
+ (ActiveSupport::TimeZone[current_user_object.timezone])
86
+ else
87
+ Time.zone
88
+ end
84
89
 
85
90
 
86
91
  uses_dst = (current_user_object.try(:locale_uses_dst)) || false
@@ -93,18 +98,28 @@ module HotGlue
93
98
  field_list.include?(k.to_sym)
94
99
  end
95
100
 
96
- parsables = {datetime: "%Y-%m-%d %H:%M %z",
97
- time: "%H:%M %z"}
101
+ parsables = {
102
+ datetime: "%Y-%m-%d %H:%M %z",
103
+ time: "%H:%M %z"
104
+ }
105
+
98
106
 
99
107
  if include_me && params[k].present?
100
108
  if use_timezone
101
109
  natural_offset = use_timezone.formatted_offset
102
- hour = natural_offset.split(":").first
103
- min = natural_offset.split(":").last
104
- hour = hour.to_i - 1 if uses_dst && is_dst_now?
110
+ hour = natural_offset.split(":").first.to_i
111
+ min = natural_offset.split(":").last.to_i
105
112
 
106
- use_offset = "#{hour}:#{min}"
113
+ hour = hour + 1 if uses_dst && is_dst_now?
114
+
115
+ use_offset = format_timezone_offset(hour, min)
107
116
  parse_date = "#{params[k].gsub("T", " ")} #{use_offset}"
117
+
118
+
119
+ Rails.logger.info("use_offset: #{use_offset}")
120
+
121
+ Rails.logger.info("parse_date: #{parse_date}")
122
+
108
123
  # note: as according to https://stackoverflow.com/questions/20111413/html5-datetime-local-control-how-to-hide-seconds
109
124
  # there is no way to set the seconds to 00 in the datetime-local input field
110
125
  # as I have implemented a "seconds don't matter" solution,
@@ -117,6 +132,11 @@ module HotGlue
117
132
  else
118
133
  parsed_time = Time.strptime(parse_date, parsables[field_list[k.to_sym]])
119
134
  end
135
+ Rails.logger.info "parsed_time #{parsed_time}"
136
+ Rails.logger.info "Timezone: #{use_timezone.name}"
137
+ Rails.logger.info "Offset: #{use_timezone.formatted_offset}"
138
+ Rails.logger.info "DST? #{uses_dst} | is_dst_now? => #{is_dst_now?}"
139
+ Rails.logger.info "Final offset used: #{use_offset}"
120
140
 
121
141
  params[k] = parsed_time
122
142
  end
@@ -229,12 +249,12 @@ module HotGlue
229
249
  when 'is_at_exactly'
230
250
  ["EXTRACT(HOUR FROM #{field}) = ?
231
251
  AND EXTRACT(MINUTE FROM #{field}) = ? ", search_start.split(":")[0], search_start.split(":")[1]]
232
- # when 'is_at_or_after'
233
- # ["#{field} = ? OR #{field} > ?", search_start, search_start]
234
- # when "is_before_or_at"
235
- # ["#{field} = ? OR #{field} < ?", search_end, search_end]
236
- # when "is_between"
237
- # ["#{field} BETWEEN ? AND ?", search_start, search_end]
252
+ # when 'is_at_or_after'
253
+ # ["#{field} = ? OR #{field} > ?", search_start, search_start]
254
+ # when "is_before_or_at"
255
+ # ["#{field} = ? OR #{field} < ?", search_end, search_end]
256
+ # when "is_between"
257
+ # ["#{field} BETWEEN ? AND ?", search_start, search_end]
238
258
  end
239
259
  end
240
260
  end
@@ -265,8 +285,5 @@ module HotGlue
265
285
 
266
286
  private
267
287
 
268
- # def server_timezone_offset # returns integer of hours to add/subtract from UTC
269
- # Time.now.in_time_zone(Rails.application.config.time_zone).strftime("%z").to_i/100
270
- # end
271
288
  end
272
289
  end
@@ -154,8 +154,12 @@ class AssociationField < Field
154
154
 
155
155
 
156
156
 
157
+ if modify_as && modify_as[:include_blank]
158
+ include_blank = ", include_blank: true"
159
+ end
160
+
157
161
  (is_owner ? "<% unless @#{assoc_name} %>\n" : "") +
158
- " <%= f.collection_select(:#{name}, #{hawked_association}, :id, :#{display_column}, { prompt: true, selected: #{singular}.#{name} }, class: 'form-control'#{data_attr}) %>\n" +
162
+ " <%= f.collection_select(:#{name}, #{hawked_association}, :id, :#{display_column}, { prompt: true, selected: #{singular}.#{name}#{include_blank} }, class: 'form-control'#{data_attr}) %>\n" +
159
163
  (is_owner ? "<% else %>\n <%= @#{assoc_name}.#{display_column} %>" : "") +
160
164
  (is_owner ? "\n<% end %>" : "")
161
165
  end
@@ -21,14 +21,21 @@ class FloatField < Field
21
21
  # end
22
22
 
23
23
  def search_field_output
24
- ""
24
+ " <div>" +
25
+ "\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['=', '='], " +
26
+ "\n ['≥', '≥'], ['>', '>'], " +
27
+ "\n ['≤', '≤'], ['<', '<']], @q[\'0\']['#{name}_match'] ), {} ," +
28
+ "\n { class: 'form-control match' } %>"+
29
+ "\n <%= f.text_field 'q[0][#{name}_search]', {value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 4, class: 'form-control', type: 'number'} %>" +
30
+ "\n </div>"
25
31
  end
26
32
 
27
33
 
28
34
  def where_query_statement
35
+ ".where(\"#{name} \#{#{name}_query }\")"
29
36
  end
30
37
 
31
38
  def load_all_query_statement
32
- raise "Float field search not implemented"
39
+ "#{name}_query = integer_query_constructor(@q['0'][:#{name}_match], @q['0'][:#{name}_search])"
33
40
  end
34
41
  end
@@ -60,16 +60,19 @@ module HotGlue
60
60
  unless @big_edit
61
61
  # how_many_downnest = downnest_object.size
62
62
  if(!stacked_downnesting)
63
- bootstrap_columns = bootstrap_columns - (downnest_object.collect{|k,v| v}.sum)
63
+ bootstrap_columns = bootstrap_columns - (downnest_object.size * 4)
64
64
  else
65
65
  bootstrap_columns = bootstrap_columns - 4
66
66
  end
67
67
 
68
68
  # downnest_children_width = []
69
- downnest_object.each do |child, size|
70
- layout_object[:portals][child] = {size: size}
69
+
70
+
71
+ @downnest_object.each do |child, data|
72
+ layout_object[:portals][child] = {size: data[:extra_size] + 4}
71
73
  end
72
74
  end
75
+
73
76
  available_columns = (bootstrap_columns / bootstrap_column_width).floor
74
77
 
75
78
  # when set to 2, turns the 12-column grid into a 6-column grid
@@ -34,10 +34,12 @@ class LayoutStrategy::Bootstrap < LayoutStrategy::Base
34
34
  end
35
35
 
36
36
  def column_width
37
+
37
38
  builder.layout_object[:columns][:size_each]
38
39
  end
39
40
 
40
41
  def downnest_portal_column_width(downnest)
42
+
41
43
  "col-sm-#{ builder.layout_object[:portals][downnest][:size] }"
42
44
  end
43
45
 
@@ -195,11 +195,11 @@ module HotGlue
195
195
  end
196
196
 
197
197
 
198
- the_output = add_spaces_each_line( "\n <span #{@tinymce_stimulus_controller}class='<%= \"alert alert-danger\" if #{singular}.errors.details.keys.include?(:#{field_error_name}) %>' #{data_attr} >\n" +
198
+ the_output = add_spaces_each_line( "\n <div #{@tinymce_stimulus_controller}class='<%= \"alert alert-danger\" if #{singular}.errors.details.keys.include?(:#{field_error_name}) %>' #{data_attr} >\n" +
199
199
  add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
200
200
  + field_result +
201
201
  (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
202
- "\n </span>\n ", 2)
202
+ "\n </div>\n ", 2)
203
203
 
204
204
 
205
205
  if hidden_create.include?(col.to_sym) || hidden_update.include?(col.to_sym)
@@ -244,7 +244,6 @@ module HotGlue
244
244
  def all_line_fields(layout_strategy:,
245
245
  perc_width:)
246
246
 
247
-
248
247
  inline_list_labels = @inline_list_labels || 'omit'
249
248
  columns = layout_object[:columns][:container]
250
249
 
@@ -30,7 +30,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
30
30
  :self_auth, :namespace_value, :record_scope, :related_sets,
31
31
  :search_clear_button, :search_autosearch, :include_object_names,
32
32
  :stimmify, :stimmify_camel, :hidden_create, :hidden_update,
33
- :invisible_create, :invisible_update
33
+ :invisible_create, :invisible_update, :phantom_create_params,
34
+ :phantom_update_params, :lazy
34
35
  # important: using an attr_accessor called :namespace indirectly causes a conflict with Rails class_name method
35
36
  # so we use namespace_value instead
36
37
 
@@ -113,7 +114,9 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
113
114
  class_option :new_button_position, type: :string, default: 'above'
114
115
  class_option :downnest_shows_headings, type: :boolean, default: nil
115
116
  class_option :stimmify, type: :string, default: nil
116
-
117
+ class_option :phantom_create_params, type: :string, default: nil
118
+ class_option :phantom_update_params, type: :string, default: nil
119
+ class_option :controller_prefix, type: :string, default: nil
117
120
 
118
121
  # SEARCH OPTIONS
119
122
  class_option :search, default: nil # set or predicate
@@ -170,7 +173,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
170
173
  get_default_from_config(key: :bootstrap_column_width) || 2
171
174
 
172
175
 
173
-
176
+ @controller_prefix = options['controller_prefix']
174
177
  @default_boolean_display = get_default_from_config(key: :default_boolean_display)
175
178
  if options['layout']
176
179
  layout = options['layout']
@@ -195,18 +198,25 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
195
198
  end
196
199
 
197
200
  args = meta_args[0]
198
- @singular = args.first.tableize.singularize # should be in form hello_world
201
+ @singular = args.first.tableize.singularize # should be in form hello_world
199
202
 
200
203
  if @singular.include?("/")
201
204
  @singular = @singular.split("/").last
202
205
  end
203
206
 
204
- @plural = options['plural'] || @singular.pluralize # respects what you set in inflections.rb, to override, use plural option
207
+ @plural = (options['plural'] || args.first.tableize.singularize.pluralize) # respects what you set in inflections.rb, to override, use plural option
208
+
209
+ puts "SINGULAR: #{@singular}"
210
+ puts "PLURAL: #{@plural}"
211
+
212
+
205
213
  @namespace = options['namespace'] || nil
206
214
  @namespace_value = @namespace
207
- use_controller_name = plural.titleize.gsub(" ", "")
208
- @controller_build_name = ((@namespace.titleize.gsub(" ", "") + "::" if @namespace) || "") + use_controller_name + "Controller"
209
- @controller_build_folder = use_controller_name.underscore
215
+ use_controller_name = plural.titleize.gsub(" ", "")
216
+
217
+
218
+ @controller_build_name = ((@namespace.titleize.gsub(" ", "") + "::" if @namespace) || "") + (@controller_prefix ? @controller_prefix.titleize : "") + use_controller_name + "Controller"
219
+ @controller_build_folder = (@controller_prefix ? @controller_prefix.downcase + "_" : "") + use_controller_name.underscore
210
220
  @controller_build_folder_singular = singular
211
221
 
212
222
  @auth = options['auth'] || "current_user"
@@ -293,6 +303,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
293
303
  @modify_as[key.to_sym] = {typeahead: 1, nested: nested}
294
304
  elsif $2 == "timezone"
295
305
  @modify_as[key.to_sym] = {timezone: 1, badges: $3}
306
+ elsif $2 == "include_blank"
307
+ @modify_as[key.to_sym] = {include_blank: true}
296
308
  elsif $2 == "none"
297
309
  @modify_as[key.to_sym] = {none: 1, badges: $3}
298
310
  else
@@ -387,6 +399,9 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
387
399
 
388
400
  @no_nav_menu = options['no_nav_menu']
389
401
 
402
+ @phantom_create_params = options['phantom_create_params'] || ""
403
+ @phantom_update_params = options['phantom_update_params'] || ""
404
+
390
405
  if get_default_from_config(key: :pundit_default)
391
406
  raise "please note the config setting `pundit_default` has been renamed `pundit`. please update your hot_glue.yml file"
392
407
  end
@@ -409,11 +424,31 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
409
424
 
410
425
  @downnest_children = [] # TODO: defactor @downnest_children in favor of downnest_object
411
426
  @downnest_object = {}
427
+
412
428
  if @downnest
413
- @downnest_children = @downnest.split(",").map { |child| child.gsub("+", "") }
414
- @downnest_object = HotGlue.construct_downnest_object(@downnest)
429
+ @downnest_children = @downnest.split(",")
430
+
431
+ @downnest_children.each do |child|
432
+ if child.include?("(")
433
+ child =~ /(.*)\((.*)\)/
434
+ child_name, polymorph_as = $1, $2
435
+ else
436
+ child_name = child
437
+ end
438
+ extra_size = child_name.count("+")
439
+
440
+ child_name.gsub!("+","")
441
+
442
+
443
+ @downnest_object[child] = {
444
+ name: child_name,
445
+ extra_size: extra_size,
446
+ polymorph_as: polymorph_as
447
+ }
448
+ end
415
449
  end
416
450
 
451
+
417
452
  @include_object_names = options['include_object_names'] || get_default_from_config(key: :include_object_names)
418
453
 
419
454
 
@@ -458,17 +493,26 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
458
493
 
459
494
  if !@nested.nil?
460
495
  @nested_set = @nested.split("/").collect { |arg|
461
- is_optional = arg.start_with?("~")
462
- arg.gsub!("~", "")
496
+ if arg.include?("(")
497
+ arg =~ /(.*)\((.*)\)/
498
+ singular, polymorph_as = $1, $2
499
+ else
500
+ singular = arg
501
+ end
502
+
463
503
  {
464
- singular: arg,
465
- plural: arg.pluralize,
466
- optional: is_optional
504
+ singular: singular,
505
+ plural: singular.pluralize,
506
+ polymorph_as: polymorph_as
467
507
  }
468
508
  }
469
509
  puts "NESTING: #{@nested_set}"
470
510
  end
471
511
 
512
+ if @nested_set.any?
513
+ @lazy = true
514
+ end
515
+
472
516
  # related_sets
473
517
  related_set_input = options['related_sets'].split(",")
474
518
  @related_sets = {}
@@ -489,6 +533,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
489
533
 
490
534
  # OBJECT OWNERSHIP & NESTING
491
535
  @reference_name = HotGlue.derrive_reference_name(singular_class)
536
+
537
+
492
538
  if @auth && @self_auth
493
539
  @object_owner_sym = @auth.gsub("current_", "").to_sym
494
540
  @object_owner_eval = @auth
@@ -506,10 +552,11 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
506
552
  @object_owner_eval = @auth
507
553
  else
508
554
  if @nested_set.any?
509
- @object_owner_sym = @nested_set.last[:singular].to_sym
510
- @object_owner_eval = "@#{@nested_set.last[:singular]}"
511
- @object_owner_name = @nested_set.last[:singular]
512
- @object_owner_optional = @nested_set.last[:optional]
555
+
556
+ @object_owner_sym = (@nested_set.last[:polymorph_as] || @nested_set.last[:singular]).to_sym
557
+
558
+ @object_owner_eval = "#{( @nested_set.last[:singular])}"
559
+ @object_owner_name = (@nested_set.last[:polymorph_as] || @nested_set.last[:singular])
513
560
  else
514
561
  @object_owner_sym = nil
515
562
  @object_owner_eval = ""
@@ -534,7 +581,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
534
581
  @code_after_update = options['code_after_update']
535
582
  @code_after_new = options['code_after_new']
536
583
 
537
- buttons_width = ((!@no_edit && 1) || 0) + ((!@no_delete && 1) || 0) + @magic_buttons.count
584
+ buttons_width = ((!@no_edit && 1) || 0) + ((!@no_delete && 1) || 0) + (@magic_buttons.any? ? 1 : 0)
538
585
 
539
586
 
540
587
  # alt_lookups_entry =
@@ -546,6 +593,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
546
593
  setting =~ /(.*){(.*)}/
547
594
  key, lookup_as = $1, $2
548
595
 
596
+
597
+
549
598
  if !eval("#{class_name}.reflect_on_association(:#{key.to_s.gsub("_id","")})")
550
599
  raise "couldn't find association for #{key} in the object #{class_name}"
551
600
  end
@@ -665,6 +714,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
665
714
  builder = HotGlue::Layout::Builder.new(generator: self,
666
715
  include_setting: options['include'],
667
716
  buttons_width: buttons_width)
717
+
668
718
  @layout_object = builder.construct
669
719
 
670
720
 
@@ -1195,7 +1245,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1195
1245
 
1196
1246
  def form_path_edit_helper
1197
1247
  HotGlue.optionalized_ternary(namespace: @namespace,
1198
- target: @singular,
1248
+ target: @singular,
1249
+ prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1199
1250
  nested_set: @nested_set,
1200
1251
  with_params: false,
1201
1252
  put_form: true,
@@ -1204,6 +1255,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1204
1255
 
1205
1256
  def delete_path_helper
1206
1257
  HotGlue.optionalized_ternary(namespace: @namespace,
1258
+ prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1207
1259
  target: @singular,
1208
1260
  nested_set: @nested_set,
1209
1261
  with_params: false,
@@ -1212,6 +1264,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1212
1264
 
1213
1265
  def edit_path_helper
1214
1266
  HotGlue.optionalized_ternary(namespace: @namespace,
1267
+ prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1215
1268
  target: @singular,
1216
1269
  nested_set: @nested_set,
1217
1270
  modifier: "edit_",
@@ -1219,6 +1272,16 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1219
1272
  put_form: true)
1220
1273
  end
1221
1274
 
1275
+ def new_path_name
1276
+ HotGlue.optionalized_ternary(namespace: @namespace,
1277
+ target: singular,
1278
+ prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1279
+ nested_set: @nested_set,
1280
+ modifier: "new_",
1281
+ with_params: false)
1282
+ end
1283
+
1284
+
1222
1285
  def path_arity
1223
1286
  res = ""
1224
1287
  if @nested_set.any? && @nested
@@ -1239,13 +1302,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1239
1302
  "#{@namespace + "/" if @namespace}#{@controller_build_folder}/list"
1240
1303
  end
1241
1304
 
1242
- def new_path_name
1243
- HotGlue.optionalized_ternary(namespace: @namespace,
1244
- target: singular,
1245
- nested_set: @nested_set,
1246
- modifier: "new_",
1247
- with_params: false)
1248
- end
1249
1305
 
1250
1306
  def nested_assignments
1251
1307
  return "" if @nested_set.none?
@@ -1486,6 +1542,10 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1486
1542
  res -= %w{_list _line index}
1487
1543
  end
1488
1544
 
1545
+ if @lazy
1546
+ res << '_lazy_list'
1547
+ end
1548
+
1489
1549
  res
1490
1550
  end
1491
1551
 
@@ -86,6 +86,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
86
86
  authorize @<%= plural_name %><% elsif @pundit && @pundit_policy_override %>
87
87
  skip_authorization
88
88
  raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.index?<% end %>
89
+ <% if @lazy %>render layout: (params[:__lazy].present? ? false : true)<% end %>
89
90
  rescue Pundit::NotAuthorizedError
90
91
  flash[:alert] = 'You are not authorized to perform this action.'
91
92
  render 'layouts/error'<% end %>
@@ -116,6 +117,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
116
117
 
117
118
  <%= controller_attachment_orig_filename_pickup_syntax %>
118
119
  <%= creation_syntax %>
120
+ <%= @code_after_new ? @code_after_new.gsub(";","\n") + "\n" : "" %>
119
121
 
120
122
  <% if @pundit %><% @related_sets.each do |key, related_set| %>
121
123
  check_<%= related_set[:association_ids_method].to_s %>_permissions(modified_params, :create)<% end %><% end %>
@@ -144,14 +146,13 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
144
146
  rescue Pundit::NotAuthorizedError => e
145
147
  flash[:alert] = "Not authorized."
146
148
  @<%= singular %>.errors.add(:base, e.message)
147
- <% unless @display_edit_after_create %>render :create, status: :unprocessable_entity<% else %>redirect_to <%= HotGlue.optionalized_ternary(namespace: @namespace,
149
+ <% unless @display_edit_after_create %>render :create, status: :unprocessable_entity<% else %>redirect_to <%= HotGlue.optionalized_ternary(namespace: @namespace,
148
150
  top_level: true,
149
- target: @singular,
151
+ target: @plural,
150
152
  nested_set: @nested_set,
151
- modifier: 'edit_',
152
153
  with_params: true,
153
- instance_last_item: true,
154
- put_form: true).gsub("(#{singular}", "(@#{singular}") %><% end %>
154
+ instance_last_item: false,
155
+ put_form: false).gsub("(#{singular}", "(@#{singular}") %><% end %>
155
156
 
156
157
  <% end %>
157
158
  end
@@ -164,7 +165,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
164
165
  raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.show?<% end %>
165
166
  redirect_to <%= HotGlue.optionalized_ternary(namespace: @namespace,
166
167
  target: @singular,
167
- top_level: false,
168
+ top_level: true,
168
169
  nested_set: @nested_set,
169
170
  modifier: 'edit_',
170
171
  with_params: true,
@@ -287,13 +288,13 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
287
288
  end<% end %><% end %>
288
289
 
289
290
  def <%=singular_name%>_params
290
- fields = <%= ((fields_filtered_for_strong_params - @show_only) + @magic_buttons.collect{|x| "__#{x}"}).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>
291
+ fields = <%= ((fields_filtered_for_strong_params - @show_only) + @magic_buttons.collect{|x| "__#{x}"} + @phantom_create_params.split(",")).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>
291
292
  params.require(:<%= testing_name %>).permit(fields)
292
293
  end<% if @update_show_only %>
293
294
 
294
295
  <% unless @no_edit %>
295
296
  def update_<%=singular_name%>_params
296
- fields = <%= ((fields_filtered_for_strong_params - @update_show_only) + @magic_buttons.collect{|x| "__#{x}"}).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>
297
+ fields = <%= ((fields_filtered_for_strong_params - @update_show_only) + @magic_buttons.collect{|x| "__#{x}"} + @phantom_update_params.split(",")).collect{|sym| ":#{sym}"}.join(", ") %><%= ", " + @related_sets.collect{|key, rs| "#{rs[:association_ids_method]}: []"}.join(", ") if @related_sets.any? %><%= ", " + @alt_lookups.collect{|k,v| ":__lookup_#{v[:assoc].downcase}_#{v[:lookup_as]}" }.join(", ") if @alt_lookups.any? %>
297
298
  <%= (fields_filtered_for_strong_params - @update_show_only).collect{|col|
298
299
  # TODO : fields not on show only also not invisible should be checked here
299
300
  # for _able? methods and added only when able
@@ -2,8 +2,7 @@
2
2
  <%= form_fields_html %>
3
3
 
4
4
  <div class="<%= @layout_strategy.column_classes_for_button_column %>">
5
- <\%= link_to "Cancel", <%=
6
- @nested_set.none? ? path_helper_plural : edit_parent_path_helper %>, {class: "btn btn-secondary"} %><% if @no_field_form %>
5
+ <\%= link_to "Cancel", <%= path_helper_plural %> + (params[:page] ? "?page=" + params[:page] : ""), {class: "btn btn-secondary"} %><% if @no_field_form %>
7
6
  <\%= f.hidden_field "_________" %><% end %>
8
7
  <\%= f.submit "Save", class: "btn btn-primary pull-right" %>
9
8
  </div>
@@ -0,0 +1,7 @@
1
+ <\%= tag.turbo_frame id: "<%= @namespace %>__<%= plural %>-list" <%= nested_for_turbo_id_list_constructor %>,
2
+ src: <%= path_helper_plural %> + "?__lazy",
3
+ loading: "lazy" do %>
4
+
5
+ <div class="text-muted">Loading ...</div>
6
+
7
+ <\% end %>
@@ -19,19 +19,22 @@
19
19
  <% if @downnest_object.any? && !@big_edit %>
20
20
  <% if !@stacked_downnesting %>
21
21
  <%= @layout_strategy.downnest_column_style %>
22
- <% @downnest_object.each do |downnest,i| %>
23
- <div class=" scaffold-col-heading <%= @layout_strategy.downnest_portal_column_width(downnest) %> <%= @layout_strategy.downnest_column_style %>">
22
+
23
+ <% @downnest_object.each_with_index do |data,i| %>
24
+ <% downnest = data[1] %>
25
+ <div class=" scaffold-col-heading <%= @layout_strategy.downnest_portal_column_width(downnest[:name]) %> <%= @layout_strategy.downnest_column_style %>">
24
26
  <strong>
25
- <%= downnest.titleize %>
27
+ <%= downnest[:name].titleize %>
26
28
  </strong>
27
29
  </div>
28
30
  <% end %>
29
31
  <% else %>
30
32
  <div class=" scaffold-col-heading <%= @layout_strategy.downnest_portal_stacked_column_width %> <%= @layout_strategy.downnest_column_style %>">
31
33
  <%= @layout_strategy.downnest_column_style %>
32
- <% @downnest_object.each do |downnest,i| %>
34
+ <% @downnest_object.each_with_index do |data,i| %>
35
+ <% downnest = data[1] %>
33
36
  <strong>
34
- <%= downnest.titleize %>
37
+ <%= downnest[:name].titleize %>
35
38
  </strong>
36
39
  <% end %>
37
40
  </div>
@@ -1,7 +1,5 @@
1
1
  <%= all_line_fields %>
2
2
 
3
-
4
-
5
3
  <% if @downnest_children.any? && ! @big_edit %>
6
4
  <% each_downnest_width = @downnest_children.count == 1 ? 33 : (53/@downnest_children.count).floor %>
7
5
  <% if @stacked_downnesting %><div class="<%= @layout_strategy.downnest_portal_stacked_column_width %> scaffold-downnest" ><% end %>
@@ -39,11 +37,11 @@
39
37
 
40
38
  <% if destroy_action %>
41
39
  <\%= form_with url: <%= delete_path_helper %>, html: {data: {'<%= @ujs_syntax ? 'confirm' : 'turbo-confirm' %>': "Are you sure you want to delete #{ <%= @singular + "." + display_class %> }?" }, style: "display: inline-block;"}, method: :delete do |f| %>
42
- <\%= f.submit "Delete".html_safe, class: "delete-<%= singular %>-button btn btn-primary btn-sm" %>
40
+ <\%= f.submit "Delete".html_safe, class: "delete-<%= singular %>-button btn btn-primary btn-sm", disabled: (<%= @singular %>.respond_to?(:delete_able?) && ! <%= @singular %>.delete_able? ) %>
43
41
  <\% end %>
44
42
  <% end %>
45
43
 
46
44
  <% unless @no_edit %>
47
- <\%= link_to "Edit <% if @button_icons == 'font-awesome' %><i class='fa fa-1x fa-list-alt'></i><% end %>".html_safe, <%= edit_path_helper %>, <% if @big_edit %>'data-turbo' => 'false', <% end %>disable_with: "Loading...", class: "edit-<%= singular %>-button btn btn-primary btn-sm" %>
45
+ <\%= link_to "Edit <% if @button_icons == 'font-awesome' %><i class='fa fa-1x fa-list-alt'></i><% end %>".html_safe, <%= edit_path_helper %> + (params[:page] ? "?page=#{params[:page]}" : ""), <% if @big_edit %>'data-turbo' => 'false', <% end %>disable_with: "Loading...", class: "edit-<%= singular %>-button btn btn-primary btn-sm" %>
48
46
  <% end %>
49
47
  </div>
@@ -1,6 +1,6 @@
1
1
  <\% if @<%= singular %>.errors.none? %>
2
2
  <\%= turbo_stream.replace "<%= @namespace %>__<%= plural %>-list" + <%= nested_for_turbo_nested_constructor %> do %>
3
- <\%= render partial: "list", locals: {<%= plural %>: @<%= plural %><% if @nested_set.any? %>, <%= @nested_set.collect{|arg| "#{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %>, nested_for: '<%= @nested_set.collect{|arg| "\"#{arg[:singular]}-\#{@#{arg[:singular]}.id}\""}.join("__") %>' <% end %> } %>
3
+ <\%= render partial: "list", locals: {<%= plural %>: @<%= plural %><% if @nested_set.any? %>, <%= @nested_set.collect{|arg| "#{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %>, nested_for: "<%= @nested_set.collect{|arg| "#{arg[:singular]}-\#{@#{arg[:singular]}.id}"}.join("__") %>" <% end %> } %>
4
4
 
5
5
  <\% end %>
6
6
  <\% end %>
@@ -15,27 +15,29 @@
15
15
  <% if @downnest_children.any? && @big_edit %>
16
16
  <div class="container" data-controller="bootstrap-tabbed-nav">
17
17
  <ul class="nav nav-tabs" id="<%= singular + "_downnest_portals" %>" role="tablist">
18
- <% @downnest_object.each_with_index do |data,index| %>
19
- <% downnest = data[0] %>
18
+ <% @downnest_object.each_with_index do |set, index| %>
19
+ <% key = set[0]; downnest = set[1] %>
20
20
  <li class="nav-item" role="presentation">
21
- <button class="nav-link <%= "active" if index==0 %>" id="<%= downnest %>-tab" data-bs-toggle="tab" data-bs-target="#<%= downnest %>-portal" type="button" role="tab" aria-controls="home" aria-selected="true">
22
- <%= downnest.titlecase.pluralize %>
21
+ <button class="nav-link <%= "active" if index==0 %>" id="<%= downnest[:name] %>-tab" data-bs-toggle="tab" data-bs-target="#<%= downnest[:name] %>-portal" type="button" role="tab" aria-controls="home" aria-selected="true">
22
+ <%= downnest[:name].titlecase.pluralize %>
23
23
  </button>
24
24
  </li>
25
25
  <% end %>
26
26
  </ul>
27
27
 
28
28
  <div class="tab-content" id="myTabContent">
29
- <% @downnest_object.each_with_index do |data, index| %>
30
- <% downnest = data[0] %>
31
- <div class="tab-pane fade <%= "show active" if index==0 %>" id="<%= downnest %>-portal" role="tabpanel" aria-labelledby="<%= downnest %>-tab">
32
- <% downnest_object = eval("#{singular_class}.reflect_on_association(:#{downnest})") %>
33
- <% if downnest_object.nil?; raise "no relationship for downnested portal `#{downnest}` found on `#{singular_class}`; please check relationship for has_many :#{downnest}"; end; %>
29
+ <% @downnest_object.each_with_index do |set, index| %>
30
+ <% key = set[0]; downnest = set[1] %>
31
+ <div class="tab-pane fade <%= "show active" if index==0 %>" id="<%= downnest[:name] %>-portal" role="tabpanel" aria-labelledby="<%= downnest[:name] %>-tab">
32
+
33
+ <% downnest_object = eval("#{singular_class}.reflect_on_association(:#{downnest[:polymorph_as] || downnest[:name]})") %>
34
+ <% if downnest_object.nil?; raise "no relationship for downnested portal `#{downnest[:name]}` found on `#{singular_class}`; please check relationship for has_many :#{downnest[:name]} or use ( .. ) to work with polymorphism"; end; %>
34
35
  <% downnest_class = downnest_object.class_name %>
35
- <% downnest_object_name = eval("#{downnest_class}.table_name") %>
36
+ <% downnest_object_name = downnest[:name] %>
36
37
  <% downnest_style = @layout_strategy.downnest_style %>
37
38
 
38
- <\%= 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 %> } \%>
39
+ <\%= render partial: "<%= namespace_with_trailing_dash %><%= downnest_object_name %>/lazy_list", locals: {<%= @singular %>: @<%= @singular %>, <%= @nested_set.collect{|x| "#{x[:singular]}: @#{x[:singular]}"}.join(", ") %><%= @nested_set.any? ? "," : "" %> nested_for: "<%= (@nested_set).collect{|x| "#{x[:singular]}-" + "\#{" + "@#{x[:singular]}.id}"}.join("__") %><%= "__" if @nested_set.any? %><%= singular %>-#{@<%= @singular %>.id}" } \%>
40
+
39
41
  </div>
40
42
  <% end %>
41
43
  </div>
@@ -6,6 +6,7 @@
6
6
 
7
7
  <%= @layout_strategy.page_begin %>
8
8
  <\%= render partial: '<%= list_path_partial %>',
9
- locals: {<%= plural %>: @<%= plural %>}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {nested_for: \"" + arg[:singular] + "-\#{@" + arg[:singular] + ".id}\"" + ", " + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> \%>
9
+ locals: {<%= plural %>: @<%= plural %> <% if @nested_set.any? %>, nested_for: "<%= @nested_set.collect{|n| "#{n[:singular]}-\#{@#{n[:singular]}.id}"}.join("__") %>", <%= @nested_set.collect{|n| "#{n[:singular]}: @#{n[:singular]}"}.join(", ") %><% end %>
10
+ } \%>
10
11
  <%= @layout_strategy.page_end %>
11
12
  </div>
data/lib/hot-glue.rb CHANGED
@@ -22,18 +22,10 @@ module HotGlue
22
22
  end
23
23
 
24
24
 
25
- def self.construct_downnest_object(input)
26
- res = input.split(",").map { |child|
27
- child_name = child.gsub("+","")
28
- extra_size = child.count("+")
29
- {child_name => 4+extra_size}
30
- }
31
- Hash[*res.collect{|hash| hash.collect{|key,value| [key,value].flatten}.flatten}.flatten]
32
- end
33
-
34
25
  def self.optionalized_ternary(namespace: nil,
35
26
  target:,
36
27
  nested_set:,
28
+ prefix: "", # is this used
37
29
  modifier: "",
38
30
  with_params: false,
39
31
  top_level: false,
@@ -43,43 +35,44 @@ module HotGlue
43
35
 
44
36
 
45
37
  if nested_set.nil? || nested_set.empty?
46
- return modifier + "#{(namespace + '_') if namespace}#{target}_path" + (("(#{instance_sym}#{target})" if put_form) || "")
47
- elsif nested_set[0][:optional] == false
38
+ return modifier + "#{(namespace + '_') if namespace}#{prefix}#{target}_path" + (("(#{instance_sym}#{target})" if put_form) || "")
48
39
 
40
+ else
49
41
  res = modifier + ((namespace + "_" if namespace) || "") + nested_set.collect{|x|
50
42
  x[:singular] + "_"
51
- }.join() + target + "_path" + (("(#{nested_set.collect{
43
+ }.join() + prefix + target + "_path" + (("(#{nested_set.collect{
52
44
  |x| instance_sym + x[:singular] }.join(",")
53
45
  }#{ put_form ? ',' + (instance_last_item ? "@" : instance_sym) + target : '' })") || "")
54
46
 
55
47
  res
56
-
57
- else
48
+ # else
49
+ # raise "optional nested set is deprecated"
58
50
  # copy the first item, make a ternery in this cycle, and recursively move to both the
59
51
  # is present path and the is optional path
60
52
 
61
- nonoptional = nested_set[0].dup
62
- nonoptional[:optional] = false
63
- rest_of_nest = nested_set[1..-1]
64
-
65
- is_present_path = HotGlue.optionalized_ternary(
66
- namespace: namespace,
67
- target: target,
68
- modifier: modifier,
69
- top_level: top_level,
70
- with_params: with_params,
71
- put_form: put_form,
72
- nested_set: [nonoptional, *rest_of_nest])
73
-
74
- is_missing_path = HotGlue.optionalized_ternary(
75
- namespace: namespace,
76
- target: target,
77
- modifier: modifier,
78
- top_level: top_level,
79
- with_params: with_params,
80
- put_form: put_form,
81
- nested_set: rest_of_nest )
82
- return "defined?(#{instance_sym + nested_set[0][:singular]}2) ? #{is_present_path} : #{is_missing_path}"
53
+ # nonoptional = nested_set[0].dup
54
+ # nonoptional[:optional] = false
55
+ # rest_of_nest = nested_set[1..-1]
56
+ #
57
+ # is_present_path = HotGlue.optionalized_ternary(
58
+ # namespace: namespace,
59
+ # target: target,
60
+ # modifier: modifier,
61
+ # top_level: top_level,
62
+ # with_params: with_params,
63
+ # put_form: put_form,
64
+ # nested_set: [nonoptional, *rest_of_nest])
65
+
66
+ # is_missing_path = HotGlue.optionalized_ternary(
67
+ # namespace: namespace,
68
+ # target: target,
69
+ # modifier: modifier,
70
+ # top_level: top_level,
71
+ # with_params: with_params,
72
+ # put_form: put_form,
73
+ # nested_set: rest_of_nest )
74
+ #
75
+ # return "#{is_present_path}"
83
76
  end
84
77
  end
85
78
 
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.6.21.2'
3
+ CURRENT = '0.6.23'
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.21.2
4
+ version: 0.6.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Fleetwood-Boldt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-10 00:00:00.000000000 Z
11
+ date: 2025-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -115,6 +115,7 @@ files:
115
115
  - lib/generators/hot_glue/templates/erb/_edit.erb
116
116
  - lib/generators/hot_glue/templates/erb/_flash_notices.erb
117
117
  - lib/generators/hot_glue/templates/erb/_form.erb
118
+ - lib/generators/hot_glue/templates/erb/_lazy_list.erb
118
119
  - lib/generators/hot_glue/templates/erb/_line.erb
119
120
  - lib/generators/hot_glue/templates/erb/_list.erb
120
121
  - lib/generators/hot_glue/templates/erb/_nav.html.erb