hot-glue 0.6.16 → 0.6.17

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: '09ebf223206d1bf224f02aaafe2796774bfa6b3d61c6892c1b0144ec2a8dd0f3'
4
- data.tar.gz: 1fd04b5e1909ebcf2ecc9eeb5c4e212f7a6878d49c0fe918305e43e441cd73d3
3
+ metadata.gz: a6abeaf2b7456e5f554928c58bb5deae7fb01a0c664ad0d717572f20c6185537
4
+ data.tar.gz: da58696031065cf910116374477ce9f8f3b388bef0a2ad756289953b70a31d8b
5
5
  SHA512:
6
- metadata.gz: c01e39699fe999a7024850b9d738d5e205b4e5abe8c39d3619d10efa74fbed2384ac6cadfa4010d52c0a9fdea3878ae599aa23002cd439d323701f4b8524c3eb
7
- data.tar.gz: 79c20e1f78772f79e396cecfc81990a70b3a0d17988962baebd6c2ea0b10eabedea59ebd8624f34d0ad7ce864d59bf7ad7be2afb8c712ef01f605ab06e272fce
6
+ metadata.gz: 626a8d361bef7d74109406ba5beae46b0daf4db297c2f95fcd92431bc1b6f9ea97d224acb8ef898bb0281b8cb3bc369d5507f6956751da137c4e5ecb4e3a93d5
7
+ data.tar.gz: 3a646740022a5c699051ad99c400178f1abdafc911a9c1e40c400b52a16458f3181e98ed73e0e6db8fc64fdaa8829b93f8432efcf4026873dbb2236708c2308e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.6.15)
4
+ hot-glue (0.6.17)
5
5
  ffaker (~> 2.16)
6
6
  kaminari (~> 1.2)
7
7
  rails (> 5.1)
@@ -140,7 +140,7 @@ GEM
140
140
  mini_mime (1.1.2)
141
141
  mini_portile2 (2.8.4)
142
142
  minitest (5.16.3)
143
- net-imap (0.5.6)
143
+ net-imap (0.5.8)
144
144
  date
145
145
  net-protocol
146
146
  net-pop (0.1.2)
data/README.md CHANGED
@@ -868,6 +868,92 @@ Remember, if there's a corresponding `*_able?` method on the policy, it will be
868
868
  As shown in the method `name_able?` of the example ThingPolicy above, if this field on your policy returns true, the field will be editable. If it returns false, the field will be viewable (read-only).
869
869
 
870
870
 
871
+ ### `--hidden`
872
+
873
+ Separate list of fields.
874
+
875
+ These fields will be hidden from the form but will exist as hidden_field, and so the update will still work.
876
+
877
+
878
+ EXAMPLE:
879
+
880
+ ```
881
+ bin/rails generate hot_glue:scaffold Wrapper --namespace='account_dashboard' --no-nav-menu --big-edit --smart-layout --stimmify --hidden=raw_source
882
+ ```
883
+
884
+ In the `wrappers` folder, I am using a special sticky partial `_edit_within_form.html.erb`, which contains code preserved from build-to-build and included in the form:
885
+
886
+
887
+ ```
888
+ <div class="row" style="position: relative; width: 100%; overflow: auto;">
889
+ <div class="col-md-12">
890
+ <div id="wrapper__raw_source"
891
+ style="position: static">
892
+
893
+ <div id="wrapper__raw_source-toolbar">
894
+
895
+ </div>
896
+
897
+
898
+ <div cols="60"
899
+ data-wrapper-form-target="editor"
900
+ id="wrapper__raw_source-editor" >
901
+ </div>
902
+ </div>
903
+
904
+
905
+ </div>
906
+ </div>
907
+ <div class="col-md-2">
908
+ </div>
909
+ ```
910
+
911
+
912
+ Then, create a `app/javascript/controllers/wrapper_form_controller.js` file with the following code:
913
+
914
+ ```javascript
915
+
916
+
917
+ import { Controller } from "@hotwired/stimulus"
918
+
919
+ import {basicSetup} from "codemirror"
920
+ import {EditorView} from "@codemirror/view"
921
+
922
+ // Connects to data-controller="wrapper-form"
923
+ export default class extends Controller {
924
+ static targets = ['rawSource', 'name', 'nameWrapper', 'editor'];
925
+
926
+ connect() {
927
+ console.log("WrapperFormController connected")
928
+ this.account_id = this.element.dataset['accountId']
929
+ this.crusade_id = this.element.dataset['crusadeId']
930
+ this.wrapper_id = this.element.dataset['wrapperId']
931
+
932
+ const view = new EditorView({
933
+ doc: this.rawSourceTarget.value,
934
+ parent: this.editorTarget,
935
+ extensions: [basicSetup]
936
+ })
937
+
938
+ this.view = view;
939
+ this.element.addEventListener('submit', this.formSubmit.bind(this))
940
+ // this.previewButtonTarget.addEventListener('click', this.previewClick.bind(this))
941
+ }
942
+
943
+ formSubmit(event) {
944
+ this.rawSourceTarget.value = this.view.state.doc.toString();
945
+ }
946
+ }
947
+ ```
948
+
949
+ Notice we are also using `--stimmify` to decorate the form with a Stimulus controller.
950
+
951
+ The code above uses Code Mirror to act as a code editor, which requires pulling the value off the hidden form element (putting it into the code mirror interface) and pushing it back into the hidden form element when the Submit button is clicked.
952
+
953
+
954
+
955
+
956
+
871
957
  ### `--ujs_syntax=true` (Default is set automatically based on whether you have turbo-rails installed)
872
958
 
873
959
  If you are pre-Turbo (UJS), your delete buttons will come out like this:
@@ -1381,6 +1467,54 @@ Then run:
1381
1467
  This will 1) copy the dropzone_controller.js file into your app and 2) add the dropzone css into your app's application.css or application.bootstrap.css file.
1382
1468
 
1383
1469
 
1470
+ ### Attach Stimulus JS Controllers to Your Forms with `--stimmify` or `--stimmify=xyz`
1471
+
1472
+ Automatically build the new and edit form with `data-controller='xyz'` to attach cooresponding stimulus controllers.
1473
+
1474
+ If you use the shorthand (specify no `=`) your stimulus controller's name will be inferred from the Singular form of the scaffolding beild built, with dashes for underscores, and ending with `-form`
1475
+
1476
+ `@singular.gsub("_", "-") + "-form"`
1477
+
1478
+ (For example, `rails g hot_glue:scaffold Thing --stimmy` generates a form that looks like
1479
+
1480
+ ```
1481
+ <%= form_with model: thing,
1482
+ url: things_path(account,crusade,email_template),
1483
+ html: {
1484
+ 'data-controller': "thing-form"
1485
+ }
1486
+ %>
1487
+ ...
1488
+
1489
+ ```
1490
+
1491
+ Note that your fields also appended with `data-thing-target=abc` and also `data-thing-target=abcWrapper`
1492
+ (assuming `thing` is the scaffold being built and abc is the field name)
1493
+
1494
+
1495
+ Here, we are building a `thing` scaffold. The field `name` is decorated twice: once for the wrapper span and again for the specific form element itself.
1496
+ ```
1497
+ <span class="" data-thing-form-target="nameWrapper">
1498
+ <input value="asdfadf" autocomplete="off" size="40" class="form-control" type="" data-thing-form-target="name" name="thing[name]" id="thing_name">
1499
+
1500
+
1501
+ <label class="text-muted small form-text" for="">Name</label>
1502
+ </span>
1503
+ ```
1504
+
1505
+ Your stimulus controller will need two targets for each field:
1506
+
1507
+ ```
1508
+ static targets = ['name', 'nameWrapper'];
1509
+ ```
1510
+ You can interact with the wrapper for things like clicks or hovers, or to hide/show the entire box surrounding the form element.
1511
+
1512
+ Use the form field element itself to affect things like enabled or the value of the field.
1513
+
1514
+
1515
+ For a crash course on Stimulus, see
1516
+ https://jasonfleetwoodboldt.com/courses/rails-7-crash-course/rails-7-stimulus-js-basics-with-importmap-rails/
1517
+
1384
1518
 
1385
1519
 
1386
1520
  ### `--factory-creation={ ... }`
@@ -1768,6 +1902,43 @@ These automatic pickups for partials are detected at build time. This means that
1768
1902
 
1769
1903
  # VERSION HISTORY
1770
1904
 
1905
+ #### 2025-05-0097 - v0.6.17
1906
+
1907
+
1908
+ • Adds Stimulus JS & `--stimmify` or `--stimmify=xyz`
1909
+
1910
+ Automatically build the new and edit form with `data-controller='xyz'` to attach stimulus
1911
+
1912
+ If you use the shorthand (specify no `=`) your stimulus controller's name will be inferred from the Singular form of the scaffolding beild built, with dashes for underscores, and ending with `-form`
1913
+
1914
+ (For example, `rails g hot_glue:scaffold Thing --stimmy` generates a form that looks like
1915
+
1916
+ ```
1917
+ <%= form_with model: thing,
1918
+ url: things_path(account,crusade,email_template),
1919
+ html: {
1920
+ 'data-controller': "thing-form"
1921
+ }
1922
+ %>
1923
+ ...
1924
+
1925
+ ```
1926
+
1927
+ Note that your fields also appended with `data-thing-target=abc` and also `data-thing-target=abcWrapper`
1928
+
1929
+ See section "Attach Stimulus JS Controllers to Your Forms with `--stimmify` or `--stimmify=xyz`"
1930
+
1931
+ For a crash course on Stimulus, see
1932
+ https://jasonfleetwoodboldt.com/courses/rails-7-crash-course/rails-7-stimulus-js-basics-with-importmap-rails/
1933
+
1934
+
1935
+ • Adds `--hidden` option
1936
+ Pass a list of fields, like include or show-only. This will make the field hidden on the form *but still updated via its submission*
1937
+
1938
+
1939
+
1940
+
1941
+
1771
1942
  #### 2025-03-31 v0.6.16
1772
1943
 
1773
1944
  • Bootstrap Tab Panes For Downnested Portals
@@ -53,25 +53,25 @@ class FieldFactory
53
53
  raise "Field type could be identified #{name} "
54
54
  end
55
55
 
56
- @field = field_class.new(name: name,
57
- layout_strategy: generator.layout_strategy,
58
- form_placeholder_labels: generator.form_placeholder_labels,
59
- form_labels_position: generator.form_labels_position,
60
- ownership_field: generator.ownership_field,
61
- hawk_keys: generator.hawk_keys,
62
- auth: generator.auth,
63
- class_name: generator.singular_class,
64
- alt_lookup: generator.alt_lookups[name] || nil,
65
- singular: generator.singular,
66
- self_auth: generator.self_auth,
67
- update_show_only: generator.update_show_only,
68
- attachment_data: generator.attachments[name.to_sym],
69
- sample_file_path: generator.sample_file_path,
70
- modify_as: generator.modify_as[name.to_sym] || nil,
71
- plural: generator.plural,
72
- display_as: generator.display_as[name.to_sym] || nil,
73
- default_boolean_display: generator.default_boolean_display,
74
- namespace: generator.namespace_value,
75
- pundit: generator.pundit )
56
+ @field = field_class.new(scaffold: generator, name: name)
57
+ # layout_strategy: generator.layout_strategy,
58
+ # form_placeholder_labels: generator.form_placeholder_labels,
59
+ # form_labels_position: generator.form_labels_position,
60
+ # ownership_field: generator.ownership_field,
61
+ # hawk_keys: generator.hawk_keys,
62
+ # auth: generator.auth,
63
+ # class_name: generator.singular_class,
64
+ # alt_lookup: generator.alt_lookups[name] || nil,
65
+ # singular: generator.singular,
66
+ # self_auth: generator.self_auth,
67
+ # update_show_only: generator.update_show_only,
68
+ # attachment_data: generator.attachments[name.to_sym],
69
+ # sample_file_path: generator.sample_file_path,
70
+ # modify_as: generator.modify_as[name.to_sym] || nil,
71
+ # plural: generator.plural,
72
+ # display_as: generator.display_as[name.to_sym] || nil,
73
+ # default_boolean_display: generator.default_boolean_display,
74
+ # namespace: generator.namespace_value,
75
+ # pundit: generator.pundit )
76
76
  end
77
77
  end
@@ -5,17 +5,8 @@ class AssociationField < Field
5
5
 
6
6
  attr_accessor :assoc_name, :assoc_class, :assoc, :alt_lookup
7
7
 
8
- def initialize( alt_lookup: ,
9
- class_name: ,
10
- default_boolean_display:, display_as: ,
11
- name: , singular: ,
12
- update_show_only: ,
13
- hawk_keys: , auth: , sample_file_path:, ownership_field: ,
14
- attachment_data: nil , layout_strategy: , form_placeholder_labels: nil,
15
- form_labels_position:, modify_as: , self_auth: , namespace:, pundit: , plural: )
8
+ def initialize(scaffold: , name: )
16
9
  super
17
-
18
-
19
10
  @assoc_model = eval("#{class_name}.reflect_on_association(:#{assoc})")
20
11
 
21
12
  if assoc_model.nil?
@@ -97,14 +88,14 @@ class AssociationField < Field
97
88
  def form_field_output
98
89
  assoc_name = name.to_s.gsub("_id","")
99
90
  assoc = eval("#{class_name}.reflect_on_association(:#{assoc_name})")
100
-
101
- if alt_lookup
91
+ if alt_lookup.keys.include?(name.to_sym)
102
92
  alt = alt_lookup[:lookup_as]
103
93
  assoc_name = name.to_s.gsub("_id","")
104
94
  assoc = eval("#{class_name}.reflect_on_association(:#{assoc_name})")
105
95
 
106
96
  alt = alt_lookup[:lookup_as]
107
- "<%= f.text_field :__lookup_#{alt}, value: @#{singular}.#{assoc_name}.try(:#{alt}), placeholder: \"search by #{alt}\" %>"
97
+ parts = name.split('_')
98
+ "<%= f.text_field :__lookup_#{alt}, value: @#{singular}.#{assoc_name}.try(:#{alt}), placeholder: \"search by #{alt}\" " + (stimmify ? ", 'data-#{@stimmify}-target': '#{camelcase_name}' " : "") + "%>"
108
99
 
109
100
  # if modify_as
110
101
  # modified_display_output
@@ -156,8 +147,17 @@ class AssociationField < Field
156
147
  end
157
148
 
158
149
 
150
+ if @stimmify
151
+ col_target = HotGlue.to_camel_case(name.to_s.gsub("_", " "))
152
+ data_attr = ", data: {'#{@stimmify}-target': '#{col_target}'} "
153
+ els
154
+ data_attr = ""
155
+ end
156
+
157
+
158
+
159
159
  (is_owner ? "<% unless @#{assoc_name} %>\n" : "") +
160
- " <%= f.collection_select(:#{name}, #{hawked_association}, :id, :#{display_column}, {prompt: true, selected: #{singular}.#{name} }, class: 'form-control') %>\n" +
160
+ " <%= f.collection_select(:#{name}, #{hawked_association}, :id, :#{display_column}, { prompt: true, selected: #{singular}.#{name} }, class: 'form-control'#{data_attr}) %>\n" +
161
161
  (is_owner ? "<% else %>\n <%= @#{assoc_name}.#{display_column} %>" : "") +
162
162
  (is_owner ? "\n<% end %>" : "")
163
163
  end
@@ -1,25 +1,6 @@
1
1
  class AttachmentField < Field
2
2
  attr_accessor :attachment_data
3
- def initialize(alt_lookup:,
4
- attachment_data:,
5
- plural:,
6
- auth:,
7
- class_name:,
8
- display_as:, singular:,
9
- default_boolean_display: ,
10
- form_placeholder_labels: ,
11
- form_labels_position:,
12
- hawk_keys:,
13
- layout_strategy: ,
14
- name:,
15
- namespace:,
16
- modify_as:,
17
- ownership_field:,
18
- pundit: ,
19
- sample_file_path: nil,
20
- self_auth:,
21
- update_show_only:
22
- )
3
+ def initialize(scaffold:, name:)
23
4
  super
24
5
 
25
6
  @attachment_data = attachment_data
@@ -23,9 +23,9 @@ class BooleanField < Field
23
23
 
24
24
  def radio_button_display
25
25
  " <%= f.radio_button(:#{name}, '0', checked: #{singular}.#{name} ? '' : 'checked', class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" +
26
- " <%= f.label(:#{name}, value: '#{modify_binary? && modify_as[:binary][:falsy] || 'No'}', for: '#{singular}_#{name}_0') %>\n" +
26
+ " <%= f.label(:#{name}, value: '#{modify_binary? && modify_as[name.to_sym][:binary][:falsy] || 'No'}', for: '#{singular}_#{name}_0') %>\n" +
27
27
  " <br /> <%= f.radio_button(:#{name}, '1', checked: #{singular}.#{name} ? 'checked' : '' , class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" +
28
- " <%= f.label(:#{name}, value: '#{modify_binary? && modify_as[:binary][:truthy] || 'Yes'}', for: '#{singular}_#{name}_1') %>\n"
28
+ " <%= f.label(:#{name}, value: '#{modify_binary? && modify_as[name.to_sym][:binary][:truthy] || 'Yes'}', for: '#{singular}_#{name}_1') %>\n"
29
29
  end
30
30
 
31
31
  def checkbox_display
@@ -38,16 +38,16 @@ class BooleanField < Field
38
38
 
39
39
  def form_field_display
40
40
  if display_boolean_as.nil?
41
-
42
41
  end
42
+
43
43
  "<span class='#{@layout_strategy.form_checkbox_wrapper_class} #{'form-switch' if display_boolean_as == 'switch'}'>\n" +
44
44
  (if display_boolean_as == 'radio'
45
- radio_button_display
46
- elsif display_boolean_as == 'checkbox'
47
- checkbox_display
48
- elsif display_boolean_as == 'switch'
49
- switch_display
50
- end) + "</span> \n"
45
+ radio_button_display
46
+ elsif display_boolean_as == 'checkbox'
47
+ checkbox_display
48
+ elsif display_boolean_as == 'switch'
49
+ switch_display
50
+ end) + "</span> \n"
51
51
  end
52
52
 
53
53
  def form_field_output
@@ -59,9 +59,9 @@ class BooleanField < Field
59
59
  "<% if #{singular}.#{name}.nil? %>
60
60
  <span class=''>MISSING</span>
61
61
  <% elsif #{singular}.#{name} %>
62
- #{modify_as[:binary][:truthy]}
62
+ #{modify_as[name.to_sym][:binary][:truthy]}
63
63
  <% else %>
64
- #{modify_as[:binary][:falsy]}
64
+ #{modify_as[name.to_sym][:binary][:falsy]}
65
65
  <% end %>"
66
66
  else
67
67
  "<% if #{singular}.#{name}.nil? %>
@@ -75,11 +75,11 @@ class BooleanField < Field
75
75
  end
76
76
 
77
77
  def truthy_value
78
- modify_as[:binary][:truthy] || 'Yes'
78
+ modify_as[name.to_sym][:binary][:truthy] || 'Yes'
79
79
  end
80
80
 
81
81
  def falsy_value
82
- modify_as[:binary][:falsy] || 'No'
82
+ modify_as[name.to_sym][:binary][:falsy] || 'No'
83
83
  end
84
84
 
85
85
  def label_class
@@ -10,8 +10,10 @@ class DateField < Field
10
10
 
11
11
 
12
12
  def form_field_output
13
- "<%= date_field_localized(f, :#{name}, #{singular}.#{name}, label: '#{ name.to_s.humanize }') %>"
14
- end
13
+ parts = name.to_s.split('_')
14
+ camelcase_name = parts.map(&:capitalize).join
15
+ "<%= date_field_localized(f, :#{name}, #{singular}.#{name}, label: '#{ name.to_s.humanize }'" + (stimmify ? ", html: {'data-#{@stimmify}-target': '#{camelcase_name}'}" : "") + ") %>"
16
+ end
15
17
 
16
18
  def line_field_output
17
19
  "<% unless #{singular}.#{name}.nil? %>
@@ -31,6 +31,11 @@ class EnumField < Field
31
31
  end
32
32
 
33
33
  def form_field_output
34
+ if @stimmify
35
+ col_target = HotGlue.to_camel_case(name.to_s.gsub("_", " "))
36
+ data_attr = ", data: {'#{@stimmify}-target': '#{col_target}'} "
37
+ end
38
+
34
39
  enum_type = eval("#{class_name}.columns.select{|x| x.name == '#{name}'}[0].sql_type")
35
40
 
36
41
  if eval("defined? #{class_name}.#{enum_type}_labels") == "method"
@@ -39,7 +44,7 @@ class EnumField < Field
39
44
  enum_definer = "#{class_name}.defined_enums['#{name}']"
40
45
  end
41
46
 
42
- res = "<%= f.collection_select(:#{name}, enum_to_collection_select(#{enum_definer}), :key, :value, {include_blank: true, selected: #{singular}.#{name} }, class: 'form-control') %>"
47
+ res = "<%= f.collection_select(:#{name}, enum_to_collection_select(#{enum_definer}), :key, :value, {include_blank: true, selected: #{singular}.#{name} }, class: 'form-control' #{data_attr} )%>"
43
48
 
44
49
 
45
50
  if modify_as && modify_as[:enum] == :partials
@@ -5,50 +5,37 @@ class Field
5
5
  :hawk_keys, :layout_strategy, :limit, :modify_as, :name, :object, :sample_file_path,
6
6
  :self_auth,
7
7
  :singular_class, :singular, :sql_type, :ownership_field,
8
- :update_show_only, :namespace, :pundit, :plural
8
+ :update_show_only, :namespace, :pundit, :plural,
9
+ :stimmify, :hidden, :attachment_data
10
+
9
11
 
10
12
  def initialize(
11
- auth: ,
12
- attachment_data: nil,
13
- class_name: ,
14
- alt_lookup: ,
15
- default_boolean_display: ,
16
- display_as: ,
17
- form_labels_position:,
18
- form_placeholder_labels: ,
19
- hawk_keys: nil,
20
- layout_strategy: ,
21
- modify_as: , #note non-standard naming as to avoid collision with Ruby reserved word modify
22
- name: ,
23
- ownership_field: ,
24
- sample_file_path: nil,
25
- singular: ,
26
- update_show_only:,
27
- self_auth:,
28
- namespace:,
29
- pundit: ,
30
- plural:
13
+ scaffold:, name:
14
+
31
15
  )
32
16
  @name = name
33
- @layout_strategy = layout_strategy
34
- @alt_lookup = alt_lookup
35
- @singular = singular
36
- @class_name = class_name
37
- @update_show_only = update_show_only
38
- @hawk_keys = hawk_keys
39
- @auth = auth
40
- @sample_file_path = sample_file_path
41
- @form_placeholder_labels = form_placeholder_labels
42
- @ownership_field = ownership_field
43
- @form_labels_position = form_labels_position
44
- @modify_as = modify_as
45
- @display_as = display_as
46
- @pundit = pundit
47
- @plural = plural
48
-
49
- @self_auth = self_auth
50
- @default_boolean_display = default_boolean_display
51
- @namespace = namespace
17
+ @layout_strategy = scaffold.layout_strategy
18
+ @alt_lookup = scaffold.alt_lookups
19
+ @singular = scaffold.singular
20
+ @class_name = scaffold.singular_class
21
+ @update_show_only = scaffold.update_show_only
22
+ @hawk_keys = scaffold.hawk_keys
23
+ @auth = scaffold.auth
24
+ @sample_file_path = scaffold.sample_file_path
25
+ @form_placeholder_labels = scaffold.form_placeholder_labels
26
+ @ownership_field = scaffold.ownership_field
27
+ @form_labels_position = scaffold.form_labels_position
28
+ @modify_as = scaffold.modify_as
29
+ @display_as = scaffold.display_as
30
+ @pundit = scaffold.pundit
31
+ @plural = scaffold.plural
32
+ @self_auth = scaffold.self_auth
33
+ @default_boolean_display = scaffold.default_boolean_display
34
+ @namespace = scaffold.namespace_value
35
+ @stimmify = scaffold.stimmify
36
+ @hidden = scaffold.hidden
37
+ @attachment_data = scaffold.attachments[name.to_sym]
38
+
52
39
 
53
40
  # TODO: remove knowledge of subclasses from Field
54
41
  unless self.class == AttachmentField || self.class == RelatedSetField
@@ -125,7 +112,7 @@ class Field
125
112
  end
126
113
 
127
114
  def viewable_output
128
- if modify_as
115
+ if modify_as[:modify]
129
116
  modified_display_output(show_only: true)
130
117
  else
131
118
  field_view_output
@@ -179,20 +166,34 @@ class Field
179
166
  if modify_as && modify_as[:timezone]
180
167
  "<%= f.time_zone_select :#{name}, ActiveSupport::TimeZone.all, {}, {class: 'form-control'} %>"
181
168
  else
182
- " <%= f.text_field :#{name}, value: #{singular}.#{name}, autocomplete: 'off', size: #{width}, class: 'form-control', type: '#{type}'" + (form_placeholder_labels ? ", placeholder: '#{name.to_s.humanize}'" : "") + " %>\n " + "\n"
169
+ parts = name.split('_')
170
+ camelcase_name = parts.first + parts[1..].map(&:capitalize).join
171
+ " <%= f.text_field :#{name}, value: #{singular}.#{name}, autocomplete: 'off', size: #{width}, class: 'form-control', type: '#{type}'" + (form_placeholder_labels ? ", placeholder: '#{name.to_s.humanize}'" : "") + (stimmify ? ", 'data-#{@stimmify}-target': '#{camelcase_name}' " : "") + " %>\n " + "\n"
183
172
  end
184
173
  end
185
174
 
175
+ def hidden_output
176
+ parts = name.split('_')
177
+ camelcase_name = parts.first + parts[1..].map(&:capitalize).join
178
+ "<%= f.hidden_field :#{name}, value: #{singular}.#{name} " +
179
+ (@stimmify ? ", 'data-#{@stimmify}-target': '#{camelcase_name}' " : "") +
180
+ " %>"
181
+ end
182
+
186
183
  def text_area_output(field_length, extra_classes: "")
187
184
  lines = field_length % 40
188
185
  if lines > 5
189
186
  lines = 5
190
187
  end
191
- "<%= f.text_area :#{name}, class: 'form-control#{extra_classes}', autocomplete: 'off', cols: 40, rows: '#{lines}'" + ( form_placeholder_labels ? ", placeholder: '#{name.to_s.humanize}'" : "") + " %>"
188
+
189
+ parts = name.split('_')
190
+ camelcase_name = parts.first + parts[1..].map(&:capitalize).join
191
+ "<%= f.text_area :#{name}, class: 'form-control#{extra_classes}', autocomplete: 'off', cols: 40, rows: '#{lines}'" + ( form_placeholder_labels ? ", placeholder: '#{name.to_s.humanize}'" : "") +
192
+ (@stimmify ? ", 'data-#{@stimmify}-target': '#{camelcase_name}' " : "") + " %>"
192
193
  end
193
194
 
194
- def modify_binary? # safe
195
- !!(modify_as && modify_as[:binary])
195
+ def modify_binary?
196
+ !!(modify_as && modify_as[name.to_sym] && modify_as[name.to_sym][:binary])
196
197
  end
197
198
 
198
199
  def display_boolean_as
@@ -201,8 +202,8 @@ class Field
201
202
  @default_boolean_display = "radio"
202
203
  end
203
204
 
204
- if display_as
205
- return display_as[:boolean] || "radio"
205
+ if display_as[name.to_sym]
206
+ return display_as[name.to_sym][:boolean] || "radio"
206
207
  else
207
208
  return @default_boolean_display
208
209
  end
@@ -2,13 +2,7 @@ class RelatedSetField < Field
2
2
 
3
3
  attr_accessor :assoc_name, :assoc_class, :assoc
4
4
 
5
- def initialize( class_name: , default_boolean_display:, display_as: ,
6
- name: , singular: , plural:,
7
- alt_lookup: ,
8
- update_show_only: ,
9
- hawk_keys: , auth: , sample_file_path:, ownership_field: ,
10
- attachment_data: nil , layout_strategy: , form_placeholder_labels: nil,
11
- form_labels_position:, modify_as: , self_auth: , namespace:, pundit:)
5
+ def initialize( scaffold: , name: )
12
6
  super
13
7
 
14
8
  @related_set_model = eval("#{class_name}.reflect_on_association(:#{name})")
@@ -37,12 +31,13 @@ class RelatedSetField < Field
37
31
 
38
32
  def form_field_output
39
33
  disabled_syntax = +""
34
+
40
35
  if pundit
41
36
  disabled_syntax << ", {disabled: ! #{class_name}Policy.new(#{auth}, @#{singular}).role_ids_able?}"
42
37
  end
43
38
  " <%= f.collection_check_boxes :#{association_ids_method}, #{association_class_name}.all, :id, :label, {}#{disabled_syntax} do |m| %>
44
- <%= m.check_box %> <%= m.label %><br />
45
- <% end %>"
39
+ <%= m.check_box %> <%= m.label %><br />
40
+ <% end %>"
46
41
  end
47
42
 
48
43
  def association_ids_method
@@ -10,7 +10,8 @@ module HotGlue
10
10
  :form_placeholder_labels, :hawk_keys, :update_show_only,
11
11
  :attachments, :show_only, :columns_map, :pundit, :related_sets,
12
12
  :search, :search_fields, :search_query_fields, :search_position,
13
- :form_path, :layout_object, :search_clear_button, :search_autosearch
13
+ :form_path, :layout_object, :search_clear_button, :search_autosearch,
14
+ :stimmify, :stimmify_camel, :hidden
14
15
 
15
16
 
16
17
  def initialize(singular:, singular_class: ,
@@ -22,7 +23,7 @@ module HotGlue
22
23
  update_show_only:, attachments: , columns_map:, pundit:, related_sets:,
23
24
  search:, search_fields:, search_query_fields: , search_position:,
24
25
  search_clear_button:, search_autosearch:, layout_object:,
25
- form_path: )
26
+ form_path: , stimmify: , stimmify_camel:, hidden: )
26
27
 
27
28
 
28
29
  @form_path = form_path
@@ -31,6 +32,9 @@ module HotGlue
31
32
  @search_by_query = search_query_fields
32
33
  @search_position = search_position
33
34
  @layout_object = layout_object
35
+ @stimmify = stimmify
36
+ @stimmify_camel = stimmify_camel
37
+ @hidden = hidden
34
38
 
35
39
  @singular = singular
36
40
  @singular_class = singular_class
@@ -169,11 +173,21 @@ module HotGlue
169
173
 
170
174
  @tinymce_stimulus_controller = (columns_map[col].modify_as == {tinymce: 1} ? "data-controller='tiny-mce' " : "")
171
175
 
172
- add_spaces_each_line( "\n <span #{@tinymce_stimulus_controller}class='<%= \"alert alert-danger\" if #{singular}.errors.details.keys.include?(:#{field_error_name}) %>' #{'style="display: inherit;"'} >\n" +
173
- add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
174
- + field_result +
175
- (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
176
- "\n </span>\n ", 2)
176
+ if @stimmify
177
+ col_target = HotGlue.to_camel_case(col.to_s.gsub("_", " "))
178
+ data_attr = " data-#{@stimmify}-target='#{col_target}Wrapper'"
179
+ end
180
+
181
+ unless hidden.include?(col.to_sym)
182
+ add_spaces_each_line( "\n <span #{@tinymce_stimulus_controller}class='<%= \"alert alert-danger\" if #{singular}.errors.details.keys.include?(:#{field_error_name}) %>' #{data_attr} >\n" +
183
+ add_spaces_each_line( (form_labels_position == 'before' ? (the_label || "") + "<br />\n" : "") +
184
+ + field_result +
185
+ (form_labels_position == 'after' ? ( columns_map[col].newline_after_field? ? "<br />\n" : "") + (the_label || "") : "") , 4) +
186
+ "\n </span>\n ", 2)
187
+ else
188
+ columns_map[col].hidden_output
189
+ end
190
+
177
191
 
178
192
  }.join("") + "\n </div>"
179
193
  }.join("\n")
@@ -214,6 +228,7 @@ module HotGlue
214
228
  if eval("#{singular_class}.columns_hash['#{col}']").nil? && !attachments.keys.include?(col) && !related_sets.include?(col)
215
229
  raise "Can't find column '#{col}' on #{singular_class}, are you sure that is the column name?"
216
230
  end
231
+
217
232
  field_output = columns_map[col].line_field_output
218
233
 
219
234
  label = "<label class='small form-text text-muted'>#{col.to_s.humanize}</label>"
@@ -28,7 +28,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
28
28
  :layout_strategy, :form_placeholder_labels,
29
29
  :form_labels_position, :no_nav_menu, :pundit,
30
30
  :self_auth, :namespace_value, :record_scope, :related_sets,
31
- :search_clear_button, :search_autosearch, :include_object_names
31
+ :search_clear_button, :search_autosearch, :include_object_names,
32
+ :stimmify, :stimmify_camel, :hidden
32
33
  # important: using an attr_accessor called :namespace indirectly causes a conflict with Rails class_name method
33
34
  # so we use namespace_value instead
34
35
 
@@ -56,6 +57,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
56
57
  class_option :big_edit, type: :boolean, default: false
57
58
  class_option :show_only, type: :string, default: ""
58
59
  class_option :update_show_only, type: :string, default: ""
60
+ class_option :hidden, type: :string, default: ""
59
61
  class_option :ujs_syntax, type: :boolean, default: nil
60
62
  class_option :downnest, type: :string, default: nil
61
63
  class_option :magic_buttons, type: :string, default: nil
@@ -102,6 +104,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
102
104
  class_option :include_object_names, type: :boolean, default: false
103
105
  class_option :new_button_position, type: :string, default: 'above'
104
106
  class_option :downnest_shows_headings, type: :boolean, default: nil
107
+ class_option :stimmify, type: :string, default: nil
105
108
 
106
109
 
107
110
  # SEARCH OPTIONS
@@ -221,25 +224,18 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
221
224
  @include_fields += options['include'].split(":").collect { |x| x.split(",") }.flatten.collect(&:to_sym)
222
225
  end
223
226
 
224
- # @show_only_data = {}
225
- # if !options['show_only'].empty?
226
- # show_only_input = options['show_only'].split(",")
227
- # show_only_input.each do |setting|
228
- # if setting.include?("[")
229
- # setting =~ /(.*)\[(.*)\]/
230
- # key, lookup_as = $1, $2
231
- # @show_only_data[key.to_sym] = {cast: $2 }
232
- # else
233
- # @show_only_data[setting.to_sym] = {cast: nil}
234
- # end
235
- # end
236
- # end
227
+
237
228
 
