hot-glue 0.6.1 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -1
- data/Gemfile.lock +2 -2
- data/README.md +193 -14
- data/app/helpers/hot_glue/controller_helper.rb +116 -12
- data/app/helpers/hot_glue_helper.rb +2 -0
- data/lib/generators/hot_glue/fields/association_field.rb +73 -2
- data/lib/generators/hot_glue/fields/boolean_field.rb +30 -0
- data/lib/generators/hot_glue/fields/date_field.rb +21 -1
- data/lib/generators/hot_glue/fields/date_time_field.rb +21 -1
- data/lib/generators/hot_glue/fields/enum_field.rb +22 -1
- data/lib/generators/hot_glue/fields/field.rb +4 -0
- data/lib/generators/hot_glue/fields/float_field.rb +12 -0
- data/lib/generators/hot_glue/fields/string_field.rb +18 -0
- data/lib/generators/hot_glue/fields/text_field.rb +20 -0
- data/lib/generators/hot_glue/fields/time_field.rb +17 -1
- data/lib/generators/hot_glue/markup_templates/erb.rb +61 -15
- data/lib/generators/hot_glue/scaffold_generator.rb +139 -16
- data/lib/generators/hot_glue/set_search_interface_install_generator.rb +26 -0
- data/lib/generators/hot_glue/templates/controller.rb.erb +50 -39
- data/lib/generators/hot_glue/templates/erb/_edit.erb +3 -1
- data/lib/generators/hot_glue/templates/erb/_list.erb +5 -0
- data/lib/generators/hot_glue/templates/erb/_new_form.erb +4 -1
- data/lib/generators/hot_glue/templates/javascript/date_range_picker_controller.js +45 -0
- data/lib/generators/hot_glue/templates/javascript/search_form_controller.js +16 -0
- data/lib/generators/hot_glue/templates/javascript/time_range_picker_controller.js +38 -0
- data/lib/generators/hot_glue/templates/typeahead_controller.rb.erb +1 -0
- data/lib/generators/hot_glue/typeahead_generator.rb +5 -1
- data/lib/hotglue/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54b2a13a1fb9740be4ca14f108d98b72d8f1250b6131389c5e1d2130f07578cb
|
4
|
+
data.tar.gz: 15010c5e7fb34e35503b5a43aa563159d52f19c0b36f690ff03d67b7f1468060
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b4c9a3e48ea19199f95314a4f0bbff28ca7dfd5635c8ee34e3e2304944ad227d31cb359db1d7c4200f75336ebdb20133cfdb3c762cbe034590630e6d3051a0a
|
7
|
+
data.tar.gz: c6c26412000d4bfa3ee27571aa430a8cf7b1c6dc454c55254a618bd312a14f754088ddb2dd77cf485dd69f433d06fe985a6ba34242f3df47607f4a3955703653
|
data/.github/FUNDING.yml
CHANGED
@@ -1 +1 @@
|
|
1
|
-
custom: ["https://twitter.com/HotGlueForRails", "https://school.
|
1
|
+
custom: ["https://twitter.com/HotGlueForRails", "https://school.jfbcodes.com/8188?utm_source=github.com&utm_campaign=github_hot_glue_repo_funding_link"]
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
hot-glue (0.6.
|
4
|
+
hot-glue (0.6.2)
|
5
5
|
ffaker (~> 2.16)
|
6
6
|
kaminari (~> 1.2)
|
7
7
|
rails (> 5.1)
|
@@ -139,7 +139,7 @@ GEM
|
|
139
139
|
mini_mime (1.1.2)
|
140
140
|
mini_portile2 (2.8.4)
|
141
141
|
minitest (5.16.3)
|
142
|
-
net-imap (0.4.
|
142
|
+
net-imap (0.4.7)
|
143
143
|
date
|
144
144
|
net-protocol
|
145
145
|
net-pop (0.1.2)
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ Other than the opinionated differences and additional features, Hot Glue produce
|
|
38
38
|
|
39
39
|
# Get Hot Glue
|
40
40
|
|
41
|
-
## [GET THE COURSE TODAY](https://school.
|
41
|
+
## [GET THE COURSE TODAY](https://school.jfbcodes.com/8188/?utm_source=github.com&utm_campaign=github_hot_glue_readme_page) **only $60 USD!**
|
42
42
|
|
43
43
|
|
44
44
|
---
|
@@ -63,10 +63,14 @@ Instantly get a simple CRUD interface
|
|
63
63
|
_If you are on Rails 6, see [LEGACY SETUP FOR RAILS 6](https://github.com/jasonfb/hot-glue/README2.md) and complete those steps FIRST._
|
64
64
|
|
65
65
|
## The Super-Quick Setup
|
66
|
+
https://jasonfleetwoodboldt.com/courses/stepping-up-rails/jason-fleetwood-boldts-rails-cookbook/hot-glue-quick-install-mega-script/
|
67
|
+
Copy & paste the whole code block into your terminal.
|
68
|
+
Remember, there is a small "Copy" button at the top-right of the code block.
|
69
|
+
Be sure to use your Node + Ruby version managers to switch into the Node & Ruby versions **before running the quick script**.
|
66
70
|
|
67
|
-
https://jasonfleetwoodboldt.com/courses/stepping-up-rails/jason-fleetwood-boldts-rails-cookbook/
|
68
71
|
|
69
|
-
|
72
|
+
For more of a step-by-step, see the full cookbook at:
|
73
|
+
https://jasonfleetwoodboldt.com/courses/stepping-up-rails/jason-fleetwood-boldts-rails-cookbook/
|
70
74
|
|
71
75
|
These are the sections you need, you can ignore any others:
|
72
76
|
|
@@ -522,7 +526,7 @@ The short form looks like this. It presumes there is a 'pets' association from `
|
|
522
526
|
|
523
527
|
(The long form equivalent of this would be `--hawk=pet_id{current_user.pets}`)
|
524
528
|
|
525
|
-
This is covered in [Example #3 in the Hot Glue Tutorial](https://school.
|
529
|
+
This is covered in [Example #3 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
|
526
530
|
|
527
531
|
To hawk to a scope that is not the currently authenticated user, use the long form with `{...}`
|
528
532
|
to specify the scope. Be sure to note to add the association name itself, like `users`:
|
@@ -532,7 +536,7 @@ to specify the scope. Be sure to note to add the association name itself, like `
|
|
532
536
|
This would hawk the Appointment's `user_id` key to any users who are within the scope of the
|
533
537
|
current_user's has_many association (so, for any other "my" family, would be `current_user.family.users`).
|
534
538
|
|
535
|
-
This is covered in [Example #4 in the Hot Glue Tutorial](https://school.
|
539
|
+
This is covered in [Example #4 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
|
536
540
|
|
537
541
|
|
538
542
|
### `--plural=`
|
@@ -691,11 +695,14 @@ If you enable Pundit, your controllers will look for a Policy that matches the n
|
|
691
695
|
**Realtime Field-level Access**
|
692
696
|
Hot Glue gives you automatic field level access control if you create `*_able?` methods for each field you'd like to control on your Pundit policy.
|
693
697
|
|
694
|
-
(Although this is not what Pundit recommends for field level access control, it has been implemented this way to provide field-by-field user feedback to the user shown in red just like Rails validation errors are currently shown.
|
698
|
+
(Although this is not what Pundit recommends for field level access control, it has been implemented this way to provide field-by-field user feedback to the user shown in red just like Rails validation errors are currently shown. A user having access to input text into a field they shouldn't have access to is *only hypothetical*, because the Hot Glue will correctly show the the user connect edit was view-only anyway, making unallowed entry only something that could be achieved through a mistake or hacking. Nevertheless, rest assured that if there was a input mistake-- like a user having a field editable when it shouldn't be, if you implement your backend policy correctly with an `update?` method guards against the disallowed input, your user will show an error message and the record will not be updated.)
|
699
|
+
|
700
|
+
The `*_able?` method should return true or false depending on whether or not the field can be edited. No distinction is made between the `new` and `edit` contexts. You may check if the record is new using `new_record?`.
|
701
|
+
|
702
|
+
|
703
|
+
**The `*_able?` method is used by Hot Glue only on the new and edit actions. You must incorporate it into the policy's `update?` method as in the example, or else no guard will check prevent the user doesn't pass a value to input it anyway.**
|
695
704
|
|
696
|
-
The `*_able?` method should return true or false depending on whether or not the field can be edited. (No distinction is made between the different contexts. You may check if the record is new using `new_record?`.
|
697
705
|
|
698
|
-
The `*_able?` method is used by Hot Glue only on the new and edit actions, but you should incorporate it into the policy's `update?` method as in the example, which will extend other operations since Hot Glue will use the policy to authorize the action
|
699
706
|
Add one `*_able?` method to the policy for each field you want to allow field-level access control.
|
700
707
|
|
701
708
|
Replace `*` with the name of the field you want under access control. Remember to include `_id` for foreign keys. You do not need it for any field you don't want under access control.
|
@@ -852,7 +859,7 @@ This means you can be a somewhat lazy about your bang methods, but keep in mind
|
|
852
859
|
|
853
860
|
Finally, you can raise an ActiveRecord error which will also get passed to the user in the flash alert area.
|
854
861
|
|
855
|
-
For more information see Example 5 in the Tutorial
|
862
|
+
For more information see [Example 5 in the Tutorial](https://school.jfbcodes.com/8188)
|
856
863
|
|
857
864
|
|
858
865
|
### `--downnest`
|
@@ -980,7 +987,7 @@ rails generate hot_glue:scaffold User --related-sets=roles --include=email,roles
|
|
980
987
|
|
981
988
|
Note this leaves open a privileged escalation attack (a security vulnerability).
|
982
989
|
|
983
|
-
To fix this, you'll need to use Pundit with special syntax designed for this purpose. Please see Example #
|
990
|
+
To fix this, you'll need to use Pundit with special syntax designed for this purpose. Please see [Example #17 in the Hot Glue Tutorial](https://school.jfbcodes.com/8188)
|
984
991
|
|
985
992
|
|
986
993
|
## "Thing" Label
|
@@ -1048,6 +1055,87 @@ Use `before` to make the labels come before or `after` to make them come after.
|
|
1048
1055
|
Omits the heading of column names that appears above the 1st row of data.
|
1049
1056
|
|
1050
1057
|
|
1058
|
+
|
1059
|
+
`--code-before-create`
|
1060
|
+
`--code-after-create`
|
1061
|
+
`--code-before-update`
|
1062
|
+
`--code-after-update`
|
1063
|
+
|
1064
|
+
Insert some code into the `create` action or `update` actions.
|
1065
|
+
The **before code** is called _after authorization_ but _before saving_ (which creates the record, or fails validation).
|
1066
|
+
The **after create** code is called after the record is saved (and thus has an id in the case of the create action).
|
1067
|
+
Both should be wrapped in quotation marks when specified in the command line, and use semicolons to separate multiple lines of code.
|
1068
|
+
(Notice the funky indentation of the lines in the generated code. Adjust you input to get the indentation correct.)
|
1069
|
+
|
1070
|
+
|
1071
|
+
## Searching
|
1072
|
+
|
1073
|
+
### `--search` (options: simple, set, false predicate, default: false)
|
1074
|
+
|
1075
|
+
|
1076
|
+
#### Set Search
|
1077
|
+
If you specify `--search` to `set`, you will get a whole bar across the top of the list with search fields for each field.
|
1078
|
+
Within the set, the search query is **_combinative_** ("and"), so records matching all criteria are shown as the **result set.**
|
1079
|
+
For date pickers and time pickers, you need the additional Stimulus.
|
1080
|
+
Install this with :
|
1081
|
+
|
1082
|
+
```
|
1083
|
+
bin/rails generate hot_glue:set_search_interface_install
|
1084
|
+
```
|
1085
|
+
|
1086
|
+
_Additional search option for Set Search_
|
1087
|
+
##### `--search-fields=aaa,bbb,ccc,ddd,eee`
|
1088
|
+
to specify which fields you want to be searchable.
|
1089
|
+
|
1090
|
+
|
1091
|
+
##### `--search-query-fields=aaa,ddd`
|
1092
|
+
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)
|
1093
|
+
|
1094
|
+
##### `--search-position=vertical`
|
1095
|
+
to specify vertical or horizontal (default: horizontal)
|
1096
|
+
|
1097
|
+
##### `--search-clear-button` (no option)
|
1098
|
+
to specify whether to show a clear button to clear the whole search form at once (default: false)
|
1099
|
+
|
1100
|
+
##### `--search-autosearch` (no option)
|
1101
|
+
to specify whether to automatically search when the user exit or changes any field (default: false)
|
1102
|
+
|
1103
|
+
examples:
|
1104
|
+
```
|
1105
|
+
bin/rails generate Thing --include=name,description --search=set --search-fields=name,description
|
1106
|
+
```
|
1107
|
+
|
1108
|
+
_Make a searchable table with two foreign keys (author_id and category_id) and a query field for title, including a clear button._
|
1109
|
+
```
|
1110
|
+
bin/rails generate Articles --inclue=title,author_id,category_id --search=set --search-fields=title,author_id,category_id --search-query-fields=title --search-clear-button
|
1111
|
+
```
|
1112
|
+
|
1113
|
+
_Make a searchable table with vertical position and autosearch on._
|
1114
|
+
```
|
1115
|
+
bin/rails generate Inications --inclue=patient_id,drug_id,quantity --search=set --search-fields=patient_id,drug_id --search-position=vertical --search-autosearch
|
1116
|
+
```
|
1117
|
+
|
1118
|
+
|
1119
|
+
Here's how you would add a search interface to Example #1 in the [Hot Glue Tutorial](https://school.jfbcodes.com/8188)
|
1120
|
+
```
|
1121
|
+
bin/rails generate Book --include=name,author_id --search=set --search-fields=name,author_id
|
1122
|
+
```
|
1123
|
+
|
1124
|
+
|
1125
|
+
|
1126
|
+
|
1127
|
+
|
1128
|
+
#### Predicate
|
1129
|
+
NOT IMPLEMENTED YET
|
1130
|
+
TODO: implement me
|
1131
|
+
|
1132
|
+
|
1133
|
+
|
1134
|
+
|
1135
|
+
|
1136
|
+
|
1137
|
+
|
1138
|
+
|
1051
1139
|
## Special Features
|
1052
1140
|
|
1053
1141
|
### `--attachments`
|
@@ -1345,6 +1433,10 @@ Child portals have the headings omitted automatically (there is a heading identi
|
|
1345
1433
|
- Enum - displayed as a drop-down list (defined the enum values on your model).
|
1346
1434
|
- For Rails 6 see https://jasonfleetwoodboldt.com/courses/stepping-up-rails/enumerated-types-in-rails-and-postgres/
|
1347
1435
|
- You must specify the enum definition both in your model and also in your database migration for both Rails 6 + Rails 7
|
1436
|
+
- You can modify an enum so that instead of a drop down list, it displays a partial view that you must build. See the [v0.5.23 Release notes](https://github.com/hot-glue-for-rails/hot-glue#2023-10-01---v0523) for details.
|
1437
|
+
|
1438
|
+
|
1439
|
+
|
1348
1440
|
|
1349
1441
|
# Note about enums
|
1350
1442
|
|
@@ -1389,6 +1481,8 @@ def self.status_labels
|
|
1389
1481
|
|
1390
1482
|
Now, your labels will show up on the front-end as defined in the `_labels` ("Is currently pending", etc) instead of the database-values.
|
1391
1483
|
|
1484
|
+
You can modify an enum so that instead of a drop down list, it displays a partial view that you must build. See the [v0.5.23 Release notes](https://github.com/hot-glue-for-rails/hot-glue#2023-10-01---v0523) for details.
|
1485
|
+
|
1392
1486
|
### Validation Magic
|
1393
1487
|
|
1394
1488
|
Use ActiveRecord validations or hooks to validate your data. Hot Glue will automatically display the errors in the UI.
|
@@ -1491,9 +1585,94 @@ For example, to display the field `my_story` on the object `Thing`, you'd genera
|
|
1491
1585
|
bin/rails generate Thing --include=my_story --modify='my_story{tinymce}'
|
1492
1586
|
```
|
1493
1587
|
|
1588
|
+
### Pickup Partial Includes for `_edit` and `_new` Screens
|
1589
|
+
|
1590
|
+
If you have a partial already in the view folder called `_edit_within_form.html.erb`, it with get included within the edit form.
|
1591
|
+
If you have a partial already in the view folder called `_new_within_form.html.erb`, it with get included within the new form.
|
1592
|
+
For these, you can use any of the objects by local variable name or the special `f` local variable to access the form itself.
|
1593
|
+
|
1594
|
+
These partials are good for including extra functionality in the form, like interactive widgets or hidden fields.
|
1595
|
+
If you have a partial already in the view folder called `_edit_after_form.html.erb`, it with get included **_after_** the edit form.
|
1596
|
+
If you have a partial already in the view folder called `_new_after_form.html.erb`, it with get included **_after_** the new form.
|
1597
|
+
You can use any of the objects by local variable name (but you cannot use the form object `f` because it is not in scope.)
|
1598
|
+
|
1599
|
+
The `within` partials should do operations within the form (like hidden fields), and the `after` partials should do entirely unrelated operations, like a different form entirely.
|
1600
|
+
|
1601
|
+
These automatic pickups for partials are detected at buildtime. This means that if you add these partials later, you must rebuild your scaffold.
|
1602
|
+
|
1603
|
+
|
1604
|
+
|
1605
|
+
|
1606
|
+
|
1494
1607
|
# VERSION HISTORY
|
1495
1608
|
|
1496
|
-
#### v0.6.
|
1609
|
+
#### D2024-01-15 - v0.6.3
|
1610
|
+
|
1611
|
+
## Set Searching
|
1612
|
+
|
1613
|
+
### `--search` (options: set, false default: false)
|
1614
|
+
|
1615
|
+
(Future options include simple, predicate)
|
1616
|
+
|
1617
|
+
A set search is a search form that allows you to search across multiple fields at once. It is a set of search fields, each of which is a search field for a single field.
|
1618
|
+
|
1619
|
+
|
1620
|
+
#### Set Search
|
1621
|
+
If you specify `--search` to `set`, you will get a whole bar across the top of the list with search fields for each field.
|
1622
|
+
Within the set, the search query is **_combinative_** ("and"), so records matching all criteria are shown as the **result set.**
|
1623
|
+
For date pickers, time pickers, and the clear form interaction, you need the additional Stimulus JS.
|
1624
|
+
Install this with :
|
1625
|
+
|
1626
|
+
```
|
1627
|
+
bin/rails generate hot_glue:set_search_interface_install
|
1628
|
+
```
|
1629
|
+
|
1630
|
+
_Additional search option for Set Search_
|
1631
|
+
##### `--search-fields=aaa,bbb,ccc,ddd,eee`
|
1632
|
+
to specify which fields you want to be searchable.
|
1633
|
+
|
1634
|
+
##### `--search-query-fields=aaa,ddd`
|
1635
|
+
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)
|
1636
|
+
|
1637
|
+
##### `--search-position=vertical`
|
1638
|
+
to specify vertical or horizontal (default: horizontal)
|
1639
|
+
|
1640
|
+
##### `--search-clear-button` (no option)
|
1641
|
+
to specify whether to show a clear button to clear the whole search form at once (default: false)
|
1642
|
+
|
1643
|
+
|
1644
|
+
|
1645
|
+
|
1646
|
+
|
1647
|
+
|
1648
|
+
#### 2023-12-02 - v0.6.2
|
1649
|
+
|
1650
|
+
• Fixes to typeahead when using Pundit.
|
1651
|
+
|
1652
|
+
• New Code Hooks: Add Custom Code Before/After the Update or Create Actions
|
1653
|
+
|
1654
|
+
`--code-before-create`
|
1655
|
+
`--code-after-create`
|
1656
|
+
`--code-before-update`
|
1657
|
+
`--code-after-update`
|
1658
|
+
|
1659
|
+
|
1660
|
+
Insert some code into the `create` action or `update` actions.
|
1661
|
+
|
1662
|
+
The **before code** is called _after authorization_ but _before saving_ (which creates the record, or fails validation).
|
1663
|
+
|
1664
|
+
The **after create** code is called after the record is saved (and thus has an id in the case of the create action).
|
1665
|
+
|
1666
|
+
Both should be wrapped in quotation marks when specified in the command line, and use semicolons to separate multiple lines of code.
|
1667
|
+
(Notice the funky indentation of the lines in the generated code. Adjust you input to get the indentation correct.)
|
1668
|
+
|
1669
|
+
• New Automatic Pickup Partial Includes for `_edit` and `_new` Screens
|
1670
|
+
|
1671
|
+
See "Pickup Partial Includes for `_edit` and `_new` Screens" above
|
1672
|
+
|
1673
|
+
|
1674
|
+
|
1675
|
+
#### 2023-11-21 - v0.6.1 - `--related-sets`
|
1497
1676
|
|
1498
1677
|
Used to show a checkbox set of related records. The relationship should be a `has_and_belongs_to_many` or a `has_many through:` from the object being built.
|
1499
1678
|
|
@@ -1511,7 +1690,7 @@ Note that when making a scaffold like this, you may leave open a privileged esca
|
|
1511
1690
|
|
1512
1691
|
To fix this, you'll need to use Pundit with special syntax designed for this purpose.
|
1513
1692
|
|
1514
|
-
For a complete solution, please see Example
|
1693
|
+
For a complete solution, please see Example 17 in the [Hot Glue Tutorial](https://school.jfbcodes.com/8188).
|
1515
1694
|
|
1516
1695
|
Without Pundit, due to a quirk in how this code works with ActiveRecord, all update operates to the related sets table are permitted (and go through), even if the update operation otherwise fails validation for the fields on the object. (ActiveRecord doesn't seem to have a way to validate the related sets directly.)
|
1517
1696
|
|
@@ -1910,8 +2089,8 @@ License check has been removed (Hot Glue is now free to use for hobbyists and in
|
|
1910
2089
|
#### 2022-03-23 - v0.5.2 - Hawked Foreign Keys
|
1911
2090
|
|
1912
2091
|
- You can now protect your foreign keys from malicious input and also restrict the scope of drop downs to show only records with the specified access control restriction.
|
1913
|
-
- [Example #3](https://school.
|
1914
|
-
- [Example #4](https://school.
|
2092
|
+
- [Example #3](https://school.jfbcodes.com/8188) in the Hot Glue Tutorial shows you how to use the hawk to limit the scope to the logged in user.
|
2093
|
+
- [Example #4](https://school.jfbcodes.com/8188) in the Hot Glue Tutorial shows how to hawk to a non-usual scope, the inverse of the current user's belongs_to (that is, hawk the scope to X where current_user `belongs_to :x`)
|
1915
2094
|
|
1916
2095
|
|
1917
2096
|
#### 2022-03-12 - v0.5.1 - Inline List Labels
|
@@ -5,28 +5,42 @@ 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,
|
8
|
+
def datetime_field_localized(form_object, field_name, value, **args )
|
9
9
|
current_timezone
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
|
11
|
+
args = args.merge({class: 'form-control',
|
12
|
+
type: 'datetime-local' })
|
13
|
+
|
14
|
+
if !value.nil?
|
15
|
+
args[:value] = date_to_current_timezone(value, current_timezone) + timezonize(current_timezone)
|
16
|
+
end
|
17
|
+
|
18
|
+
form_object.text_field(field_name, args)
|
19
|
+
|
13
20
|
end
|
14
21
|
|
15
22
|
|
16
|
-
def date_field_localized(form_object, field_name, value,
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
def date_field_localized(form_object, field_name, value, **args)
|
24
|
+
|
25
|
+
form_object.text_field(field_name, args.merge({class: 'form-control',
|
26
|
+
type: 'date',
|
27
|
+
value: value }))
|
20
28
|
end
|
21
29
|
|
22
|
-
def time_field_localized(form_object, field_name, value,
|
30
|
+
def time_field_localized(form_object, field_name, value, **args )
|
23
31
|
current_timezone
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
|
33
|
+
form_object.text_field(field_name, args.merge({class: 'form-control',
|
34
|
+
type: 'time',
|
35
|
+
value: value }))
|
27
36
|
|
28
37
|
end
|
29
38
|
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
30
44
|
def current_timezone
|
31
45
|
# returns a TimeZone (https://apidock.com/rails/TimeZone) object
|
32
46
|
if defined?(current_user)
|
@@ -104,6 +118,96 @@ module HotGlue
|
|
104
118
|
modified_params
|
105
119
|
end
|
106
120
|
|
121
|
+
def string_query_constructor(match, search)
|
122
|
+
if match.blank? || search.blank?
|
123
|
+
nil
|
124
|
+
else
|
125
|
+
case match
|
126
|
+
when 'contains'
|
127
|
+
"%#{search}%"
|
128
|
+
when 'is_exactly'
|
129
|
+
"#{search}"
|
130
|
+
when 'starts_with'
|
131
|
+
"#{search}%"
|
132
|
+
when 'ends_with'
|
133
|
+
"%#{search}"
|
134
|
+
else
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def enum_constructor(field_name, value, **args)
|
141
|
+
return nil if value.blank?
|
142
|
+
["#{field_name} = ?", value]
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def date_query_constructor(field, match, search_start, search_end)
|
147
|
+
if match.blank?
|
148
|
+
nil
|
149
|
+
elsif ['is_on', 'not_on'].include?(match) && search_start.blank?
|
150
|
+
nil
|
151
|
+
elsif ['is_on_or_after','is_between'].include?(match) && (search_start.blank? )
|
152
|
+
nil
|
153
|
+
elsif ['is_before_or_on'].include?(match) && (search_end.blank? )
|
154
|
+
nil
|
155
|
+
elsif ['is_between'].include?(match) && (search_start.blank? || search_end.blank? )
|
156
|
+
nil
|
157
|
+
else
|
158
|
+
case match
|
159
|
+
when 'is_on'
|
160
|
+
["#{field} = ?", search_start]
|
161
|
+
when 'is_on_or_after'
|
162
|
+
["#{field} = ? OR #{field} > ?", search_start, search_start]
|
163
|
+
when "is_before_or_on"
|
164
|
+
["#{field} = ? OR #{field} < ?", search_end, search_end]
|
165
|
+
when "is_between"
|
166
|
+
["#{field} BETWEEN ? AND ?", search_start, search_end]
|
167
|
+
when "not_on"
|
168
|
+
["#{field} != ?", search_start]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def time_query_constructor(field, match, search_start, search_end)
|
174
|
+
if match.blank?
|
175
|
+
nil
|
176
|
+
elsif ['is_at'].include?(match) && search_start.blank?
|
177
|
+
nil
|
178
|
+
elsif ['is_ar_or_after', 'is_before_or_at', 'is_between'].include?(match) && (search_start.blank? || search_end.blank?)
|
179
|
+
nil
|
180
|
+
else
|
181
|
+
case match
|
182
|
+
when 'is_at_exactly'
|
183
|
+
["EXTRACT(HOUR FROM #{field}) = ?
|
184
|
+
AND EXTRACT(MINUTE FROM #{field}) = ? ", search_start.split(":")[0], search_start.split(":")[1]]
|
185
|
+
# when 'is_at_or_after'
|
186
|
+
# ["#{field} = ? OR #{field} > ?", search_start, search_start]
|
187
|
+
# when "is_before_or_at"
|
188
|
+
# ["#{field} = ? OR #{field} < ?", search_end, search_end]
|
189
|
+
# when "is_between"
|
190
|
+
# ["#{field} BETWEEN ? AND ?", search_start, search_end]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def association_constructor(field, search)
|
196
|
+
unless search.blank?
|
197
|
+
["#{field} = ?", search]
|
198
|
+
else
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def boolean_query_constructor(field, search)
|
204
|
+
unless search.blank?
|
205
|
+
["#{field} = ?", search]
|
206
|
+
else
|
207
|
+
nil
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
107
211
|
private
|
108
212
|
|
109
213
|
def server_timezone_offset # returns integer of hours to add/subtract from UTC
|
@@ -78,9 +78,8 @@ class AssociationField < Field
|
|
78
78
|
def form_field_output
|
79
79
|
assoc_name = name.to_s.gsub("_id","")
|
80
80
|
assoc = eval("#{class_name}.reflect_on_association(:#{assoc_name})")
|
81
|
-
|
82
81
|
if modify_as && modify_as[:typeahead]
|
83
|
-
search_url = "#{namespace ? namespace + "_" : ""}#{assoc.
|
82
|
+
search_url = "#{namespace ? namespace + "_" : ""}#{assoc.class_name.downcase.pluralize}_typeahead_index_url"
|
84
83
|
"<div class='typeahead typeahead--#{assoc.name}_id'
|
85
84
|
data-controller='typeahead'
|
86
85
|
data-typeahead-url-value='<%= #{search_url} %>'
|
@@ -150,4 +149,76 @@ class AssociationField < Field
|
|
150
149
|
|
151
150
|
"<%= #{singular}.#{assoc}.try(:#{display_column}) || '<span class=\"content \">MISSING</span>'.html_safe %>"
|
152
151
|
end
|
152
|
+
|
153
|
+
|
154
|
+
def search_field_output
|
155
|
+
|
156
|
+
assoc_name = name.to_s.gsub("_id","")
|
157
|
+
assoc = eval("#{class_name}.reflect_on_association(:#{assoc_name})")
|
158
|
+
if modify_as && modify_as[:typeahead]
|
159
|
+
search_url = "#{namespace ? namespace + "_" : ""}#{assoc.class_name.downcase.pluralize}_typeahead_index_url"
|
160
|
+
|
161
|
+
# \"q[0][#{name}_search]\"
|
162
|
+
# @q['0']['#{name}_search']
|
163
|
+
"<div class='typeahead typeahead--q_0_#{name}_search'
|
164
|
+
data-controller='typeahead'
|
165
|
+
data-typeahead-url-value='<%= #{search_url} %>'
|
166
|
+
data-typeahead-typeahead-results-outlet='#search-results'>
|
167
|
+
<%= text_field_tag \'q[0][#{name}_search]_query\', '', placeholder: 'Search #{assoc.plural_name}', class: 'search__input',
|
168
|
+
data: { action: 'keyup->typeahead#fetchResults keydown->typeahead#navigateResults', typeahead_target: 'query' },
|
169
|
+
autofocus: true,
|
170
|
+
autocomplete: 'off',
|
171
|
+
value: @q['0']['#{name}'] ? #{assoc.class_name}.find(@q['0']['#{name}']).try(:name) : \"\" %>
|
172
|
+
<%= f.hidden_field \'q[0][#{name}]\', value: @q['0']['#{name}_search'].try(:id), 'data-typeahead-target': 'hiddenFormValue' %>
|
173
|
+
<div data-typeahead-target='results'></div>
|
174
|
+
<div data-typeahead-target='classIdentifier' data-id=\"typeahead--q_0_#{name}_search\"></div>
|
175
|
+
</div>"
|
176
|
+
else
|
177
|
+
if assoc.nil?
|
178
|
+
exit_message = "*** Oops. on the #{class_name} object, there doesn't seem to be an association called '#{assoc_name}'"
|
179
|
+
exit
|
180
|
+
end
|
181
|
+
|
182
|
+
is_owner = name == ownership_field
|
183
|
+
assoc_class_name = assoc.class_name.to_s
|
184
|
+
display_column = HotGlue.derrive_reference_name(assoc_class_name)
|
185
|
+
|
186
|
+
if hawk_keys[assoc.foreign_key.to_sym]
|
187
|
+
hawk_definition = hawk_keys[assoc.foreign_key.to_sym]
|
188
|
+
hawked_association = hawk_definition[:bind_to].join(".")
|
189
|
+
else
|
190
|
+
hawked_association = "#{assoc.class_name}.all"
|
191
|
+
end
|
192
|
+
|
193
|
+
(is_owner ? "<% unless @#{assoc_name} %>\n" : "") +
|
194
|
+
" <%= f.collection_select(\"q[0][#{name}_search]\", #{hawked_association}, :id, :#{display_column}, {include_blank: true, selected: @q['0']['#{name}_search'] }, class: 'form-control') %>\n" +
|
195
|
+
(is_owner ? "<% else %>\n <%= @#{assoc_name}.#{display_column} %>" : "") +
|
196
|
+
(is_owner ? "\n<% end %>" : "")
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
# " "+
|
201
|
+
# "\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['is on', 'is_on'], " +
|
202
|
+
# "\n ['is between', 'is_between'], ['is on or after', 'is_on_or_after'], " +
|
203
|
+
# "\n ['is before or on', 'is_before_or_on'], ['not on', 'not_on']], @q[\'0\']['#{name}_match'] ), {} ," +
|
204
|
+
# "\n { class: 'form-control match', 'data-action': 'change->date-range-picker#matchSelection' } %>"+
|
205
|
+
# "\n <%= date_field_localized f, 'q[0][#{name}_search_start]', @q[\'0\'][:#{name}_search_start], autocomplete: 'off', size: 40, class: 'form-control', type: 'text', placeholder: 'start', 'data-date-range-picker-target': 'start' %>" +
|
206
|
+
# "\n <%= date_field_localized f, 'q[0][#{name}_search_end]', @q[\'0\'][:#{name}_search_end], autocomplete: 'off', size: 40, class: 'form-control', type: 'text', placeholder: 'end' , 'data-date-range-picker-target': 'end'%>" +
|
207
|
+
# "\n "
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
def where_query_statement
|
212
|
+
".where(*#{name}_query)"
|
213
|
+
end
|
214
|
+
|
215
|
+
def load_all_query_statement
|
216
|
+
if modify_as && modify_as[:typeahead]
|
217
|
+
"#{name}_query = association_constructor(:#{name}, @q['0'][:#{name}])"
|
218
|
+
else
|
219
|
+
"#{name}_query = association_constructor(:#{name}, @q['0'][:#{name}_search])"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
|
153
224
|
end
|
@@ -78,4 +78,34 @@ class BooleanField < Field
|
|
78
78
|
def label_class
|
79
79
|
super + " form-check-label"
|
80
80
|
end
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
def search_field_output
|
85
|
+
" <%= f.radio_button('q[0][#{name}_match]', '-1', checked: @q[\'0\']['#{name}_match']==-1 ? '' : 'checked', class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" +
|
86
|
+
" <%= f.label('q[0][#{name}_match]', value: '-1', for: 'q[0][#{name}_match]_-1' ) %>\n" +
|
87
|
+
|
88
|
+
" <%= f.radio_button('q[0][#{name}_match]', '0', checked: @q[\'0\']['#{name}_match']==0 ? '' : 'checked', class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" +
|
89
|
+
" <%= f.label('q[0][#{name}_match]_0', value: '#{modify_binary? && modify_as[:binary][:falsy] || 'No'}', for: 'q[0][#{name}_match]_0') %>\n" +
|
90
|
+
" <br /> <%= f.radio_button('q[0][#{name}_match]', '1', checked: @q[\'0\']['#{name}_match']==1 ? 'checked' : '' , class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" +
|
91
|
+
" <%= f.label('q[0][#{name}_match]_1', value: '#{modify_binary? && modify_as[:binary][:truthy] || 'Yes'}', for: 'q[0][#{name}_match]_1') %>\n"
|
92
|
+
|
93
|
+
|
94
|
+
# "<%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['contains', 'contains'], ['is exactly', 'is_exactly'], ['starts with', 'starts_with'], ['ends with', 'ends_with']], @q[\'0\']['#{name}_match'] ), {} , { class: 'form-control match' } %>"+
|
95
|
+
# "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text' %>"
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def where_query_statement
|
100
|
+
".where('#{name} ILIKE ?', #{name}_query)"
|
101
|
+
end
|
102
|
+
|
103
|
+
def load_all_query_statement
|
104
|
+
"#{name}_query = boolean_query_constructor(@q['0'][:#{name}_match], @q['0'][:#{name}_search])"
|
105
|
+
end
|
106
|
+
|
107
|
+
# def code_to_reset_match_if_search_is_blank
|
108
|
+
# " @q['0'][:#{name}_match] = '' if @q['0'][:#{name}_search] == ''"
|
109
|
+
# end
|
110
|
+
|
81
111
|
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}, label: '#{ name.to_s.humanize }') %>"
|
14
14
|
end
|
15
15
|
|
16
16
|
def line_field_output
|
@@ -20,4 +20,24 @@ class DateField < Field
|
|
20
20
|
<span class=''>MISSING</span>
|
21
21
|
<% end %>"
|
22
22
|
end
|
23
|
+
|
24
|
+
def search_field_output
|
25
|
+
" <div data-controller='date-range-picker' >"+
|
26
|
+
"\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['is on', 'is_on'], " +
|
27
|
+
"\n ['is between', 'is_between'], ['is on or after', 'is_on_or_after'], " +
|
28
|
+
"\n ['is before or on', 'is_before_or_on'], ['not on', 'not_on']], @q[\'0\']['#{name}_match'] ), {} ," +
|
29
|
+
"\n { class: 'form-control match', 'data-action': 'change->date-range-picker#matchSelection' } %>"+
|
30
|
+
"\n <%= date_field_localized f, 'q[0][#{name}_search_start]', @q[\'0\'][:#{name}_search_start], autocomplete: 'off', size: 40, class: 'form-control', type: 'text', placeholder: 'start', 'data-date-range-picker-target': 'start' %>" +
|
31
|
+
"\n <%= date_field_localized f, 'q[0][#{name}_search_end]', @q[\'0\'][:#{name}_search_end], autocomplete: 'off', size: 40, class: 'form-control', type: 'text', placeholder: 'end' , 'data-date-range-picker-target': 'end'%>" +
|
32
|
+
"\n </div>"
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def where_query_statement
|
37
|
+
".where(*#{name}_query)"
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_all_query_statement
|
41
|
+
"#{name}_query = date_query_constructor(:#{name}, @q['0'][:#{name}_match], @q['0'][:#{name}_search_start], @q['0'][:#{name}_search_end])"
|
42
|
+
end
|
23
43
|
end
|