hot-glue 0.7.5 → 0.7.7

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: 1ac74e056098b9847487adc764e7bb03b0024c94bdbdf151eb24eb688cc53309
4
- data.tar.gz: a23e9e6c569539b30542b97446bea589317cd1118bd87bf7c4330fff3a08603d
3
+ metadata.gz: 19812e0ed05939741ec5f22550345a5d94a07ad32f3925540bd6bb2ec76e0f6f
4
+ data.tar.gz: d2fba980427bd8c8961a6fb475b063305956d8a99ac428ffc28317291b961209
5
5
  SHA512:
6
- metadata.gz: eae357a387a886f4f24156f78f666a56f81a829d1aab3dd3e128698cbf59ede7136bf9d6c0c8e286b9840205860133ea6d17085ff7e340aea4a3b9be8bcacae1
7
- data.tar.gz: d5d23aaac0178a602cbc0f1893819aba6b12d57270f218389742a54bdfdea5a59c6bd800b1a5e7b05cedb75d452bd6a8da170a6cc3a57fd6c2caf9edcca4c2e1
6
+ metadata.gz: 2a6c67fe502cce091097b4e72f1806a23b9126eea7c1498a8ba5f38aec5a581ddb59fd54543e4ba00257f70bfaa987aaf9ae07c9f80de093cccd0a3584dd9133
7
+ data.tar.gz: 3143a9b97b4338421c2bc4b910afd2f18cddb96178e8e27e6beaf4189d705d54222aa6335c425b7beb2ea2910557979135d84919c410ad63d3c357d3c5e06885
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.7.5)
4
+ hot-glue (0.7.6.2)
5
5
  ffaker (~> 2.16)
6
6
  rails (> 5.1)
7
7
 
@@ -129,7 +129,7 @@ GEM
129
129
  mini_mime (1.1.2)
130
130
  mini_portile2 (2.8.4)
131
131
  minitest (5.16.3)
132
- net-imap (0.6.2)
132
+ net-imap (0.6.4)
133
133
  date
134
134
  net-protocol
135
135
  net-pop (0.1.2)
@@ -225,7 +225,7 @@ GEM
225
225
  stimulus-rails (1.1.1)
226
226
  railties (>= 6.0.0)
227
227
  thor (1.2.1)
228
- timeout (0.6.0)
228
+ timeout (0.6.1)
229
229
  turbo-rails (1.3.2)
230
230
  actionpack (>= 6.0.0)
231
231
  activejob (>= 6.0.0)
data/README.md CHANGED
@@ -712,6 +712,17 @@ current_user's has_many association (so, for any other "my" family, would be `cu
712
712
 
713
713
  This is covered in [Example #4 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
714
714
 
715
+ ##### Polymoprhism with the Hawk
716
+
717
+ If the field (foreign key) being hawked is a polymorphic foreign key, you need to list multiple objects which define the allowed scopes (one for each kind of parent type).
718
+
719
+ In this case, you will use **spaces** to separate scopes (NOT commas)
720
+
721
+ for example, if we have a `thing` that can belong (via parent_id and parent_type) to either people or places, we could restrict this thing to only people and places associated from the `account` object (which would be in-scope based on, for example, the nesting arrangement or a logged in-user, or the account currently being managed)
722
+
723
+ `--hawk=parent_id{account.people account.places}`
724
+
725
+ Hot glue wil convert the spaces to commas when writing the controller code.
715
726
 
716
727
  ##### Using the object inside of the hawk
717
728
  In the example above, we aren't using the name of the scaffold within the hawk.
@@ -882,7 +893,7 @@ You may use semi-colons to separate multiple lines of code.
882
893
 
883
894
  For example, a user Factory might be called like so:
884
895
 
885
- `./bin/rails generate hot_glue:scaffold User --factory-creation={factory = UserFactory.new(params: user_params)} --gd`
896
+ `./bin/rails generate hot_glue:scaffold User --factory-creation='factory = UserFactory.new(params: user_params)' --gd`
886
897
 
887
898
  (Note we are relying on the `user_params` method provided by the controller.)
888
899
 
@@ -2478,6 +2489,47 @@ These automatic pickups for partials are detected at build time. This means that
2478
2489
 
2479
2490
  # VERSION HISTORY
2480
2491
 
2492
+ #### 2026-04-08 - v0.7.6.2
2493
+ - Syntax fix in code, no functional changes
2494
+
2495
+ #### 2026-04-06 - v0.7.6.1
2496
+ - Syntax fix in code, no functional changes
2497
+
2498
+ #### 2026-03-08 - v0.7.6
2499
+ - Makes hawk polymorphic aware. As well, a child controller with a polymorphic parent is also aware (the last item in the nest list should be specified using `(`...`)`)
2500
+
2501
+ Using the hawk with a polymorphic foreign key:
2502
+
2503
+ If the field (foreign key) being hawked is a polymorphic foreign key, you need to list multiple objects which define the allowed scopes (one for each kind of parent type).
2504
+
2505
+ In this case, you will use **spaces** to separate scopes (NOT commas)
2506
+
2507
+ for example, if we have a `thing` that can belong (via parent_id and parent_type) to either people or places, we could restrict this thing to only people and places associated from the `account` object (which would be in-scope based on, for example, the nesting arrangement or a logged in-user, or the account currently being managed)
2508
+
2509
+ `--hawk=parent_id{account.people account.places}`
2510
+
2511
+ Hot glue wil convert the spaces to commas when writing the controller code.
2512
+
2513
+ A child controller to with a polymorphic parent:
2514
+ - This special case assume that that the parent being build is not actually a polymoprh, it is a real object, but its children have foreign keys to it which are polymorphic.
2515
+
2516
+ `--nested=abc(parent) `
2517
+
2518
+ Example: In my data model, targets have a polymorphic parent (parent_id and parent_type) and can belong to either Companies or Schools. Here, we are building the Companies view with a child to Targets, but notice for these targets we are using polymorphism and also using a controller prefix, so tha this child controller will be built as CompanyTargets. In the companies build, we downnest `company_targets(targets)` (`company_targets` is the name of the child controller, but it is acting on an object called `targets`, as seen in the downnest specification.)
2519
+
2520
+ ```
2521
+ bin/rails generate hot_glue:scaffold Company --namespace='account_dashboard' --nested='account' --downnest='company_targets(targets)'
2522
+ ```
2523
+
2524
+ When building Targets, notice that the nested chain ends with `company(parent)`. This means our routes behave like normal routes (account/company), but this tells Hot Glue that the relationship from Target to Company is via the polymorphic parent_id & parent_type key.
2525
+
2526
+ ```
2527
+ bin/rails generate hot_glue:scaffold Target --namespace='account_dashboard' --nested='account/company(parent)' --controller-prefix='Company'
2528
+ ```
2529
+
2530
+ - Pagy support for Pagy 42 + 43. Breaking changes bewteen Pagy version 9 and version 42 force you to rebuild everything (every view) when upgrading Pagy. Hot Glue now detects which version of Pagy is installed and outputs the syntax for that version. (You will still need to rebuild all controllers when upgrading past Pagy 9)
2531
+
2532
+
2481
2533
  #### 2026-01-11 - v0.7.5
2482
2534
  This is mostly a maintenance release to address these two issues related to the hawk:
2483
2535
  - removes the hawk in the create action because we are alrady doing it in the creation_syntax
@@ -120,68 +120,6 @@ module HotGlue
120
120
  "#{sign}#{hour_abs}#{minute_str}"
121
121
  end
122
122
 
123
- # def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = {})
124
- #
125
- # use_timezone = if current_user_object.try(:timezone)
126
- # (ActiveSupport::TimeZone[current_user_object.timezone])
127
- # else
128
- # Time.zone
129
- # end
130
- #
131
- #
132
- # uses_dst = (current_user_object.try(:locale_uses_dst)) || false
133
- #
134
- # modified_params = modified_params.tap do |params|
135
- # params.keys.each{|k|
136
- # if field_list.is_a?(Hash)
137
- # include_me = field_list[k.to_sym].present?
138
- # elsif field_list.is_a?(Array)
139
- # field_list.include?(k.to_sym)
140
- # end
141
- #
142
- # parsables = {
143
- # datetime: "%Y-%m-%d %H:%M %z",
144
- # time: "%H:%M %z"
145
- # }
146
- #
147
- #
148
- # if include_me && params[k].present?
149
- # input_value = params[k].gsub("T", " ") # e.g. "2025-09-24 14:00" or "14:00"
150
- #
151
- # if field_list.is_a?(Array)
152
- # # Datetime inputs (e.g. datetime-local)
153
- # parsed_time = Time.strptime(input_value, "%Y-%m-%d %H:%M")
154
- # parsed_time = parsed_time.utc.change(sec: 0)
155
- # else
156
- # case field_list[k.to_sym]
157
- # when :datetime
158
- # parsed_time = Time.strptime(input_value, "%Y-%m-%d %H:%M")
159
- # parsed_time = parsed_time.utc.change(sec: 0)
160
- # when :time
161
- #
162
- # Rails.logger.info("input_value: #{input_value}")
163
- # # Parse as hour/minute only, no zone
164
- # t = Time.strptime(input_value, "%H:%M")
165
- #
166
- # # Build a UTC time with today's date
167
- # parsed_time = Time.utc(Time.now.year, Time.now.month, Time.now.day, t.hour, t.min, 0)
168
- # # Convert back to a plain "time of day" (for DB `time` column)
169
- # parsed_time = parsed_time.to_time.change(sec: 0)
170
- # Rails.logger.info("parsed_time: #{parsed_time}")
171
- #
172
- # else
173
- # raise "Unsupported field type: #{field_list[k.to_sym]}"
174
- # end
175
- # end
176
- #
177
- # Rails.logger.info "parsed_time #{parsed_time}"
178
- # params[k] = parsed_time
179
- # end
180
- # }
181
- # end
182
- # modified_params
183
- # end
184
-
185
123
  def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = {})
186
124
  use_timezone =
187
125
  if current_user_object.try(:timezone)
@@ -229,17 +167,33 @@ module HotGlue
229
167
  end
230
168
 
231
169
  def hawk_params(hawk_schema, modified_params)
232
- @hawk_alarm = ""
233
- hawk_schema.each do |hawk_key,hawk_definition|
234
- hawk_root = hawk_definition[0]
235
- # hawk_scope = hawk_definition[1]
236
-
237
- unless modified_params[hawk_key.to_s].blank?
238
- begin
239
- eval("hawk_root").find(modified_params[hawk_key.to_s])
240
- rescue ActiveRecord::RecordNotFound => e
241
- @hawk_alarm << "You aren't allowed to set #{hawk_key.to_s} to #{modified_params[hawk_key.to_s]}. "
242
- modified_params.tap { |hs| hs.delete(hawk_key.to_s) }
170
+ @hawk_alarm = +""
171
+ hawk_schema.each do |hawk_key, hawk_definition|
172
+ if hawk_definition[0].to_s.start_with?("[") # the hawk is polymorphic
173
+ # hawk_definition[0] is like "[account.companies,account.vc_firms]"
174
+ scopes = hawk_definition[0].to_s.gsub(/^\[|\]$/, "").split(",").map(&:strip)
175
+
176
+ unless modified_params[hawk_key.to_s].blank?
177
+ passed = scopes.any? do |scope_str|
178
+ relation = scope_str.split(".").inject(self) { |obj, method_name| obj.send(method_name) }
179
+ relation.where(id: modified_params[hawk_key.to_s]).exists?
180
+ end
181
+
182
+ unless passed
183
+ @hawk_alarm << "You aren't allowed to set #{hawk_key.to_s} to #{modified_params[hawk_key.to_s]}. "
184
+ modified_params.tap { |hs| hs.delete(hawk_key.to_s) }
185
+ end
186
+ end
187
+ else
188
+ hawk_root = hawk_definition[0]
189
+ unless modified_params[hawk_key.to_s].blank?
190
+ begin
191
+
192
+ hawk_definition.where(modified_params[hawk_key.to_s])
193
+ rescue ActiveRecord::RecordNotFound => e
194
+ @hawk_alarm << "You aren't allowed to set #{hawk_key.to_s} to #{modified_params[hawk_key.to_s]}. "
195
+ modified_params.tap { |hs| hs.delete(hawk_key.to_s) }
196
+ end
243
197
  end
244
198
  end
245
199
  end
@@ -54,24 +54,6 @@ class FieldFactory
54
54
  end
55
55
 
56
56
  @field = field_class.new(scaffold: generator, name: name)
57
- # layout_strategy: generator.layout_strategy,
58
- # form_placeholder_labels: generator.form_placeholder_labels,
59
- # form_labels_position: generator.form_labels_position,
60
- # ownership_field: generator.ownership_field,
61
- # hawk_keys: generator.hawk_keys,
62
- # auth: generator.auth,
63
- # class_name: generator.singular_class,
64
- # alt_lookup: generator.alt_lookups[name] || nil,
65
- # singular: generator.singular,
66
- # self_auth: generator.self_auth,
67
- # update_show_only: generator.update_show_only,
68
- # attachment_data: generator.attachments[name.to_sym],
69
- # sample_file_path: generator.sample_file_path,
70
- # modify_as: generator.modify_as[name.to_sym] || nil,
71
- # plural: generator.plural,
72
- # display_as: generator.display_as[name.to_sym] || nil,
73
- # default_boolean_display: generator.default_boolean_display,
74
- # namespace: generator.namespace_value,
75
- # pundit: generator.pundit )
57
+
76
58
  end
77
59
  end
@@ -3,7 +3,7 @@ require_relative './field.rb'
3
3
 
4
4
  class AssociationField < Field
5
5
 
6
- attr_accessor :assoc_name, :assoc_class, :assoc, :alt_lookup
6
+ attr_accessor :assoc_name, :assoc_class, :assoc, :alt_lookup, :polymorphic_parents
7
7
 
8
8
  def initialize(scaffold: , name: )
9
9
  super
@@ -108,7 +108,7 @@ class AssociationField < Field
108
108
  if @modify_as[:nested].any?
109
109
  search_url << "(" + modify_as[:nested].collect{|x| "#{x}"}.join(",") + ")"
110
110
  end
111
-
111
+ @polymorphic_parents = []
112
112
  "<div class='typeahead typeahead--#{assoc.name}_id'
113
113
  data-controller='typeahead'
114
114
  data-typeahead-url-value='<%= #{search_url} %>'
@@ -118,8 +118,11 @@ class AssociationField < Field
118
118
  autofocus: true,
119
119
  autocomplete: 'off',
120
120
  value: #{singular}.try(:#{assoc.name}).try(:name) %>
121
- <%= f.hidden_field :#{assoc.name}_id, value: #{singular}.try(:#{assoc.name}).try(:id), 'data-typeahead-target': 'hiddenFormValue' %>
122
- <div data-typeahead-target='results'></div>
121
+ <%= f.hidden_field :#{assoc.name}_id, value: #{singular}.try(:#{assoc.name}).try(:id), 'data-typeahead-target': 'hiddenFormValue' %>" +
122
+ ( @polymorphic_parents.include?( (assoc.name.to_s + "_id")) ?
123
+ "\n <%= f.hidden_field :#{assoc.name}_type, value: #{singular}.try(:#{assoc.name}_type), 'data-typeahead-target': 'hiddenFormType' %>"
124
+ : "") +
125
+ "\n <div data-typeahead-target='results'></div>
123
126
  <div data-typeahead-target='classIdentifier' data-id=\"typeahead--#{assoc_name}_id\"></div>
124
127
  </div>"
125
128
  else
@@ -6,7 +6,8 @@ class Field
6
6
  :self_auth,
7
7
  :singular_class, :singular, :sql_type, :ownership_field,
8
8
  :update_show_only, :namespace, :pundit, :plural,
9
- :stimmify, :hidden_create, :hidden_update, :attachment_data, :god
9
+ :stimmify, :hidden_create, :hidden_update, :attachment_data, :god,
10
+ :polymorphic_parents
10
11
 
11
12
 
12
13
  def initialize(
@@ -37,7 +38,7 @@ class Field
37
38
  @hidden_update = scaffold.hidden_update
38
39
  @attachment_data = scaffold.attachments[name.to_sym]
39
40
  @god = scaffold.god
40
-
41
+ @polymorphic_parents = scaffold.polymorphic_parents
41
42
 
42
43
 
43
44
  # TODO: remove knowledge of subclasses from Field
@@ -12,7 +12,8 @@ module HotGlue
12
12
  :search, :search_fields, :search_query_fields, :search_position,
13
13
  :form_path, :layout_object, :search_clear_button, :search_autosearch,
14
14
  :stimmify, :stimmify_camel, :hidden_create, :hidden_update, :invisible_create,
15
- :invisible_update, :plural, :phantom_search, :pagination_style
15
+ :invisible_update, :plural, :phantom_search, :pagination_style,
16
+ :namespace, :controller_build_folder
16
17
 
17
18
 
18
19
  def initialize(singular:, singular_class: ,
@@ -26,7 +27,7 @@ module HotGlue
26
27
  search_clear_button:, search_autosearch:, layout_object:,
27
28
  form_path: , stimmify: , stimmify_camel:, hidden_create:, hidden_update: ,
28
29
  invisible_create:, invisible_update: , plural: , phantom_search:,
29
- pagination_style: )
30
+ pagination_style:, namespace: nil, controller_build_folder: nil )
30
31
 
31
32
 
32
33
  @form_path = form_path
@@ -68,6 +69,12 @@ module HotGlue
68
69
  @related_sets = related_sets
69
70
  @phantom_search = phantom_search
70
71
  @pagination_style = pagination_style
72
+ @namespace = namespace
73
+ @controller_build_folder = controller_build_folder
74
+ end
75
+
76
+ def pickup_partial_path(partial_name)
77
+ "#{@namespace + "/" if @namespace}#{@controller_build_folder}/#{partial_name}"
71
78
  end
72
79
 
73
80
  def add_spaces_each_line(text, num_spaces)
@@ -204,7 +211,7 @@ module HotGlue
204
211
  col = full_col.to_s.gsub("=", "").gsub("-", "").to_sym
205
212
 
206
213
  if col.to_s.starts_with?("**") && layout_object[:columns][:fields][col][:form]
207
- the_output = "<%= render partial: '#{col.to_s.gsub!("**","")}', locals: {#{singular}: #{singular} } %>"
214
+ the_output = "<%= render partial: '#{pickup_partial_path(col.to_s.gsub!("**",""))}', locals: {#{singular}: #{singular} } %>"
208
215
  elsif ! layout_object[:columns][:fields][col][:form]
209
216
  # omit from show action
210
217
  else
@@ -285,12 +292,18 @@ module HotGlue
285
292
  "<% if #{plural}.respond_to?(:total_pages) %><%= paginate(#{plural}) %> <% end %>"
286
293
  elsif @pagination_style == "will_paginate"
287
294
  "<% if #{plural}.respond_to?(:total_pages) %><%= will_paginate(#{plural}) %> <% end %>"
288
- elsif @pagination_style == "pagy"
295
+ elsif @pagination_style == "pagy9"
289
296
  if !@layout_strategy == "bootstrap"
290
297
  "<%== pagy_nav(@pagy, anchor_string: 'data-turbo-action=\"advance\"') %>"
291
298
  else
292
299
  "<%== pagy_bootstrap_nav(@pagy, anchor_string: 'data-turbo-action=\"advance\"') %>"
293
300
  end
301
+ elsif @pagination_style == "pagy43"
302
+ if @layout_strategy.is_a?(LayoutStrategy::Bootstrap)
303
+ "<%== @pagy.series_nav(:bootstrap) if @pagy && @pagy.pages > 1 %>"
304
+ else
305
+ "<%== @pagy.series_nav if @pagy && @pagy.pages > 1 %>"
306
+ end
294
307
  end
295
308
  end
296
309
 
@@ -321,7 +334,7 @@ module HotGlue
321
334
  raise "column #{col} not found on the layout data"
322
335
  end
323
336
  if col.starts_with?("**") && layout_object[:columns][:fields][col][:show]
324
- the_output = "<%= render partial: '#{col.to_s.gsub!("**","")}', locals: {#{singular}: #{singular} } %>"
337
+ the_output = "<%= render partial: '#{pickup_partial_path(col.to_s.gsub!("**",""))}', locals: {#{singular}: #{singular} } %>"
325
338
  elsif ! layout_object[:columns][:fields][col][:show]
326
339
  the_output = ""
327
340
  elsif eval("#{singular_class}.columns_hash['#{col}']").nil? && !attachments.keys.include?(col) && !related_sets.include?(col)
@@ -34,7 +34,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
34
34
  :search_clear_button, :search_autosearch, :include_object_names,
35
35
  :stimmify, :stimmify_camel, :hidden_create, :hidden_update,
36
36
  :invisible_create, :invisible_update, :phantom_create_params,
37
- :phantom_update_params, :lazy, :back_link_to_parent
37
+ :phantom_update_params, :lazy, :back_link_to_parent, :polymorphic_parents
38
+
38
39
  # important: using an attr_accessor called :namespace indirectly causes a conflict with Rails class_name method
39
40
  # so we use namespace_value instead
40
41
 
@@ -121,6 +122,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
121
122
  class_option :phantom_update_params, type: :string, default: nil
122
123
  class_option :controller_prefix, type: :string, default: nil
123
124
  class_option :code_in_controller, type: :string, default: nil
125
+ class_option :polymorphic_parent, type: :string, default: nil
124
126
 
125
127
  # SEARCH OPTIONS
126
128
  class_option :search, default: nil # set or predicate
@@ -177,7 +179,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
177
179
  if Gem::Specification.find_all_by_name('pagy').first.version.to_s.split(".").first.to_i <= 9
178
180
  @pagination_style = 'pagy9'
179
181
  else
180
- raise "Pagy version 43 not yet compatible"
182
+ # warn "Pagy version 43 not yet compatible"
181
183
  @pagination_style = 'pagy43'
182
184
  end
183
185
  elsif Gem::Specification.find_all_by_name('will_paginate').any?
@@ -260,6 +262,23 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
260
262
 
261
263
  setup_attachments
262
264
 
265
+ # polymorphic parents
266
+ # input = options["polymorphic_parent"]
267
+ # "parent_id[company|vc_firm|press_outlet],thing_id[apple|banana]"
268
+
269
+ if @nested && @nested.split("/").last.include?("(")
270
+ @polymorphic_parents = [@nested.split("/").last[/\(([^)]+)\)/, 1] + "_id"]
271
+
272
+ else
273
+ @polymorphic_parents = []
274
+ # do we need to be able to set these via a config?
275
+ # the use case I've implemented only supports a polymorphic parent in
276
+ # how you build the nest structure (last nested parent)
277
+ # what if there are two or more fields which are polymorphic on the object
278
+ end
279
+
280
+ puts "polymhic_parents: #{@polymorphic_parents}"
281
+
263
282
  @exclude_fields = []
264
283
  @exclude_fields += options['exclude'].split(",").collect(&:to_sym)
265
284
 
@@ -283,6 +302,9 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
283
302
  end
284
303
  end
285
304
  }.flatten.compact.collect(&:to_sym)
305
+ @include_fields += @polymorphic_parents.collect{ |x|
306
+ [x.to_sym, x.to_s.gsub("_id","_type").to_sym]
307
+ }.flatten
286
308
  puts "INCLUDED FIELDS: #{@include_fields}"
287
309
  end
288
310
 
@@ -904,7 +926,9 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
904
926
  invisible_create: @invisible_create,
905
927
  invisible_update: @invisible_update,
906
928
  phantom_search: @phantom_search,
907
- pagination_style: @pagination_style
929
+ pagination_style: @pagination_style,
930
+ namespace: @namespace,
931
+ controller_build_folder: @controller_build_folder
908
932
  )
909
933
  elsif @markup == "slim"
910
934
  raise(HotGlue::Error, "SLIM IS NOT IMPLEMENTED")
@@ -932,6 +956,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
932
956
 
933
957
  if options["hawk"]
934
958
  options['hawk'].split(",").each do |hawk_entry|
959
+
935
960
  # format is: abc_id[thing]
936
961
  if hawk_entry.include?("{")
937
962
  hawk_entry =~ /(.*){(.*)}/
@@ -941,20 +966,43 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
941
966
  hawk_to = @auth
942
967
  end
943
968
 
944
- hawk_scope = key.gsub("_id", "").pluralize
945
969
 
946
- if eval(singular_class + ".reflect_on_association(:#{key.gsub('_id', '')})").nil?
947
- raise "Could not find `#{key.gsub('_id', '')}` association; add this to the #{singular_class} class: \nbelongs_to :#{key.gsub('_id', '')} "
970
+ hawk_scope = key.gsub("_id", "").pluralize
971
+ reflection = eval(singular_class + ".reflect_on_association(:#{key.gsub('_id', '')})")
972
+ raise "Could not find `#{key.gsub('_id', '')}` association; add this to the #{singular_class} class: \nbelongs_to :#{key.gsub('_id', '')} " if reflection.nil?
973
+
974
+ optional = reflection.options[:optional]
975
+
976
+ # if hawk_to.include?(" ")
977
+ # @hawk_keys[key.to_sym] = { bind_to: [hawk_to.gsub(" ", ",")],
978
+ # polymorphic: true,
979
+ # optional: optional }
980
+
981
+ # # hawk_to.start_with?("[")
982
+ # # # Polymorphic hawk: space-separated scopes inside brackets
983
+ # # # e.g. [account.companies account.vc_firms]
984
+ # # raise "#{key} is not a polymorphic association; add `polymorphic: true` to belongs_to :#{key.gsub('_id', '')} in #{singular_class}" unless reflection.options[:polymorphic]
985
+ # # scopes = hawk_to.gsub(/^\[|\]$/, "").split(" ")
986
+ # # @hawk_keys[key.to_sym] = { bind_to: scopes, polymorphic: true, optional: optional }
987
+ # else
988
+ #
989
+ if hawk_to.include?(" ")
990
+ hawk_to.gsub!(" ", ",")
991
+ polymorphic = true
992
+ else
993
+ polymorphic = false
948
994
  end
949
995
 
950
- optional = eval(singular_class + ".reflect_on_association(:#{key.gsub('_id', '')})").options[:optional]
951
-
952
- @hawk_keys[key.to_sym] = { bind_to: [hawk_to], optional: optional }
996
+ @hawk_keys[key.to_sym] = { bind_to: [hawk_to],
997
+ optional: optional ,
998
+ polymorphic: polymorphic}
999
+
1000
+
953
1001
  use_shorthand = !options["hawk"].include?("{")
954
-
955
1002
  if use_shorthand # only include the hawk scope if using the shorthand
956
1003
  @hawk_keys[key.to_sym][:bind_to] << hawk_scope
957
1004
  end
1005
+ # end
958
1006
 
959
1007
  end
960
1008
 
@@ -1162,7 +1210,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1162
1210
 
1163
1211
  def creation_syntax
1164
1212
  if @factory_creation.nil? && ! @alt_lookups.any?
1165
- (@hawk_keys.any? ? "modified_params = hawk_params({#{ hawk_to_ruby(in_controller: true) }}, modified_params)\n " : "") + "@#{singular } = #{ class_name }.new(modified_params)"
1213
+
1214
+ res = (@hawk_keys.any? ? "modified_params = hawk_params({#{ hawk_to_ruby(in_controller: true) }}, modified_params)\n " : "") + "@#{singular } = #{ class_name }.new(modified_params)"
1166
1215
  elsif @factory_creation.nil? && @alt_lookups.any?
1167
1216
 