238
229
  @show_only = options['show_only'].split(",").collect(&:to_sym)
239
230
  if @show_only.any?
240
231
  puts "show only field #{@show_only}}"
241
232
  end
242
233
 
234
+ @hidden = options['hidden'].split(",").collect(&:to_sym)
235
+ if @hidden.any?
236
+ puts "hidden fields #{@hidden}}"
237
+ end
238
+
243
239
  @modify_as = {}
244
240
  if !options['modify'].empty?
245
241
  modify_input = options['modify'].split(",")
@@ -441,6 +437,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
441
437
  @related_sets = {}
442
438
  related_set_input.each do |setting|
443
439
  name = setting.to_sym
440
+ byebug
444
441
  association_ids_method = eval("#{singular_class}.reflect_on_association(:#{setting.to_sym})").class_name.underscore + "_ids"
445
442
  class_name = eval("#{singular_class}.reflect_on_association(:#{setting.to_sym})").class_name
446
443
 
@@ -524,7 +521,11 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
524
521
  # { key: value }
525
522
  # : nil}.compact
526
523
 
527
-
524
+ @stimmify = options['stimmify']
525
+ if @stimmify === "stimmify"
526
+ @stimmify = @singular.gsub("_", "-") + "-form"
527
+ @stimify_camel = @stimmify.camelize
528
+ end
528
529
 
529
530
  # build a new polymorphic object
530
531
  @associations = []
@@ -649,7 +650,10 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
649
650
  search_position: @search_position,
650
651
  search_clear_button: @search_clear_button,
651
652
  search_autosearch: @search_autosearch,
652
- form_path: form_path_new_helper
653
+ form_path: form_path_new_helper,
654
+ stimmify: @stimmify,
655
+ stimmify_camel: @stimmify_camel,
656
+ hidden: @hidden
653
657
  )
654
658
  elsif @markup == "slim"
655
659
  raise(HotGlue::Error, "SLIM IS NOT IMPLEMENTED")
@@ -1085,6 +1089,17 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1085
1089
  top_level: top_level)
1086
1090
  end
1087
1091
 
1092
+ def edit_parent_path_helper
1093
+ # the path to the edit route of the PARENT
1094
+ if @nested_set.any? && @nested
1095
+ "edit_#{@namespace + "_" if @namespace}#{(@nested_set.collect { |x| x[:singular] }.join("_") + "_" if @nested_set.any?)}path(" +
1096
+ "#{@nested_set.collect { |x| x[:singular] }.join(", ")}" + ")"
1097
+
1098
+ else
1099
+ "edit_#{@namespace + "_" if @namespace}path"
1100
+ end
1101
+ end
1102
+
1088
1103
  def datetime_fields_list
1089
1104
  @columns.select do |col|
1090
1105
  if @the_object.columns_hash[col.to_s]
@@ -4,7 +4,9 @@
4
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
- <\%= form_with model: <%= singular %>, url: <%= form_path_edit_helper %><%= ", html: {'data-turbo': false}" if @big_edit %> do |f| %>
7
+ <\%= form_with model: <%= singular %>,
8
+ <% if @stimmify %> data: {controller: '<%= @stimmify %>' },
9
+ <% end %>url: <%= form_path_edit_helper %><%= ", html: {'data-turbo': false}" if @big_edit %> do |f| %>
8
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 %> \%>
9
11
  <% 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 %>
10
12
  <\% end %>
@@ -2,7 +2,8 @@
2
2
  <%= form_fields_html %>
3
3
 
4
4
  <div class="<%= @layout_strategy.column_classes_for_button_column %>">
5
- <\%= link_to "Cancel", <%= path_helper_plural %>, {class: "btn btn-secondary"} %><% if @no_field_form %>
5
+ <\%= link_to "Cancel", <%=
6
+ @nested_set.none? ? path_helper_plural : edit_parent_path_helper %>, {class: "btn btn-secondary"} %><% if @no_field_form %>
6
7
  <\%= f.hidden_field "_________" %><% end %>
7
8
  <\%= f.submit "Save", class: "btn btn-primary pull-right" %>
8
9
  </div>
@@ -2,7 +2,9 @@
2
2
  <h3>
3
3
  <%= @new_form_heading %>
4
4
  </h3>
5
- <\%= form_with model: <%= singular %>, url: <%= form_path_new_helper %>, method: :post<%= @display_edit_after_create ? ", html: {'data-turbo': false}" : "" %> do |f| \%>
5
+ <\%= form_with model: <%= singular %>,
6
+ <% if @stimmify %>data: {controller: '<%= @stimmify %>' },
7
+ <% end %>url: <%= form_path_new_helper %>, method: :post<%= @display_edit_after_create ? ", html: {'data-turbo': false}" : "" %> do |f| \%>
6
8
  <\%= render partial: "<%= namespace_with_slash + @controller_build_folder %>/form",
7
9
  locals: { <%= singular %>: <%= singular %>, f: f}<%= @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}}: {})" }.join %> \%>
8
10
 
data/lib/hot-glue.rb CHANGED
@@ -10,6 +10,18 @@ module HotGlue
10
10
  class Error < StandardError
11
11
  end
12
12
 
13
+
14
+ def self.to_camel_case(str)
15
+ words = str.split(/[^a-zA-Z0-9]/) # split by non-alphanumeric characters
16
+ return '' if words.empty?
17
+
18
+ first_word = words.first.downcase
19
+ rest_words = words[1..-1].map { |w| w.capitalize }
20
+
21
+ first_word + rest_words.join
22
+ end
23
+
24
+
13
25
  def self.construct_downnest_object(input)
14
26
  res = input.split(",").map { |child|
15
27
  child_name = child.gsub("+","")
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.6.16'
3
+ CURRENT = '0.6.17'
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.16
4
+ version: 0.6.17
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-03-31 00:00:00.000000000 Z
11
+ date: 2025-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails