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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +27 -13
- data/app/helpers/hot_glue/controller_helper.rb +143 -61
- data/lib/generators/hot_glue/fields/time_field.rb +6 -6
- data/lib/generators/hot_glue/scaffold_generator.rb +8 -3
- data/lib/hotglue/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5a077658d795c25ef41694b36014191f2590351f6a1fa87e8419b049f03f5ad
|
4
|
+
data.tar.gz: f2effbb98f5728fd70e2b7807903f86ad352a1dd3598562c3a450a804dcd51fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc1d59497e67ba88e9da7305bfe5b2664874178243c744860bc30335a6f835808e784cf0b67533bf7c31166c48fdc4e5af0db55382623e9c07a3dc9f754beb81
|
7
|
+
data.tar.gz: 6fbc0bfbc0cb9651802525f711566bf3b67ad9b34a4742600412c1addfe69fb20eab93a6bab363776a7eead66245fc120e22a554ecbd45303fafbdaaec49468f
|
data/Gemfile.lock
CHANGED
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
|
-
|
1623
|
+
### `--search-fields=aaa,bbb,ccc,ddd,eee`
|
1624
1624
|
to specify which fields you want to be searchable.
|
1625
1625
|
|
1626
1626
|
|
1627
|
-
|
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
|
-
|
1630
|
+
### `--search-position=vertical`
|
1631
1631
|
to specify vertical or horizontal (default: horizontal)
|
1632
1632
|
|
1633
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
102
|
-
|
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
|
-
|
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}
|
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}
|
14
|
-
|
15
|
-
|
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'] ||
|
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 }"
|
data/lib/hotglue/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2025-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|