hot-glue 0.6.31 → 0.7.1

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: fd6bfe4a874a5e894d6b315e72c145f1a563ac3a851e2d4849e41eb127b4402f
4
- data.tar.gz: d88aeb4540484a0335150c0d637a8781b1781ff33b227cd65b60d96fa13703ac
3
+ metadata.gz: 62c0a8b7999c39d4d93ece238d6447baa2ecdf34613d9fb11b5fe49842919424
4
+ data.tar.gz: c0a2d741d113fe2d3a366b5f33f110fc5aa8956a1b52af7ea2deb56085e76a4b
5
5
  SHA512:
6
- metadata.gz: 16e4242488830bb7b4da394a9e6e919b32ef885ddf5ce506e05954ca5830b918a47b2662661289d0bf4adbb6b3e4bf5d89fdb2ddbb59f9333126622461fabb81
7
- data.tar.gz: 9ac25352156d69caf694303602f968990441cded93504d6fc340df1b92091bb450f6372d409c10ba2be8a0599e1217e5d037e053b908c2b5136bad148f7719cb
6
+ metadata.gz: dd02b7553a585535eb7eb2ac195ce55eb0192151edccfb41cb9f3b4c8110ed125096200bbe3442cdc79dbb27db48cc0b8a10fdbc3366c9c0371f7ba46dc3684f
7
+ data.tar.gz: 235257f2f9150c0ac038fd7b11d4ac0551d4a224213e5cf244311ed43ca4cbdc02e25bf6427090213e30abde0de7289a0b01b887e265f0e2496a63c9fc9b12e0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.6.30)
4
+ hot-glue (0.7)
5
5
  ffaker (~> 2.16)
6
6
  rails (> 5.1)
7
7
 
data/README.md CHANGED
@@ -655,7 +655,16 @@ If you specify an include list, it will be treated as a whitelist: no fields wil
655
655
 
656
656
  You may not specify both include and exclude.
657
657
 
658
- Include setting is affected by both specified grouping mode and smart layouts, explained below.
658
+ Many options allow you to specify fields in multiple modes:
659
+ • Standard
660
+ • Smart layout
661
+ • Specified grouping
662
+
663
+ see "Layout & Manipulation features" for details on how to build your `--include` statement, including how to use `:` and `,`
664
+
665
+ Also review Omitted Fields (`-` and `=`), Dynamic Blocks (`**`), Omitted Dynamic Blocks (`**-` and `**=`)
666
+
667
+ Also see Set Column Widths ( `(` ... `)` ) to set the column widths explicitly in your `--include`
659
668
 
660
669
 
661
670
  ### `--exclude=`
@@ -665,7 +674,6 @@ By default, all fields are included unless they are on the default exclude list.
665
674
 
666
675
  If you specify any exclude list, those excluded **and** the default exclude list will be excluded. (If you need any of the fields on the default exclude list, you must use `--include` instead.)
667
676
 
668
-
669
677
  `./bin/rails generate hot_glue:scaffold Account --exclude=password`
670
678
 
671
679
 
@@ -1059,10 +1067,72 @@ This is what would happen if 9 fields, specified in the order A,B,C,D,E,F,G,H,I,
1059
1067
  (If you had a number of fields that wasn't easily divisible by the number of columns, it would leave the final column one or a few fields short of the others.)
1060
1068
 
1061
1069
 
1062
- ### `--new-button-position` (above, below; default: above)
1063
- Show the new button above or below the list.
1064
1070
 
1071
+ ### Omitted fields
1072
+
1073
+ Note that these additions all affect how you write your `--include` setting, but they operate in addition to all of the field visibility rules above. Notice that omitting a form means we by design omit it on both the new and edit actions. If you want to determine if it shows up on new vs. edit granularly, use a different approach.
1074
+
1075
+ Generally, you will only omit a field in special cases. To omit a field, prefix either `-` (omit on list) or `=` to omit on form (new & edit), like so :
1076
+
1077
+ `--include=-abd,=dfg`
1078
+
1079
+ Here, the abc field will be omitted on the list and the dfg will be omitted on the form (new & edit)
1080
+
1081
+
1082
+ This works for any include setting type, whether the grouping mode is specified or not.
1083
+
1084
+ `--include=-abd,=dfg`
1085
+ `--include=-abd:=dfg` # same as above but builder will use specified grouping mode
1086
+
1087
+ ### Dynamic Blocks
1088
+
1089
+ Next, you can now dynamically pull any partials from the view folder (of the name of your choosing) into your object's field display- before, between, or after any part of the column groupings using the `:` and `,` characters. (Review specified grouping mode in "Layout & Manipulation Features.")
1090
+
1091
+ You simply make up any partial name, treat it as if it is one of the fields in the field list, and prefix it with `**`. (When specified, don't use `_` but remember to use the `_` in the file name.)
1092
+
1093
+ It looks like this (let's assume I've made an arbitrary partial called `_jello.erb` in my build folder, which expects to be passed a `thing` local variable)
1094
+
1095
+ `rails generate hot_glue:scaffold Thing --include='**jello:name:birthday:gender'`
1096
+
1097
+ Here, you'll get four columns:
1098
+
1099
+ The first column will pull a partial from the same folder being built. You build this partial, not Hot Glue.
1065
1100
 
1101
+ ```
1102
+ <%= render partial: 'jello', locals: {thing: thing } %>
1103
+ ```
1104
+
1105
+ (Remember, the partial file name begins with `_` but is referenced without the underscore)
1106
+
1107
+ Because you separated that first column using `:` from the other three, the partial will take up the whole column.
1108
+ The next 3 columns will contain name, birthday & gender, respectively.
1109
+
1110
+ If you specify it this way:
1111
+ rails generate hot_glue:scaffold Thing --include='**jello,name:birthday:gender'
1112
+
1113
+ Notice that between **jello and name is now a comma, putting the jello partial and name into the same column. Now the partial will be displayed before the name in that column.
1114
+
1115
+ You can mix & match any number of partials inserted anywhere into the field layout.
1116
+
1117
+
1118
+ ### Omitted Dynamic Blocks
1119
+
1120
+ Dynamic blocks respond to the new `-` and `=` settings for omitting fields the same way fields do. Prefixing a dynamic partial with `**-` will mean it will be omitted from the list, and `**=` will omit it from the form (new & edit)
1121
+
1122
+ This way, the omitted syntax is parallel to the dynamic block structure. No other tye-in is made with the other field visibility settings.
1123
+
1124
+
1125
+ ### Set Column Widths
1126
+
1127
+ In other modes, Hot Glue decides how many bootstrap columns each column should span, defaulting to 2 unless using `--smart-layout`, in which case it ranges from 2 to 5 depending on how many fields you have. Generally, I start my scaffolds using either standard or smart, but soon, I'm often upgrading to specified grouping mode for fine-grained control.
1128
+
1129
+ When using specified group mode (activated when a `:` character appears in the `--include` list), you can append to the end of EACH COLUMN (not each field) a parenthetical setting for the number of bootstrap columns.
1130
+
1131
+ (When the setting includes this special character, you'll need `'` to wrap the entire setting for `--include` on the command line.)
1132
+
1133
+
1134
+ ### `--new-button-position` (above, below; default: above)
1135
+ Show the new button above or below the list.
1066
1136
 
1067
1137
  ### `--button-icons` (default is no icons)
1068
1138
  You can specify this either as builder flag or as a config setting (in `config/hot_glue.yml`)
@@ -1075,8 +1145,6 @@ Can also be specified globally in `config/hot_glue.yml`
1075
1145
 
1076
1146
 
1077
1147
  ### `--modify=field1{...},field2{...}`
1078
-
1079
-
1080
1148
  You can apply modification to the viewable (non-edit) display of field using the `--modify` switch.
1081
1149
 
1082
1150
  The syntax is `--modify=cost{$},price{$}`
@@ -1179,7 +1247,7 @@ In these contexts, the lookup must be exact match to the text entered (no partia
1179
1247
 
1180
1248
  Use `+` to map to the `find_or_create_by` method. (Without `+` it will use `find_by`.)
1181
1249
 
1182
- This is accomlished using a little magic of a lookup field called `__lookup_X_Y` passed in the form parameters.
1250
+ This is accomplished using a little magic of a lookup field called `__lookup_X_Y` passed in the form parameters.
1183
1251
 
1184
1252
  The lookup field is used to look up the associated record, then deleted from the params.
1185
1253
 
@@ -1443,10 +1511,15 @@ As shown in the method `name_able?` of the example ThingPolicy above, if this fi
1443
1511
  ### `--hidden=` (affects both create + update actions)
1444
1512
  ### `--create-hidden=`
1445
1513
  ### `--update-hidden=`
1446
- TODO: RENAME ME TO INVISIBLE
1447
1514
  Separate list of fields.
1448
1515
 
1449
- These fields will exist on the create or update form exist as hidden_field, and so the update will still work._
1516
+ These fields will exist on the create or update form exist as `<input type='hidden'>` (html) field and so the *actual update or create action will continue to expect data from the front end.**
1517
+
1518
+ (The data, being in a hidden field, is not visible to the user but it is in the browser's form, available to a hacker, and passed to the back-end for updating.)
1519
+
1520
+ You do your own javascript here to insert/set the value programmatically. The example below uses a library called EditorView to create a HTML editor.
1521
+
1522
+ The dynamic interface's value from the edited html gets stuffed back into the form by Javascript before the form is updated
1450
1523
 
1451
1524
 
1452
1525
  EXAMPLE:
@@ -1539,10 +1612,9 @@ Like show only, these will check for `*_able?` methods on the object or Policy a
1539
1612
 
1540
1613
  It will also block the field from being updated on the backend, so don't use this if you want to create a hidden_field tag but still allow the controller to update it. (For that, see `--hidden=`.)
1541
1614
 
1542
-
1543
1615
  Like show-only, note special behavior with pundit.
1544
1616
 
1545
- A field can be marked invisible and show-only, in which case the invisible rule take prescedence when the access control is denied (field removed from form) but th show-only rule takes prescedance when the access control is granted,
1617
+ A field can be marked invisible and show-only, in which case the invisible rule take precedence when the access control is denied (field removed from form) but the show-only rule takes precedence when the access control is granted.
1546
1618
 
1547
1619
  Hidden can be used with invisible. In this case, the access control will be applied to the field. When editable, the hidden field output will be used.
1548
1620
 
@@ -1598,6 +1670,12 @@ If thing is not a Thing, then it is active relation (so this applies to the colu
1598
1670
  Remember, since the `_able?` methods are not otherwise called during Pundit's index cycle, this applies only to the list column headings and has no bearing on create, update, read, delete for which the access control can be anything you want that is available to the Poilcy.
1599
1671
 
1600
1672
 
1673
+ ###
1674
+
1675
+
1676
+
1677
+
1678
+
1601
1679
 
1602
1680
  ## Code Insertion Features
1603
1681
  '
@@ -1639,6 +1717,7 @@ This is a good place to set your created_by user id, like so
1639
1717
 
1640
1718
 
1641
1719
 
1720
+
1642
1721
  ```
1643
1722
  def new
1644
1723
  @email_template = EmailTemplate.new(crusade: crusade)
@@ -1670,6 +1749,16 @@ def create
1670
1749
  ```
1671
1750
  TODO: build a solution for inserting code only in the `new` action but NOT the create action
1672
1751
 
1752
+ #### `--code-in-controller=''` (escape newlines with `;` )
1753
+
1754
+ This code will be inserted directly into your controller (as a class-level definition)
1755
+
1756
+
1757
+ ```
1758
+ --code-in-controller="before_action -> { @thing = 'abc' }"
1759
+ ```
1760
+ A one-liner hook works well for these. if you define an entire method, be sure to use `;` to separate line breaks
1761
+
1673
1762
 
1674
1763
 
1675
1764
  ## Searching
@@ -2331,6 +2420,58 @@ These automatic pickups for partials are detected at build time. This means that
2331
2420
 
2332
2421
  # VERSION HISTORY
2333
2422
 
2423
+ #### 2025-11-12 - v0.7.1
2424
+ - 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;
2425
+ -
2426
+ - also removes search text if you change the match selector back to empty
2427
+ -
2428
+ - removes vestiges of `.merge` from old implementation (#236)
2429
+
2430
+ - `--code-in-controller` option for inserting controller code directly into your controller
2431
+
2432
+
2433
+ #### 2025-11-05 - v0.7
2434
+
2435
+ 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.
2436
+
2437
+ Remember that Hot Glue's opinionated design has two ways a field is displayed: show (which appears on the list view and is always just viewable), and form (which is usees by both the new and edit actions to display a form). Within the `form` output, the form might be used for either new or edit, and further refinements can be applied to new or edit.
2438
+
2439
+ Here's a quick review those methods now:
2440
+
2441
+ --pundit
2442
+ • No distinction is made between the new and edit contexts
2443
+ • If policy says not able (_able? method returns false), the field is shown as viewable-only. That is, a user with access to the controller but not the field-level access granted by Pundit will see the field's value but cannot edit it. If you want instead the Policy to determine if the field is editable or completely invisible (not shown to the user), use invible below
2444
+
2445
+ --show-only=
2446
+ • Turns fields on this list viewable only for both create & edit actions
2447
+ • overrides what's on the policy, but asks the policy if not on this list
2448
+
2449
+ --update-show-only=
2450
+ • Turns fields on this list viewable only for the edit action
2451
+ • overrides what's on the policy, but asks the policy if not on this list
2452
+
2453
+
2454
+ --hidden=, --create-hidden=, --update-hidden=
2455
+ • Creates <input type='hidden'> fields on the form, not seen by the user but available to a hacker in the browser, and submitted to the back end.
2456
+
2457
+ --invisible=, --create-invisible=, --update-invisible=
2458
+ • Must be used with Pundit.
2459
+ • If policy returns true, the field is viewable on list & editable as normal.
2460
+ • If the policy return false, the field is taken away (invisible) for that user
2461
+
2462
+
2463
+
2464
+ Today, with v0.7 of this gem, I'm introducing three more features that are all available from within the `--include` setting.
2465
+
2466
+ • Omitted fields: using `-` is omit on list & show; use `=` to omit the field on the form (new & edit)
2467
+ • Dynamic blocks (which can also be omitted using
2468
+ • Set column widths when using specified grouping made (--include contains `:`)
2469
+
2470
+ For details, see "Layout & Manipulation Features"
2471
+
2472
+
2473
+
2474
+
2334
2475
  #### 2025-10-28 - v0.6.31
2335
2476
 
2336
2477
  - new modification directive: `urlwrap`: use to wrap the field contents in a clickable link.
@@ -219,7 +219,7 @@ class AssociationField < Field
219
219
  autofocus: true,
220
220
  autocomplete: 'off',
221
221
  value: @q['0']['#{name}'].present? ? #{assoc.class_name}.find(@q['0']['#{name}']).try(:name) : \"\" %>
222
- <%= f.hidden_field \'q[0][#{name}]\', value: @q['0']['#{name}_search'].try(:id), 'data-typeahead-target': 'hiddenFormValue' %>
222
+ <%= f.hidden_field \'q[0][#{name}]\', value: @q['0']['#{name}'], 'data-typeahead-target': 'hiddenFormValue' %>
223
223
  <div data-typeahead-target='results'></div>
224
224
  <div data-typeahead-target='classIdentifier' data-id=\"typeahead--q_0_#{name}_search\"></div>
225
225
  </div>"
@@ -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
@@ -11,8 +11,8 @@ module HotGlue
11
11
  :stacked_downnesting, :bootstrap_column_width
12
12
 
13
13
  def initialize(generator: ,
14
- include_setting: ,
15
- buttons_width: )
14
+ include_setting:,
15
+ buttons_width: )
16
16
 
17
17
 
18
18
  @generator = generator
@@ -41,7 +41,11 @@ module HotGlue
41
41
  columns: {
42
42
  size_each: smart_layout ? bootstrap_column_width : (specified_grouping_mode ? nil : 1),
43
43
  container: [] , # array of arrays,
44
- bootstrap_column_width: []
44
+ bootstrap_column_width: [],
45
+ column_custom_widths: [],
46
+ fields: {
47
+
48
+ }, # hash of fields
45
49
  },
46
50
  portals: {
47
51
 
@@ -103,15 +107,21 @@ module HotGlue
103
107
  }
104
108
  layout_object[:columns][:container] = (0..available_columns-1).collect { |x| [columns[x]] }
105
109
  layout_object[:columns][:container].reject!{|x| x == [nil]}
106
- # layout_object[:columns][:size_each] = bootstrap_column_width
110
+
107
111
  end
112
+
108
113
  elsif ! specified_grouping_mode
109
114
  # not smart and no specified grouping
110
115
  layout_object[:columns][:button_columns] = bootstrap_column_width
111
116
 
112
117
  layout_object[:columns][:container] = columns.collect{|col| [col]}
113
118
 
114
- else # specified grouping mode -- the builder is given control
119
+ if include_setting.split(",").any?{|col| col.include?("(")}
120
+ raise "Your include list has a ( character; to specify a column width, use specified grouping mode only"
121
+ end
122
+
123
+
124
+ else # specified grouping mode -- the builder is given control unless a column width is explicitly set using (..)
115
125
  layout_object[:columns][:button_columns] = bootstrap_column_width
116
126
 
117
127
  (0..available_columns-1).each do |int|
@@ -121,16 +131,22 @@ module HotGlue
121
131
  # input control
122
132
 
123
133
  user_layout_columns = @include_setting.split(":")
134
+ fixed_widths = {}
135
+
136
+ user_layout_columns.each_with_index do |column_data,i |
137
+ if column_data.include?("(")
138
+ column_data =~ /(.*)\((.*)\)/
139
+ column_fields = $1
140
+ fixed_col_width = $2
141
+ fixed_widths[i] = fixed_col_width
142
+ else
143
+ column_fields = column_data
144
+ end
145
+ user_layout_columns[i] = column_fields
146
+ end
124
147
 
125
- extra_columns = available_columns - user_layout_columns.size
126
- # size_each = (bootstrap_columns / user_layout_columns.count).floor # this is the bootstrap size
127
- #
128
- # layout_object[:columns][:size_each] = size_each
129
-
130
- # if user_layout_columns.size > available_columns
131
- # raise "Your include statement #{@include_setting } has #{user_layout_columns.size} columns, but I can only construct up to #{available_columns}"
132
- # end
133
148
 
149
+ extra_columns = available_columns - user_layout_columns.size
134
150
 
135
151
  columns_to_work_with = (12 - @buttons_width)
136
152
 
@@ -142,10 +158,22 @@ module HotGlue
142
158
  extra_columns = columns_to_work_with % user_layout_columns.size
143
159
 
144
160
 
145
- user_layout_columns.each_with_index do |column,i|
146
- layout_object[:columns][:container][i] = column.split(",").collect(&:to_sym)
147
- layout_object[:columns][:bootstrap_column_width][i] = target_col_size
148
- if i < extra_columns
161
+ user_layout_columns.each_with_index do |column, i|
162
+
163
+ layout_object[:columns][:container][i] = column.split(",").collect{|x|
164
+ x.gsub("-","").gsub("=","")
165
+ if x.include?("(")
166
+ x =~ /(.*)\((.*)\)/
167
+ x = $1
168
+
169
+ end
170
+ x
171
+ }.collect(&:to_sym)
172
+
173
+
174
+ layout_object[:columns][:bootstrap_column_width][i] = fixed_widths[i] || target_col_size
175
+
176
+ if i < extra_columns && ! fixed_widths[i]
149
177
  layout_object[:columns][:bootstrap_column_width][i] += 1
150
178
  end
151
179
  end
@@ -153,14 +181,46 @@ module HotGlue
153
181
  if user_layout_columns.size < layout_object[:columns][:container].size
154
182
  layout_object[:columns][:container].reject!{|x| x == []}
155
183
  end
184
+ end
156
185
 
186
+ # go through the columns and build the split visibility (show / form)
187
+
188
+ columns.each do |col|
189
+ layout_object[:columns][:fields][col] = {show: true, form: true}
157
190
  end
158
191
 
192
+ @include_setting.split(":").collect { |column_list|
193
+ column_list.split(",").collect do |field|
194
+ if field.include?("(")
195
+ field =~ /(.*)\((.*)\)/
196
+ field_short = $1
197
+ else
198
+ field_short = field
199
+ end
200
+
201
+ field_short = field_short.gsub("-", "").gsub("=", "")
202
+
203
+ layout_object[:columns][:fields][field_short.to_sym] = {
204
+ show: true,
205
+ form: true
206
+ }
159
207
 
160
- puts "*** constructed smart layout columns #{layout_object.inspect}"
208
+ if field.starts_with?("**")
209
+ nil
210
+ end
211
+
212
+ if field.include?("-")
213
+ layout_object[:columns][:fields][field_short.to_sym][:show] = false;
214
+ end
215
+ if field.include?("=")
216
+ layout_object[:columns][:fields][field_short.to_sym][:form] = false;
217
+ end
218
+
219
+ end
220
+ }
221
+ puts "*** SPECIFIED GROUPING SETTINGS: #{layout_object.inspect}"
161
222
  layout_object
162
223
  end
163
-
164
224
  end
165
225
  end
166
226
  end
@@ -98,11 +98,15 @@ module HotGlue
98
98
  size = layout_object[:columns][:bootstrap_column_width][i]
99
99
  "<div class='#{layout_strategy.column_classes_for_column_headings(size)} hg-heading-row heading--#{singular}--#{column.join("-")}' " + col_style + ">" +
100
100
  column.map(&:to_s).map{|col_name|
101
- the_output = "#{col_name.humanize}"
101
+ unless col_name.starts_with?("**")
102
+ the_output = "#{col_name.humanize}"
103
+ else
104
+ the_output = ""
105
+ end
102
106
  if invisible_update.include?(col_name.to_sym)
103
107
  if_statements = []
104
108
  if_statements << "false" if invisible_update.include?(col_name.to_sym)
105
- # if_statements << "@action == 'new'" if invisible_create.include?(col_name.to_sym)
109
+
106
110
  the_output = "<% if ( " + if_statements.join(" || ") + " || policy(#{@plural}).#{col_name}_able? ) %>" +
107
111
  + the_output + "<% end %>"
108
112
 
@@ -158,7 +162,7 @@ module HotGlue
158
162
  dom_label = choice[:label].downcase.gsub(" ","_")
159
163
  if data[:type] == "radio"
160
164
  res << "\n<input type='radio'
161
- id='#{search_field}_search__#{dom_label}}'
165
+ id='#{search_field}_search__#{dom_label}'
162
166
  name='q[0][#{search_field}_search]' value='#{dom_label}'
163
167
  <%= 'checked' if @q['0'][:#{search_field}_search] == \"#{dom_label}\" %> />"
164
168
  elsif data[:type] == "checkboxes"
@@ -195,66 +199,75 @@ module HotGlue
195
199
  size = layout_object[:columns][:bootstrap_column_width][columns.index(column)]
196
200
 
197
201
  " <div class='#{layout_strategy.column_classes_for_form_fields(size)} cell--#{singular}--#{column.join("-")}' >" +
198
- column.map { |col|
202
+ column.map { |full_col|
199
203
 
200
- field_error_name = columns_map[col].field_error_name
204
+ col = full_col.to_s.gsub("=", "").gsub("-", "").to_sym
201
205
 
202
- label_class = columns_map[col].label_class
203
- label_for = columns_map[col].label_for
206
+ if col.to_s.starts_with?("**") && layout_object[:columns][:fields][col][:form]
207
+ the_output = "<%= render partial: '#{col.to_s.gsub!("**","")}', locals: {#{singular}: #{singular} } %>"
208
+ elsif ! layout_object[:columns][:fields][col][:form]
209
+ # omit from show action
210
+ else
204
211
 
205
- the_label = "\n<label class='#{label_class}' for='#{label_for}'>#{col.to_s.humanize}</label>"
206
-
207
-
208
- field_result =
209
- if show_only.include?(col)
210
- columns_map[col].form_show_only_output
211
- elsif update_show_only.include?(col) && !@pundit
212
- "<% if @action == 'edit' %>" + columns_map[col].form_show_only_output + "<% else %>" + columns_map[col].form_field_output + "<% end %>"
213
- elsif update_show_only.include?(col) && @pundit && eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym)
214
- "<% if @action == 'new' && policy(@#{singular}).#{col}_able? %>" + columns_map[col].form_field_output + "<% else %>" + columns_map[col].form_show_only_output + "<% end %>"
215
- # show only on the update action overrides any pundit policy
216
- elsif @pundit && eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym)
217
- "<% if policy(@#{singular}).#{col}_able? %>" + columns_map[col].form_field_output + "<% else %>" + columns_map[col].form_show_only_output + "<% end %>"
218
- elsif update_show_only.include?(col)
219
- "<% if @action == 'edit' %>" + columns_map[col].form_show_only_output + "<% else %>" + columns_map[col].form_field_output + "<% end %>"
220
- else
221
- columns_map[col].form_field_output
222
- end
212
+ field_error_name = columns_map[col].field_error_name
223
213
 
214
+ label_class = columns_map[col].label_class
215
+ label_for = columns_map[col].label_for
224
216
 
225
- @tinymce_stimulus_controller = (columns_map[col].modify_as == {tinymce: 1} ? "data-controller='tiny-mce' " : "")
217
+ the_label = "\n<label class='#{label_class}' for='#{label_for}'>#{col.to_s.humanize}</label>"
226
218
 
227
- if @stimmify
228
- col_target = HotGlue.to_camel_case(col.to_s.gsub("_", " "))
229
- data_attr = " data-#{@stimmify}-target='#{col_target}Wrapper'"
230
- end
231
219
 
220
+ field_result =
221
+ if show_only.include?(col)
222
+ columns_map[col].form_show_only_output
223
+ elsif update_show_only.include?(col) && !@pundit
224
+ "<% if @action == 'edit' %>" + columns_map[col].form_show_only_output + "<% else %>" + columns_map[col].form_field_output + "<% end %>"
225
+ elsif update_show_only.include?(col) && @pundit && eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym)
226
+ "<% if @action == 'new' && policy(@#{singular}).#{col}_able? %>" + columns_map[col].form_field_output + "<% else %>" + columns_map[col].form_show_only_output + "<% end %>"
227
+ # show only on the update action overrides any pundit policy
228
+ elsif @pundit && eval("defined? #{singular_class}Policy") && eval("#{singular_class}Policy").instance_methods.include?("#{col}_able?".to_sym)
229
+ "<% if policy(@#{singular}).#{col}_able? %>" + columns_map[col].form_field_output + "<% else %>" + columns_map[col].form_show_only_output + "<% end %>"
230
+ elsif update_show_only.include?(col)
231
+ "<% if @action == 'edit' %>" + columns_map[col].form_show_only_output + "<% else %>" + columns_map[col].form_field_output + "<% end %>"
232
+ else
233
+ columns_map[col].form_field_output
234
+ end
232
235
 
233
- 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" +
234
- add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
235
- + field_result +
236
- (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
237
- "\n </div>\n ", 2)
238
236
 
237
+ @tinymce_stimulus_controller = (columns_map[col].modify_as == {tinymce: 1} ? "data-controller='tiny-mce' " : "")
239
238
 
240
- if hidden_create.include?(col.to_sym) || hidden_update.include?(col.to_sym)
241
- if_statements = []
242
- if_statements << "@action == 'edit'" if hidden_update.include?(col.to_sym)
243
- if_statements << "@action == 'new'" if hidden_create.include?(col.to_sym)
239
+ if @stimmify
240
+ col_target = HotGlue.to_camel_case(col.to_s.gsub("_", " "))
241
+ data_attr = " data-#{@stimmify}-target='#{col_target}Wrapper'"
242
+ end
244
243
 
245
- the_output = "<% if " + if_statements.join(" || ") + " %>" +
246
- columns_map[col].hidden_output + "<% else %>" + the_output + "<% end %>"
247
- end
248
244
 
249
- if invisible_create.include?(col) || invisible_update.include?(col)
250
- if_statements = []
251
- if_statements << "@action == 'edit'" if invisible_update.include?(col.to_sym)
252
- if_statements << "@action == 'new'" if invisible_create.include?(col.to_sym)
245
+ 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" +
246
+ add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
247
+ + field_result +
248
+ (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
249
+ "\n </div>\n ", 2)
253
250
 
254
- the_output = "<% if !(" + if_statements.join(" || ") + ") || policy(@#{singular}).#{col}_able? %>" +
255
- + the_output + "<% end %>"
256
- end
257
251
 
252
+ if hidden_create.include?(col.to_sym) || hidden_update.include?(col.to_sym)
253
+ if_statements = []
254
+ if_statements << "@action == 'edit'" if hidden_update.include?(col.to_sym)
255
+ if_statements << "@action == 'new'" if hidden_create.include?(col.to_sym)
256
+
257
+ the_output = "<% if " + if_statements.join(" || ") + " %>" +
258
+ columns_map[col].hidden_output + "<% else %>" + the_output + "<% end %>"
259
+ end
260
+
261
+ if invisible_create.include?(col) || invisible_update.include?(col)
262
+ if_statements = []
263
+ if_statements << "@action == 'edit'" if invisible_update.include?(col.to_sym)
264
+ if_statements << "@action == 'new'" if invisible_create.include?(col.to_sym)
265
+
266
+ the_output = "<% if !(" + if_statements.join(" || ") + ") || policy(@#{singular}).#{col}_able? %>" +
267
+ + the_output + "<% end %>"
268
+ end
269
+
270
+ end # end of if col.to_s.starts_with?("**")
258
271
 
259
272
 
260
273
  the_output
@@ -298,35 +311,45 @@ module HotGlue
298
311
  style_with_flex_basis = layout_strategy.style_with_flex_basis(perc_width)
299
312
 
300
313
  result = columns.map.with_index{ |column,i|
301
-
302
314
  size = layout_object[:columns][:bootstrap_column_width][i]
303
-
304
315
  "<div class='hg-col #{layout_strategy.column_classes_for_line_fields(size)} #{singular}--#{column.join("-")}'#{style_with_flex_basis}> " +
305
- column.map { |col|
306
- if eval("#{singular_class}.columns_hash['#{col}']").nil? && !attachments.keys.include?(col) && !related_sets.include?(col)
307
- raise "Can't find column '#{col}' on #{singular_class}, are you sure that is the column name?"
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"
308
322
  end
323
+ if col.starts_with?("**") && layout_object[:columns][:fields][col][:show]
324
+ the_output = "<%= render partial: '#{col.to_s.gsub!("**","")}', locals: {#{singular}: #{singular} } %>"
325
+ elsif ! layout_object[:columns][:fields][col][:show]
326
+ the_output = ""
327
+ elsif eval("#{singular_class}.columns_hash['#{col}']").nil? && !attachments.keys.include?(col) && !related_sets.include?(col)
328
+ raise "Can't find column '#{col}' on #{singular_class}, are you sure that is the column name?"
329
+ # omit from show action
330
+ else
309
331
 
310
- field_output = columns_map[col].line_field_output
311
-
312
- label = "<label class='small form-text text-muted'>#{col.to_s.humanize}</label>"
332
+ field_output = columns_map[col].line_field_output
313
333
 
314
- the_output = "#{inline_list_labels == 'before' ? label + "<br/>" : ''}#{field_output}#{inline_list_labels == 'after' ? "<br/>" + label : ''}"
315
- if invisible_create.include?(col) || invisible_update.include?(col)
316
- if_statements = []
317
- if invisible_update.include?(col.to_sym) && invisible_create.include?(col.to_sym)
318
- # elsif invisible_create.include?(col.to_sym)
319
- # if_statements << "!(@action == 'new')"
320
- else
321
- if_statements << "@action == 'edit'"
322
- end
334
+ label = "<label class='small form-text text-muted'>#{col.to_s.humanize}</label>"
323
335
 
324
- if_statements << " policy(#{singular}).#{col}_able?"
325
- the_output = "<% if " + if_statements.join(" || ") + " %>" +
326
- + the_output + "<% end %>"
336
+ the_output = "#{inline_list_labels == 'before' ? label + "<br/>" : ''}#{field_output}#{inline_list_labels == 'after' ? "<br/>" + label : ''}"
337
+ the_output += "\n"
338
+ if invisible_create.include?(col) || invisible_update.include?(col)
339
+ if_statements = []
340
+ if invisible_update.include?(col.to_sym) && invisible_create.include?(col.to_sym)
341
+ else
342
+ if_statements << "@action == 'edit'"
343
+ end
344
+
345
+ if_statements << " policy(#{singular}).#{col}_able?"
346
+ the_output = "<% if " + if_statements.join(" || ") + " %>" +
347
+ + the_output + "<% end %>"
348
+ end
327
349
  end
350
+
328
351
  the_output
329
- }.join( "<br />") + "</div>"
352
+ }.join( "<br />\n") + "</div>"
330
353
  }.join("\n")
331
354
  return result
332
355
 
@@ -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
@@ -256,11 +257,26 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
256
257
  @include_fields = []
257
258
 
258
259
  # semicolon to denote layout columns; commas separate fields
259
- @include_fields += options['include'].split(":").collect { |x| x.split(",") }.flatten.collect(&:to_sym)
260
+ #
261
+ @include_fields += options['include'].split(":").collect { |column_list|
262
+ column_list.split(",").collect do |field|
263
+ if field.include?("(")
264
+ field =~ /(.*)\((.*)\)/
265
+ field_short = $1
266
+ else
267
+ field_short = field
268
+ end
269
+ if field_short.starts_with?("**")
270
+ nil
271
+ else
272
+ field_short.gsub("-", "").gsub("=", "")
273
+ end
274
+ end
275
+ }.flatten.compact.collect(&:to_sym)
276
+ puts "INCLUDED FIELDS: #{@include_fields}"
260
277
  end
261
278
 
262
279
 
263
-
264
280
  @show_only = options['show_only'].split(",").collect(&:to_sym)
265
281
  if @show_only.any?
266
282
  puts "show only field #{@show_only}}"
@@ -424,6 +440,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
424
440
  @phantom_create_params = options['phantom_create_params'] || ""
425
441
  @phantom_update_params = options['phantom_update_params'] || ""
426
442
 
443
+
427
444
  if get_default_from_config(key: :pundit_default)
428
445
  raise "please note the config setting `pundit_default` has been renamed `pundit`. please update your hot_glue.yml file"
429
446
  end
@@ -630,6 +647,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
630
647
  @code_before_update = options['code_before_update']
631
648
  @code_after_update = options['code_after_update']
632
649
  @code_after_new = options['code_after_new']
650
+ @code_in_controller = options['code_in_controller'] || ""
633
651
 
634
652
  buttons_width = ((!@no_edit && 1) || 0) + ((!@no_delete && 1) || 0) + (@magic_buttons.any? ? 1 : 0)
635
653
 
@@ -672,6 +690,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
672
690
  # build a new polymorphic object
673
691
  @associations = []
674
692
  @columns_map = {}
693
+
675
694
  @columns.each do |col|
676
695
  # if !(@the_object.columns_hash.keys.include?(col.to_s) || @attachments.keys.include?(col))
677
696
  # raise "couldn't find #{col} in either field list or attachments list"
@@ -1062,6 +1081,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1062
1081
  @columns = @the_object.columns.map(&:name).map(&:to_sym).reject { |field| !@include_fields.include?(field) }
1063
1082
  end
1064
1083
 
1084
+
1065
1085
  @columns = @columns - @nested_set.collect { |set| (set[:singular] + "_id").to_sym }
1066
1086
 
1067
1087
  if @attachments.any?
@@ -1084,6 +1104,19 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1084
1104
  end
1085
1105
  end
1086
1106
 
1107
+ # def omit_fields_show
1108
+ #
1109
+ # layout_object[:columns][:fields].each { |key, value|
1110
+ # value[:show] == false
1111
+ # }.reduce(:&)
1112
+ # end
1113
+
1114
+ def omit_fields_form
1115
+ layout_object[:columns][:fields].collect { |key, value| value[:form] == false ? key : nil }.compact.reject!{|x| x.starts_with?("**")} || []
1116
+ end
1117
+
1118
+
1119
+
1087
1120
  def check_if_sample_file_is_present
1088
1121
  if sample_file_path.nil?
1089
1122
  puts "you have no sample file path set in config/hot_glue.yml"
@@ -1616,12 +1649,12 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1616
1649
  append_text = " <li class='nav-item'>
1617
1650
  <%= link_to '#{@list_label_heading.humanize}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>
1618
1651
  </li>"
1619
- alt_append_text = " <li class='nav-item'>
1620
- <%= link_to '#{@list_label_heading.humanize.upcase}', #{path_helper_plural(@nested_set.any? ? true: false)}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>
1621
- </li>"
1652
+ 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}'}\" %>"
1653
+
1654
+ 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}'}\" %>"
1622
1655
 
1623
1656
  text = File.read(nav_file)
1624
- if text.include?(append_text) || text.include?(alt_append_text)
1657
+ if text.include?(check_for_existing_append) || text.include?(alt_append_text)
1625
1658
  puts "SKIPPING: Nav link for #{singular_name} already exists in #{nav_file}"
1626
1659
  else
1627
1660
  puts "APPENDING: nav link for #{singular_name} #{nav_file}"
@@ -1892,7 +1925,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1892
1925
  if phantom_data[:type] == "radio"
1893
1926
  phantom_data[:choices].each do |choice|
1894
1927
  unless choice[:scope] == ".all"
1895
- res << "\n @#{plural} = @#{plural}#{choice[:scope]} if @q['0'][:#{phantom_key}_search] == \"#{choice[:label]}\""
1928
+ res << "\n @#{plural} = @#{plural}#{choice[:scope]} if @q['0'][:#{phantom_key}_search] == \"#{choice[:label].downcase.gsub(" ","_")}\""
1896
1929
  end
1897
1930
  end
1898
1931
  elsif phantom_data[:type] == "checkboxes"
@@ -1903,7 +1936,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1903
1936
  res << "\n @#{plural} = @#{plural}#{choice[:scope]} if @q['0'][:#{phantom_key}_search__#{choice[:label].gsub(" ", "_").downcase}] == \"1\""
1904
1937
  end
1905
1938
  unless choice[:scope_negative] == ".all"
1906
- res << "\n @#{plural} = @#{plural}#{choice[:scope_negative]} if @q['0'][:#{phantom_key}_search___#{choice[:label].gsub(" ", "_").downcase}] != \"1\""
1939
+ res << "\n @#{plural} = @#{plural}#{choice[:scope_negative]} if @q['0'][:#{phantom_key}_search__#{choice[:label].gsub(" ", "_").downcase}] != \"1\""
1907
1940
  end
1908
1941
  end
1909
1942
  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|
@@ -288,13 +289,13 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
288
289
  end<% end %><% end %>
289
290
 
290
291
  def <%=singular_name%>_params
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? %>
292
+ fields = <%= ((fields_filtered_for_strong_params - @show_only - omit_fields_form) + @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? %>
292
293
  params.require(:<%= testing_name %>).permit(fields)
293
294
  end<% if @update_show_only %>
294
295
 
295
296
  <% unless @no_edit %>
296
297
  def update_<%=singular_name%>_params
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? %>
298
+ fields = <%= ((fields_filtered_for_strong_params - @update_show_only - omit_fields_form) + @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? %>
298
299
  <%= (fields_filtered_for_strong_params - @update_show_only).collect{|col|
299
300
  # TODO : fields not on show only also not invisible should be checked here
300
301
  # for _able? methods and added only when able
@@ -7,7 +7,11 @@
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,7 @@
1
1
  <% if @big_edit %>
2
- <div class="container">
2
+ <% if include_nav_template %><%= @layout_strategy.page_begin %><\%= render partial: "<%= nav_template %>", locals: {nav: "<%= @plural %>"} %><%= @layout_strategy.page_end %><% end %>
3
+
4
+ <div class="container">
3
5
  <div class="row">
4
6
  <div class="col-md-12">
5
7
  <\%= 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 +12,6 @@
10
12
  </div>
11
13
  </div>
12
14
 
13
-
14
-
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">
@@ -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.6.31'
3
+ CURRENT = '0.7.1'
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.31
4
+ version: 0.7.1
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-10-28 00:00:00.000000000 Z
11
+ date: 2025-11-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails