hot-glue 0.5.16 → 0.5.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +10 -2
- data/.github/workflows/test_suite.yml +46 -0
- data/Gemfile.lock +1 -1
- data/README.md +101 -9
- data/app/helpers/hot_glue/controller_helper.rb +42 -30
- data/lib/generators/hot_glue/fields/date_field.rb +1 -1
- data/lib/generators/hot_glue/fields/date_time_field.rb +6 -6
- data/lib/generators/hot_glue/fields/time_field.rb +12 -4
- data/lib/generators/hot_glue/fields/uuid_field.rb +1 -1
- data/lib/generators/hot_glue/nav_template_generator.rb +22 -0
- data/lib/generators/hot_glue/scaffold_generator.rb +51 -7
- data/lib/generators/hot_glue/templates/controller.rb.erb +19 -6
- data/lib/generators/hot_glue/templates/erb/_nav.html.erb +3 -0
- data/lib/generators/hot_glue/templates/system_spec.rb.erb +11 -27
- data/lib/hotglue/version.rb +1 -1
- metadata +5 -3
- data/.github/workflows/test.yml +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a492a1e3528a8f8cbe485fe6e2c489896740184e55b8a910312bbaaeb9439e72
|
4
|
+
data.tar.gz: e935b8bb498f464b978a34d9714603f41bc7af3d530a9844b560693184344bf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa2d1676222bc0a91b945654bd93d452a5c102625068f5ab6acbca3eb250f79b6e3030fdcaf2b3a61672d143d8813fc39280581542c8bb2e4d94760d0f0ad944
|
7
|
+
data.tar.gz: b048a9fb6f22d24f10920da6f67297aa8c1e57ccea6850ee442d6fe27f85be4ceeba76f86c5fdc2f79afd2691b0bbdc6f897738dc63a0857076824d15d1e332c
|
data/.circleci/config.yml
CHANGED
@@ -3,7 +3,7 @@ version: 2.1
|
|
3
3
|
|
4
4
|
orbs:
|
5
5
|
ruby: circleci/ruby@1.0
|
6
|
-
browser-tools: circleci/browser-tools@1.4.
|
6
|
+
browser-tools: circleci/browser-tools@1.4.4
|
7
7
|
|
8
8
|
jobs:
|
9
9
|
build:
|
@@ -47,7 +47,15 @@ jobs:
|
|
47
47
|
RAILS_ENV: test
|
48
48
|
|
49
49
|
steps:
|
50
|
-
-
|
50
|
+
- run: sudo apt-get update
|
51
|
+
- browser-tools/install-browser-tools:
|
52
|
+
chrome-version: 116.0.5845.96 # TODO: remove when chromedriver downloads are fixed
|
53
|
+
replace-existing-chrome: true
|
54
|
+
- browser-tools/install-chrome:
|
55
|
+
# TODO remove following line when fixed https://github.com/CircleCI-Public/browser-tools-orb/issues/90
|
56
|
+
chrome-version: 116.0.5845.96
|
57
|
+
replace-existing: true
|
58
|
+
|
51
59
|
- browser-tools/install-chromedriver
|
52
60
|
- checkout
|
53
61
|
- ruby/install-deps
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub. They are
|
2
|
+
# provided by a third-party and are governed by separate terms of service,
|
3
|
+
# privacy policy, and support documentation.
|
4
|
+
#
|
5
|
+
# This workflow will install a prebuilt Ruby version, install dependencies, and
|
6
|
+
# run tests and linters.
|
7
|
+
name: "Test Suite"
|
8
|
+
on:
|
9
|
+
push:
|
10
|
+
branches: [ "main" ]
|
11
|
+
pull_request:
|
12
|
+
branches: [ "main" ]
|
13
|
+
jobs:
|
14
|
+
test:
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
services:
|
17
|
+
postgres:
|
18
|
+
image: postgres:11-alpine
|
19
|
+
ports:
|
20
|
+
- "5432:5432"
|
21
|
+
env:
|
22
|
+
POSTGRES_DB: rails_test
|
23
|
+
POSTGRES_USER: rails
|
24
|
+
POSTGRES_PASSWORD: password
|
25
|
+
chrome:
|
26
|
+
image: selenium/standalone-chrome:latest
|
27
|
+
ports:
|
28
|
+
- 4444:4444
|
29
|
+
env:
|
30
|
+
RAILS_ENV: test
|
31
|
+
DATABASE_URL: "postgres://rails:password@localhost:5432/rails_test"
|
32
|
+
|
33
|
+
|
34
|
+
steps:
|
35
|
+
- name: Checkout code
|
36
|
+
uses: actions/checkout@v3
|
37
|
+
- name: Install Ruby and gems
|
38
|
+
uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
|
39
|
+
with:
|
40
|
+
bundler-cache: true
|
41
|
+
|
42
|
+
- name: internal tests
|
43
|
+
run: bundle exec rspec
|
44
|
+
- name: system tests
|
45
|
+
run: script/test
|
46
|
+
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -663,9 +663,22 @@ The syntax is `--modify=cost{$},price{$}`
|
|
663
663
|
|
664
664
|
Here, the `cost` and `price` fields will be displayed as wrapped in `number_to_currency()` when displayed on the list view and when displayed as show-only.
|
665
665
|
|
666
|
-
|
666
|
+
You can also use a binary modifier, which can apply to booleans, datetimes, times, dates or anything else. When using the binary modify, a specific value is displayed if the field is truthy and another one is display if the field is falsy.
|
667
|
+
You specify it using a pipe | character like so:
|
667
668
|
|
668
|
-
|
669
|
+
`--modify=paid_at{paid|unpaid}`
|
670
|
+
|
671
|
+
here, even though `paid_at` is a datetime field, it will display as-if it is a binary -- showing either the truthy
|
672
|
+
label or the falsy label depending on if `paid_at` is or is not null in the database.
|
673
|
+
For all fields except booleans, this affects only the viewable output —
|
674
|
+
what you see on the list page and on the edit page for show-only fields.
|
675
|
+
For booleans, it affects those outputs as well as the normal edit output:
|
676
|
+
The labels you specify are used as the labels for the radio buttons that the user can select.
|
677
|
+
|
678
|
+
|
679
|
+
You will need to separately specify them as show-only if you want them to be non-editable.
|
680
|
+
|
681
|
+
The available modifiers are:
|
669
682
|
|
670
683
|
| modifier | what it does | can be used on | | |
|
671
684
|
|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|---|---|
|
@@ -1146,13 +1159,13 @@ factory = AgentFactory.new(find_or_create_by_email: agent_company_params[:__look
|
|
1146
1159
|
params: modified_params)
|
1147
1160
|
```
|
1148
1161
|
|
1149
|
-
Here the new AgentFactory will
|
1162
|
+
Here the new AgentFactory will receive any variables by keyword argument, and since you're specifying the calling code here, Hot Glue does not dictate your factory's setup.
|
1150
1163
|
However, two special variables are in scope which you can use in your calling code.
|
1151
1164
|
|
1152
1165
|
`*_params` (where * is the name of the thing you are building)
|
1153
|
-
`modified_params`
|
1166
|
+
`modified_params`
|
1154
1167
|
|
1155
|
-
Either one must be
|
1168
|
+
Either one must be received by your factory for your factory to create data based off the inputted data.
|
1156
1169
|
|
1157
1170
|
Rememebr, `*_params` has the input params passed only the through the sanitizer, and modified_params has it passed through the timezone aware mechanism and other Hot Glue-specific defaults.
|
1158
1171
|
|
@@ -1165,14 +1178,35 @@ Always:
|
|
1165
1178
|
|
1166
1179
|
Don't include this last line in your factory code.
|
1167
1180
|
|
1181
|
+
## Nav Templates
|
1182
|
+
At the namespace level, you can have a file called `_nav.html.erb` to create tabbed bootstrap nav
|
1168
1183
|
|
1184
|
+
To create the file for the first time (at each namespace), start by running
|
1185
|
+
```
|
1186
|
+
bin/rails generate hot_glue:nav_template --namespace=xyz
|
1187
|
+
```
|
1169
1188
|
|
1170
|
-
|
1189
|
+
This will append the file `_nav.html.erb` to the views folder at `views/xyz`. To begin, this file contains only the following:
|
1190
|
+
|
1191
|
+
```
|
1192
|
+
<ul class='nav nav-tabs'>
|
1193
|
+
</ul>
|
1194
|
+
```
|
1171
1195
|
|
1172
|
-
|
1196
|
+
Once the file is present, any further builds in this namespace will:
|
1173
1197
|
|
1174
|
-
|
1198
|
+
1) Append to this `_nav.html.erb` file, adding a tab for the new built scaffold
|
1199
|
+
2) On the list view of the scaffold being built, it will include a render to the _nav partial, passing the name of the currently-viewed thing as the local variable `nav` (this is how the nav template knows which tab to make active).
|
1200
|
+
```
|
1201
|
+
<%= render partial: "owner/nav", locals: {nav: "things"} %>
|
1202
|
+
```
|
1203
|
+
(In this example `owner/` is the namespace and `things` is the name of the scaffold being built)
|
1204
|
+
|
1205
|
+
## Automatic Base Controller
|
1206
|
+
|
1207
|
+
Hot Glue will copy a file named `base_controller.rb` to the same folder where it tries to create any controller (so to the namespace), unless such a file exists there already.
|
1175
1208
|
|
1209
|
+
The created controller will always have this base controller as its subclass. You are encouraged to implement functionality common to all the controllers in the *namespace* in the base class. For example, authorizing your user to access that part of the app.
|
1176
1210
|
|
1177
1211
|
## Special Table Labels
|
1178
1212
|
|
@@ -1266,10 +1300,68 @@ end
|
|
1266
1300
|
```
|
1267
1301
|
|
1268
1302
|
|
1303
|
+
# VERSION HISTORY
|
1269
1304
|
|
1305
|
+
#### 2023-09-01 - v0.5.18
|
1306
|
+
- there three ways Hot Glue deals with Datetime fields:
|
1307
|
+
-
|
1308
|
+
- (1) with current users who have `timezone` method (field or method)
|
1309
|
+
- (2) without current user timezone and no global timezone set for your app
|
1310
|
+
- (3) without current user timezone and global timezone set for your app
|
1270
1311
|
|
1312
|
+
- For #1, previously this method returned a time zone offset (integer). After v0.5.18, the preferred return value is a Timezone string, but legacy implementation returning offset values will continue to work.
|
1313
|
+
Your user objects should have a field called `timezone` which should be a string.
|
1314
|
+
- Note that daylight savings time is accounted for in this implementation.
|
1315
|
+
|
1316
|
+
- For #2 (your user does not have a timezone field and you _have not_ set the timezone globally), all datetimes will display in UTC timezone.
|
1317
|
+
|
1318
|
+
- For #3 (your user does not have a timezone field but you _have_ set the timezone globally), your datetimes will display in the Rails-specified timezone.
|
1319
|
+
|
1320
|
+
- be sure to configure in `config/application.rb` this:
|
1321
|
+
```
|
1322
|
+
config.time_zone = 'Eastern Time (US & Canada)'
|
1323
|
+
```
|
1324
|
+
This should be your business's default timezone.
|
1325
|
+
|
1326
|
+
|
1327
|
+
(#93)
|
1328
|
+
fixes variables be more clear if they are TimeZone objects (https://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html) or are UTC offset (integers -/+ from UTC)
|
1329
|
+
- fixes spec assertions for DateTime and Time fields
|
1330
|
+
- removes randomness causing race conditions in the datetime object specs
|
1331
|
+
**- fixes issue where setting bootstrap-column-width was not preferred if… (#88)**
|
1332
|
+
- fixes flash notice output
|
1333
|
+
|
1334
|
+
|
1335
|
+
#### 2023-08-18 - v0.5.17
|
1336
|
+
|
1337
|
+
• Nav templates (`_nav.html.erb`) are now automatically appended to if they exist. Remember nav template live in the views folder at the root of the *namespace*, which is one folder up from whatever folder is being built.
|
1338
|
+
If a file exists `_nav.html.erb`, it will get appnded to with content like this:
|
1339
|
+
|
1340
|
+
```
|
1341
|
+
<li class="nav-item">
|
1342
|
+
<%= link_to "Domains", domains_path, class: "nav-link #{'active' if nav == 'domains'}" %>
|
1343
|
+
</li>
|
1344
|
+
```
|
1345
|
+
|
1346
|
+
This append to the `_nav.html.erb` template happens in addition to the partial itself being included from the layout.
|
1347
|
+
(Also only if it exists, so be sure create it before running the scaffold generators for the namespace. Of course, you only need to create it once for each namespace)
|
1348
|
+
|
1349
|
+
• To create a new `_nav.html.erb` template use
|
1350
|
+
|
1351
|
+
```
|
1352
|
+
bin/rails generate hot_glue:nav_template --namespace=xyz
|
1353
|
+
```
|
1354
|
+
|
1355
|
+
Here, you give only the namespace. It will create an empty nav template:
|
1356
|
+
```
|
1357
|
+
<ul class='nav nav-tabs'>
|
1358
|
+
</ul>
|
1359
|
+
```
|
1360
|
+
|
1361
|
+
• Fixes to specs for datetimes
|
1362
|
+
|
1363
|
+
• Fixes way the flash notices where created that violated frozen string literal
|
1271
1364
|
|
1272
|
-
# VERSION HISTORY
|
1273
1365
|
|
1274
1366
|
#### 2023-08-17 - v0.5.16
|
1275
1367
|
|
@@ -5,10 +5,11 @@ module HotGlue
|
|
5
5
|
(tz >= 0 ? "+" : "-") + sprintf('%02d',tz.abs) + ":00"
|
6
6
|
end
|
7
7
|
|
8
|
-
def datetime_field_localized(form_object, field_name, value, label
|
8
|
+
def datetime_field_localized(form_object, field_name, value, label )
|
9
|
+
current_timezone
|
9
10
|
form_object.text_field(field_name, class: 'form-control',
|
10
11
|
type: 'datetime-local',
|
11
|
-
value: date_to_current_timezone(value,
|
12
|
+
value: date_to_current_timezone(value, current_timezone)) + timezonize(current_timezone)
|
12
13
|
end
|
13
14
|
|
14
15
|
|
@@ -18,53 +19,65 @@ module HotGlue
|
|
18
19
|
value: value )
|
19
20
|
end
|
20
21
|
|
21
|
-
def time_field_localized(form_object, field_name, value, label
|
22
|
+
def time_field_localized(form_object, field_name, value, label )
|
23
|
+
current_timezone
|
22
24
|
form_object.text_field(field_name, class: 'form-control',
|
23
25
|
type: 'time',
|
24
|
-
value:
|
26
|
+
value: value && value.strftime("%H:%M"))
|
25
27
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def current_timezone
|
31
|
+
# returns a TimeZone (https://apidock.com/rails/TimeZone) object
|
29
32
|
if defined?(current_user)
|
30
33
|
if current_user.try(:timezone)
|
31
|
-
|
34
|
+
current_user.timezone
|
35
|
+
|
36
|
+
# Time.now.in_time_zone(current_user.timezone.to_i).zone
|
32
37
|
else
|
33
|
-
|
38
|
+
Rails.application.config.time_zone
|
39
|
+
# Time.zone.name
|
34
40
|
end
|
35
41
|
else
|
36
|
-
|
42
|
+
Rails.application.config.time_zone
|
43
|
+
# Time.zone.name
|
37
44
|
end
|
38
45
|
end
|
39
46
|
|
40
47
|
def date_to_current_timezone(date, timezone = nil)
|
41
|
-
#
|
42
|
-
|
43
|
-
if
|
44
|
-
|
48
|
+
# used for displaying when in EDIT mode
|
49
|
+
# (this format is how the browser expectes to receive the value='' of the input field)
|
50
|
+
if date.nil?
|
51
|
+
return nil
|
52
|
+
else
|
53
|
+
return date.in_time_zone(timezone).strftime("%Y-%m-%dT%H:%M")
|
45
54
|
end
|
46
|
-
|
47
|
-
return nil if date.nil?
|
48
|
-
|
49
|
-
return date.in_time_zone(timezone).strftime("%Y-%m-%dT%H:%M")
|
50
|
-
# begin
|
51
|
-
#
|
52
|
-
# rescue
|
53
|
-
# return nil
|
54
|
-
# end
|
55
55
|
end
|
56
56
|
|
57
|
-
def modify_date_inputs_on_params(modified_params, current_user_object = nil)
|
58
|
-
|
59
|
-
use_timezone = (current_user_object.try(:timezone)) || Time.now.strftime("%z")
|
57
|
+
def modify_date_inputs_on_params(modified_params, current_user_object = nil, field_list = nil)
|
58
|
+
use_offset = (current_user_object.try(:timezone)) || server_timezone_offset
|
60
59
|
|
61
60
|
modified_params = modified_params.tap do |params|
|
62
61
|
params.keys.each{|k|
|
63
|
-
if k.ends_with?("_at") || k.ends_with?("_date")
|
64
62
|
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
if field_list.nil? # legacy pre v0.5.18 behavior
|
64
|
+
include_me = k.ends_with?("_at") || k.ends_with?("_date")
|
65
|
+
else
|
66
|
+
include_me = field_list.include?(k.to_sym)
|
67
|
+
end
|
68
|
+
if include_me
|
69
|
+
if use_offset != 0
|
70
|
+
puts "changing #{params[k]}"
|
71
|
+
|
72
|
+
if use_offset.is_a? String
|
73
|
+
puts "parsing #{use_offset}"
|
74
|
+
zone = DateTime.now.in_time_zone(use_offset).zone
|
75
|
+
params[k] = DateTime.parse(params[k].gsub("T", " ") + " #{zone}")
|
76
|
+
else
|
77
|
+
puts "parsing #{use_offset}"
|
78
|
+
params[k] = DateTime.strptime("#{params[k]} #{use_offset}", '%Y-%m-%dT%H:%M %z').new_offset(0)
|
79
|
+
end
|
80
|
+
puts "changed #{params[k]}"
|
68
81
|
|
69
82
|
end
|
70
83
|
end
|
@@ -73,7 +86,6 @@ module HotGlue
|
|
73
86
|
modified_params
|
74
87
|
end
|
75
88
|
|
76
|
-
|
77
89
|
def hawk_params(hawk_schema, modified_params)
|
78
90
|
@hawk_alarm = ""
|
79
91
|
hawk_schema.each do |hawk_key,hawk_definition|
|
@@ -94,8 +106,8 @@ module HotGlue
|
|
94
106
|
|
95
107
|
private
|
96
108
|
|
97
|
-
def
|
98
|
-
Time.now.strftime("%z").to_i/100
|
109
|
+
def server_timezone_offset # returns integer of hours to add/subtract from UTC
|
110
|
+
Time.now.in_time_zone(Rails.application.config.time_zone).strftime("%z").to_i/100
|
99
111
|
end
|
100
112
|
end
|
101
113
|
end
|
@@ -10,7 +10,7 @@ class DateField < Field
|
|
10
10
|
|
11
11
|
|
12
12
|
def form_field_output
|
13
|
-
"<%= date_field_localized(f, :#{name}, #{singular}.#{name}, '#{ name.to_s.humanize }'
|
13
|
+
"<%= date_field_localized(f, :#{name}, #{singular}.#{name}, '#{ name.to_s.humanize }') %>"
|
14
14
|
end
|
15
15
|
|
16
16
|
def line_field_output
|
@@ -4,21 +4,21 @@ class DateTimeField < Field
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def spec_setup_and_change_act(which_partial = nil)
|
7
|
-
" " + "new_#{name} = DateTime.current +
|
7
|
+
" " + "new_#{name} = DateTime.current + 1.year \n" +
|
8
8
|
' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
|
9
9
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def spec_make_assertion
|
13
13
|
if !modify_binary?
|
14
|
-
"expect(page).to have_content(new_#{name}.in_time_zone(
|
14
|
+
"expect(page).to have_content(new_#{name}.in_time_zone(testing_timezone).strftime('%m/%d/%Y @ %l:%M %p %Z').gsub(' ', ' '))"
|
15
15
|
else
|
16
16
|
"expect(page).to have_content('#{modify[:binary][:truthy]}'"
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
def spec_setup_let_arg
|
21
|
-
"#{name}: DateTime.current +
|
21
|
+
"#{name}: DateTime.current + 1.day"
|
22
22
|
end
|
23
23
|
|
24
24
|
def spec_list_view_assertion
|
@@ -30,11 +30,11 @@ class DateTimeField < Field
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def spec_list_view_natural_assertion
|
33
|
-
"expect(page).to have_content(#{singular}#{1}.#{name}.in_time_zone(
|
33
|
+
"expect(page).to have_content(#{singular}#{1}.#{name}.in_time_zone(testing_timezone).strftime('%m/%d/%Y @ %l:%M %p %Z').gsub(' ', ' '))"
|
34
34
|
end
|
35
35
|
|
36
36
|
def form_field_output
|
37
|
-
"<%= datetime_field_localized(f, :#{name}, #{singular}.#{name}, '#{ name.to_s.humanize }'
|
37
|
+
"<%= datetime_field_localized(f, :#{name}, #{singular}.#{name}, '#{ name.to_s.humanize }') %>"
|
38
38
|
end
|
39
39
|
|
40
40
|
def line_field_output
|
@@ -42,7 +42,7 @@ class DateTimeField < Field
|
|
42
42
|
modified_display_output
|
43
43
|
else
|
44
44
|
"<% unless #{singular}.#{name}.nil? %>
|
45
|
-
<%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%m/%d/%Y @ %l:%M %p ')
|
45
|
+
<%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%m/%d/%Y @ %l:%M %p %Z') %>
|
46
46
|
<% else %>
|
47
47
|
<span class='alert-danger'>MISSING</span>
|
48
48
|
<% end %>"
|
@@ -5,22 +5,30 @@ class TimeField < Field
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def form_field_output
|
8
|
-
"<%= time_field_localized(f, :#{name}, #{singular}.#{name}, '#{ name.to_s.humanize }'
|
8
|
+
"<%= time_field_localized(f, :#{name}, #{singular}.#{name}, '#{ name.to_s.humanize }') %>"
|
9
9
|
end
|
10
10
|
|
11
11
|
def line_field_output
|
12
12
|
"<% unless #{singular}.#{name}.nil? %>
|
13
|
-
<%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%l:%M %p ')
|
13
|
+
<%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%l:%M %p ') %>
|
14
14
|
<% else %>
|
15
15
|
<span class='alert-danger'>MISSING</span>
|
16
16
|
<% end %>"
|
17
17
|
end
|
18
18
|
|
19
|
+
def spec_setup_and_change_act(which_partial = nil)
|
20
|
+
" new_#{name} = Time.current + 5.seconds \n" +
|
21
|
+
' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})"
|
22
|
+
|
23
|
+
end
|
24
|
+
|
19
25
|
def spec_make_assertion
|
20
|
-
"
|
26
|
+
"expect(page).to have_content(new_#{name}.strftime('%l:%M %p').strip)"
|
21
27
|
end
|
22
28
|
|
23
29
|
def spec_list_view_assertion
|
24
|
-
"
|
30
|
+
# "expect(page).to have_content(#{singular}#{1}.#{name})"
|
25
31
|
end
|
32
|
+
|
33
|
+
|
26
34
|
end
|
@@ -5,7 +5,7 @@ class UUIDField < AssociationField
|
|
5
5
|
|
6
6
|
def spec_list_view_assertion
|
7
7
|
assoc_name = name.to_s.gsub('_id','')
|
8
|
-
association = eval("#{
|
8
|
+
association = eval("#{class_name}.reflect_on_association(:#{assoc_name})")
|
9
9
|
"expect(page).to have_content(#{singular}#{1}.#{assoc_name}.#{HotGlue.derrive_reference_name(association.class_name)})"
|
10
10
|
end
|
11
11
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HotGlue
|
2
|
+
class NavTemplateGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path('templates', __dir__)
|
4
|
+
class_option :namespace, type: :string, default: nil
|
5
|
+
|
6
|
+
def filepath_prefix
|
7
|
+
# todo: inject the context
|
8
|
+
'spec/dummy/' if $INTERNAL_SPECS
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(*args) #:nodoc:
|
13
|
+
super
|
14
|
+
@namespace = options['namespace']
|
15
|
+
copy_file "erb/_nav.html.erb", "#{'spec/dummy/' if $INTERNAL_SPECS}app/views/#{@namespace ? @namespace + "/" : ""}_nav.html.erb"
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
@@ -115,7 +115,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
115
115
|
|
116
116
|
@markup = get_default_from_config(key: :markup)
|
117
117
|
@sample_file_path = get_default_from_config(key: :sample_file_path)
|
118
|
-
@bootstrap_column_width ||=
|
118
|
+
@bootstrap_column_width ||= options['bootstrap_column_width'] ||
|
119
|
+
get_default_from_config(key: :bootstrap_column_width) || 2
|
119
120
|
|
120
121
|
if options['layout']
|
121
122
|
layout = options['layout']
|
@@ -154,6 +155,8 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
154
155
|
@controller_build_folder_singular = singular
|
155
156
|
|
156
157
|
@auth = options['auth'] || "current_user"
|
158
|
+
|
159
|
+
@god = options['god'] || options['gd'] || false
|
157
160
|
@auth_identifier = options['auth_identifier'] || (!@god && @auth.gsub("current_", "")) || nil
|
158
161
|
|
159
162
|
if options['nest']
|
@@ -256,7 +259,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
256
259
|
raise HotGlue::Error, "You passed '#{@inline_list_labels}' as the setting for --inline-list-labels but the only allowed options are before, after, and omit (default)"
|
257
260
|
end
|
258
261
|
|
259
|
-
@god = options['god'] || options['gd'] || false
|
260
262
|
@specs_only = options['specs_only'] || false
|
261
263
|
|
262
264
|
@no_specs = options['no_specs'] || false
|
@@ -292,7 +294,6 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
292
294
|
if @god
|
293
295
|
@auth = nil
|
294
296
|
end
|
295
|
-
|
296
297
|
# when in self auth, the object is the same as the authenticated object
|
297
298
|
|
298
299
|
if @auth && auth_identifier == @singular
|
@@ -645,9 +646,7 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
645
646
|
end
|
646
647
|
end
|
647
648
|
|
648
|
-
|
649
|
-
"authenticate_" + @auth_identifier.split(".")[0] + "!"
|
650
|
-
end
|
649
|
+
|
651
650
|
|
652
651
|
def formats
|
653
652
|
[format]
|
@@ -761,9 +760,14 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
761
760
|
res
|
762
761
|
end
|
763
762
|
|
763
|
+
def nested_path
|
764
|
+
@nested_set.collect{| arg| arg[:plural] + "/\#{#{arg[:singular]}.id}/" }.join("/")
|
765
|
+
end
|
766
|
+
|
767
|
+
|
764
768
|
def objest_nest_params_by_id_for_specs
|
765
769
|
@nested_set.map { |arg|
|
766
|
-
"#{arg[:singular]}_id: #{arg[:singular]}.id"
|
770
|
+
"#{arg[:singular]}_id: #{arg[:singular] }.id"
|
767
771
|
}.join(",\n ")
|
768
772
|
end
|
769
773
|
|
@@ -813,6 +817,14 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
813
817
|
nested_set: @nested_set)
|
814
818
|
end
|
815
819
|
|
820
|
+
def datetime_fields_list
|
821
|
+
@columns.select do |col|
|
822
|
+
if @the_object.columns_hash[col.to_s]
|
823
|
+
@the_object.columns_hash[col.to_s].type == :datetime
|
824
|
+
end
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
816
828
|
def form_path_new_helper
|
817
829
|
HotGlue.optionalized_ternary(namespace: @namespace,
|
818
830
|
target: @controller_build_folder,
|
@@ -946,6 +958,15 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
946
958
|
@auth
|
947
959
|
end
|
948
960
|
|
961
|
+
def current_user_object
|
962
|
+
default_current_user = options['auth'] || "current_user"
|
963
|
+
if eval("defined?(#{default_current_user})")
|
964
|
+
default_current_user
|
965
|
+
else
|
966
|
+
"nil"
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
949
970
|
def no_devise_installed
|
950
971
|
!Gem::Specification.sort_by { |g| [g.name.downcase, g.version] }.group_by { |g| g.name }['devise']
|
951
972
|
end
|
@@ -1023,6 +1044,29 @@ class HotGlue::ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
|
|
1023
1044
|
end
|
1024
1045
|
end
|
1025
1046
|
|
1047
|
+
def insert_into_nav_template
|
1048
|
+
# how does this get called(?)
|
1049
|
+
nav_file = "#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}"
|
1050
|
+
if include_nav_template
|
1051
|
+
append_text = " <li class='nav-item'>
|
1052
|
+
<%= link_to '#{@list_label_heading}', #{path_helper_plural}, class: \"nav-link \#{'active' if nav == '#{plural_name}'}\" %>
|
1053
|
+
</li>"
|
1054
|
+
|
1055
|
+
text = File.read(nav_file)
|
1056
|
+
if text.include?(append_text)
|
1057
|
+
puts "SKIPPING: Nav link for #{singular_name} already exists in #{nav_file}"
|
1058
|
+
else
|
1059
|
+
puts "APPENDING: nav link for #{singular_name} #{nav_file}"
|
1060
|
+
|
1061
|
+
replace = text.gsub(/\<\/ul\>/, append_text + "\n</ul>")
|
1062
|
+
|
1063
|
+
File.open("#{Rails.root}/app/views/#{namespace_with_trailing_dash}_nav.html.#{@markup}", "w") { |file|
|
1064
|
+
file.puts replace
|
1065
|
+
}
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
|
1026
1070
|
def namespace_with_dash
|
1027
1071
|
if @namespace
|
1028
1072
|
"/#{@namespace}"
|
@@ -5,7 +5,7 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
5
5
|
helper :hot_glue
|
6
6
|
include HotGlue::ControllerHelper
|
7
7
|
|
8
|
-
<% unless @
|
8
|
+
<% unless @god %>before_action :<%= "authenticate_" + @auth_identifier.split(".")[0] + "!" %><% end %><% if any_nested? %>
|
9
9
|
<% nest_chain = [] %> <% @nested_set.each { |arg|
|
10
10
|
|
11
11
|
if auth_identifier == arg[:singular]
|
@@ -86,8 +86,16 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
86
86
|
<% if @alt_lookups.any? %><%= @alt_lookups.collect{|key, data|
|
87
87
|
" #{data[:assoc].downcase} = #{data[:assoc]}.#{data[:with_create] ? "find_or_create_by" : "find_by"}(#{data[:lookup_as]}: #{ singular_name }_params[:__lookup_#{data[:lookup_as]}])\n"
|
88
88
|
}.join("/n") %><% end %> <% merge_lookups = @alt_lookups.collect{|key, data| "#{key.gsub("_id", "")}: #{key.gsub("_id", "")}" }.join(",") %>
|
89
|
-
modified_params = modify_date_inputs_on_params(<%= singular_name %>_params.dup<%= controller_update_params_tap_away_alt_lookups
|
90
|
-
modified_params =
|
89
|
+
modified_params = modify_date_inputs_on_params(<%= singular_name %>_params.dup<%= controller_update_params_tap_away_alt_lookups %>, <%= current_user_object %>, <%= datetime_fields_list %>) <% if @object_owner_sym && eval("#{class_name}.reflect_on_association(:#{@object_owner_sym})").class == ActiveRecord::Reflection::BelongsToReflection %>
|
90
|
+
modified_params = modified_params.merge(<%= @object_owner_sym %>: <%= @object_owner_eval %>) <% elsif @object_owner_optional && any_nested? %>
|
91
|
+
modified_params = modified_params.merge(<%= @object_owner_name %> ? {<%= @object_owner_sym %>: <%= @object_owner_eval %>} : {}) <% elsif !@object_owner_eval.empty? %>
|
92
|
+
modified_params = modified_params.merge(<%= @object_owner_eval %>) <% end %><% if !merge_lookups.empty? %>
|
93
|
+
modified_params = modified_params.merge(<%= merge_lookups %>)
|
94
|
+
<% end %>
|
95
|
+
|
96
|
+
<% if @hawk_keys.any? %>
|
97
|
+
modified_params = hawk_params({<%= hawk_to_ruby %>}, modified_params)<% end %>
|
98
|
+
<%= controller_attachment_orig_filename_pickup_syntax %>
|
91
99
|
<%= creation_syntax %>
|
92
100
|
|
93
101
|
if @<%= singular_name %>.save
|
@@ -112,7 +120,12 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
112
120
|
}.join("\n") %><% end %> <% merge_lookups = @alt_lookups.filter{|key,d| ! @update_show_only.include?(key.to_sym) }.collect{|key, data| "#{key.gsub("_id", "")}: #{key.gsub("_id", "")}" }.join(",") %>
|
113
121
|
<% @magic_buttons.each { |button| %>@<%= singular_name %>.<%= button %>! if <%= singular_name %>_params[:__<%= button %>]
|
114
122
|
<% } %>
|
115
|
-
modified_params = modify_date_inputs_on_params(<%= singular_name %>_params.dup<% if @object_owner_sym && eval("#{class_name}.reflect_on_association(:#{@object_owner_sym})").class == ActiveRecord::Reflection::BelongsToReflection
|
123
|
+
modified_params = modify_date_inputs_on_params(<%= singular_name %>_params.dup<%= controller_update_params_tap_away_alt_lookups %>, <%= current_user_object %>, <%= datetime_fields_list %>) <% if @object_owner_sym && eval("#{class_name}.reflect_on_association(:#{@object_owner_sym})").class == ActiveRecord::Reflection::BelongsToReflection %>
|
124
|
+
modified_params = modified_params.merge(<%= @object_owner_sym %>: <%= @object_owner_eval %>) <% elsif @object_owner_optional && any_nested? %>
|
125
|
+
modified_params = modified_params.merge(<%= @object_owner_name %> ? {<%= @object_owner_sym %>: <%= @object_owner_eval %>} : {}) <% elsif !@object_owner_eval.empty? %>
|
126
|
+
modified_params = modified_params.merge(<%= @object_owner_eval %>) <% end %><% if !merge_lookups.empty? %>
|
127
|
+
modified_params = modified_params.merge(<%= merge_lookups %>)
|
128
|
+
<% end %>
|
116
129
|
|
117
130
|
<% if @hawk_keys.any? %> modified_params = hawk_params({<%= hawk_to_ruby %>}, modified_params)<% end %>
|
118
131
|
<% if @alt_lookups.any? %><%= @alt_lookups.collect{|key, data|
|
@@ -125,11 +138,11 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
125
138
|
}.join("/n") %><% end %><%= controller_attachment_orig_filename_pickup_syntax %>
|
126
139
|
if @<%= singular_name %>.update(modified_params)
|
127
140
|
<% if @display_list_after_update %> load_all_<%= plural %><% end %>
|
128
|
-
flash[:notice] =
|
141
|
+
flash[:notice] = "#{flash[:notice]} Saved #{@<%= singular %>.<%= display_class %>}"
|
129
142
|
flash[:alert] = @hawk_alarm if @hawk_alarm
|
130
143
|
render :update
|
131
144
|
else
|
132
|
-
flash[:alert] =
|
145
|
+
flash[:alert] = "#{flash[:notice]} <%= singular_name.titlecase %> could not be saved. #{@hawk_alarm}"
|
133
146
|
render :update, status: :unprocessable_entity
|
134
147
|
end
|
135
148
|
end
|
@@ -19,8 +19,8 @@ describe 'interaction for <%= controller_class_name %>' do
|
|
19
19
|
before do
|
20
20
|
login_as(<%= @auth %>)
|
21
21
|
end <% end %> <% if any_datetime_fields? %>
|
22
|
-
let(:
|
23
|
-
|
22
|
+
let(:testing_timezone) {
|
23
|
+
Rails.application.config.time_zone
|
24
24
|
}<% end %>
|
25
25
|
describe "index" do
|
26
26
|
it "should show me the list" do
|
@@ -59,30 +59,14 @@ describe 'interaction for <%= controller_class_name %>' do
|
|
59
59
|
<%= capybara_make_updates(:update) %>
|
60
60
|
click_button "Save"
|
61
61
|
within("turbo-frame#<%= @namespace %>__#{dom_id(<%= singular %>1)} ") do
|
62
|
-
<%=
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
elsif type == :boolean
|
71
|
-
' expect(page).to have_content(new_' + col.to_s + ' ? "YES" : "NO")'
|
72
|
-
|
73
|
-
elsif type == :enum && eval("#{singular_class}.respond_to?(:#{col}_labels)")
|
74
|
-
" expect(page).to have_content(#{singular_class}.#{col}_labels[new_#{col}])"
|
75
|
-
elsif type == :string && eval("#{singular_class}.respond_to?(:devise_modules)") &&
|
76
|
-
#devise confirmable makes email updates go into unconfirmed_email
|
77
|
-
eval("#{singular_class}.devise_modules.include?(:confirmable)") && col.to_s == "email"
|
78
|
-
" expect(page).to have_content(#{ singular }1.#{col.to_s})"
|
79
|
-
elsif type == :datetime
|
80
|
-
" expect(page).to have_content(new_#{col.to_s}.in_time_zone(current_timezone).strftime('%m/%d/%Y @ %l:%M %p ') + timezonize(current_timezone))"
|
81
|
-
else
|
82
|
-
" expect(page).to have_content(new_#{col.to_s})"
|
83
|
-
end
|
84
|
-
}.compact.join("\n")
|
85
|
-
%>
|
62
|
+
<%= " " + @columns_map.map{ |col, col_object|
|
63
|
+
if @attachments.keys.collect(&:to_sym).include?(col)
|
64
|
+
elsif @show_only.include?(col)
|
65
|
+
else
|
66
|
+
col_object.spec_make_assertion
|
67
|
+
end
|
68
|
+
}.compact.join("\n ")
|
69
|
+
%>
|
86
70
|
end
|
87
71
|
end
|
88
72
|
end <% end %>
|
@@ -91,7 +75,7 @@ describe 'interaction for <%= controller_class_name %>' do
|
|
91
75
|
it "should destroy" do
|
92
76
|
visit <%= path_helper_plural %>
|
93
77
|
accept_alert do
|
94
|
-
find("form[action='<%= namespace_with_dash %>/<%= plural %>/#{<%= singular %>1.id}'] > input.delete-<%= singular %>-button").click
|
78
|
+
find("form[action='<%= namespace_with_dash %>/<%= nested_path %><%= plural %>/#{<%= singular %>1.id}'] > input.delete-<%= singular %>-button").click
|
95
79
|
end
|
96
80
|
expect(page).to_not have_content(<%= singular %>1.<%= @display_class %>)
|
97
81
|
expect(<%= singular_class %>.where(id: <%= singular %>1.id).count).to eq(0)
|
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.5.
|
4
|
+
version: 0.5.18
|
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: 2023-
|
11
|
+
date: 2023-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -61,7 +61,7 @@ extra_rdoc_files: []
|
|
61
61
|
files:
|
62
62
|
- ".circleci/config.yml"
|
63
63
|
- ".github/FUNDING.yml"
|
64
|
-
- ".github/workflows/
|
64
|
+
- ".github/workflows/test_suite.yml"
|
65
65
|
- ".gitignore"
|
66
66
|
- ".ruby-version"
|
67
67
|
- ".travis.yml"
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- lib/generators/hot_glue/layout_strategy/tailwind.rb
|
105
105
|
- lib/generators/hot_glue/markup_templates/base.rb
|
106
106
|
- lib/generators/hot_glue/markup_templates/erb.rb
|
107
|
+
- lib/generators/hot_glue/nav_template_generator.rb
|
107
108
|
- lib/generators/hot_glue/scaffold_generator.rb
|
108
109
|
- lib/generators/hot_glue/templates/base_controller.rb.erb
|
109
110
|
- lib/generators/hot_glue/templates/capybara_login.rb
|
@@ -114,6 +115,7 @@ files:
|
|
114
115
|
- lib/generators/hot_glue/templates/erb/_form.erb
|
115
116
|
- lib/generators/hot_glue/templates/erb/_line.erb
|
116
117
|
- lib/generators/hot_glue/templates/erb/_list.erb
|
118
|
+
- lib/generators/hot_glue/templates/erb/_nav.html.erb
|
117
119
|
- lib/generators/hot_glue/templates/erb/_new_button.erb
|
118
120
|
- lib/generators/hot_glue/templates/erb/_new_form.erb
|
119
121
|
- lib/generators/hot_glue/templates/erb/_show.erb
|
data/.github/workflows/test.yml
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
name: Test
|
2
|
-
on: [ push, pull_request ]
|
3
|
-
|
4
|
-
jobs:
|
5
|
-
internal_tests:
|
6
|
-
services:
|
7
|
-
# Label used to access the service container
|
8
|
-
postgres:
|
9
|
-
# Docker Hub image
|
10
|
-
image: postgres
|
11
|
-
# Provide the password for postgres
|
12
|
-
env:
|
13
|
-
POSTGRES_USER: postgres
|
14
|
-
POSTGRES_PASSWORD: postgres
|
15
|
-
POSTGRES_DB: test_database_name
|
16
|
-
ports:
|
17
|
-
- 5432:5432
|
18
|
-
# Set health checks to wait until postgres has started
|
19
|
-
options: >-
|
20
|
-
--health-cmd pg_isready
|
21
|
-
--health-interval 10s
|
22
|
-
--health-timeout 5s
|
23
|
-
--health-retries 5
|
24
|
-
runs-on: ubuntu-latest
|
25
|
-
steps:
|
26
|
-
- uses: actions/checkout@v3
|
27
|
-
- name: Set up Ruby
|
28
|
-
uses: ruby/setup-ruby@v1
|
29
|
-
with:
|
30
|
-
# Not needed with a .ruby-version file
|
31
|
-
# runs 'bundle install' and caches installed gems automatically
|
32
|
-
bundler-cache: true
|
33
|
-
- name: bundle install
|
34
|
-
run: cd dummy && bundle install && cd ..
|
35
|
-
|
36
|
-
- name: run tests
|
37
|
-
env:
|
38
|
-
RUBYOPT: "-W:no-deprecated -W:no-experimental" # Suppress Rails 6 deprecation warnings for ruby 2.7
|
39
|
-
RAILS_ENV: "test"
|
40
|
-
DATABASE_URL: "postgres://postgres:postgres@localhost:5432/test_database_name"
|
41
|
-
|
42
|
-
run: |
|
43
|
-
cd dummy && bin/setup && cd ..
|
44
|
-
bundle install
|
45
|
-
bundle exec rspec --fail-fast --backtrace
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|