hot-glue 0.7.7 → 0.7.8

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: 19812e0ed05939741ec5f22550345a5d94a07ad32f3925540bd6bb2ec76e0f6f
4
- data.tar.gz: d2fba980427bd8c8961a6fb475b063305956d8a99ac428ffc28317291b961209
3
+ metadata.gz: 0e4491ce711db972625af0df18b41705f69a87451b34237a1a3ab1c565fc4729
4
+ data.tar.gz: 13799e4f92016ccf6156abf90a85cd5bda7418b1287ce3d7c112112b853ff92f
5
5
  SHA512:
6
- metadata.gz: 2a6c67fe502cce091097b4e72f1806a23b9126eea7c1498a8ba5f38aec5a581ddb59fd54543e4ba00257f70bfaa987aaf9ae07c9f80de093cccd0a3584dd9133
7
- data.tar.gz: 3143a9b97b4338421c2bc4b910afd2f18cddb96178e8e27e6beaf4189d705d54222aa6335c425b7beb2ea2910557979135d84919c410ad63d3c357d3c5e06885
6
+ metadata.gz: 32a835125c53bd4aa3d14080ac048c17d556db3b6db7716ce4a21eefba18db38fe33f76403e16f5e8a2f9415d6591430412d0a33029d0634ec65118f8363d345
7
+ data.tar.gz: 55a21a7aba9909332640275d0b6cb24dcfb0784323eb9ca7e8248db70143ec4a6520f4c1537aa39c25ca36c4ac3f844ec42eab20e3345db3298915569c203897
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.4
1
+ 3.3.10
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hot-glue (0.7.6.2)
4
+ hot-glue (0.7.7)
5
5
  ffaker (~> 2.16)
6
6
  rails (> 5.1)
7
7
 
@@ -117,19 +117,19 @@ GEM
117
117
  loofah (2.19.0)
118
118
  crass (~> 1.0.2)
119
119
  nokogiri (>= 1.5.9)
120
- mail (2.9.0)
120
+ mail (2.9.1)
121
121
  logger
122
122
  mini_mime (>= 0.1.1)
123
123
  net-imap
124
124
  net-pop
125
125
  net-smtp
126
- marcel (1.1.0)
126
+ marcel (1.2.1)
127
127
  matrix (0.4.2)
128
128
  method_source (1.0.0)
129
129
  mini_mime (1.1.2)
130
130
  mini_portile2 (2.8.4)
131
131
  minitest (5.16.3)
132
- net-imap (0.6.4)
132
+ net-imap (0.6.6)
133
133
  date
134
134
  net-protocol
135
135
  net-pop (0.1.2)
@@ -148,7 +148,7 @@ GEM
148
148
  puma (5.6.5)
149
149
  nio4r (~> 2.0)
150
150
  racc (1.6.0)
151
- rack (2.2.4)
151
+ rack (2.2.24)
152
152
  rack-test (2.0.2)
153
153
  rack (>= 1.3)
154
154
  rails (7.0.4)
@@ -235,7 +235,7 @@ GEM
235
235
  warden (1.2.9)
236
236
  rack (>= 2.0.9)
237
237
  websocket (1.2.9)
238
- websocket-driver (0.8.0)
238
+ websocket-driver (0.8.2)
239
239
  base64
240
240
  websocket-extensions (>= 0.1.0)
241
241
  websocket-extensions (0.1.5)
@@ -247,6 +247,7 @@ PLATFORMS
247
247
  arm64-darwin-21
248
248
  arm64-darwin-22
249
249
  arm64-darwin-23
250
+ arm64-darwin-25
250
251
  x86_64-linux
251
252
 
252
253
  DEPENDENCIES
data/README.md CHANGED
@@ -2489,6 +2489,14 @@ These automatic pickups for partials are detected at build time. This means that
2489
2489
 
2490
2490
  # VERSION HISTORY
2491
2491
 
2492
+ #### 2026-08-22 - v0.7.8
2493
+ - fix for time field output when no user is present (in gd mode)
2494
+ - in new builds, create and edit will now interact as turbo-stream interactions (except for big edit); this is to remove a race condition in the capy spec; note that your old edit.erb and new.erb will get deleted if you rebuild
2495
+
2496
+
2497
+ #### 2026-06-01 - v0.7.7
2498
+ - Dynamic blocks (denoted with **) now use fully namespaced paths when rendering, this avoid out of scope issues when an action was used accross controllers
2499
+
2492
2500
  #### 2026-04-08 - v0.7.6.2
2493
2501
  - Syntax fix in code, no functional changes
2494
2502
 
@@ -57,7 +57,7 @@ module HotGlue
57
57
  end
58
58
 
59
59
  def formatted_time_display(object, method, current_user)
60
- tz = ActiveSupport::TimeZone[current_user.timezone]
60
+ tz = current_user.try(:timezone) ? ActiveSupport::TimeZone[current_user.timezone] : ActiveSupport::TimeZone['UTC']
61
61
 
62
62
  t = object.public_send(method)
63
63
 
@@ -78,9 +78,10 @@ module HotGlue
78
78
  end
79
79
 
80
80
  def formatted_time_field(object, method, current_user)
81
- tz = ActiveSupport::TimeZone[current_user.timezone]
81
+ tz = current_user.try(:timezone) ? ActiveSupport::TimeZone[current_user.timezone] : ActiveSupport::TimeZone['UTC']
82
82
 
83
83
  t = object.public_send(method)
84
+ return nil if t.nil?
84
85
 
85
86
  # Build UTC datetime from the stored time
86
87
  utc_datetime = Time.utc(
@@ -45,7 +45,7 @@ class AssociationField < Field
45
45
  if which_partial == :update_show_only && update_show_only.include?(name)
46
46
 
47
47
  else
48
- " #{name}_selector = find(\"[name='#{singular}[#{name}]']\").click \n" +
48
+ " #{name}_selector = find(\"[name='#{singular}[#{name}]']\")\n" +
49
49
  " #{name}_selector.first('option', text: #{assoc}1.name).select_option"
50
50
  end
51
51
  end
@@ -10,7 +10,10 @@ class DateTimeField < Field
10
10
 
11
11
 
12
12
  def spec_setup_and_change_act(which_partial = nil)
13
- " " + "new_#{name} = DateTime.current + 1.year \n" +
13
+ # beginning_of_minute: an html5 datetime-local input rejects (as invalid,
14
+ # silently blocking form submit) a value whose seconds don't match the
15
+ # field's precision, so keep the test value minute-aligned.
16
+ " " + "new_#{name} = DateTime.current.beginning_of_minute + 1.year \n" +
14
17
  ' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
15
18
 
16
19
  end
@@ -24,7 +27,9 @@ class DateTimeField < Field
24
27
  end
25
28
 
26
29
  def spec_setup_let_arg
27
- "#{name}: DateTime.current + 1.day"
30
+ # minute-aligned so the rendered datetime-local edit field is valid when
31
+ # the spec fills it (see spec_setup_and_change_act)
32
+ "#{name}: DateTime.current.beginning_of_minute + 1.day"
28
33
  end
29
34
 
30
35
  def spec_list_view_assertion
@@ -5,12 +5,12 @@ class TimeField < Field
5
5
  end
6
6
 
7
7
  def form_field_output
8
- "<%= time_field_localized(f, :#{name}, formatted_time_field(#{singular}, :#{name}, current_user), label: \"#{ name.to_s.humanize } \#{ current_user.timezone }\") %>"
8
+ "<%= time_field_localized(f, :#{name}, formatted_time_field(#{singular}, :#{name}, try(:current_user)), label: \"#{ name.to_s.humanize } \#{ try(:current_user).try(:timezone) }\") %>"
9
9
  end
10
10
 
11
11
  def line_field_output
12
12
  "\n <% unless #{singular}.#{name}.nil? %>
13
- <%= formatted_time_display(#{singular}, :#{name}, current_user) %>
13
+ <%= formatted_time_display(#{singular}, :#{name}, try(:current_user)) %>
14
14
  <% else %>
15
15
  <span class=''>MISSING</span>
16
16
  <% end %>\n"
@@ -1437,12 +1437,16 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1437
1437
 
1438
1438
 
1439
1439
  def datetime_fields_list
1440
- @columns.each_with_object({}) do |col, hash|
1440
+ # build the hash literal as a string explicitly rather than interpolating a
1441
+ # live Ruby Hash into the template: Hash#inspect renders symbol keys as
1442
+ # `:k=>v` on Ruby <= 3.3 but `k: v` on Ruby 3.4+, so relying on it made the
1443
+ # generated controller's syntax depend on the host app's Ruby version.
1444
+ pairs = @columns.filter_map do |col|
1441
1445
  column = @the_object.columns_hash[col.to_s]
1442
- if column && [:datetime, :time].include?(column.type)
1443
- hash[col.to_sym] = column.type
1444
- end
1446
+ next unless column && [:datetime, :time].include?(column.type)
1447
+ "#{col}: :#{column.type}"
1445
1448
  end
1449
+ "{#{pairs.join(', ')}}"
1446
1450
  end
1447
1451
 
1448
1452
 
@@ -1699,6 +1703,28 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1699
1703
 
1700
1704
  end
1701
1705
  end
1706
+
1707
+ remove_stale_html_new_edit_views
1708
+ end
1709
+
1710
+ # When new/edit are served as turbo_stream we no longer generate the classic
1711
+ # html new.erb / edit.erb pages. A host app previously built by an older Hot
1712
+ # Glue would still have those stale files on disk; clean them out on
1713
+ # re-generation so the app doesn't keep serving (or fall back to) them.
1714
+ def remove_stale_html_new_edit_views
1715
+ return if @specs_only
1716
+ return unless turbo_stream_new_and_edit?
1717
+
1718
+ stale_views = []
1719
+ stale_views << 'new' unless @no_create
1720
+ stale_views << 'edit' unless @no_edit
1721
+
1722
+ stale_views.each do |view|
1723
+ dest_filename = cc_filename_with_extensions("#{view}", "#{@markup}")
1724
+ dest_filepath = File.join("#{filepath_prefix}app/views#{namespace_with_dash}",
1725
+ @controller_build_folder, dest_filename)
1726
+ remove_file dest_filepath if File.exist?(dest_filepath)
1727
+ end
1702
1728
  end
1703
1729
 
1704
1730
  def append_model_callbacks
@@ -1767,15 +1793,25 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1767
1793
  @namespace ? "#{@namespace}/" : ""
1768
1794
  end
1769
1795
 
1796
+ # new & edit are served as turbo_stream (frame replace) rather than as
1797
+ # standalone html pages, EXCEPT in --big-edit mode (edit is a full page)
1798
+ # and --display-edit-after-create mode (create redirects to the html edit
1799
+ # page). In those modes we keep the classic html new.erb / edit.erb views.
1800
+ def turbo_stream_new_and_edit?
1801
+ !@big_edit && !@display_edit_after_create
1802
+ end
1803
+
1770
1804
  def all_views
1771
1805
  res = %w(index _line _list _show)
1772
1806
 
1773
1807
  unless @no_create
1774
- res += %w(new _new_form _new_button)
1808
+ res += %w(_new_form _new_button)
1809
+ res << 'new' unless turbo_stream_new_and_edit?
1775
1810
  end
1776
1811
 
1777
1812
  unless @no_edit
1778
- res += %w{edit _edit}
1813
+ res << '_edit'
1814
+ res << 'edit' unless turbo_stream_new_and_edit?
1779
1815
  end
1780
1816
 
1781
1817
  if !(@no_edit && @no_create)
@@ -1801,6 +1837,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
1801
1837
 
1802
1838
  unless @no_create
1803
1839
  res << 'create'
1840
+ res << 'new' if turbo_stream_new_and_edit?
1804
1841
  end
1805
1842
 
1806
1843
  unless @no_edit
@@ -5,4 +5,11 @@ def login_as(user)
5
5
  fill_in 'Password', with: 'password'
6
6
  end
7
7
  click_button 'Log in'
8
+
9
+ # headless Chrome/Selenium occasionally drops this click without submitting
10
+ # the form at all (confirmed via server log: no POST /users/sign_in received).
11
+ # Retry once before failing for real.
12
+ click_button 'Log in' unless page.has_content?('Signed in successfully', wait: 3)
13
+
14
+ expect(page).to have_content("Signed in successfully")
8
15
  end
@@ -105,7 +105,10 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
105
105
  authorize @<%= singular %><% elsif @pundit && @pundit_policy_override %>
106
106
  skip_authorization
107
107
  raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.new?<% end %>
108
- @action = 'new' <% if @pundit %>
108
+ @action = 'new'<% if turbo_stream_new_and_edit? %>
109
+ respond_to do |format|
110
+ format.turbo_stream
111
+ end<% else %> <% end %><% if @pundit %>
109
112
  rescue Pundit::NotAuthorizedError
110
113
  flash[:alert] = 'You are not authorized to perform this action.'
111
114
  load_all_<%= plural %>
@@ -181,7 +184,9 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
181
184
  skip_authorization
182
185
  raise Pundit::NotAuthorizedError if ! <%= @pundit_policy_override %>.edit?<% end %>
183
186
  @action = 'edit'
184
- render :edit<% if @pundit %>
187
+ <% if turbo_stream_new_and_edit? %>respond_to do |format|
188
+ format.turbo_stream
189
+ end<% else %>render :edit<% end %><% if @pundit %>
185
190
  rescue Pundit::NotAuthorizedError
186
191
  flash[:notice] = "Editing <%= singular + ' ' if @include_object_names %>#{@<%= singular %>.<%= display_class %>} not authorized."
187
192
  render :index <% end %>
@@ -1,5 +1,5 @@
1
1
  <\%= turbo_frame_tag "<%= @namespace %>__<%= singular %>-new" do %>
2
2
  <div>
3
- <\%= link_to "<%= @new_button_label %>", <%= new_path_name %>, disable_with: "Loading...", class: "new-<%= singular %>-button btn btn-primary pull-right <%= 'btn-sm' if @nested_set.any? %> " %>
3
+ <\%= link_to "<%= @new_button_label %>", <%= new_path_name %>, disable_with: "Loading...", class: "new-<%= singular %>-button btn btn-primary pull-right <%= 'btn-sm' if @nested_set.any? %> "<% if turbo_stream_new_and_edit? %>, data: { turbo_stream: true }<% end %> %>
4
4
  </div>
5
5
  <\% end %>
@@ -42,6 +42,6 @@
42
42
  <% end %>
43
43
 
44
44
  <% unless @no_edit %>
45
- <\%= link_to "Edit <% if @button_icons == 'font-awesome' %><i class='fa fa-1x fa-list-alt'></i><% end %>".html_safe, <%= edit_path_helper %> + (params[:page] ? "?page=#{params[:page]}" : ""), <% if @big_edit %>'data-turbo' => 'false', <% end %>disable_with: "Loading...", class: "edit-<%= singular %>-button btn btn-primary btn-sm" %>
45
+ <\%= link_to "Edit <% if @button_icons == 'font-awesome' %><i class='fa fa-1x fa-list-alt'></i><% end %>".html_safe, <%= edit_path_helper %> + (params[:page] ? "?page=#{params[:page]}" : ""), <% if @big_edit %>'data-turbo' => 'false', <% end %>disable_with: "Loading...", class: "edit-<%= singular %>-button btn btn-primary btn-sm"<% if turbo_stream_new_and_edit? %>, data: { turbo_stream: true }<% end %> %>
46
46
  <% end %>
47
47
  </div>
@@ -1,3 +1,3 @@
1
1
  <\%= turbo_stream.replace "<%= @namespace %>__#{dom_id(@<%= singular %>)}" do %>
2
- <\%= render 'edit', locals: { <%= singular %>: @<%= singular %><%= @nested_set.collect{|arg| ", #{arg[:singular]}: @#{arg[:singular]} " }.join("") %> } %>
2
+ <\%= render partial: 'edit', locals: { <%= singular %>: @<%= singular %><%= @nested_set.collect{|arg| ", #{arg[:singular]}: @#{arg[:singular]} " }.join("") %> } %>
3
3
  <\% end %>
@@ -0,0 +1,3 @@
1
+ <\%= turbo_stream.replace "<%= @namespace %>__<%= singular %>-new" do %>
2
+ <\%= render partial: "new_form", locals: { <%= singular %>: @<%= singular %><%= @nested_set.collect{|arg| ", #{arg[:singular]}: @#{arg[:singular]}" }.join %> } %>
3
+ <\% end %>
@@ -69,7 +69,7 @@ describe 'interaction for <%= controller_class_name %>' do
69
69
  expect(page).to have_content("Editing #{<%= factory_testing_name %>.<%= @display_class %>.squish || "(no name)"}")
70
70
  <%= capybara_make_updates(:update) %>
71
71
  click_button "Save"
72
- within("turbo-frame#<%= @namespace %>__#{dom_id(<%= factory_testing_name %>)} ") do
72
+ within("turbo-frame#<%= @namespace %>__#{dom_id(<%= factory_testing_name %>)}") do
73
73
  <%= " " + @columns_map.map{ |col, col_object|
74
74
  if @attachments.keys.collect(&:to_sym).include?(col)
75
75
  elsif @show_only.include?(col)
@@ -1,5 +1,5 @@
1
1
  module HotGlue
2
2
  class Version
3
- CURRENT = '0.7.7'
3
+ CURRENT = '0.7.8'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hot-glue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.8
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: 2026-06-01 00:00:00.000000000 Z
11
+ date: 2026-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -115,6 +115,7 @@ files:
115
115
  - lib/generators/hot_glue/templates/erb/edit.turbo_stream.erb
116
116
  - lib/generators/hot_glue/templates/erb/index.erb
117
117
  - lib/generators/hot_glue/templates/erb/new.erb
118
+ - lib/generators/hot_glue/templates/erb/new.turbo_stream.erb
118
119
  - lib/generators/hot_glue/templates/erb/update.turbo_stream.erb
119
120
  - lib/generators/hot_glue/templates/javascript/date_range_picker_controller.js
120
121
  - lib/generators/hot_glue/templates/javascript/dropzone_controller.js
@@ -164,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
165
  - !ruby/object:Gem::Version
165
166
  version: '0'
166
167
  requirements: []
167
- rubygems_version: 3.4.19
168
+ rubygems_version: 3.5.22
168
169
  signing_key:
169
170
  specification_version: 4
170
171
  summary: A gem to build Turbo Rails scaffolding.