hot-glue 0.6.26 → 0.6.27

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: 0cfc8d749ec345dbcb5d02abf0c850e80a4c3265f02c259f719022a8125fdd12
4
- data.tar.gz: 02ec813fd1529fd21efacab451c9ce1f5c2eaa5704177ade118f9dff0f412449
3
+ metadata.gz: a5a077658d795c25ef41694b36014191f2590351f6a1fa87e8419b049f03f5ad
4
+ data.tar.gz: f2effbb98f5728fd70e2b7807903f86ad352a1dd3598562c3a450a804dcd51fb
5
5
  SHA512:
6
- metadata.gz: aa56cf2a1e0f84aec69426daab2c7e2d7dc2fcf3771ff2e797a387e3da37995f72f97074b11cc50a90f38ac16f15b10e211b63ef5f2f72f79d51b918456c2ad3
7
- data.tar.gz: b005066d967a6ed1377b4ecefcd62ca2223c0d20d36861fa1b4a03e04309e84c6675d9a07c00caf831b211fd12d0482d54414573fa6cb44c8a7de39aa2131911
6
+ metadata.gz: bc1d59497e67ba88e9da7305bfe5b2664874178243c744860bc30335a6f835808e784cf0b67533bf7c31166c48fdc4e5af0db55382623e9c07a3dc9f754beb81
7
+ data.tar.gz: 6fbc0bfbc0cb9651802525f711566bf3b67ad9b34a4742600412c1addfe69fb20eab93a6bab363776a7eead66245fc120e22a554ecbd45303fafbdaaec49468f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.6.25)
4
+ hot-glue (0.6.26.1)
5
5
  ffaker (~> 2.16)
6
6
  kaminari (~> 1.2)
7
7
  rails (> 5.1)
data/README.md CHANGED
@@ -1620,20 +1620,20 @@ bin/rails generate hot_glue:set_search_interface_install
1620
1620
  ```
1621
1621
 
1622
1622
  _Additional search option for Set Search_
1623
- ##### `--search-fields=aaa,bbb,ccc,ddd,eee`
1623
+ ### `--search-fields=aaa,bbb,ccc,ddd,eee`
1624
1624
  to specify which fields you want to be searchable.
1625
1625
 
1626
1626
 
1627
- ##### `--search-query-fields=aaa,ddd`
1627
+ ### `--search-query-fields=aaa,ddd`
1628
1628
  to specify a list of strings only which will be taken out of the search set and presented in a singular query box (allowing search across multiple string fields)
1629
1629
 
1630
- ##### `--search-position=vertical`
1630
+ ### `--search-position=vertical`
1631
1631
  to specify vertical or horizontal (default: horizontal)
1632
1632
 
1633
- ##### `--search-clear-button` (no option)
1633
+ ### `--search-clear-button` (no option)
1634
1634
  to specify whether to show a clear button to clear the whole search form at once (default: false)
1635
1635
 
1636
- ##### `--search-autosearch` (no option)
1636
+ ### `--search-autosearch` (no option)
1637
1637
  to specify whether to automatically search when the user exit or changes any field (default: false)
1638
1638
 
1639
1639
  examples:
@@ -1657,8 +1657,7 @@ Here's how you would add a search interface to Example #1 in the [Hot Glue Tutor
1657
1657
  bin/rails generate Book --include=name,author_id --search=set --search-fields=name,author_id
1658
1658
  ```
1659
1659
 
1660
-
1661
- `--phantom-search='{type}_{name}[All|choice A:scope_a|choice B:scope_b],radio_yyyy[choice C:scope_c|]`
1660
+ ### `--phantom-search='{type}_{name}[All|choice A:scope_a|choice B:scope_b]`
1662
1661
 
1663
1662
  A phantom search is a search we are doing on this result set that doesn't correspond to a single field. Currently, the only available implementation is for scopes with no arguments, as in the example below. It is called 'phantom' because it could be (probably is) querying fields within the scope, but the search doesn't match up with a single field on your model. (So it's like creating 'phantom' criteria.). Only RADIO type is implemented, dropdown & checkboxes an a way to input a search value passed into the scope as an argument is TBD.
1664
1663
 
@@ -1666,13 +1665,24 @@ A phantom search is a search we are doing on this result set that doesn't corres
1666
1665
 
1667
1666
  {name} is a designation for this phantom search. Should NOT match any field name on your table. This should describe the kind of categorization we are performing.
1668
1667
 
1669
-
1670
1668
  Your phantom search selector will be appended to the search fields and will be treated like a first-class search input, able to be combined with any of the other fields specified in a set search.
1671
1669
 
1672
- After the type & name, comes a block marked by square braces [ ... ] . Within the square braces, each search option is separated by a pipe (|) character. Within each option is a label & ruby scope, separated by a colon (:). The label comes before the colon the ruby scope. The scope should be specified here without a dot should be defined on your model. If there is scope specified, we assume "all", but we still need to specify a label for "All", which is why in the example above "All" has no colon after it.
1670
+ Note that multiple phantom searched can be specified by separating them by comma (`,`), like so:
1671
+
1672
+ `--phantom-search='radio_xxxx[All|choice A:scope_a|choice B:scope_b],radio_yyyy[All|choice W:scope_w|choice Z:scope_z|]`
1673
+ (this creates two phantom searches: `xxxx` and `yyyy` both radio selection lists)
1673
1674
 
1675
+ After the type & name, comes a block marked by square braces [ ... ]
1674
1676
 
1675
- ### `--phantom-search='radio_status[Pending:pending|Rejected:rejected|Accepted:accepted]|All'`
1677
+ Within the square braces, each search option is separated by a pipe (|) character.
1678
+
1679
+ Within each option is a label and ruby scope, separated by a colon (:).
1680
+
1681
+ The label comes before the colon the ruby scope. The ruby scope should be specified here without a dot. Each ruby scope must be defined on your model. If there is no scope specified, we assume "all", but we still need to specify a label for "All", which is why in the example above "All" has no colon after it.
1682
+
1683
+ example
1684
+
1685
+ `--phantom-search='radio_status[Pending:pending|Rejected:rejected|Accepted:accepted|All]'`
1676
1686
 
1677
1687
  On my model, I have these scopes defined:
1678
1688
 
@@ -1682,7 +1692,6 @@ scope :rejected, -> { where.not(rejected_at: nil) }
1682
1692
  scope :not_rejected, -> { where(rejected_at: nil) }
1683
1693
  scope :not_approved, -> { where(approved_at: nil) }
1684
1694
 
1685
-
1686
1695
  This produces a search interface with four options listed as radio buttons:
1687
1696
  ° Pending
1688
1697
  ° Approved
@@ -1693,13 +1702,11 @@ This produces a search interface with four options listed as radio buttons:
1693
1702
 
1694
1703
  The pending, approved, and rejected options will return search results with the corresponding scopes applied. The 'All' option will behave as a no-op, leaving the root search intact (giving all of the other modifications that Hot glue provides in different functionality).
1695
1704
 
1696
-
1697
1705
  #### Predicate Search
1698
1706
  NOT IMPLEMENTED YET
1699
1707
  TODO: implement me
1700
1708
 
1701
1709
 
1702
-
1703
1710
  ### `--stimmify` or `--stimmify=xyz`
1704
1711
 
1705
1712
  Automatically build the new and edit form with `data-controller='xyz'` to attach stimulus
@@ -2203,6 +2210,13 @@ These automatic pickups for partials are detected at build time. This means that
2203
2210
 
2204
2211
 
2205
2212
  # VERSION HISTORY
2213
+ #### 2025-09-24 - v0.6.27
2214
+ - Fixes to namespaced models (this is when the model file has a namespace); it now correctly does not namespace the route (fix to plurality)
2215
+
2216
+ - Fixes timezone awareness on **time field** inputs; your current_user must have a timezone (string) object
2217
+ This jerry-rigs a timezone (based on the current user's timezone & daylight savings time) onto the time object, storing it as-if it is in UTC (even though time fields are not associated with a timezone)
2218
+
2219
+
2206
2220
  #### 2025-09-16 - v0.6.26
2207
2221
  • Phantom Searching
2208
2222
  `--phantom-search='{type}_{name}[All|choice A:scope_a|choice B:scope_b],radio_yyyy[choice C:scope_c|]`
@@ -45,9 +45,7 @@ module HotGlue
45
45
  # returns a TimeZone (https://apidock.com/rails/TimeZone) object
46
46
  if defined?(current_user)
47
47
  if current_user.try(:timezone)
48
- current_user.timezone
49
-
50
- # Time.now.in_time_zone(current_user.timezone.to_i).zone
48
+ ActiveSupport::TimeZone[current_user.timezone]
51
49
  else
52
50
  Rails.application.config.time_zone
53
51
  # Time.zone.name
@@ -58,6 +56,49 @@ module HotGlue
58
56
  end
59
57
  end
60
58
 
59
+ def formatted_time_display(object, method, current_user)
60
+ tz = ActiveSupport::TimeZone[current_user.timezone]
61
+
62
+ t = object.public_send(method)
63
+
64
+ # Build UTC datetime for today + stored time
65
+ utc_datetime = Time.utc(
66
+ Time.now.year,
67
+ Time.now.month,
68
+ Time.now.day,
69
+ t.hour,
70
+ t.min,
71
+ t.sec
72
+ )
73
+
74
+ # Convert to user's timezone (DST-aware)
75
+ local_time = utc_datetime.in_time_zone(tz)
76
+
77
+ local_time.strftime('%-l:%M %p %Z')
78
+ end
79
+
80
+ def formatted_time_field(object, method, current_user)
81
+ tz = ActiveSupport::TimeZone[current_user.timezone]
82
+
83
+ t = object.public_send(method)
84
+
85
+ # Build UTC datetime from the stored time
86
+ utc_datetime = Time.utc(
87
+ Time.now.year,
88
+ Time.now.month,
89
+ Time.now.day,
90
+ t.hour,
91
+ t.min,
92
+ t.sec
93
+ )
94
+
95
+ # Convert to user's timezone (DST-aware)
96
+ local_time = utc_datetime.in_time_zone(tz)
97
+
98
+ # Format for HTML5 <input type="time"> (24h clock, HH:MM)
99
+ local_time.strftime('%H:%M')
100
+ end
101
+
61
102
  def date_to_current_timezone(date, timezone = nil)
62
103
  # used for displaying when in EDIT mode
63
104
  # (this format is how the browser expectes to receive the value='' of the input field)
@@ -79,71 +120,112 @@ module HotGlue
79
120
  "#{sign}#{hour_abs}#{minute_str}"
80
121
  end
81
122
 
82
- def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = {})
83
-
84
- use_timezone = if current_user_object.try(:timezone)
85
- (ActiveSupport::TimeZone[current_user_object.timezone])
86
- else
87
- Time.zone
88
- end
123
+ # def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = {})
124
+ #
125
+ # use_timezone = if current_user_object.try(:timezone)
126
+ # (ActiveSupport::TimeZone[current_user_object.timezone])
127
+ # else
128
+ # Time.zone
129
+ # end
130
+ #
131
+ #
132
+ # uses_dst = (current_user_object.try(:locale_uses_dst)) || false
133
+ #
134
+ # modified_params = modified_params.tap do |params|
135
+ # params.keys.each{|k|
136
+ # if field_list.is_a?(Hash)
137
+ # include_me = field_list[k.to_sym].present?
138
+ # elsif field_list.is_a?(Array)
139
+ # field_list.include?(k.to_sym)
140
+ # end
141
+ #
142
+ # parsables = {
143
+ # datetime: "%Y-%m-%d %H:%M %z",
144
+ # time: "%H:%M %z"
145
+ # }
146
+ #
147
+ #
148
+ # if include_me && params[k].present?
149
+ # input_value = params[k].gsub("T", " ") # e.g. "2025-09-24 14:00" or "14:00"
150
+ #
151
+ # if field_list.is_a?(Array)
152
+ # # Datetime inputs (e.g. datetime-local)
153
+ # parsed_time = Time.strptime(input_value, "%Y-%m-%d %H:%M")
154
+ # parsed_time = parsed_time.utc.change(sec: 0)
155
+ # else
156
+ # case field_list[k.to_sym]
157
+ # when :datetime
158
+ # parsed_time = Time.strptime(input_value, "%Y-%m-%d %H:%M")
159
+ # parsed_time = parsed_time.utc.change(sec: 0)
160
+ # when :time
161
+ #
162
+ # Rails.logger.info("input_value: #{input_value}")
163
+ # # Parse as hour/minute only, no zone
164
+ # t = Time.strptime(input_value, "%H:%M")
165
+ #
166
+ # # Build a UTC time with today's date
167
+ # parsed_time = Time.utc(Time.now.year, Time.now.month, Time.now.day, t.hour, t.min, 0)
168
+ # # Convert back to a plain "time of day" (for DB `time` column)
169
+ # parsed_time = parsed_time.to_time.change(sec: 0)
170
+ # Rails.logger.info("parsed_time: #{parsed_time}")
171
+ #
172
+ # else
173
+ # raise "Unsupported field type: #{field_list[k.to_sym]}"
174
+ # end
175
+ # end
176
+ #
177
+ # Rails.logger.info "parsed_time #{parsed_time}"
178
+ # params[k] = parsed_time
179
+ # end
180
+ # }
181
+ # end
182
+ # modified_params
183
+ # end
89
184
 
185
+ def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = {})
186
+ use_timezone =
187
+ if current_user_object.try(:timezone)
188
+ ActiveSupport::TimeZone[current_user_object.timezone]
189
+ else
190
+ Time.zone
191
+ end
90
192
 
91
- uses_dst = (current_user_object.try(:locale_uses_dst)) || false
193
+ modified_params.tap do |params|
194
+ params.keys.each do |k|
195
+ include_me =
196
+ if field_list.is_a?(Hash)
197
+ field_list[k.to_sym].present?
198
+ elsif field_list.is_a?(Array)
199
+ field_list.include?(k.to_sym)
200
+ end
92
201
 
93
- modified_params = modified_params.tap do |params|
94
- params.keys.each{|k|
95
- if field_list.is_a?(Hash)
96
- include_me = field_list[k.to_sym].present?
97
- elsif field_list.is_a?(Array)
98
- field_list.include?(k.to_sym)
202
+ next unless include_me && params[k].present?
203
+
204
+ input_value = params[k].gsub("T", " ") # "13:00" or "2025-09-24 13:00"
205
+
206
+ case field_list[k.to_sym]
207
+ when :datetime
208
+ # Interpret input as in user's local time zone
209
+ local_time = use_timezone.strptime(input_value, "%Y-%m-%d %H:%M")
210
+ # Convert to UTC for storage
211
+ parsed_time = local_time.utc.change(sec: 0)
212
+ when :time
213
+ # Parse as HH:MM (local wall clock time)
214
+ t = Time.strptime(input_value, "%H:%M")
215
+ # Interpret in user's timezone, with today's date
216
+ local_time = use_timezone.local(Time.now.year, Time.now.month, Time.now.day, t.hour, t.min, 0)
217
+ # Convert to UTC for storage
218
+ parsed_time = local_time.utc
219
+ else
220
+ next
99
221
  end
100
222
 
101
- parsables = {
102
- datetime: "%Y-%m-%d %H:%M %z",
103
- time: "%H:%M %z"
104
- }
105
-
106
-
107
- if include_me && params[k].present?
108
- if use_timezone
109
- natural_offset = use_timezone.formatted_offset
110
- hour = natural_offset.split(":").first.to_i
111
- min = natural_offset.split(":").last.to_i
112
-
113
- hour = hour + 1 if uses_dst && is_dst_now?
114
-
115
- use_offset = format_timezone_offset(hour, min)
116
- parse_date = "#{params[k].gsub("T", " ")} #{use_offset}"
117
-
223
+ Rails.logger.info "input_value: #{input_value}"
224
+ Rails.logger.info "parsed_time: #{parsed_time} (#{parsed_time.zone})"
118
225
 
119
- Rails.logger.info("use_offset: #{use_offset}")
120
-
121
- Rails.logger.info("parse_date: #{parse_date}")
122
-
123
- # note: as according to https://stackoverflow.com/questions/20111413/html5-datetime-local-control-how-to-hide-seconds
124
- # there is no way to set the seconds to 00 in the datetime-local input field
125
- # as I have implemented a "seconds don't matter" solution,
126
- # the only solution is to avoid setting any non-00 datetime values into the database
127
- # if they already exist in your database, you should zero them out
128
- # or apply .change(sec: 0) when displaying them as output in the form
129
- # this will prevent seconds from being added by the browser
130
- if field_list.is_a?(Array)
131
- parsed_time = Time.strptime(parse_date, "%Y-%m-%d %H:%M %z")
132
- else
133
- parsed_time = Time.strptime(parse_date, parsables[field_list[k.to_sym]])
134
- end
135
- Rails.logger.info "parsed_time #{parsed_time}"
136
- Rails.logger.info "Timezone: #{use_timezone.name}"
137
- Rails.logger.info "Offset: #{use_timezone.formatted_offset}"
138
- Rails.logger.info "DST? #{uses_dst} | is_dst_now? => #{is_dst_now?}"
139
- Rails.logger.info "Final offset used: #{use_offset}"
140
-
141
- params[k] = parsed_time
142
- end
143
- end
144
- }
226
+ params[k] = parsed_time
227
+ end
145
228
  end