1168
1217
  prelookup_syntax = @alt_lookups.collect{|lookup, data|
@@ -1181,13 +1230,13 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1181
1230
  #{@factory_creation}
1182
1231
  "
1183
1232
  res << "\n " + "@#{singular} = factory.#{singular}" unless res.include?("@#{singular} = factory.#{singular}")
1184
- res << "\n rescue ActiveRecord::RecordInvalid
1185
- @#{singular} = factory.#{singular}
1186
- @action = 'new'
1187
- end"
1188
- res
1233
+ res << "\n rescue ActiveRecord::RecordInvalid"
1234
+ res << "\n @#{singular} = factory.#{singular}"
1235
+ res << "\n @action = 'new'"
1189
1236
  end
1237
+ res
1190
1238
  end
1239
+
1191
1240
 
1192
1241
  def formats
1193
1242
  [format]
@@ -1498,13 +1547,14 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1498
1547
  end
1499
1548
 
1500
1549
  def object_scope
1501
- if @nested_set.any? && @nested_set.last[:parent_name]
1502
- last_parent = @nested_set.last[:parent_name]
1503
- foreign_key = eval("#{singular_class}.reflect_on_association(:#{last_parent})").foreign_key
1504
- possible_associations = eval(singular_class).reflect_on_association(@nested_set.last[:parent_name].to_sym)
1505
- .klass.reflect_on_all_associations(:has_many)
1506
- .to_a
1507
-
1550
+ if @nested_set.any? && @nested_set.last[:parent_name] && !@nested_set.last[:polymorph_as]
1551
+ if @nested_set.last[:polymorph_as]
1552
+ possible_associations = [@nested_set.last[:parent_name].pluralize]
1553
+ else
1554
+ possible_associations = eval(singular_class).reflect_on_association(( @nested_set.last[:parent_name]).to_sym)
1555
+ .klass.reflect_on_all_associations(:has_many)
1556
+ .to_a
1557
+ end
1508
1558
 
1509
1559
  association = possible_associations.find{|x|
1510
1560
  if x.source_reflection
@@ -1986,7 +2036,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1986
2036
  res << " @#{plural} = @#{plural}.page(params[:page])#{ ".per(per)" if @paginate_per_page_selector }"
1987
2037
  elsif @pagination_style == "will_paginate"
1988
2038
  res << " @#{plural} = @#{plural}.paginate(page: params[:page], #{ ", per_page: per" if @paginate_per_page_selector })"
1989
- elsif @pagination_style == "pagy"
2039
+ elsif @pagination_style == "pagy9" || @pagination_style == "pagy43"
1990
2040
  res << " @pagy, @#{plural} = pagy(@#{plural})"
1991
2041
  end
1992
2042
  res
@@ -2006,19 +2056,10 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
2006
2056
 
2007
2057
  def hawk_to_ruby(in_controller: false)
2008
2058
  # false for views; true for controller
2009
-
2010
2059
  res = @hawk_keys.collect { |k, v|
2011
- bind_to_array = v[:bind_to].dup
2012
- bind_to = bind_to_array.collect{|bt|
2013
- if in_controller
2014
- bt.gsub(singular, "@#{singular}")
2015
- else
2016
- bt
2017
- end
2018
- }
2019
-
2020
- "#{k.to_s}: [#{bind_to.join(".")}]"
2060
+ "#{k}: #{v[:bind_to]}"
2021
2061
  }.compact.join(", ")
2062
+
2022
2063
  res
2023
2064
  end
2024
2065
 
@@ -29,14 +29,12 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
29
29
  def <%= @nested_set[0][:singular] %><% if @god
30
30
  next_object = nil
31
31
  collect_objects = @nested_set.reverse.collect {|x|
32
- assoc_name = x[:parent_name] || x[:singular]
33
- if eval("#{next_object || class_name}.reflect_on_association(:#{assoc_name})").nil?
34
- raise "***** Unable to find the association `#{assoc_name}` on the class #{next_object || class_name} ..... you probably want to add `belongs_to :#{assoc_name}` to the #{next_object || class_name} object?"
35
- end
32
+ assoc_name = x[:parent_name] || x[:singular]
33
+ if eval("#{next_object || class_name}.reflect_on_association(:#{assoc_name})").nil?
34
+ raise "***** Unable to find the association `#{assoc_name}` on the class #{next_object || class_name} ..... you probably want to add `belongs_to :#{assoc_name}` to the #{next_object || class_name} object?"
35
+ end
36
36
 
37
-
38
-
39
- next_object = eval("#{next_object || class_name}.reflect_on_association(:#{assoc_name})").class_name
37
+ next_object = eval("#{next_object || class_name}.reflect_on_association(:#{assoc_name})").class_name
40
38
  }
41
39
  root_object = collect_objects.last
42
40
  else
@@ -204,9 +202,8 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
204
202
  end
205
203
  <% end %>
206
204
 
207
- modified_params = modify_date_inputs_on_params(<% if @update_show_only %>update_<% end %><%= singular_name %>_params.dup<%= controller_update_params_tap_away_magic_buttons %>, <%= current_user_object %>, <%= datetime_fields_list %>)<% if @object_owner_sym && eval("#{class_name}.reflect_on_association(:#{@object_owner_sym})").class == ActiveRecord::Reflection::BelongsToReflection %>
208
- modified_params = modified_params.merge(<%= @object_owner_sym %>: <%= @object_owner_eval %>) <% elsif @object_owner_optional && any_nested? %>
209
- modified_params = modified_params.merge(<%= @object_owner_name %> ? {<%= @object_owner_sym %>: <%= @object_owner_eval %>} : {}) <% end %>
205
+ modified_params = modify_date_inputs_on_params(<% if @update_show_only %>update_<% end %><%= singular_name %>_params.dup<%= controller_update_params_tap_away_magic_buttons %>, <%= current_user_object %>, <%= datetime_fields_list %>)
206
+
210
207
  <% if @pundit %><% @related_sets.each do |key, related_set| %>
211
208
  check_<%= related_set[:association_ids_method].to_s %>_permissions(modified_params, :update)<% end %><% end %>
212
209
  <% if (@alt_lookups.any?) %>
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.7.5'
3
+ CURRENT = '0.7.7'
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.7.5
4
+ version: 0.7.7
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: 2026-01-11 00:00:00.000000000 Z
11
+ date: 2026-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails