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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -1
  3. data/Gemfile.lock +2 -2
  4. data/README.md +193 -14
  5. data/app/helpers/hot_glue/controller_helper.rb +116 -12
  6. data/app/helpers/hot_glue_helper.rb +2 -0
  7. data/lib/generators/hot_glue/fields/association_field.rb +73 -2
  8. data/lib/generators/hot_glue/fields/boolean_field.rb +30 -0
  9. data/lib/generators/hot_glue/fields/date_field.rb +21 -1
  10. data/lib/generators/hot_glue/fields/date_time_field.rb +21 -1
  11. data/lib/generators/hot_glue/fields/enum_field.rb +22 -1
  12. data/lib/generators/hot_glue/fields/field.rb +4 -0
  13. data/lib/generators/hot_glue/fields/float_field.rb +12 -0
  14. data/lib/generators/hot_glue/fields/string_field.rb +18 -0
  15. data/lib/generators/hot_glue/fields/text_field.rb +20 -0
  16. data/lib/generators/hot_glue/fields/time_field.rb +17 -1
  17. data/lib/generators/hot_glue/markup_templates/erb.rb +61 -15
  18. data/lib/generators/hot_glue/scaffold_generator.rb +139 -16
  19. data/lib/generators/hot_glue/set_search_interface_install_generator.rb +26 -0
  20. data/lib/generators/hot_glue/templates/controller.rb.erb +50 -39
  21. data/lib/generators/hot_glue/templates/erb/_edit.erb +3 -1
  22. data/lib/generators/hot_glue/templates/erb/_list.erb +5 -0
  23. data/lib/generators/hot_glue/templates/erb/_new_form.erb +4 -1
  24. data/lib/generators/hot_glue/templates/javascript/date_range_picker_controller.js +45 -0
  25. data/lib/generators/hot_glue/templates/javascript/search_form_controller.js +16 -0
  26. data/lib/generators/hot_glue/templates/javascript/time_range_picker_controller.js +38 -0
  27. data/lib/generators/hot_glue/templates/typeahead_controller.rb.erb +1 -0
  28. data/lib/generators/hot_glue/typeahead_generator.rb +5 -1
  29. data/lib/hotglue/version.rb +1 -1
  30. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98f341b6907ade21c64956cab48b59503280aefaf9836b718d33e26ed854d32a
4
- data.tar.gz: ada2f154b32372c90c107c7507cb57d65850e761017bd17c04e6d5afc7302097
3
+ metadata.gz: 54b2a13a1fb9740be4ca14f108d98b72d8f1250b6131389c5e1d2130f07578cb
4
+ data.tar.gz: 15010c5e7fb34e35503b5a43aa563159d52f19c0b36f690ff03d67b7f1468060
5
5
  SHA512:
6
- metadata.gz: 7f38775a9cd7743c6dfb39fe4c4bed6e8b27e6987dd455b460205db6b0e13f8ea5c25e7bd24d59992ead7ca8d689f31fc71c73c5f40ea7578aaace49c41dfaca
7
- data.tar.gz: ce1fa03301507fdc462d8a43d6d0253a4bbf8d1c2184d1a582683d56ff67d5d5b9eb21b60902a8d4dfe390d41e4b3ade485e255db659ab7683e34558d838388a
6
+ metadata.gz: 7b4c9a3e48ea19199f95314a4f0bbff28ca7dfd5635c8ee34e3e2304944ad227d31cb359db1d7c4200f75336ebdb20133cfdb3c762cbe034590630e6d3051a0a
7
+ data.tar.gz: c6c26412000d4bfa3ee27571aa430a8cf7b1c6dc454c55254a618bd312a14f754088ddb2dd77cf485dd69f433d06fe985a6ba34242f3df47607f4a3955703653
data/.github/FUNDING.yml CHANGED
@@ -1 +1 @@
1
- custom: ["https://twitter.com/HotGlueForRails", "https://school.jasonfleetwoodboldt.com/8188?utm_source=github.com&utm_campaign=github_hot_glue_repo_funding_link"]
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.0.1)
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.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.jasonfleetwoodboldt.com/8188/?utm_source=github.com&utm_campaign=github_hot_glue_readme_page) **only $60 USD!**
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
- Copy & paste the whole code block from each section into your terminal. Remember, there is a small "Copy" button at the top-right of each code block to help you copy & paste the script into your terminal.
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.jasonfleetwoodboldt.com/8188)
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.jasonfleetwoodboldt.com/8188)
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. This is is only hypothetical, because the interface correctly shows the field as viewable or editable anyway, making bad 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, the backend policy would guard against the disallowed input and show an error message.)
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 #16 in the [https://school.jfbcodes.com/8188](Hot Glue Tutorial).
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.1 - `--related-sets`
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 #16 in the [https://school.jfbcodes.com/8188](Hot Glue Tutorial).
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.jasonfleetwoodboldt.com/8188) in the Hot Glue Tutorial shows you how to use the hawk to limit the scope to the logged in user.
1914
- - [Example #4](https://school.jasonfleetwoodboldt.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`)
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, label, nothing = nil )
8
+ def datetime_field_localized(form_object, field_name, value, **args )
9
9
  current_timezone
10
- form_object.text_field(field_name, class: 'form-control',
11
- type: 'datetime-local',
12
- value: date_to_current_timezone(value, current_timezone)) + timezonize(current_timezone)
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, label, timezone = nil )
17
- form_object.text_field(field_name, class: 'form-control',
18
- type: 'date',
19
- value: value )
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, label )
30
+ def time_field_localized(form_object, field_name, value, **args )
23
31
  current_timezone
24
- form_object.text_field(field_name, class: 'form-control',
25
- type: 'time',
26
- value: value && value.strftime("%H:%M"))
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
@@ -1,5 +1,7 @@
1
1
  module HotGlueHelper
2
2
 
3
+
4
+
3
5
  def enum_to_collection_select(hash)
4
6
  hash.collect{|k,v| OpenStruct.new({key: k, value: v})}
5
7
 
@@ -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.plural_name}_typeahead_index_url"
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