146
- modified_params
147
229
  end
148
230
 
149
231
  def hawk_params(hawk_schema, modified_params)
@@ -5,15 +5,15 @@ class TimeField < Field
5
5
  end
6
6
 
7
7
  def form_field_output
8
- "<%= time_field_localized(f, :#{name}, #{singular}.#{name}&.in_time_zone(current_user.timezone)&.strftime('%H:%M'), label: '#{ name.to_s.humanize }') %>"
8
+ "<%= time_field_localized(f, :#{name}, formatted_time_field(#{singular}, :#{name}, current_user), label: \"#{ name.to_s.humanize } \#{ current_user.timezone }\") %>"
9
9
  end
10
10
 
11
11
  def line_field_output
12
- "<% unless #{singular}.#{name}.nil? %>
13
- <%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%l:%M %p %Z') %>
14
- <% else %>
15
- <span class=''>MISSING</span>
16
- <% end %>"
12
+ "\n <% unless #{singular}.#{name}.nil? %>
13
+ <%= formatted_time_display(#{singular}, :#{name}, current_user) %>
14
+ <% else %>
15
+ <span class=''>MISSING</span>
16
+ <% end %>\n"
17
17
  end
18
18
 
19
19
  def spec_setup_and_change_act(which_partial = nil)
@@ -202,18 +202,22 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
202
202
  end
203
203
 
204
204
  args = meta_args[0]
205
+
206
+
205
207
  @singular = args.first.tableize.singularize # should be in form hello_world
206
208
 
207
209
  if @singular.include?("/")
208
210
  @singular = @singular.split("/").last
209
211
  end
210
212
 
211
- @plural = (options['plural'] || args.first.tableize.singularize.pluralize) # respects what you set in inflections.rb, to override, use plural option
212
-
213
+ @plural = (options['plural'] || args.first.tableize.singularize.pluralize)
214
+ if @plural.include?("/")
215
+ @plural = @plural.split("/").last
216
+ end
217
+ # respects what you set in inflections.rb, to override, use plural option
213
218
  puts "SINGULAR: #{@singular}"
214
219
  puts "PLURAL: #{@plural}"
215
220
 
216
-
217
221
  @namespace = options['namespace'] || nil
218
222
  @namespace_value = @namespace
219
223
  use_controller_name = plural.titleize.gsub(" ", "")
@@ -1835,6 +1839,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1835
1839
  res << "\n @#{plural} = @#{plural}#{choice[:scope]} if @q['0'][:#{phantom_key}_search] == \"#{choice[:label]}\""
1836
1840
  end
1837
1841
  end
1842
+ res << "\n"
1838
1843
  end
1839
1844
 
1840
1845
  res << " @#{plural} = @#{plural}.page(params[:page])#{ ".per(per)" if @paginate_per_page_selector }"
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.6.26'
3
+ CURRENT = '0.6.27'
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.26
4
+ version: 0.6.27
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-09-16 00:00:00.000000000 Z
11
+ date: 2025-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails