hot-glue 0.7 → 0.7.2

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: c823d274b52531414bbf843d1f44ccd364400ab9c736405a74b1cc5793774b48
4
- data.tar.gz: 52d175d729d5b57c063da43bb7b7cb2940ea334be16e99637ade700ddaa2593a
3
+ metadata.gz: fede733fb9491fae14d59e1e00a446335a5bd613f600f4a12c141fd8cf497519
4
+ data.tar.gz: dab8840cd386193dfdf6b893edf02ccbeb5884f4b650c7abf6db8a06441b4e8d
5
5
  SHA512:
6
- metadata.gz: a1c8b8309b61f0ddbb1f27ca3a73bde4e8349f13fb114c6f3f5dcc15343dcc597fa4fbdd11476c1456d38c772cfe29b61d7ca4bee7d79ad421592227cf305d49
7
- data.tar.gz: 9b915be8972458f2fee1163e00c9f6c58a6ddf74ec829da2449989f3a5640597d16efb93c5ede4ea449bc786714997f511d3830a584029ff33222c6cef4d626e
6
+ metadata.gz: 10547fd449b51c3b1968f10a3f1ae12c8a20190924d9b4dc39e338193e7c0095030b5455aa34701a7a0fb16bf85cfde39b9978248ce0aba024d9374f2c66bf96
7
+ data.tar.gz: 9b088a35487336500561ce7510cf6b3df90e8542cf05b6176c882630bfac91b783ad4f6ab94a519d195bc0506af624843a1aabf26117eb23ca7222efe1d2a634
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.7)
4
+ hot-glue (0.7.1)
5
5
  ffaker (~> 2.16)
6
6
  rails (> 5.1)
7
7
 
data/README.md CHANGED
@@ -712,6 +712,49 @@ 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
+
716
+ ##### Using the object inside of the hawk
717
+ In the example above, we aren't using the name of the scaffold within the hawk.
718
+
719
+ However, if you are using the object's name in the hawk (for example `thing` for a `ThingsController`), the view will need this as a local variable `thing` and
720
+ the controller will need this as an instsance variable `@thing`
721
+
722
+ In this special case, Hot Glue converts the local variable `thing` used within your hawk code into instance variable `@thing`
723
+
724
+ For example, if we were building a Job scaffold, here we want restrict follow_up_target_id to a list
725
+ of eligible targets (in our case, with an email address that matches the company's website), we could use a view helper.
726
+
727
+ Notice that `targets_by_company` is defined in the helper below, and we pass it a relation from the current object which is `job`
728
+
729
+ `hot_glue:scaffold Job --hawk='follow_up_target_id{targets_by_company(job.company)}' --code-in-controller='include JobHelper;`
730
+ (notice that it is `job` not `@job`)
731
+
732
+ // app/helpers/job_helper.rb
733
+ ```
734
+ module JobHelper
735
+ def targets_by_company(company)
736
+ domain = company.website
737
+ Target.where("email LIKE ?", "%#{domain}%" )
738
+ end
739
+ end
740
+ ```
741
+
742
+ The generated controller code looks like:
743
+ ```
744
+ modified_params = hawk_params({follow_up_target_id: [targets_by_company(@job.company)]}, modified_params)
745
+ ```
746
+ (`hawk_param` is defined in Hot Glue itself. Notice that the `@` was appended to the front of `job`)
747
+
748
+ The edit form will look like this:
749
+ ```
750
+ <%= f.collection_select(:follow_up_target_id, targets_by_company(job.company), :id, :name, { prompt: true, selected: job.follow_up_target_id }) %>
751
+ ```
752
+ (In the _form view, the `job` is a local variable as we do not rely on the instance variables in subviews.)
753
+
754
+
755
+
756
+
757
+
715
758
  ### `--with-turbo-streams`
716
759
 
717
760
  If and only if you specify `--with-turbo-streams`, your views will contain `turbo_stream_from` directives. Whereas your views will always contain `turbo_frame_tags` (whether or not this flag is specified) and will use the Turbo stream replacement mechanism for non-idempotent actions (create & update). This flag just brings the magic of live-reload to the scaffold interfaces themselves.
@@ -1717,6 +1760,7 @@ This is a good place to set your created_by user id, like so
1717
1760
 
1718
1761
 
1719
1762
 
1763
+
1720
1764
  ```
1721
1765
  def new
1722
1766
  @email_template = EmailTemplate.new(crusade: crusade)
@@ -1748,6 +1792,16 @@ def create
1748
1792
  ```
1749
1793
  TODO: build a solution for inserting code only in the `new` action but NOT the create action
1750
1794
 
1795
+ #### `--code-in-controller=''` (escape newlines with `;` )
1796
+
1797
+ This code will be inserted directly into your controller (as a class-level definition)
1798
+
1799
+
1800
+ ```
1801
+ --code-in-controller="before_action -> { @thing = 'abc' }"
1802
+ ```
1803
+ A one-liner hook works well for these. if you define an entire method, be sure to use `;` to separate line breaks
1804
+
1751
1805
 
1752
1806
 
1753
1807
  ## Searching
@@ -2409,6 +2463,24 @@ These automatic pickups for partials are detected at build time. This means that
2409
2463
 
2410
2464
  # VERSION HISTORY
2411
2465
 
2466
+
2467
+ #### 2025-12-12
2468
+ - Using the object (of the scaffold being built) inside of the hawk now adds `@` to a variable named as the singular name of the scaffold;
2469
+ see "Using the object inside of the hawk"
2470
+ - error catching for missing parent relationships
2471
+ - fixes path for magic button when the controller has a prefix
2472
+ - fix nav active target for a controller with a prefix (the nav template uses the snake_case of the full controller name including prefix)
2473
+
2474
+
2475
+ #### 2025-11-12 - v0.7.1
2476
+ - in set searches, automatically sets the match field if the search text is input, removes match field (back to default) when search text is removed;
2477
+ -
2478
+ - also removes search text if you change the match selector back to empty
2479
+ -
2480
+ - removes vestiges of `.merge` from old implementation (#236)
2481
+
2482
+ - `--code-in-controller` option for inserting controller code directly into your controller
2483
+
2412
2484
  #### 2025-11-05 - v0.7
2413
2485
 
2414
2486
  Hot Glue already has a robust set of tools to provide field-by-field access control, hiding or turning visible-only fields by multiple methods, described under Access Control & Field Visibility Features.
@@ -49,8 +49,8 @@ class StringField < Field
49
49
  end
50
50
 
51
51
  def search_field_output
52
- "<%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['contains', 'contains'], ['is exactly', 'is_exactly'], ['starts with', 'starts_with'], ['ends with', 'ends_with']], @q[\'0\']['#{name}_match'] ), {} , { class: 'form-control match' } %>"+
53
- "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text' %>"
52
+ "<%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['contains', 'contains'], ['is exactly', 'is_exactly'], ['starts with', 'starts_with'], ['ends with', 'ends_with']], @q[\'0\']['#{name}_match'] ), {} , { class: 'form-control match' , 'data-search-form-target': \"textMatch\"} %>"+
53
+ "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text' , 'data-search-form-target': \"textSearch\" %>"
54
54
  end
55
55
 
56
56
 
@@ -31,8 +31,8 @@ class TextField < Field
31
31
 
32
32
 
33
33
  def search_field_output
34
- "<%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['contains', 'contains'], ['is exactly', 'is_exactly'], ['starts with', 'starts_with'], ['ends with', 'ends_with']], @q[\'0\']['#{name}_match'] ), {} , { class: 'form-control match' } %>"+
35
- "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text' %>"
34
+ "<%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['contains', 'contains'], ['is exactly', 'is_exactly'], ['starts with', 'starts_with'], ['ends with', 'ends_with']], @q[\'0\']['#{name}_match'] ), {} , { class: 'form-control match', 'data-search-form-target': \"textMatch\" } %>"+
35
+ "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text', 'data-search-form-target': \"textSearch\" %>"
36
36
  end
37
37
 
38
38
 
@@ -45,7 +45,7 @@ class TextField < Field
45
45
  end
46
46
 
47
47
  def code_to_reset_match_if_search_is_blank
48
- " @q['0'][:#{name}_match] = '' if @q['0'][:#{name}_search] == ''"
48
+ # " @q['0'][:#{name}_match] = '' if @q['0'][:#{name}_search] == ''"
49
49
  end
50
50
 
51
51
  end
@@ -162,8 +162,13 @@ module HotGlue
162
162
 
163
163
  layout_object[:columns][:container][i] = column.split(",").collect{|x|
164
164
  x.gsub("-","").gsub("=","")
165
- }.collect(&:to_sym)
165
+ if x.include?("(")
166
+ x =~ /(.*)\((.*)\)/
167
+ x = $1
166
168
 
169
+ end
170
+ x
171
+ }.collect(&:to_sym)
167
172
 
168
173
 
169
174
  layout_object[:columns][:bootstrap_column_width][i] = fixed_widths[i] || target_col_size
@@ -199,7 +199,10 @@ module HotGlue
199
199
  size = layout_object[:columns][:bootstrap_column_width][columns.index(column)]
200
200
 
201
201
  " <div class='#{layout_strategy.column_classes_for_form_fields(size)} cell--#{singular}--#{column.join("-")}' >" +
202
- column.map { |col|
202
+ column.map { |full_col|
203
+
204
+ col = full_col.to_s.gsub("=", "").gsub("-", "").to_sym
205
+
203
206
  if col.to_s.starts_with?("**") && layout_object[:columns][:fields][col][:form]
204
207
  the_output = "<%= render partial: '#{col.to_s.gsub!("**","")}', locals: {#{singular}: #{singular} } %>"
205
208
  elsif ! layout_object[:columns][:fields][col][:form]
@@ -310,7 +313,13 @@ module HotGlue
310
313
  result = columns.map.with_index{ |column,i|
311
314
  size = layout_object[:columns][:bootstrap_column_width][i]
312
315
  "<div class='hg-col #{layout_strategy.column_classes_for_line_fields(size)} #{singular}--#{column.join("-")}'#{style_with_flex_basis}> " +
313
- column.map { |col|
316
+
317
+ column.map { |full_col|
318
+ col = full_col.to_s.gsub("=", "").gsub("-","").to_sym
319
+
320
+ if layout_object[:columns][:fields][col].nil?
321
+ raise "column #{col} not found on the layout data"
322
+ end
314
323
  if col.starts_with?("**") && layout_object[:columns][:fields][col][:show]
315
324
  the_output = "<%= render partial: '#{col.to_s.gsub!("**","")}', locals: {#{singular}: #{singular} } %>"
316
325
  elsif ! layout_object[:columns][:fields][col][:show]
@@ -117,6 +117,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
117
117
  class_option :phantom_create_params, type: :string, default: nil
118
118
  class_option :phantom_update_params, type: :string, default: nil
119
119
  class_option :controller_prefix, type: :string, default: nil
120
+ class_option :code_in_controller, type: :string, default: nil
120
121
 
121
122
  # SEARCH OPTIONS
122
123
  class_option :search, default: nil # set or predicate
@@ -439,6 +440,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
439
440
  @phantom_create_params = options['phantom_create_params'] || ""
440
441
  @phantom_update_params = options['phantom_update_params'] || ""
441
442
 
443
+
442
444
  if get_default_from_config(key: :pundit_default)
443
445
  raise "please note the config setting `pundit_default` has been renamed `pundit`. please update your hot_glue.yml file"
444
446
  end
@@ -645,6 +647,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
645
647
  @code_before_update = options['code_before_update']
646
648
  @code_after_update = options['code_after_update']
647
649
  @code_after_new = options['code_after_new']
650
+ @code_in_controller = options['code_in_controller'] || ""
648
651
 
649
652
  buttons_width = ((!@no_edit && 1) || 0) + ((!@no_delete && 1) || 0) + (@magic_buttons.any? ? 1 : 0)
650
653
 
@@ -687,6 +690,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
687
690
  # build a new polymorphic object
688
691
  @associations = []
689
692
  @columns_map = {}
693
+
690
694
  @columns.each do |col|
691
695
  # if !(@the_object.columns_hash.keys.include?(col.to_s) || @attachments.keys.include?(col))
692
696
  # raise "couldn't find #{col} in either field list or attachments list"
@@ -750,51 +754,56 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
750
754
 
751
755
  if options['phantom_search']
752
756
  ps_input = options['phantom_search']
757
+ @phantom_search = {}
753
758
 
754
- ps_input =~ /(.*)\[(.*)\]/
755
- type_and_label, settings = $1, $2
756
759
 
760
+ ps_input.split(",").each do |input_setting|
761
+ input_setting =~ /(.*)\[(.*)\]/
762
+ type_and_label, settings = $1, $2
757
763
 
758
- type = type_and_label.split("_")[0]
759
- label = type_and_label.split("_")[1]
760
764
 
761
- @phantom_search = {}
762
- choices = settings.split("|")
765
+ type = type_and_label.split("_")[0]
766
+ label = type_and_label.split("_")[1]
763
767
 
768
+ choices = settings.split("|")
764
769
 
765
- @phantom_search[label.to_sym] = {
766
- type: type,
767
- name: label.humanize,
768
- choices: []
769
- }
770
770
 
771
- choices.each do |choice|
772
- if type == "radio"
773
- choice_label = choice.split(":")[0]
774
- choice_scope = choice.split(":")[1]
775
- elsif type == "checkboxes"
776
- choice_label = choice.split(":")[0]
777
- choice_scope_negative = choice.split(":")[1]
778
- choice_scope = choice.split(":")[2]
779
- end
771
+ @phantom_search[label.to_sym] = {
772
+ type: type,
773
+ name: label.humanize,
774
+ choices: []
775
+ }
780
776
 
781
- if choice_scope.nil? || choice_scope.strip.empty?
782
- choice_scope = "all"
783
- end
777
+ choices.each do |choice|
778
+ if type == "radio"
779
+ choice_label = choice.split(":")[0]
780
+ choice_scope = choice.split(":")[1]
781
+ elsif type == "checkboxes"
782
+ choice_label = choice.split(":")[0]
783
+ choice_scope_negative = choice.split(":")[1]
784
+ choice_scope = choice.split(":")[2]
785
+ end
784
786
 
785
- if choice_scope_negative.nil? || choice_scope_negative.strip.empty?
786
- choice_scope_negative = "all"
787
- end
787
+ if choice_scope.nil? || choice_scope.strip.empty?
788
+ choice_scope = "all"
789
+ end
788
790
 
789
- choice_scope = ".#{choice_scope}" if !choice_scope.start_with?(".")
790
- choice_scope_negative = ".#{choice_scope_negative}" if !choice_scope_negative.start_with?(".")
791
+ if choice_scope_negative.nil? || choice_scope_negative.strip.empty?
792
+ choice_scope_negative = "all"
793
+ end
791
794
 
792
- @phantom_search[label.to_sym][:choices] << {
793
- label: choice_label,
794
- scope: choice_scope,
795
- scope_negative: choice_scope_negative,
796
- }
795
+ choice_scope = ".#{choice_scope}" if !choice_scope.start_with?(".")
796
+ choice_scope_negative = ".#{choice_scope_negative}" if !choice_scope_negative.start_with?(".")
797
+
798
+ @phantom_search[label.to_sym][:choices] << {
799
+ label: choice_label,
800
+ scope: choice_scope,
801
+ scope_negative: choice_scope_negative,
802
+ }
803
+ end
797
804
  end
805
+
806
+
798
807
  puts "phantom search #{@phantom_search}"
799
808
  else
800
809
  @phantom_search = {}
@@ -902,6 +911,11 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
902
911
 
903
912
  end
904
913
 
914
+ def controller_prefix_snake
915
+ @controller_prefix&.underscore
916
+ end
917
+
918
+
905
919
  def setup_hawk_keys
906
920
  @hawk_keys = {}
907
921
 
@@ -1077,6 +1091,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1077
1091
  @columns = @the_object.columns.map(&:name).map(&:to_sym).reject { |field| !@include_fields.include?(field) }
1078
1092
  end
1079
1093
 
1094
+
1080
1095
  @columns = @columns - @nested_set.collect { |set| (set[:singular] + "_id").to_sym }
1081
1096
 
1082
1097
  if @attachments.any?
@@ -1381,7 +1396,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1381
1396
  def form_path_edit_helper
1382
1397
  HotGlue.optionalized_ternary(namespace: @namespace,
1383
1398
  target: @singular,
1384
- prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1399
+ prefix: (@controller_prefix ? controller_prefix_snake + "_" : ""),
1385
1400
  nested_set: @nested_set,
1386
1401
  with_params: false,
1387
1402
  put_form: true,
@@ -1389,17 +1404,18 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1389
1404
  end
1390
1405
 
1391
1406
  def delete_path_helper
1392
- HotGlue.optionalized_ternary(namespace: @namespace,
1393
- prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1407
+ res = HotGlue.optionalized_ternary(namespace: @namespace,
1408
+ prefix: (@controller_prefix ? controller_prefix_snake + "_" : ""),
1394
1409
  target: @singular,
1395
1410
  nested_set: @nested_set,
1396
1411
  with_params: false,
1397
1412
  put_form: true)
1413
+ res
1398
1414
  end
1399
1415
 
1400
1416
  def edit_path_helper
1401
1417
  HotGlue.optionalized_ternary(namespace: @namespace,
1402
- prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1418
+ prefix: (@controller_prefix ? controller_prefix_snake + "_" : ""),
1403
1419
  target: @singular,
1404
1420
  nested_set: @nested_set,
1405
1421
  modifier: "edit_",
@@ -1410,7 +1426,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1410
1426
  def new_path_name
1411
1427
  HotGlue.optionalized_ternary(namespace: @namespace,
1412
1428
  target: singular,
1413
- prefix: (@controller_prefix ? @controller_prefix.downcase + "_" : ""),
1429
+ prefix: (@controller_prefix ? controller_prefix_snake + "_" : ""),
1414
1430
  nested_set: @nested_set,
1415
1431
  modifier: "new_",
1416
1432
  with_params: false)
@@ -1471,14 +1487,23 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1471
1487
  if @nested_set.any? && @nested_set.last[:parent_name]
1472
1488
  last_parent = @nested_set.last[:parent_name]
1473
1489
  foreign_key = eval("#{singular_class}.reflect_on_association(:#{last_parent})").foreign_key
1474
- association = eval(singular_class).reflect_on_association(@nested_set.last[:parent_name].to_sym)
1490
+ possible_associations = eval(singular_class).reflect_on_association(@nested_set.last[:parent_name].to_sym)
1475
1491
  .klass.reflect_on_all_associations(:has_many)
1476
- .to_a.find{|x|
1492
+ .to_a
1477
1493
 
1494
+
1495
+ association = possible_associations.find{|x|
1478
1496
  if x.source_reflection
1479
1497
  x.table_name == plural
1480
1498
  end
1481
- }.plural_name
1499
+ }
1500
+
1501
+ if !association
1502
+ klass = eval(singular_class).reflect_on_association(@nested_set.last[:parent_name].to_sym)
1503
+ .klass.to_s
1504
+ raise "Could not find relation #{plural} on #{klass}; maybe add `has_many :#{plural}` "
1505
+ end
1506
+ association = association.plural_name
1482
1507
 
1483
1508
  else
1484
1509
  association = plural
@@ -1547,16 +1572,19 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1547
1572
  end
1548
1573
 
1549
1574
  def magic_button_output
1575
+
1576
+
1550
1577
  @template_builder.magic_button_output(
1551
1578
  path: HotGlue.optionalized_ternary( namespace: @namespace,
1579
+ prefix: (@controller_prefix ? controller_prefix_snake + "_" : ""),
1552
1580
  target: @singular,
1553
1581
  nested_set: @nested_set,
1554
1582
  with_params: false,
1555
1583
  put_form: true),
1556
- big_edit: @big_edit,
1557
- singular: singular,
1558
- magic_buttons: @magic_buttons,
1559
- small_buttons: @small_buttons
1584
+ big_edit: @big_edit,
1585
+ singular: singular,
1586
+ magic_buttons: @magic_buttons,
1587
+ small_buttons: @small_buttons
1560
1588
  )
1561
1589
  end
1562
1590
 
@@ -1644,12 +1672,12 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1644
1672
  append_text = " <li class='nav-item'>
1645
1673
  <%= link_to '#{@list_label_heading.humanize}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>
1646
1674
  </li>"
1647
- alt_append_text = " <li class='nav-item'>
1648
- <%= link_to '#{@list_label_heading.humanize.upcase}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>
1649
- </li>"
1675
+ alt_append_text = "<%= link_to '#{@list_label_heading.humanize.upcase}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>"
1676
+
1677
+ check_for_existing_append = "<%= link_to '#{@list_label_heading.humanize}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>"
1650
1678
 
1651
1679
  text = File.read(nav_file)
1652
- if text.include?(append_text) || text.include?(alt_append_text)
1680
+ if text.include?(check_for_existing_append) || text.include?(alt_append_text)
1653
1681
  puts "SKIPPING: Nav link for #{singular_name} already exists in #{nav_file}"
1654
1682
  else
1655
1683
  puts "APPENDING: nav link for #{singular_name} #{nav_file}"
@@ -1962,9 +1990,18 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1962
1990
  [name, file_format].compact.join(".")
1963
1991
  end
1964
1992
 
1965
- def hawk_to_ruby
1993
+ def hawk_to_ruby(in_controller: false) # false for views; true for controller
1994
+
1995
+
1966
1996
  res = @hawk_keys.collect { |k, v|
1967
- "#{k.to_s}: [#{v[:bind_to].join(".")}]"
1997
+ bind_to_array = v[:bind_to]
1998
+
1999
+ bind_to = bind_to_array.collect{|bt|
2000
+ bt.gsub!(singular, "@#{singular}") if in_controller
2001
+ bt
2002
+ }
2003
+
2004
+ "#{k.to_s}: [#{bind_to.join(".")}]"
1968
2005
  }.join(", ")
1969
2006
  res
1970
2007
  end
@@ -8,6 +8,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
8
8
 
9
9
  helper :hot_glue
10
10
  include HotGlue::ControllerHelper
11
+ <%= @code_in_controller.gsub(";", "\n") %>
11
12
 
12
13
  <% unless @god %>before_action :<%= "authenticate_" + @auth_identifier.split(".")[0] + "!" %><% end %><% if any_nested? %>
13
14
  <% nest_chain = [] %> <% @nested_set.each { |arg|
@@ -29,9 +30,12 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
29
30
  next_object = nil
30
31
  collect_objects = @nested_set.reverse.collect {|x|
31
32
  assoc_name = x[:parent_name] || x[:singular]
32
- # if eval("#{next_object || class_name}.reflect_on_association(:#{assoc_name})").nil?
33
- # 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?"
34
- # end
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
+
37
+
38
+
35
39
  next_object = eval("#{next_object || class_name}.reflect_on_association(:#{assoc_name})").class_name
36
40
  }
37
41
  root_object = collect_objects.last
@@ -118,7 +122,8 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
118
122
 
119
123
  <%= controller_attachment_orig_filename_pickup_syntax %>
120
124
  <%= creation_syntax %>
121
- <%= @code_after_new ? @code_after_new.gsub(";","\n") + "\n" : "" %>
125
+ <%= @code_after_new ? @code_after_new.gsub(";","\n") + "\n" : "" %>
126
+ <% if @hawk_keys.any? %> modified_params = hawk_params({<%= hawk_to_ruby(in_controller: true) %>}, modified_params)<% end %>
122
127
 
123
128
  <% if @pundit %><% @related_sets.each do |key, related_set| %>
124
129
  check_<%= related_set[:association_ids_method].to_s %>_permissions(modified_params, :create)<% end %><% end %>
@@ -218,7 +223,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
218
223
  "#{field_name}: #{field_name}" unless @update_show_only.include?(lookup.to_sym)
219
224
  }.join(",") %>)
220
225
  <% end %>
221
- <% if @hawk_keys.any? %> modified_params = hawk_params({<%= hawk_to_ruby %>}, modified_params)<% end %>
226
+ <% if @hawk_keys.any? %> modified_params = hawk_params({<%= hawk_to_ruby(in_controller: true) %>}, modified_params)<% end %>
222
227
  <%= controller_attachment_orig_filename_pickup_syntax %>
223
228
  @<%= singular_name %>.assign_attributes(modified_params)
224
229
  <% if @pundit && !@pundit_policy_override %>
@@ -1,13 +1,17 @@
1
1
  <\%= turbo_frame_tag "<%= @namespace %>__#{dom_id(<%= singular %>)}" do %>
2
2
  <div class="cell editable" style="position: relative;">
3
3
  <\% if <%= singular %>.errors.any? %>
4
- <\%= render(partial: "<%= namespace_with_trailing_dash %>errors", locals: {resource: <%= singular %> }) %>
4
+ <\%= render(partial: "<%= namespace_with_trailing_dash %>errors", locals: {resource: <%= "#{singular}" %> }) %>
5
5
  <\% end %>
6
6
  <h2>Editing <%= singular + " " if @include_object_names %><\%= <%= singular %>.<%= display_class %> %></h2>
7
7
  <\%= form_with model: <%= singular %>,
8
8
  <% if @stimmify %> data: {controller: '<%= @stimmify %>' },
9
9
  <% end %>url: <%= form_path_edit_helper %><%= ", html: {'data-turbo': false}" if @big_edit %> do |f| %>
10
- <\%= 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 %> \%>
10
+ <\%= render partial: "<%= namespace_with_trailing_dash + @controller_build_folder + "/" %>form", locals: {
11
+ <%= singular %>: <%= singular %>,
12
+ f: f,
13
+ <%= @nested_set.collect{|arg| "#{arg[:singular]}: #{arg[:singular]}" }.join(",\n ") %>
14
+ } \%>
11
15
  <% 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 %>
12
16
  <\% end %>
13
17
  <% if @edit_after_form_partial %><\%= render partial: "edit_after_form", locals: {<%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %>
@@ -1,7 +1,10 @@
1
1
  <% if @turbo_streams %><\%= turbo_stream_from <%= singular %> %>
2
2
  <% end %><\%= turbo_frame_tag "<%= @namespace %>__#{ dom_id(<%= singular %>) }" do %>
3
3
  <div class='<%= @layout_strategy.row_classes %>' data-id='<\%= <%= singular %>.id %>' data-edit='false'>
4
- <\%= render partial: '<%= show_path_partial %>', locals: { <%= singular %>: <%= singular %><% if @nested_set.any? %>, <%= @nested_set.collect{|nest_arg| "#{nest_arg[:singular]}: #{ nest_arg[:singular] }"}.join(", ") %>, nested_for: "<%= @nested_set.collect{|nest_arg| nest_arg[:singular] + "-\#{#{nest_arg[:singular]}.id}"}.join("__") %>"<% end %> } %>
4
+ <\%= render partial: '<%= show_path_partial %>', locals: {
5
+ <%= singular %>: <%= singular %><% if @nested_set.any? %>,
6
+ <%= @nested_set.collect{|nest_arg| "#{nest_arg[:singular]}: #{ nest_arg[:singular] }"}.join(",\n ") %>,
7
+ nested_for: "<%= @nested_set.collect{|nest_arg| nest_arg[:singular] + "-\#{#{nest_arg[:singular]}.id}"}.join("__") %>"<% end %> } %>
5
8
  </div>
6
9
  <\% end %>
7
10
 
@@ -8,7 +8,14 @@
8
8
  <% end %>
9
9
 
10
10
  <% if @new_button_position == 'above' %>
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 %>
11
+ <% unless @no_create %>
12
+ <%= '<%= render partial: "' + ((@namespace+"/" if @namespace) || "") +
13
+ @controller_build_folder +
14
+ "/new_button\", locals: {
15
+ #{@nested_set.collect{|arg| arg[:singular] + ": " + arg[:singular]}.join(",\n ")} }" +
16
+ ' %\>'.gsub('\\',"") %>
17
+ <br />
18
+ <% end %>
12
19
  <% end %>
13
20
 
14
21
  <% unless @no_list %>
@@ -58,10 +65,8 @@
58
65
  </div>
59
66
  <\% end %>
60
67
  <\% <%= plural %>.each do |<%= singular %>| %>
61
- <\%= render partial: '<%= line_path_partial %>', locals: {<%= singular %>: <%= singular %>}
62
- .merge(defined?(nested_for) ? {nested_for: nested_for} : {})
63
- <%= @nested_set.collect{|arg| " .merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}} : {})"}.join("\n") %>
64
- %>
68
+ <\%= render partial: '<%= line_path_partial %>', locals: {<%= singular %>: <%= singular %>,
69
+ <% if @nested_set.any? %>nested_for: nested_for, <%= @nested_set.collect{|arg| "#{arg[:singular]}: #{arg[:singular]}" }.join(", ") %><% end %> } \%>
65
70
  <\% end %>
66
71
  <% if @paginate_per_page_selector %>
67
72
  <\%= form_with url: '<%= path_helper_plural(top_level: false) %>', method: :get do |f| %>
@@ -5,8 +5,11 @@
5
5
  <\%= form_with model: <%= singular %>,
6
6
  <% if @stimmify %>data: {controller: '<%= @stimmify %>' },
7
7
  <% end %>url: <%= form_path_new_helper %>, method: :post<%= @display_edit_after_create ? ", html: {'data-turbo': false}" : "" %> do |f| \%>
8
- <\%= render partial: "<%= namespace_with_slash + @controller_build_folder %>/form",
9
- locals: { <%= singular %>: <%= singular %>, f: f}<%= @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}}: {})" }.join %> \%>
8
+ <\%= render partial: "<%= namespace_with_slash + @controller_build_folder %>/form", locals: {
9
+ <%= singular %>: <%= singular %>,
10
+ f: f,
11
+ <%= @nested_set.collect{|arg| "#{arg[:singular]}: #{arg[:singular]}" }.join(",\n ") %>
12
+ } \%>
10
13
 
11
14
  <% if @new_within_form_partial %><\%= render partial: "new_within_form", locals: {f: f, <%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %>
12
15
  <\% end %>
@@ -1,6 +1,9 @@
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 %>,
4
+ <% if @nested_set.any? %> <%= @nested_set.collect{|arg| "#{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %>,
5
+ nested_for: "<%= @nested_set.collect{|arg| "#{arg[:singular]}-\#{@#{arg[:singular]}.id}"}.join("__") %>" <% end %>
6
+ } %>
4
7
 
5
8
  <\% end %>
6
9
  <\% end %>
@@ -9,9 +12,12 @@
9
12
  <!-- errors -->
10
13
  <\%= turbo_stream.replace "<%= @namespace %>__<%= singular %>-new" do %>
11
14
  <\% if @<%= singular %>.errors.none? %>
12
- <\%= render partial: "new_button", locals: {}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {" + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> %>
15
+ <\%= render partial: "new_button", locals: {
16
+ <%= @nested_set.collect{|arg| "#{arg[:singular]}: @#{arg[:singular] }"}.join(", \n") %>
17
+
18
+ } %>
13
19
  <\% else %>
14
- <\%= render partial: "new_form", locals: {<%= singular %>: @<%= singular %>}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {" + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> %>
20
+ <\%= render partial: "new_form", locals: {<%= singular %>: @<%= singular %>, <%= @nested_set.collect{|arg| arg[:singular] + ": @" + arg[:singular]}.join(",\n ") %> } %>
15
21
  <\% end %>
16
22
  <\% end %>
17
23
  <\%= turbo_stream.replace "flash_notices" do %>
@@ -1,9 +1,12 @@
1
1
  <%= turbo_parental_updates %>
2
2
 
3
3
  <\%= turbo_stream.update "<%= @namespace %>__<%= plural %>-list" + <%= nested_for_turbo_nested_constructor %> do %>
4
- <\%= 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
+ <\%= render partial: "list", locals: {<%=plural%>: @<%=plural %> ,
5
+ <% if @nested_set.any? %>nested_for: "<%= @nested_set.collect{|arg| arg[:singular] + "-\#{@#{arg[:singular]}.id}" }.join("__") %>",
6
+ <%= @nested_set.collect{|arg| arg[:singular] + ": @" + arg[:singular] }.join(", ") %><% end %>
7
+ } \%>
5
8
  <\% end %>
6
9
 
7
10
  <\%= turbo_stream.update "flash_notices" do %>
8
11
  <\%= render partial: "layouts/flash_notices", locals: {resource: @<%= singular %>} %>
9
- <\% end %>
12
+ <\% end %>
@@ -1,5 +1,8 @@
1
1
  <% if @big_edit %>
2
- <div class="container">
2
+ <% if include_nav_template %><%= @layout_strategy.page_begin %>
3
+ <\%= render partial: "<%= nav_template %>", locals: {nav: "<%= "#{@controller_prefix ? controller_prefix_snake + "_": ""}#{@plural}" %>"} %><%= @layout_strategy.page_end %><% end %>
4
+
5
+ <div class="container">
3
6
  <div class="row">
4
7
  <div class="col-md-12">
5
8
  <\%= link_to "<% if @button_icons == 'font-awesome' %><i class='fa fa-arrow-circle-left 2x'></i><% end %> Back to list".html_safe, <%= path_helper_plural(true) %> %>
@@ -10,8 +13,6 @@
10
13
  </div>
11
14
  </div>
12
15
 
13
-
14
-
15
16
  <% if @downnest_children.any? && @big_edit %>
16
17
  <div class="container" data-controller="bootstrap-tabbed-nav">
17
18
  <ul class="nav nav-tabs" id="<%= singular + "_downnest_portals" %>" role="tablist">
@@ -1,7 +1,8 @@
1
1
  <% if @menu_file_exists %><\%= render partial: "<%= namespace_with_trailing_dash %>menu", locals: {active: '<%= plural %>'} %><% end %>
2
2
 
3
3
  <div class="<%= @container_name %>">
4
- <% if include_nav_template %><%= @layout_strategy.page_begin %><\%= render partial: "<%= nav_template %>", locals: {nav: "<%= @plural %>"} %><%= @layout_strategy.page_end %><% end %>
4
+ <% if include_nav_template %><%= @layout_strategy.page_begin %>
5
+ <\%= render partial: "<%= nav_template %>", locals: {nav: "<%= "#{@controller_prefix ? controller_prefix_snake + "_": ""}#{@plural}" %>"} %><%= @layout_strategy.page_end %><% end %>
5
6
  <% if @index_before_list_partial %><\%= render partial: "index_before_list" %><% end %>
6
7
 
7
8
  <%= @layout_strategy.page_begin %>
@@ -1 +1 @@
1
- <\%= render partial: "new_form", locals: {<%= singular %>: @<%=singular%>}<%= @nested_set.collect{|arg| ".merge(@#{arg[:singular]} ? {#{arg[:singular]}: @#{arg[:singular]}} : {})" }.join %> %>
1
+ <\%= render partial: "new_form", locals: {<%= singular %>: @<%=singular%>, <%= @nested_set.collect{|arg| "#{arg[:singular]}: @#{arg[:singular]}" }.join(", ") %>} %>
@@ -1,16 +1,67 @@
1
1
  import { Controller } from "@hotwired/stimulus"
2
2
 
3
- // Connects to data-controller="search_form"
4
3
  export default class extends Controller {
5
- static targets = ["clearButton"];
4
+ static targets = ["clearButton", "textSearch", "textMatch"]
6
5
 
7
6
  connect() {
8
-
9
7
  if (this.hasClearButtonTarget) {
10
8
  this.clearButtonTarget.addEventListener("click", (event) => {
11
9
  event.preventDefault()
12
10
  this.element.reset()
13
11
  })
14
12
  }
13
+
14
+ // watch match selects
15
+ this.textMatchTargets.forEach(target => {
16
+ target.addEventListener("change", this.handleMatchChange)
17
+ })
18
+ }
19
+
20
+ textSearchTargetConnected(target) {
21
+ target.addEventListener("input", this.handleSearchInput)
22
+ }
23
+
24
+ disconnect() {
25
+ this.textSearchTargets.forEach(target => {
26
+ target.removeEventListener("input", this.handleSearchInput)
27
+ })
28
+ this.textMatchTargets.forEach(target => {
29
+ target.removeEventListener("change", this.handleMatchChange)
30
+ })
31
+ }
32
+
33
+ handleSearchInput = (event) => {
34
+ const searchEl = event.target
35
+ const matchEl = this.findMatchFor(searchEl)
36
+ if (!matchEl) return
37
+
38
+ const val = searchEl.value.trim()
39
+ const resettable = ["contains", "starts_with", "ends_with"]
40
+
41
+ if (val.length > 0 && matchEl.value === "") {
42
+ matchEl.value = "contains"
43
+ } else if (val === "" && resettable.includes(matchEl.value)) {
44
+ matchEl.value = ""
45
+ }
46
+ }
47
+
48
+ handleMatchChange = (event) => {
49
+ const matchEl = event.target
50
+ if (matchEl.value !== "") return
51
+
52
+ const searchEl = this.findSearchFor(matchEl)
53
+ if (!searchEl) return
54
+
55
+ searchEl.value = ""
56
+ }
57
+
58
+ findMatchFor(searchEl) {
59
+ const matchName = searchEl.name.replace("search", "match")
60
+ return this.textMatchTargets.find(t => t.name === matchName)
61
+ }
62
+
63
+ findSearchFor(matchEl) {
64
+ const searchName = matchEl.name.replace("match", "search")
65
+ return this.textSearchTargets.find(t => t.name === searchName)
15
66
  }
16
67
  }
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.7'
3
+ CURRENT = '0.7.2'
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'
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Fleetwood-Boldt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-05 00:00:00.000000000 Z
11
+ date: 2025-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails