├── .circleci └── DISABLED__config.yml ├── .github ├── FUNDING.yml └── workflows │ └── test_suite.yml ├── .gitignore ├── .ruby-version ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── README2.md ├── app ├── assets │ └── config │ │ └── manifest.js ├── helpers │ ├── hot_glue │ │ └── controller_helper.rb │ └── hot_glue_helper.rb └── views │ └── layouts │ └── _flash_notices.erb ├── config ├── database.yml └── hot_glue.yml ├── db └── schema.rb ├── dummy ├── .gitattributes ├── .gitignore ├── .rspec ├── .ruby-version ├── Gemfile ├── README.md ├── Rakefile ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ └── .keep │ │ └── stylesheets │ │ │ └── application.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ └── welcome_controller.rb │ ├── helpers │ │ └── application_helper.rb │ ├── javascript │ │ └── application.js │ ├── jobs │ │ └── application_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── abc.rb │ │ ├── application_record.rb │ │ ├── appointment.rb │ │ ├── atw_display_name.rb │ │ ├── atw_full_name.rb │ │ ├── atw_to_label.rb │ │ ├── borked.rb │ │ ├── concerns │ │ │ └── .keep │ │ ├── dfg.rb │ │ ├── family.rb │ │ ├── fruits │ │ │ └── cantelope.rb │ │ ├── ghi.rb │ │ ├── hgi.rb │ │ ├── human.rb │ │ ├── jkl.rb │ │ ├── missing_label_table.rb │ │ ├── nothing.rb │ │ ├── pet.rb │ │ ├── user.rb │ │ ├── visit.rb │ │ └── xyz.rb │ └── views │ │ ├── devise │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── mailer │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── email_changed.html.erb │ │ │ ├── password_change.html.erb │ │ │ ├── reset_password_instructions.html.erb │ │ │ └── unlock_instructions.html.erb │ │ ├── passwords │ │ │ ├── edit.html.erb │ │ │ └── new.html.erb │ │ ├── registrations │ │ │ ├── edit.html.erb │ │ │ └── new.html.erb │ │ ├── sessions │ │ │ └── new.html.erb │ │ ├── shared │ │ │ ├── _error_messages.html.erb │ │ │ └── _links.html.erb │ │ └── unlocks │ │ │ └── new.html.erb │ │ ├── layouts │ │ ├── _flash_notices.erb │ │ └── application.html.erb │ │ └── welcome │ │ └── index.erb ├── bin │ ├── bundle │ ├── importmap │ ├── rails │ ├── rake │ └── setup ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── credentials.yml.enc │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── hot_glue.yml │ ├── importmap.rb │ ├── initializers │ │ ├── assets.rb │ │ ├── content_security_policy.rb │ │ ├── devise.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ └── permissions_policy.rb │ ├── locales │ │ ├── devise.en.yml │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ └── storage.yml ├── db │ ├── migrate │ │ ├── 20210306223300_create_dfgs.rb │ │ ├── 20210306223305_create_ghis.rb │ │ ├── 20210306223309_create_jkls.rb │ │ ├── 20210306223701_devise_create_users.rb │ │ ├── 20210306225506_create_xyzs.rb │ │ ├── 20211226155351_create_abcs.rb │ │ ├── 20211226165050_create_hgis.rb │ │ ├── 20220201164536_add_cantelope_id_to_dfgs.rb │ │ ├── 20220201165048_create_cantelopes.rb │ │ ├── 20220318093158_create_pets.rb │ │ ├── 20220318093211_create_human.rb │ │ ├── 20220318093221_create_appointments.rb │ │ ├── 20220320194207_create_atw_to_labels.rb │ │ ├── 20220320200217_create_atw_full_names.rb │ │ ├── 20220320200230_create_atw_display_names.rb │ │ ├── 20220320214017_create_borkeds.rb │ │ ├── 20220320223316_create_missing_label_tables.rb │ │ ├── 20220323000346_create_families.rb │ │ ├── 20220323000410_create_visits.rb │ │ └── 20230317162618_create_active_storage_tables.active_storage.rb │ ├── schema.rb │ └── seeds.rb ├── lib │ ├── assets │ │ └── .keep │ └── tasks │ │ └── .keep ├── log │ └── .keep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── favicon.ico │ └── robots.txt ├── spec │ ├── factories │ │ ├── abc_factory.rb │ │ ├── cantelope_factory.rb │ │ ├── dfg_factory.rb │ │ ├── family_factory.rb │ │ ├── ghi_factory.rb │ │ ├── hgi_factory.rb │ │ ├── human_factory.rb │ │ ├── jkl_factory.rb │ │ ├── pet_factory.rb │ │ ├── user_factory.rb │ │ └── xyz_factory.rb │ ├── files │ │ └── computer_code.jpg │ ├── rails_helper.rb │ ├── spec_helper.rb │ └── support │ │ └── capybara_login.rb ├── storage │ └── .keep ├── test │ ├── application_system_test_case.rb │ ├── channels │ │ └── application_cable │ │ │ └── connection_test.rb │ ├── controllers │ │ └── .keep │ ├── fixtures │ │ └── files │ │ │ └── .keep │ ├── helpers │ │ └── .keep │ ├── integration │ │ └── .keep │ ├── mailers │ │ └── .keep │ ├── models │ │ └── .keep │ ├── system │ │ └── .keep │ └── test_helper.rb └── vendor │ ├── .keep │ └── javascript │ └── .keep ├── hot_glue.gemspec ├── lib ├── generators │ └── hot_glue │ │ ├── default_config_loader.rb │ │ ├── direct_upload_install_generator.rb │ │ ├── dropzone_install_generator.rb │ │ ├── field_factory.rb │ │ ├── fields │ │ ├── association_field.rb │ │ ├── attachment_field.rb │ │ ├── boolean_field.rb │ │ ├── date_field.rb │ │ ├── date_time_field.rb │ │ ├── enum_field.rb │ │ ├── field.rb │ │ ├── float_field.rb │ │ ├── integer_field.rb │ │ ├── related_set_field.rb │ │ ├── string_field.rb │ │ ├── text_field.rb │ │ ├── time_field.rb │ │ └── uuid_field.rb │ │ ├── flash_notices_install_generator.rb │ │ ├── hot_glue.rb │ │ ├── install_generator.rb │ │ ├── layout │ │ └── builder.rb │ │ ├── layout_strategy │ │ ├── base.rb │ │ ├── bootstrap.rb │ │ ├── hot_glue.rb │ │ └── tailwind.rb │ │ ├── markup_templates │ │ ├── base.rb │ │ └── erb.rb │ │ ├── nav_template_generator.rb │ │ ├── scaffold_generator.rb │ │ ├── set_search_interface_install_generator.rb │ │ ├── templates │ │ ├── base_controller.rb.erb │ │ ├── capybara_login.rb │ │ ├── computer_code.jpg │ │ ├── controller.rb.erb │ │ ├── erb │ │ │ ├── _edit.erb │ │ │ ├── _flash_notices.erb │ │ │ ├── _form.erb │ │ │ ├── _line.erb │ │ │ ├── _list.erb │ │ │ ├── _nav.html.erb │ │ │ ├── _new_button.erb │ │ │ ├── _new_form.erb │ │ │ ├── _show.erb │ │ │ ├── create.turbo_stream.erb │ │ │ ├── destroy.turbo_stream.erb │ │ │ ├── edit.erb │ │ │ ├── edit.turbo_stream.erb │ │ │ ├── index.erb │ │ │ ├── new.erb │ │ │ └── update.turbo_stream.erb │ │ ├── javascript │ │ │ ├── date_range_picker_controller.js │ │ │ ├── dropzone_controller.js │ │ │ ├── search_form_controller.js │ │ │ └── time_range_picker_controller.js │ │ ├── system_spec.rb.erb │ │ ├── themes │ │ │ ├── hotglue_scaffold_dark_knight.scss │ │ │ ├── hotglue_scaffold_like_bootstrap.scss │ │ │ ├── hotglue_scaffold_like_los_gatos.scss │ │ │ └── hotglue_scaffold_like_mountain_view.scss │ │ ├── typeahead_controller.rb.erb │ │ └── typeahead_views │ │ │ ├── _thing.html.erb │ │ │ ├── index.html.erb │ │ │ ├── typeahead.scss │ │ │ ├── typeahead_controller.js │ │ │ └── typeahead_results_controller.js │ │ ├── typeahead_generator.rb │ │ └── typeahead_install_generator.rb ├── hot-glue.rb └── hotglue │ ├── engine.rb │ └── version.rb ├── script ├── clean_generated_code └── test └── spec ├── dummy ├── app │ ├── models │ │ ├── abc.rb │ │ └── dfg.rb │ └── policies │ │ └── dfg_policy.rb ├── config │ └── hot_glue.yml └── spec │ ├── files │ └── computer_code.jpg │ └── support │ └── capybara_login.rb ├── lib ├── generators │ └── hot_glue │ │ ├── direct_upload_install_generator_spec.rb │ │ ├── dropzone_install_generator_spec.rb │ │ ├── install_generator_spec.rb │ │ ├── layout │ │ └── builder_spec.rb │ │ └── scaffold_generator_spec.rb ├── helpers │ └── hot_glue_helper_spec.rb ├── hot_glue │ ├── controller_helper_spec.rb │ ├── field_factory_spec.rb │ ├── hot_glue_spec.rb │ ├── markup_templates │ │ └── erb_spec.rb │ └── version_spec.rb └── hot_glue_spec.rb ├── rails_helper.rb └── spec_helper.rb /.circleci/DISABLED__config.yml: -------------------------------------------------------------------------------- 1 | # more about orbs: https://circleci.com/docs/2.0/using-orbs/ 2 | version: 2.1 3 | 4 | orbs: 5 | ruby: circleci/ruby@1.0 6 | browser-tools: circleci/browser-tools@1.4.8 7 | 8 | jobs: 9 | build: 10 | docker: 11 | - image: cimg/ruby:3.2.2-browsers 12 | auth: 13 | username: mydockerhub-user 14 | password: $DOCKERHUB_PASSWORD 15 | - image: redis:6.2.6 16 | 17 | steps: 18 | - checkout 19 | - ruby/install-deps 20 | - run: 21 | name: Build assets 22 | command: bundle exec rails assets:precompile 23 | 24 | test: 25 | parallelism: 1 26 | docker: 27 | - image: cimg/ruby:3.2.2-browsers 28 | auth: 29 | username: mydockerhub-user 30 | password: $DOCKERHUB_PASSWORD 31 | - image: redis:6.2.6 32 | - image: cimg/postgres:13.7 33 | auth: 34 | username: mydockerhub-user 35 | password: $DOCKERHUB_PASSWORD 36 | environment: 37 | POSTGRES_USER: circleci-demo-ruby 38 | POSTGRES_DB: helios-dev-shop-test 39 | POSTGRES_PASSWORD: "" 40 | 41 | environment: 42 | BUNDLE_JOBS: "3" 43 | BUNDLE_RETRY: "3" 44 | PGHOST: 127.0.0.1 45 | PGUSER: circleci-demo-ruby 46 | PGPASSWORD: "" 47 | RAILS_ENV: test 48 | 49 | steps: 50 | - run: sudo apt-get update 51 | 52 | - browser-tools/install-chromedriver 53 | - checkout 54 | - ruby/install-deps 55 | 56 | - run: 57 | name: Wait for DB 58 | command: dockerize -wait tcp://localhost:5432 -timeout 1m 59 | 60 | - run: 61 | name: Run system tests 62 | command: script/test 63 | 64 | - run: 65 | name: Run internal tests 66 | command: bundle exec rspec 67 | 68 | workflows: 69 | version: 2 70 | build_and_test: 71 | jobs: 72 | - build 73 | - test: 74 | requires: 75 | - build 76 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://twitter.com/HotGlueForRails", "https://school.jfbcodes.com/8188?utm_source=github.com&utm_campaign=github_hot_glue_repo_funding_link"] 2 | -------------------------------------------------------------------------------- /.github/workflows/test_suite.yml: -------------------------------------------------------------------------------- 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@v1 # 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 | 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | 5 | .byebug_history 6 | 7 | .idea/ 8 | *.gem 9 | .DS_Store 10 | 11 | dump.rdb 12 | tmp/ 13 | 14 | 15 | db/*.sqlite3 16 | 17 | dummy/app/views/ 18 | dummy/app/controllers/ 19 | dummy/specs/ 20 | 21 | dummy/node_modules/ 22 | dummy/log/ 23 | dummy/Gemfile.lock 24 | dummy/config/hot_glue.yml 25 | 26 | 27 | coverage/ 28 | 29 | spec/dummy/app/controllers/ 30 | spec/dummy/app/views/ 31 | spec/dummy/app/javascript/application.js 32 | dummy/spec/features/ 33 | 34 | spec/dummy/app/javascript/controllers/dropzone_controller.js 35 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2.4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | before_install: 4 | - gem update --system 5 | 6 | 7 | branches: 8 | only: 9 | - main 10 | 11 | 12 | script: "script/test" 13 | 14 | rvm: 15 | - 3.1.2 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # THESE GEMS ARE NOT PART OF YOUR RAILS APP!!! 2 | 3 | source 'https://rubygems.org' 4 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 5 | 6 | gemspec 7 | 8 | # not required for your app 9 | gem 'sqlite3' 10 | gem 'byebug' 11 | # gem 'progress_formatter' 12 | 13 | # for testing 14 | gem 'simplecov-rcov' 15 | gem 'rake' 16 | gem "rspec-rails" 17 | gem "factory_bot_rails" 18 | gem "ffaker" 19 | gem "capybara" 20 | gem "selenium-webdriver", "4.11.0" 21 | # gem "webdrivers" 22 | 23 | gem "sprockets-rails" 24 | gem "importmap-rails" 25 | gem "stimulus-rails" 26 | gem "turbo-rails" 27 | 28 | 29 | gem "puma", "~> 5.0" 30 | 31 | gem "devise" 32 | 33 | gem "pg", "~> 1.5" 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | Copyright © 2022 Jason Fleetwood-Boldt. All Rights Reserved 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | 12 | 13 | TO PURCHASE A COMMERCIAL USAGE LICENSE PLEASE VISIT 14 | https://heliosdev.shop/hot-glue-license 15 | 16 | OR PURCHASE THE TUTORIALS AT 17 | https://school.jasonfleetwoodboldt.com/8188/?utm_source=github.com 18 | -------------------------------------------------------------------------------- /README2.md: -------------------------------------------------------------------------------- 1 | 2 | ## LEGACY SETUP FOR RAILS 6 3 | 4 | ## Legacy Step #1. ADD HOTWIRE 5 | (RAILS 6 ONLY— SKIP THIS STEP FOR RAILS 7) 6 | ``` 7 | yarn add @hotwired/turbo-rails 8 | ``` 9 | or `npm install @hotwired/turbo-rails` 10 | 11 | 12 | ## Legacy Step #2. SWITCH FROM TurblLinks to Turbo-Rails 13 | (RAILS 6 ONLY — SKIP FOR RAILS 7) 14 | (THIS WAS AUTOMATICALLY DONE BY THE HOT GLUE INSTALLATION -- CONFIRM CHANGES ONLY) 15 | - Add `gem 'turbo-rails'` to your Gemfile & `bundle install` 16 | - Then install it with `rails turbo:install` 17 | - The Turbo install has switched your action cable settings from 'async' to Redis, so be sure to start a redis server 18 | - in `app/javascript/packs/application.js` remove this line 19 | ``` 20 | import Turbolinks from "turbolinks" 21 | ``` 22 | and replace it with 23 | ``` 24 | import { Turbo } from "@hotwired/turbo-rails" 25 | ``` 26 | 27 | 28 | Also replace 29 | ``` 30 | Turbolinks.start() 31 | ``` 32 | with: 33 | ``` 34 | Turbo.start() 35 | ``` 36 | 37 | 38 | ## Legacy Step #3. INSTALL WEBPACKER 39 | (_SKIP FOR RAILS 7_ unless you want to use Webpacker with Rails 7) 40 | 41 | ** For webpacker, you must be using Node version ^12.13.0 || ^14.15.0 || >=16 ** 42 | 43 | I recommend Node Version Manager (NVM) to switch between nodes. You will not be able to get through the following command with a Node version that does not match above. 44 | 45 | Check your node version with `node -v` 46 | 47 | ``` 48 | `yarn add @rails/webpacker` 49 | ``` 50 | 51 | 52 | rails webpacker:install 53 | 54 | ## Legacy Step #4: Postgresql Enum Support for Rail 6 55 | For Enum support, I recommend activerecord-pg_enum 56 | Instructions for Rails 6 are here: 57 | https://jasonfleetwoodboldt.com/courses/stepping-up-rails/enumerated-types-in-rails-and-postgres/ 58 | 59 | _This functionality is now built-in to Rails 7._ 60 | 61 | 62 | ## Legacy Step #5: Fix Devise if adding Turbo To Your Project 63 | ## IMPORTANT: Devise currently has serious compatibility issues with Turbo Rails. In particular, your log-in screens do not work out of the box. Follow the next step to fix them. 64 | 65 | Manually port the Devise views into your app with 66 | 67 | `rails generate devise:views` 68 | 69 | Edit `devise/registrations/new`, `devise/sessions/new`, `devise/passwords/new` and `devise/confirmations/new` modifying all four templates like so: 70 | 71 | form_for(resource, as: resource_name, url: session_path(resource_name) ) do |f| 72 | 73 | add the data-turbo false option in the html key: 74 | 75 | form_for(resource, as: resource_name, **html: {'data-turbo' => "false"},** url: session_path(resource_name) ) do |f| 76 | 77 | This tells Devise to fall back to non-Turbo interaction for the log-in and registration. For the rest of the app, we will use Turbo Rails interactions. 78 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/app/assets/config/manifest.js -------------------------------------------------------------------------------- /app/helpers/hot_glue_helper.rb: -------------------------------------------------------------------------------- 1 | module HotGlueHelper 2 | class KVObject 3 | attr_accessor :key, :value 4 | def initialize(key: , value: ) 5 | @key = key 6 | @value = value 7 | end 8 | end 9 | 10 | def enum_to_collection_select(hash) 11 | hash.collect{|k,v| KVObject.new(key: k, value: v)} 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/views/layouts/_flash_notices.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_frame_tag "flash_notices" do %> 2 | <% unless notice.nil? %> 3 |
4 | <%= notice %> 5 |
6 | <% end %> 7 | <% unless alert.nil? %> 8 |
9 | <%= alert %> 10 |
11 | <% end %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem "sqlite3" 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: dummy/db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: dummy/db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: dummy/db/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/hot_glue.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :layout: hotglue 3 | :markup: erb 4 | :default_boolean_display: radio 5 | :sample_file_path: spec/files/computer_code.jpg 6 | -------------------------------------------------------------------------------- /dummy/.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark any vendored files as having been vendored. 7 | vendor/* linguist-vendored 8 | -------------------------------------------------------------------------------- /dummy/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-* 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore pidfiles, but keep the directory. 21 | /tmp/pids/* 22 | !/tmp/pids/ 23 | !/tmp/pids/.keep 24 | 25 | # Ignore uploaded files in development. 26 | /storage/* 27 | !/storage/.keep 28 | /tmp/storage/* 29 | !/tmp/storage/ 30 | !/tmp/storage/.keep 31 | 32 | /public/assets 33 | 34 | # Ignore master key for decrypting credentials and more. 35 | /config/master.key 36 | -------------------------------------------------------------------------------- /dummy/.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /dummy/.ruby-version: -------------------------------------------------------------------------------- 1 | 3.2.4 2 | -------------------------------------------------------------------------------- /dummy/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby "3.2.4" 5 | 6 | 7 | gem "rails", "~> 7.0.3", ">= 7.0.3.1" 8 | 9 | 10 | gem "sprockets-rails" 11 | gem "importmap-rails" 12 | gem "stimulus-rails" 13 | gem "turbo-rails" 14 | 15 | gem "sqlite3", "~> 1.4" 16 | 17 | gem "puma", "~> 5.0" 18 | 19 | gem "jbuilder" 20 | gem "devise" 21 | 22 | gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] 23 | 24 | gem "bootsnap", require: false 25 | 26 | 27 | # gem "sassc-rails" 28 | gem 'rake' 29 | 30 | 31 | group :development, :test do 32 | gem "rspec-rails" 33 | gem "factory_bot_rails" 34 | gem "ffaker" 35 | gem "debug", platforms: %i[ mri mingw x64_mingw ] 36 | gem "byebug" 37 | end 38 | 39 | group :development do 40 | # Use console on exceptions pages [https://github.com/rails/web-console] 41 | gem "web-console" 42 | end 43 | 44 | group :test do 45 | # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] 46 | gem "capybara" 47 | gem "selenium-webdriver" 48 | end 49 | 50 | # Use Redis for Action Cable 51 | gem "redis", "~> 4.0" 52 | gem 'hot-glue', path: "../" 53 | 54 | gem "image_processing", "~> 1.12" 55 | 56 | gem "pg", "~> 1.5" 57 | -------------------------------------------------------------------------------- /dummy/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link_tree ../../javascript .js 4 | //= link_tree ../../../vendor/javascript .js 5 | -------------------------------------------------------------------------------- /dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /dummy/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | layout 'application' 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | def index 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /dummy/app/javascript/application.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails 2 | import "@hotwired/turbo-rails" 3 | -------------------------------------------------------------------------------- /dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/models/abc.rb: -------------------------------------------------------------------------------- 1 | class Abc < ApplicationRecord 2 | @@table_label_plural = "Apples" 3 | @@table_label_singular = "Apple" 4 | 5 | has_one_attached :aaa 6 | 7 | has_one_attached :bbb do |attachable| 8 | attachable.variant :thumb, resize_to_limit: [100,100] 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/models/appointment.rb: -------------------------------------------------------------------------------- 1 | class Appointment < ApplicationRecord 2 | 3 | belongs_to :user 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/models/atw_display_name.rb: -------------------------------------------------------------------------------- 1 | class AtwDisplayName < ApplicationRecord 2 | belongs_to :xyz 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/models/atw_full_name.rb: -------------------------------------------------------------------------------- 1 | class AtwFullName < ApplicationRecord 2 | belongs_to :xyz 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/models/atw_to_label.rb: -------------------------------------------------------------------------------- 1 | class AtwToLabel < ApplicationRecord 2 | 3 | # this table has a to_label field 4 | 5 | belongs_to :xyz 6 | 7 | end 8 | -------------------------------------------------------------------------------- /dummy/app/models/borked.rb: -------------------------------------------------------------------------------- 1 | class Borked < ApplicationRecord 2 | 3 | # this table is intentionally missing belongs_to :xyz 4 | # 5 | 6 | # this tbale 7 | belongs_to :missing_label_table 8 | end 9 | -------------------------------------------------------------------------------- /dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /dummy/app/models/dfg.rb: -------------------------------------------------------------------------------- 1 | class Dfg < ApplicationRecord 2 | # can't use DEF as it is a Ruby Keyword 3 | 4 | belongs_to :user 5 | belongs_to :cantelope, class_name: "Fruits::Cantelope" 6 | 7 | has_many :ghis 8 | end 9 | -------------------------------------------------------------------------------- /dummy/app/models/family.rb: -------------------------------------------------------------------------------- 1 | class Family < ApplicationRecord 2 | 3 | has_many :users 4 | 5 | end 6 | -------------------------------------------------------------------------------- /dummy/app/models/fruits/cantelope.rb: -------------------------------------------------------------------------------- 1 | class Fruits::Cantelope < ApplicationRecord 2 | self.table_name = 'cantelopes' 3 | 4 | has_many :dfgs 5 | end 6 | -------------------------------------------------------------------------------- /dummy/app/models/ghi.rb: -------------------------------------------------------------------------------- 1 | class Ghi < ApplicationRecord 2 | 3 | belongs_to :dfg, optional: true 4 | belongs_to :xyz, optional: true 5 | has_one :user, through: :dfg 6 | 7 | def name 8 | "name_of_#{id}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/app/models/hgi.rb: -------------------------------------------------------------------------------- 1 | class Hgi < ApplicationRecord 2 | 3 | has_many :jkls 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/models/human.rb: -------------------------------------------------------------------------------- 1 | class Human < ApplicationRecord 2 | 3 | has_many :pets 4 | end 5 | -------------------------------------------------------------------------------- /dummy/app/models/jkl.rb: -------------------------------------------------------------------------------- 1 | class Jkl < ApplicationRecord 2 | belongs_to :hgi 3 | 4 | enum genre: { 5 | Fiction: "Fiction", 6 | Nonfiction: "Nonfiction", 7 | Mystery: "Mystery", 8 | Romance: "Romance", 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /dummy/app/models/missing_label_table.rb: -------------------------------------------------------------------------------- 1 | class MissingLabelTable < ApplicationRecord 2 | has_many :borked 3 | 4 | # this table is missing a name field 5 | end 6 | -------------------------------------------------------------------------------- /dummy/app/models/nothing.rb: -------------------------------------------------------------------------------- 1 | class Nothing < ApplicationRecord 2 | 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/models/pet.rb: -------------------------------------------------------------------------------- 1 | class Pet < ApplicationRecord 2 | belongs_to :human 3 | end 4 | -------------------------------------------------------------------------------- /dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | # Include default devise modules. Others available are: 3 | # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable 4 | devise :database_authenticatable, :registerable, 5 | :recoverable, :rememberable, :validatable 6 | 7 | has_many :dfgs 8 | has_many :xyzs 9 | 10 | has_many :ghis, through: :dfgs 11 | 12 | belongs_to :family 13 | end 14 | -------------------------------------------------------------------------------- /dummy/app/models/visit.rb: -------------------------------------------------------------------------------- 1 | class Visit < ApplicationRecord 2 | 3 | belongs_to :user 4 | has_one :family, through: :user 5 | end 6 | -------------------------------------------------------------------------------- /dummy/app/models/xyz.rb: -------------------------------------------------------------------------------- 1 | class Xyz < ApplicationRecord 2 | # belongs_to :nothing # association does not exist 3 | 4 | belongs_to :user 5 | 6 | has_many :ghis 7 | 8 | has_many :atw_to_label 9 | has_many :atw_full_name 10 | has_many :atw_display_name 11 | 12 | def name 13 | # "nothing here" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /dummy/app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= render "devise/shared/error_messages", resource: resource %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email", value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> 9 |
10 | 11 |
12 | <%= f.submit "Resend confirmation instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /dummy/app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /dummy/app/views/devise/mailer/email_changed.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @email %>!

2 | 3 | <% if @resource.try(:unconfirmed_email?) %> 4 |

We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.

5 | <% else %> 6 |

We're contacting you to notify you that your email has been changed to <%= @resource.email %>.

7 | <% end %> 8 | -------------------------------------------------------------------------------- /dummy/app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

We're contacting you to notify you that your password has been changed.

4 | -------------------------------------------------------------------------------- /dummy/app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password. You can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /dummy/app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /dummy/app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= render "devise/shared/error_messages", resource: resource %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
8 | <%= f.label :password, "New password" %>
9 | <% if @minimum_password_length %> 10 | (<%= @minimum_password_length %> characters minimum)
11 | <% end %> 12 | <%= f.password_field :password, autofocus: true, autocomplete: "new-password" %> 13 |
14 | 15 |
16 | <%= f.label :password_confirmation, "Confirm new password" %>
17 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %> 18 |
19 | 20 |
21 | <%= f.submit "Change my password" %> 22 |
23 | <% end %> 24 | 25 | <%= render "devise/shared/links" %> 26 | -------------------------------------------------------------------------------- /dummy/app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= render "devise/shared/error_messages", resource: resource %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %> 9 |
10 | 11 |
12 | <%= f.submit "Send me reset password instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /dummy/app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, html: {'data-turbo' => "false"}, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= render "devise/shared/error_messages", resource: resource %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %> 9 |
10 | 11 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> 12 |
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
13 | <% end %> 14 | 15 |
16 | <%= f.label :password %> (leave blank if you don't want to change it)
17 | <%= f.password_field :password, autocomplete: "new-password" %> 18 | <% if @minimum_password_length %> 19 |
20 | <%= @minimum_password_length %> characters minimum 21 | <% end %> 22 |
23 | 24 |
25 | <%= f.label :password_confirmation %>
26 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %> 27 |
28 | 29 |
30 | <%= f.label :current_password %> (we need your current password to confirm your changes)
31 | <%= f.password_field :current_password, autocomplete: "current-password" %> 32 |
33 | 34 |
35 | <%= f.submit "Update" %> 36 |
37 | <% end %> 38 | 39 |

Cancel my account

40 | 41 |

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

42 | 43 | <%= link_to "Back", :back %> 44 | -------------------------------------------------------------------------------- /dummy/app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | 3 | <%= form_for(resource, html: {'data-turbo' => "false"}, as: resource_name, url: registration_path(resource_name)) do |f| %> 4 | <%= render "devise/shared/error_messages", resource: resource %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %> 9 |
10 | 11 |
12 | <%= f.label :password %> 13 | <% if @minimum_password_length %> 14 | (<%= @minimum_password_length %> characters minimum) 15 | <% end %>
16 | <%= f.password_field :password, autocomplete: "new-password" %> 17 |
18 | 19 |
20 | <%= f.label :password_confirmation %>
21 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %> 22 |
23 | 24 |
25 | <%= f.submit "Sign up" %> 26 |
27 | <% end %> 28 | 29 | <%= render "devise/shared/links" %> 30 | -------------------------------------------------------------------------------- /dummy/app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Log in

2 | 3 | <%= form_for(resource, html: {'data-turbo' => "false"}, as: resource_name, url: session_path(resource_name)) do |f| %> 4 |
5 | <%= f.label :email %>
6 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %> 7 |
8 | 9 |
10 | <%= f.label :password %>
11 | <%= f.password_field :password, autocomplete: "current-password" %> 12 |
13 | 14 | <% if devise_mapping.rememberable? %> 15 |
16 | <%= f.check_box :remember_me %> 17 | <%= f.label :remember_me %> 18 |
19 | <% end %> 20 | 21 |
22 | <%= f.submit "Log in" %> 23 |
24 | <% end %> 25 | 26 | <%= render "devise/shared/links" %> 27 | -------------------------------------------------------------------------------- /dummy/app/views/devise/shared/_error_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.any? %> 2 |
3 |

4 | <%= I18n.t("errors.messages.not_saved", 5 | count: resource.errors.count, 6 | resource: resource.class.model_name.human.downcase) 7 | %> 8 |

9 | 14 |
15 | <% end %> 16 | -------------------------------------------------------------------------------- /dummy/app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end %> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end %> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end %> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end %> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end %> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), method: :post %>
24 | <% end %> 25 | <% end %> 26 | -------------------------------------------------------------------------------- /dummy/app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= render "devise/shared/error_messages", resource: resource %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %> 9 |
10 | 11 |
12 | <%= f.submit "Resend unlock instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /dummy/app/views/layouts/_flash_notices.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_frame_tag "flash_notices" do %> 2 | <% unless notice.nil? %> 3 |
4 | <%= notice %> 5 |
6 | <% end %> 7 | <% unless alert.nil? %> 8 |
9 | <%= alert %> 10 |
11 | <% end %> 12 | <% end %> 13 | -------------------------------------------------------------------------------- /dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | 9 | <%= stylesheet_link_tag "application" %> 10 | <%= javascript_importmap_tags %> 11 | 12 | 13 | 14 | 15 | 16 | <% if user_signed_in? %> 17 |
18 | Logged in as 19 | 20 | <%= current_user.email %> 21 | 22 |
23 | 24 | <%= button_to "Logout", destroy_user_session_path, method: :delete, :class => 'dropdown-item' %> 25 | 26 | <% else %> 27 | <%= link_to "Sign up", new_user_registration_path, :class => 'dropdown-item' %> 28 | 29 | <%= link_to "Login", new_user_session_path, :class => 'dropdown-item' %> 30 | <% end %> 31 | 32 | <%= render partial: 'layouts/flash_notices' %> 33 | 34 | <%= yield %> 35 | 36 | 37 | -------------------------------------------------------------------------------- /dummy/app/views/welcome/index.erb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/app/views/welcome/index.erb -------------------------------------------------------------------------------- /dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($0) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | bundler_version = nil 28 | update_index = nil 29 | ARGV.each_with_index do |a, i| 30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN 31 | bundler_version = a 32 | end 33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ 34 | bundler_version = $1 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../../Gemfile", __FILE__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | lockfile_contents = File.read(lockfile) 59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ 60 | Regexp.last_match(1) 61 | end 62 | 63 | def bundler_requirement 64 | @bundler_requirement ||= 65 | env_var_version || cli_arg_version || 66 | bundler_requirement_for(lockfile_version) 67 | end 68 | 69 | def bundler_requirement_for(version) 70 | return "#{Gem::Requirement.default}.a" unless version 71 | 72 | bundler_gem_version = Gem::Version.new(version) 73 | 74 | requirement = bundler_gem_version.approximate_recommendation 75 | 76 | return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0") 77 | 78 | requirement += ".a" if bundler_gem_version.prerelease? 79 | 80 | requirement 81 | end 82 | 83 | def load_bundler! 84 | ENV["BUNDLE_GEMFILE"] ||= gemfile 85 | 86 | activate_bundler 87 | end 88 | 89 | def activate_bundler 90 | gem_error = activation_error_handling do 91 | gem "bundler", bundler_requirement 92 | end 93 | return if gem_error.nil? 94 | require_error = activation_error_handling do 95 | require "bundler/version" 96 | end 97 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 98 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" 99 | exit 42 100 | end 101 | 102 | def activation_error_handling 103 | yield 104 | nil 105 | rescue StandardError, LoadError => e 106 | e 107 | end 108 | end 109 | 110 | m.load_bundler! 111 | 112 | if m.invoked_as_script? 113 | load Gem.bin_path("bundler", "bundle") 114 | end 115 | -------------------------------------------------------------------------------- /dummy/bin/importmap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require_relative "../config/application" 4 | require "importmap/commands" 5 | -------------------------------------------------------------------------------- /dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails/all" 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Dummy 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 7.0 13 | 14 | # Configuration for the application, engines, and railties goes here. 15 | # 16 | # These settings can be overridden in specific environments using the files 17 | # in config/environments, which are processed later. 18 | # 19 | # config.time_zone = "Central Time (US & Canada)" 20 | # config.eager_load_paths << Rails.root.join("extras") 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | # require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: redis 3 | url: redis://localhost:6379/1 4 | 5 | test: 6 | adapter: test 7 | 8 | production: 9 | adapter: redis 10 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 11 | channel_prefix: dummy_production 12 | -------------------------------------------------------------------------------- /dummy/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | 3Ey+ttsWUTMKYuXjx0d9Wjt6U3X0nSlaNRd3bFUg3/h9PSO+xHYcdvut9j9HQHJxx1vAaUmHuKJFrB6HCTI0AaFg+02eEdGnoZkOVAnK1MHd5rn+6lbfMzS73sikqK4jEyjsgBY9TcOc+JMF4ORY+a8qsna11qLeulYgLVPTOx5D4RSefy8BNZwg1i/+o+7Vfy7x6kiY/wov4iS5FXYW6ms59gps5Q6suVQ7OOR/xBjJcueJHcqobVGdRaEYfy2Vswwlo7SNNLZYRhirjAegl9n85Z9UniGhIIRLXjmwY+Go+XuLXDQFitYOyQxFnvg8fOV67MGVVWxLmHE2oBTeIdpeBlat50U3OXVNBD5LVc8+1LpZs4jRV26lIm0Hjm1+dpxe41YmOgE/Yed6k6A50MVi6ZakyAue22ms--/1rv47DKOUf79Eha--MVryG2Qes9KVlvuzQrS8Hw== -------------------------------------------------------------------------------- /dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.3 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On macOS with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On macOS with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem "pg" 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see Rails configuration guide 21 | # https://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | 24 | development: 25 | <<: *default 26 | database: hot_glue_dummy_development 27 | 28 | # The specified database role being used to connect to postgres. 29 | # To create additional roles in postgres see `$ createuser --help`. 30 | # When left blank, postgres will use the default role. This is 31 | # the same name as the operating system user running Rails. 32 | #username: dummy 33 | 34 | # The password associated with the postgres role (username). 35 | #password: 36 | 37 | # Connect on a TCP socket. Omitted by default since the client uses a 38 | # domain socket that doesn't need configuration. Windows does not have 39 | # domain sockets, so uncomment these lines. 40 | #host: localhost 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: hot_glue_dummy_test 61 | -------------------------------------------------------------------------------- /dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable server timing 18 | config.server_timing = true 19 | 20 | # Enable/disable caching. By default caching is disabled. 21 | # Run rails dev:cache to toggle caching. 22 | if Rails.root.join("tmp/caching-dev.txt").exist? 23 | config.action_controller.perform_caching = true 24 | config.action_controller.enable_fragment_cache_logging = true 25 | 26 | config.cache_store = :memory_store 27 | config.public_file_server.headers = { 28 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 29 | } 30 | else 31 | config.action_controller.perform_caching = false 32 | 33 | config.cache_store = :null_store 34 | end 35 | 36 | # Store uploaded files on the local file system (see config/storage.yml for options). 37 | config.active_storage.service = :local 38 | 39 | # Don't care if the mailer can't send. 40 | config.action_mailer.raise_delivery_errors = false 41 | 42 | config.action_mailer.perform_caching = false 43 | 44 | # Print deprecation notices to the Rails logger. 45 | config.active_support.deprecation = :log 46 | 47 | # Raise exceptions for disallowed deprecations. 48 | config.active_support.disallowed_deprecation = :raise 49 | 50 | # Tell Active Support which deprecation messages to disallow. 51 | config.active_support.disallowed_deprecation_warnings = [] 52 | 53 | # Raise an error on page load if there are pending migrations. 54 | config.active_record.migration_error = :page_load 55 | 56 | # Highlight code that triggered database queries in logs. 57 | config.active_record.verbose_query_logs = true 58 | 59 | # Suppress logger output for asset requests. 60 | config.assets.quiet = true 61 | 62 | # Raises error for missing translations. 63 | # config.i18n.raise_on_missing_translations = true 64 | 65 | # Annotate rendered view with file names. 66 | # config.action_view.annotate_rendered_view_with_filenames = true 67 | 68 | # Uncomment if you wish to allow Action Cable access from any origin. 69 | # config.action_cable.disable_request_forgery_protection = true 70 | end 71 | -------------------------------------------------------------------------------- /dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? 26 | 27 | # Compress CSS using a preprocessor. 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 34 | # config.asset_host = "http://assets.example.com" 35 | 36 | # Specifies the header that your server uses for sending files. 37 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 38 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX 39 | 40 | # Store uploaded files on the local file system (see config/storage.yml for options). 41 | config.active_storage.service = :local 42 | 43 | # Mount Action Cable outside main process or domain. 44 | # config.action_cable.mount_path = nil 45 | # config.action_cable.url = "wss://example.com/cable" 46 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] 47 | 48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 49 | # config.force_ssl = true 50 | 51 | # Include generic and useful information about system operation, but avoid logging too much 52 | # information to avoid inadvertent exposure of personally identifiable information (PII). 53 | config.log_level = :info 54 | 55 | # Prepend all log lines with the following tags. 56 | config.log_tags = [ :request_id ] 57 | 58 | # Use a different cache store in production. 59 | # config.cache_store = :mem_cache_store 60 | 61 | # Use a real queuing backend for Active Job (and separate queues per environment). 62 | # config.active_job.queue_adapter = :resque 63 | # config.active_job.queue_name_prefix = "dummy_production" 64 | 65 | config.action_mailer.perform_caching = false 66 | 67 | # Ignore bad email addresses and do not raise email delivery errors. 68 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 69 | # config.action_mailer.raise_delivery_errors = false 70 | 71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 72 | # the I18n.default_locale when a translation cannot be found). 73 | config.i18n.fallbacks = true 74 | 75 | # Don't log any deprecations. 76 | config.active_support.report_deprecations = false 77 | 78 | # Use default logging formatter so that PID and timestamp are not suppressed. 79 | config.log_formatter = ::Logger::Formatter.new 80 | 81 | # Use a different logger for distributed setups. 82 | # require "syslog/logger" 83 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") 84 | 85 | if ENV["RAILS_LOG_TO_STDOUT"].present? 86 | logger = ActiveSupport::Logger.new(STDOUT) 87 | logger.formatter = config.log_formatter 88 | config.logger = ActiveSupport::TaggedLogging.new(logger) 89 | end 90 | 91 | # Do not dump schema after migrations. 92 | config.active_record.dump_schema_after_migration = false 93 | end 94 | -------------------------------------------------------------------------------- /dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | # Turn false under Spring and add config.action_view.cache_template_loading = true. 12 | config.cache_classes = true 13 | 14 | # Eager loading loads your whole application. When running a single test locally, 15 | # this probably isn't necessary. It's a good idea to do in a continuous integration 16 | # system, or in some way before deploying your code. 17 | config.eager_load = ENV["CI"].present? 18 | 19 | # Configure public file server for tests with Cache-Control for performance. 20 | config.public_file_server.enabled = true 21 | config.public_file_server.headers = { 22 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 23 | } 24 | 25 | # Show full error reports and disable caching. 26 | config.consider_all_requests_local = true 27 | config.action_controller.perform_caching = false 28 | config.cache_store = :null_store 29 | 30 | # Raise exceptions instead of rendering exception templates. 31 | config.action_dispatch.show_exceptions = false 32 | 33 | # Disable request forgery protection in test environment. 34 | config.action_controller.allow_forgery_protection = false 35 | 36 | # Store uploaded files on the local file system in a temporary directory. 37 | config.active_storage.service = :test 38 | 39 | config.action_mailer.perform_caching = false 40 | 41 | # Tell Action Mailer not to deliver emails to the real world. 42 | # The :test delivery method accumulates sent emails in the 43 | # ActionMailer::Base.deliveries array. 44 | config.action_mailer.delivery_method = :test 45 | 46 | # Print deprecation notices to the stderr. 47 | config.active_support.deprecation = :stderr 48 | 49 | # Raise exceptions for disallowed deprecations. 50 | config.active_support.disallowed_deprecation = :raise 51 | 52 | # Tell Active Support which deprecation messages to disallow. 53 | config.active_support.disallowed_deprecation_warnings = [] 54 | 55 | # Raises error for missing translations. 56 | # config.i18n.raise_on_missing_translations = true 57 | 58 | # Annotate rendered view with file names. 59 | # config.action_view.annotate_rendered_view_with_filenames = true 60 | end 61 | -------------------------------------------------------------------------------- /dummy/config/hot_glue.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :layout: bootstrap 3 | :markup: erb 4 | :default_boolean_display: radio 5 | 6 | :sample_file_path: spec/files/computer_code.jpg 7 | -------------------------------------------------------------------------------- /dummy/config/importmap.rb: -------------------------------------------------------------------------------- 1 | # Pin npm packages by running ./bin/importmap 2 | 3 | pin "application", preload: true 4 | pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true 5 | -------------------------------------------------------------------------------- /dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | # Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy. 4 | # See the Securing Rails Applications Guide for more information: 5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap and inline scripts 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src) 22 | # 23 | # # Report violations without enforcing the policy. 24 | # # config.content_security_policy_report_only = true 25 | # end 26 | -------------------------------------------------------------------------------- /dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be filtered from the log file. Use this to limit dissemination of 4 | # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported 5 | # notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | inflect.irregular "human", "humans" 10 | # inflect.uncountable %w( fish sheep ) 11 | end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /dummy/config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Define an application-wide HTTP permissions policy. For further 2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy 3 | # 4 | # Rails.application.config.permissions_policy do |f| 5 | # f.camera :none 6 | # f.gyroscope :none 7 | # f.microphone :none 8 | # f.usb :none 9 | # f.fullscreen :self 10 | # f.payment :self, "https://secure.example.com" 11 | # end 12 | -------------------------------------------------------------------------------- /dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # "true": "foo" 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 12 | # terminating a worker in development environments. 13 | # 14 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" 15 | 16 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 17 | # 18 | port ENV.fetch("PORT") { 3000 } 19 | 20 | # Specifies the `environment` that Puma will run in. 21 | # 22 | environment ENV.fetch("RAILS_ENV") { "development" } 23 | 24 | # Specifies the `pidfile` that Puma will use. 25 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 26 | 27 | # Specifies the number of `workers` to boot in clustered mode. 28 | # Workers are forked web server processes. If using threads and workers together 29 | # the concurrency of the application would be max `threads` * `workers`. 30 | # Workers do not work on JRuby or Windows (both of which do not support 31 | # processes). 32 | # 33 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 34 | 35 | # Use the `preload_app!` method when specifying a `workers` number. 36 | # This directive tells Puma to first boot the application and load code 37 | # before forking the application. This takes advantage of Copy On Write 38 | # process behavior so workers use less memory. 39 | # 40 | # preload_app! 41 | 42 | # Allow puma to be restarted by `bin/rails restart` command. 43 | plugin :tmp_restart 44 | -------------------------------------------------------------------------------- /dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | devise_for :users 3 | 4 | root to: "welcome#index" 5 | resources :abcs 6 | resources :ghis 7 | resources :dfgs 8 | resources :jkls 9 | resources :cantelopes 10 | 11 | resources :pets 12 | resources :humans 13 | resources :users 14 | end 15 | -------------------------------------------------------------------------------- /dummy/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket-<%= Rails.env %> 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket-<%= Rails.env %> 23 | 24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name-<%= Rails.env %> 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /dummy/db/migrate/20210306223300_create_dfgs.rb: -------------------------------------------------------------------------------- 1 | class CreateDfgs < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :dfgs do |t| 4 | t.integer :user_id 5 | t.string :name 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20210306223305_create_ghis.rb: -------------------------------------------------------------------------------- 1 | class CreateGhis < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :ghis do |t| 4 | t.integer :dfg_id 5 | t.integer :xyz_id 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20210306223309_create_jkls.rb: -------------------------------------------------------------------------------- 1 | class CreateJkls < ActiveRecord::Migration[6.1] 2 | def change 3 | create_enum :genre, %w[Fiction Nonfiction Mystery Romance Novel] 4 | 5 | create_table :jkls do |t| 6 | t.integer :hgi_id 7 | 8 | t.string :name 9 | t.string :blurb 10 | t.text :long_description 11 | t.float :cost 12 | t.integer :how_many_printed 13 | t.datetime :approved_at 14 | t.date :release_on 15 | t.time :time_of_day 16 | t.boolean :selected 17 | t.enum :genre, enum_type: :genre 18 | t.timestamps 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /dummy/db/migrate/20210306223701_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DeviseCreateUsers < ActiveRecord::Migration[6.1] 4 | def change 5 | create_table :users do |t| 6 | ## Database authenticatable 7 | t.string :email, null: false, default: "" 8 | t.string :encrypted_password, null: false, default: "" 9 | 10 | ## Recoverable 11 | t.string :reset_password_token 12 | t.datetime :reset_password_sent_at 13 | 14 | ## Rememberable 15 | t.datetime :remember_created_at 16 | 17 | ## Trackable 18 | # t.integer :sign_in_count, default: 0, null: false 19 | # t.datetime :current_sign_in_at 20 | # t.datetime :last_sign_in_at 21 | # t.string :current_sign_in_ip 22 | # t.string :last_sign_in_ip 23 | 24 | ## Confirmable 25 | # t.string :confirmation_token 26 | # t.datetime :confirmed_at 27 | # t.datetime :confirmation_sent_at 28 | # t.string :unconfirmed_email # Only if using reconfirmable 29 | 30 | ## Lockable 31 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 32 | # t.string :unlock_token # Only if unlock strategy is :email or :both 33 | # t.datetime :locked_at 34 | 35 | t.integer :family_id 36 | t.timestamps null: false 37 | end 38 | 39 | add_index :users, :email, unique: true 40 | add_index :users, :reset_password_token, unique: true 41 | # add_index :users, :confirmation_token, unique: true 42 | # add_index :users, :unlock_token, unique: true 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /dummy/db/migrate/20210306225506_create_xyzs.rb: -------------------------------------------------------------------------------- 1 | class CreateXyzs < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :xyzs do |t| 4 | t.integer :nothing_id 5 | t.integer :user_id 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20211226155351_create_abcs.rb: -------------------------------------------------------------------------------- 1 | class CreateAbcs < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :abcs do |t| 4 | t.string :name 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20211226165050_create_hgis.rb: -------------------------------------------------------------------------------- 1 | class CreateHgis < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :hgis do |t| 4 | t.string :name 5 | t.integer :how_many 6 | t.text :hello 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220201164536_add_cantelope_id_to_dfgs.rb: -------------------------------------------------------------------------------- 1 | class AddCantelopeIdToDfgs < ActiveRecord::Migration[6.1] 2 | def change 3 | 4 | add_column :dfgs, :cantelope_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220201165048_create_cantelopes.rb: -------------------------------------------------------------------------------- 1 | class CreateCantelopes < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :cantelopes do |t| 4 | 5 | t.string :name 6 | t.string :_a_show_only_field 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220318093158_create_pets.rb: -------------------------------------------------------------------------------- 1 | class CreatePets < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :pets do |t| 4 | t.string :name 5 | t.integer :human_id 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220318093211_create_human.rb: -------------------------------------------------------------------------------- 1 | class CreateHuman < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :humans do |t| 4 | t.string :name 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220318093221_create_appointments.rb: -------------------------------------------------------------------------------- 1 | class CreateAppointments < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :appointments do |t| 4 | t.integer :pet_id 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220320194207_create_atw_to_labels.rb: -------------------------------------------------------------------------------- 1 | class CreateAtwToLabels < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :atw_to_labels do |t| 4 | t.string :to_label 5 | t.integer :xyz_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220320200217_create_atw_full_names.rb: -------------------------------------------------------------------------------- 1 | class CreateAtwFullNames < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :atw_full_names do |t| 4 | t.string :full_name 5 | t.integer :xyz_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220320200230_create_atw_display_names.rb: -------------------------------------------------------------------------------- 1 | class CreateAtwDisplayNames < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :atw_display_names do |t| 4 | t.string :display_name 5 | t.integer :xyz_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220320214017_create_borkeds.rb: -------------------------------------------------------------------------------- 1 | class CreateBorkeds < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :borkeds do |t| 4 | t.integer :xyz_id 5 | 6 | t.integer :missing_label_table_id 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220320223316_create_missing_label_tables.rb: -------------------------------------------------------------------------------- 1 | class CreateMissingLabelTables < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :missing_label_tables do |t| 4 | 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220323000346_create_families.rb: -------------------------------------------------------------------------------- 1 | class CreateFamilies < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :families do |t| 4 | t.string :name 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /dummy/db/migrate/20220323000410_create_visits.rb: -------------------------------------------------------------------------------- 1 | class CreateVisits < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :visits do |t| 4 | t.string :name 5 | t.integer :user_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /dummy/db/migrate/20230317162618_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2] 3 | def change 4 | # Use Active Record's configured type for primary and foreign keys 5 | primary_key_type, foreign_key_type = primary_and_foreign_key_types 6 | 7 | create_table :active_storage_blobs, id: primary_key_type do |t| 8 | t.string :key, null: false 9 | t.string :filename, null: false 10 | t.string :content_type 11 | t.text :metadata 12 | t.string :service_name, null: false 13 | t.bigint :byte_size, null: false 14 | t.string :checksum 15 | 16 | if connection.supports_datetime_with_precision? 17 | t.datetime :created_at, precision: 6, null: false 18 | else 19 | t.datetime :created_at, null: false 20 | end 21 | 22 | t.index [ :key ], unique: true 23 | end 24 | 25 | create_table :active_storage_attachments, id: primary_key_type do |t| 26 | t.string :name, null: false 27 | t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type 28 | t.references :blob, null: false, type: foreign_key_type 29 | 30 | if connection.supports_datetime_with_precision? 31 | t.datetime :created_at, precision: 6, null: false 32 | else 33 | t.datetime :created_at, null: false 34 | end 35 | 36 | t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true 37 | t.foreign_key :active_storage_blobs, column: :blob_id 38 | end 39 | 40 | create_table :active_storage_variant_records, id: primary_key_type do |t| 41 | t.belongs_to :blob, null: false, index: false, type: foreign_key_type 42 | t.string :variation_digest, null: false 43 | 44 | t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true 45 | t.foreign_key :active_storage_blobs, column: :blob_id 46 | end 47 | end 48 | 49 | private 50 | def primary_and_foreign_key_types 51 | config = Rails.configuration.generators 52 | setting = config.options[config.orm][:primary_key_type] 53 | primary_key_type = setting || :primary_key 54 | foreign_key_type = setting || :bigint 55 | [primary_key_type, foreign_key_type] 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /dummy/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) 7 | # Character.create(name: "Luke", movie: movies.first) 8 | -------------------------------------------------------------------------------- /dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /dummy/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/lib/tasks/.keep -------------------------------------------------------------------------------- /dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/log/.keep -------------------------------------------------------------------------------- /dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/public/apple-touch-icon.png -------------------------------------------------------------------------------- /dummy/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/public/favicon.ico -------------------------------------------------------------------------------- /dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /dummy/spec/factories/abc_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :abc do 3 | name { FFaker.name } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/factories/cantelope_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :cantelope, class: Fruits::Cantelope do 3 | name { FFaker.name } 4 | _a_show_only_field { FFaker.name } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /dummy/spec/factories/dfg_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :dfg do 3 | user { build(:user) } 4 | name { FFaker.name } 5 | cantelope { build(:cantelope) } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /dummy/spec/factories/family_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :family do 3 | name { FFaker.name } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/factories/ghi_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :ghi do 3 | 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/factories/hgi_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :hgi do 3 | name { FFaker::Name } 4 | how_many {rand(4)} 5 | hello { FFaker::Name } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /dummy/spec/factories/human_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :human do 3 | name {"xyz"} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/factories/jkl_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :jkl do 3 | 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/factories/pet_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :pet do 3 | human {create(:human)} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/factories/user_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | email { FFaker::Internet.email } 4 | password { "password" } 5 | password_confirmation { "password" } 6 | family { build(:family) } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /dummy/spec/factories/xyz_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :xyz do 3 | user { build(:user) } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /dummy/spec/files/computer_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/spec/files/computer_code.jpg -------------------------------------------------------------------------------- /dummy/spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | require 'spec_helper' 3 | ENV['RAILS_ENV'] ||= 'test' 4 | require_relative '../config/environment' 5 | # Prevent database truncation if the environment is production 6 | abort("The Rails environment is running in production mode!") if Rails.env.production? 7 | require 'rspec/rails' 8 | require 'support/capybara_login.rb' 9 | 10 | puts "reloading app..." 11 | def reload!(print = true) 12 | puts 'Reloading ...' if print 13 | # Main project directory. 14 | root_dir = File.expand_path('..', __dir__) 15 | # Directories within the project that should be reloaded. 16 | reload_dirs = %w{lib} 17 | # Loop through and reload every file in all relevant project directories. 18 | reload_dirs.each do |dir| 19 | Dir.glob("#{root_dir}/#{dir}/**/*.rb").each { |f| load(f) } 20 | end 21 | # Return true when complete. 22 | true 23 | end 24 | reload! 25 | 26 | puts "finished reloading app..." 27 | 28 | 29 | # Add additional requires below this line. Rails is not loaded until this point! 30 | 31 | # Requires supporting ruby files with custom matchers and macros, etc, in 32 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 33 | # run as spec files by default. This means that files in spec/support that end 34 | # in _spec.rb will both be required and run as specs, causing the specs to be 35 | # run twice. It is recommended that you do not name files matching this glob to 36 | # end with _spec.rb. You can configure this pattern with the --pattern 37 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 38 | # 39 | # The following line is provided for convenience purposes. It has the downside 40 | # of increasing the boot-up time by auto-requiring all files in the support 41 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 42 | # require only the support files necessary. 43 | # 44 | # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } 45 | 46 | # Checks for pending migrations and applies them before tests are run. 47 | # If you are not using ActiveRecord, you can remove these lines. 48 | begin 49 | ActiveRecord::Migration.maintain_test_schema! 50 | rescue ActiveRecord::PendingMigrationError => e 51 | puts e.to_s.strip 52 | exit 1 53 | end 54 | RSpec.configure do |config| 55 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 56 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 57 | 58 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 59 | # examples within a transaction, remove the following line or assign false 60 | # instead of true. 61 | config.use_transactional_fixtures = true 62 | 63 | # You can uncomment this line to turn off ActiveRecord support entirely. 64 | # config.use_active_record = false 65 | 66 | # RSpec Rails can automatically mix in different behaviours to your tests 67 | # based on their file location, for example enabling you to call `get` and 68 | # `post` in specs under `spec/controllers`. 69 | # 70 | # You can disable this behaviour by removing the line below, and instead 71 | # explicitly tag your specs with their type, e.g.: 72 | # 73 | # RSpec.describe UsersController, type: :controller do 74 | # # ... 75 | # end 76 | # 77 | # The different available types are documented in the features, such as in 78 | # https://relishapp.com/rspec/rspec-rails/docs 79 | config.infer_spec_type_from_file_location! 80 | 81 | # Filter lines from Rails gems in backtraces. 82 | config.filter_rails_from_backtrace! 83 | # arbitrary gems may also be filtered via: 84 | # config.filter_gems_from_backtrace("gem name") 85 | config.include FactoryBot::Syntax::Methods 86 | 87 | end 88 | 89 | Capybara.default_driver = :selenium_chrome_headless 90 | -------------------------------------------------------------------------------- /dummy/spec/support/capybara_login.rb: -------------------------------------------------------------------------------- 1 | def login_as(user) 2 | visit '/users/sign_in' 3 | within("#new_user") do 4 | fill_in 'Email', with: user.email 5 | fill_in 'Password', with: 'password' 6 | end 7 | click_button 'Log in' 8 | end 9 | -------------------------------------------------------------------------------- /dummy/storage/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/storage/.keep -------------------------------------------------------------------------------- /dummy/test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /dummy/test/channels/application_cable/connection_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase 4 | # test "connects with cookies" do 5 | # cookies.signed[:user_id] = 42 6 | # 7 | # connect 8 | # 9 | # assert_equal connection.user_id, "42" 10 | # end 11 | end 12 | -------------------------------------------------------------------------------- /dummy/test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/controllers/.keep -------------------------------------------------------------------------------- /dummy/test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/fixtures/files/.keep -------------------------------------------------------------------------------- /dummy/test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/helpers/.keep -------------------------------------------------------------------------------- /dummy/test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/integration/.keep -------------------------------------------------------------------------------- /dummy/test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/mailers/.keep -------------------------------------------------------------------------------- /dummy/test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/models/.keep -------------------------------------------------------------------------------- /dummy/test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/test/system/.keep -------------------------------------------------------------------------------- /dummy/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= "test" 2 | require_relative "../config/environment" 3 | require "rails/test_help" 4 | 5 | class ActiveSupport::TestCase 6 | # Run tests in parallel with specified workers 7 | parallelize(workers: :number_of_processors) 8 | 9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /dummy/vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/vendor/.keep -------------------------------------------------------------------------------- /dummy/vendor/javascript/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/dummy/vendor/javascript/.keep -------------------------------------------------------------------------------- /hot_glue.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("lib", __dir__) 2 | 3 | # Maintain your gem's version: 4 | require "hotglue/version" 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |spec| 8 | spec.name = "hot-glue" 9 | spec.version = HotGlue::Version::CURRENT 10 | spec.license = 'Nonstandard' 11 | spec.date = Time.now.strftime("%Y-%m-%d") 12 | spec.summary = "A gem to build Turbo Rails scaffolding." 13 | spec.description = "Simple, plug & play Rails scaffold building companion for Turbo-Rails and Hotwire" 14 | spec.authors = ["Jason Fleetwood-Boldt"] 15 | spec.email = 'hello@jfbcodes.com' 16 | 17 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 18 | files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|dummy)}) || f.match(%r{(gemspec|gem)$}) } 19 | files 20 | end 21 | 22 | spec.add_runtime_dependency "rails", '> 5.1' 23 | spec.homepage = 'https://heliosdev.shop/p/hot-glue?utm_source=rubygems.org&utm_campaign=rubygems_link' 24 | spec.metadata = { "source_code_uri" => "https://github.com/hot-glue-for-rails/hot-glue", 25 | "homepage" => "https://heliosdev.shop/hot-glue", 26 | "funding" => "https://school.jfbcodes.com/8188" } 27 | 28 | spec.add_runtime_dependency('kaminari', '~> 1.2') 29 | # spec.add_runtime_dependency('sass-rails') 30 | 31 | spec.add_dependency 'ffaker', "~> 2.16" 32 | # spec.add_runtime_dependency "turbo-rails" 33 | 34 | spec.post_install_message = <<~MSG 35 | --------------------------------------------- 36 | Welcome to Hot Glue - A Scaffold Building Companion for Hotwire + Turbo-Rails 37 | For license options visit https://heliosdev.shop/hot-glue-license 38 | --------------------------------------------- 39 | MSG 40 | end 41 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/default_config_loader.rb: -------------------------------------------------------------------------------- 1 | module DefaultConfigLoader 2 | 3 | 4 | def default_configs 5 | @yaml_from_config ||= YAML.load(File.read("config/hot_glue.yml")) 6 | end 7 | 8 | 9 | def get_default_from_config(key: ) 10 | default_configs[key] 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/direct_upload_install_generator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module HotGlue 4 | class DirectUploadInstallGenerator < Rails::Generators::Base 5 | source_root File.expand_path('templates', __dir__) 6 | 7 | def filepath_prefix 8 | # todo: inject the context 9 | 'spec/dummy/' if $INTERNAL_SPECS 10 | end 11 | 12 | def initialize(*args) #:nodoc: 13 | super 14 | 15 | if Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }['importmap-rails'] # impomrtmaps 16 | 17 | file_contents = File.read("#{filepath_prefix}app/javascript/application.js") 18 | 19 | if !file_contents.include?("//= require activestorage") 20 | file_contents << "//= require activestorage" 21 | File.write("#{filepath_prefix}app/javascript/application.js", file_contents) 22 | puts " HOTGLUE --> added to #{filepath_prefix}app/javascript/application.js: `//= require activestorage" 23 | else 24 | puts " HOTGLUE --> #{filepath_prefix}app/javascript/application.js already contains `//= require activestorage`" 25 | end 26 | 27 | elsif Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }['jsbundling-rails'] 28 | file_contents = File.read("#{filepath_prefix}app/javascript/application.js") 29 | 30 | if !file_contents.include?("ActiveStorage.start()") 31 | file_contents << "import * as ActiveStorage from \"@rails/activestorage\" 32 | ActiveStorage.start()" 33 | File.write("#{filepath_prefix}app/javascript/application.js", file_contents) 34 | puts " HOTGLUE --> added to #{filepath_prefix}app/javascript/application.js: `ActiveStorage.start()" 35 | else 36 | puts " HOTGLUE --> #{filepath_prefix}app/javascript/application.js already contains `ActiveStorage.start()`" 37 | end 38 | 39 | 40 | else 41 | puts " HOTGLUE --> could not detect either importmap-rails or jsbundling-rails app" 42 | end 43 | end 44 | end 45 | end 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/dropzone_install_generator.rb: -------------------------------------------------------------------------------- 1 | module HotGlue 2 | class DropzoneInstallGenerator < Rails::Generators::Base 3 | source_root File.expand_path('templates', __dir__) 4 | 5 | def filepath_prefix 6 | # todo: inject the context 7 | 'spec/dummy/' if $INTERNAL_SPECS 8 | end 9 | 10 | 11 | def initialize(*args) #:nodoc: 12 | super 13 | system("./bin/rails generate stimulus Dropzone") 14 | copy_file "javascript/dropzone_controller.js", "#{filepath_prefix}app/javascript/controllers/dropzone_controller.js" 15 | puts "HOT GLUE --> copying dropzone stimulus controller into app/javascript/controllers/dropzone_controller.js" 16 | 17 | 18 | if File.exist?("#{filepath_prefix}app/assets/stylesheets/application.bootstrap.scss") 19 | scss_file = "#{filepath_prefix}app/assets/stylesheets/application.bootstrap.scss" 20 | elsif File.exist?("#{filepath_prefix}app/assets/stylesheets/application.scss") 21 | scss_file = "#{filepath_prefix}app/assets/stylesheets/application.scss" 22 | else 23 | raise HotGlue::Error, "Could not detect your stylesheet, aborting..." 24 | end 25 | 26 | file_contents = File.read(scss_file) 27 | 28 | if !file_contents.include?("@import \"dropzone/dist/dropzone\"") 29 | file_contents << "\n@import \"dropzone/dist/dropzone\";\n@import \"dropzone/dist/basic\";" 30 | 31 | 32 | File.write(scss_file, file_contents) 33 | puts " HOTGLUE --> added to #{scss_file}: @import dropzone ... " 34 | else 35 | puts " HOTGLUE --> #{scss_file} already contains @import dropzone" 36 | end 37 | end 38 | end 39 | end 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/field_factory.rb: -------------------------------------------------------------------------------- 1 | require_relative "fields/association_field" 2 | require_relative "fields/boolean_field" 3 | require_relative "fields/date_field" 4 | require_relative "fields/date_time_field" 5 | require_relative "fields/enum_field" 6 | require_relative "fields/float_field" 7 | require_relative "fields/integer_field" 8 | require_relative "fields/string_field" 9 | require_relative "fields/text_field" 10 | require_relative "fields/time_field" 11 | require_relative "fields/uuid_field" 12 | require_relative "fields/attachment_field" 13 | require_relative "fields/related_set_field" 14 | 15 | class FieldFactory 16 | attr_accessor :field, :class_name 17 | def initialize(type: , name: , generator: ) 18 | field_class = case type 19 | when :integer 20 | if name.to_s.ends_with?("_id") 21 | AssociationField 22 | else 23 | IntegerField 24 | end 25 | when :uuid 26 | UUIDField 27 | when :string 28 | StringField 29 | when :text 30 | TextField 31 | when :float 32 | FloatField 33 | when :decimal 34 | FloatField 35 | when :datetime 36 | DateTimeField 37 | when :date 38 | DateField 39 | when :time 40 | TimeField 41 | when :boolean 42 | BooleanField 43 | when :enum 44 | EnumField 45 | when :attachment 46 | AttachmentField 47 | when :related_set 48 | RelatedSetField 49 | end 50 | @class_name = class_name 51 | 52 | if field_class.nil? 53 | raise "Field type could be identified #{name} " 54 | end 55 | 56 | @field = field_class.new(scaffold: generator, name: name) 57 | # layout_strategy: generator.layout_strategy, 58 | # form_placeholder_labels: generator.form_placeholder_labels, 59 | # form_labels_position: generator.form_labels_position, 60 | # ownership_field: generator.ownership_field, 61 | # hawk_keys: generator.hawk_keys, 62 | # auth: generator.auth, 63 | # class_name: generator.singular_class, 64 | # alt_lookup: generator.alt_lookups[name] || nil, 65 | # singular: generator.singular, 66 | # self_auth: generator.self_auth, 67 | # update_show_only: generator.update_show_only, 68 | # attachment_data: generator.attachments[name.to_sym], 69 | # sample_file_path: generator.sample_file_path, 70 | # modify_as: generator.modify_as[name.to_sym] || nil, 71 | # plural: generator.plural, 72 | # display_as: generator.display_as[name.to_sym] || nil, 73 | # default_boolean_display: generator.default_boolean_display, 74 | # namespace: generator.namespace_value, 75 | # pundit: generator.pundit ) 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/attachment_field.rb: -------------------------------------------------------------------------------- 1 | class AttachmentField < Field 2 | attr_accessor :attachment_data 3 | def initialize(scaffold:, name:) 4 | super 5 | 6 | @attachment_data = attachment_data 7 | end 8 | 9 | def spec_setup_let_arg 10 | nil 11 | end 12 | 13 | def spec_list_view_natural_assertion 14 | "within('div.#{singular}--#{name}') do 15 | img = page.find('img') 16 | expect(img['src']).to end_with('glass_button.png') 17 | end" 18 | 19 | # "expect(page).to have_content(#{singular}#{1}.#{name})" 20 | end 21 | 22 | 23 | def spec_setup_and_change_act(which_partial = nil) 24 | " attach_file(\"#{singular}[#{name.to_s}]\", \"#{sample_file_path}\")" 25 | end 26 | 27 | def thumbnail 28 | attachment_data[:thumbnail] 29 | end 30 | 31 | def form_field_output 32 | direct = attachment_data[:direct_upload] 33 | dropzone = attachment_data[:dropzone] 34 | field_result = (thumbnail ? "<%= #{singular}.#{name}.attached? ? image_tag(#{singular}.#{name}.variant(:#{thumbnail})) : '' %>" : "") + 35 | "
\n" + (update_show_only.include?(name) ? "" : "<%= f.file_field :#{name} #{', direct_upload: true ' if direct}#{', "data-dropzone-target": "input"' if dropzone}%>") 36 | 37 | if dropzone 38 | field_result = "
\n "+ field_result + "\n
" 39 | end 40 | return field_result 41 | end 42 | 43 | def line_field_output 44 | thumbnail = attachment_data[:thumbnail] 45 | 46 | (thumbnail ? "<%= #{singular}.#{name}.attached? ? image_tag(#{singular}.#{name}.variant(:#{thumbnail})) : '' %>" : "") 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/date_field.rb: -------------------------------------------------------------------------------- 1 | class DateField < Field 2 | def spec_setup_and_change_act(which_partial = nil) 3 | " " + "new_#{name} = Date.current + (rand(100).days) \n" + 4 | ' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})" 5 | end 6 | 7 | def spec_setup_let_arg 8 | "#{name}: Date.current + rand(50).days" 9 | end 10 | 11 | 12 | def form_field_output 13 | parts = name.to_s.split('_') 14 | camelcase_name = parts.map(&:capitalize).join 15 | "<%= date_field_localized(f, :#{name}, #{singular}.#{name}, label: '#{ name.to_s.humanize }'" + (stimmify ? ", html: {'data-#{@stimmify}-target': '#{camelcase_name}'}" : "") + ") %>" 16 | end 17 | 18 | def line_field_output 19 | "<% unless #{singular}.#{name}.nil? %> 20 | <%= #{singular}.#{name} %> 21 | <% else %> 22 | MISSING 23 | <% end %>" 24 | end 25 | 26 | def search_field_output 27 | "
"+ 28 | "\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['is on', 'is_on'], " + 29 | "\n ['is between', 'is_between'], ['is on or after', 'is_on_or_after'], " + 30 | "\n ['is before or on', 'is_before_or_on'], ['not on', 'not_on']], @q[\'0\']['#{name}_match'] ), {} ," + 31 | "\n { class: 'form-control match', 'data-action': 'change->date-range-picker#matchSelection' } %>"+ 32 | "\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' %>" + 33 | "\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'%>" + 34 | "\n
" 35 | end 36 | 37 | 38 | def where_query_statement 39 | ".where(*#{name}_query)" 40 | end 41 | 42 | def load_all_query_statement 43 | "#{name}_query = date_query_constructor(:#{name}, @q['0'][:#{name}_match], @q['0'][:#{name}_search_start], @q['0'][:#{name}_search_end])" 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/date_time_field.rb: -------------------------------------------------------------------------------- 1 | class DateTimeField < Field 2 | def spec_random_data 3 | Time.now + rand(1..5).days 4 | end 5 | 6 | 7 | def form_show_only_output 8 | viewable_output 9 | end 10 | 11 | 12 | def spec_setup_and_change_act(which_partial = nil) 13 | " " + "new_#{name} = DateTime.current + 1.year \n" + 14 | ' ' + "find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})" 15 | 16 | end 17 | 18 | def spec_make_assertion 19 | if !modify_binary? 20 | "expect(page).to have_content(new_#{name}.in_time_zone(testing_timezone).strftime('%m/%d/%Y @ %l:%M %p %Z').gsub(' ', ' '))" 21 | else 22 | "expect(page).to have_content('#{modify_as[:binary][:truthy]}'" 23 | end 24 | end 25 | 26 | def spec_setup_let_arg 27 | "#{name}: DateTime.current + 1.day" 28 | end 29 | 30 | def spec_list_view_assertion 31 | if modify_binary? 32 | "expect(page).to have_content('#{modify_as[:binary][:truthy]}')" 33 | else 34 | spec_list_view_natural_assertion 35 | end 36 | end 37 | 38 | def spec_list_view_natural_assertion 39 | "expect(page).to have_content(#{singular}#{1}.#{name}.in_time_zone(testing_timezone).strftime('%m/%d/%Y @ %l:%M %p %Z').gsub(' ', ' '))" 40 | end 41 | 42 | def form_field_output 43 | "<%= f.datetime_field( :#{name}, value: #{singular}.#{name} && #{singular}.#{name}.in_time_zone(current_timezone), class: '#{@layout_strategy.form_input_class}' ) %><%= current_timezone %>" 44 | end 45 | 46 | def viewable_output 47 | if modify_binary? 48 | modified_display_output 49 | else 50 | "<% unless #{singular}.#{name}.nil? %> 51 | <%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%m/%d/%Y @ %l:%M %p %Z') %> 52 | <% else %> 53 | MISSING 54 | <% end %>" 55 | end 56 | end 57 | 58 | 59 | 60 | 61 | def search_field_output 62 | if !modify_binary? 63 | 64 | "
"+ 65 | "\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['is on', 'is_on'], " + 66 | "\n ['is between', 'is_between'], ['is on or after', 'is_on_or_after'], " + 67 | "\n ['is before or on', 'is_before_or_on'], ['not on', 'not_on']], @q[\'0\']['#{name}_match'] ), {} ," + 68 | "\n { class: 'form-control match', 'data-action': 'change->date-range-picker#matchSelection' } %>"+ 69 | "\n <%= datetime_local_field 'q[0]', '#{name}_search_start', {value: @q[\'0\'][:#{name}_search_start], autocomplete: 'off', size: 40, class: 'form-control', placeholder: 'start', 'data-date-range-picker-target': 'start' } %>" + 70 | "\n <%= datetime_local_field 'q[0]', '#{name}_search_end', {value: @q[\'0\'][:#{name}_search_end], autocomplete: 'off', size: 40, class: 'form-control', placeholder: 'end' , 'data-date-range-picker-target': 'end' } %>" + 71 | "\n
" 72 | else 73 | " <%= f.radio_button('q[0][#{name}_match]', '-1', checked: @q[\'0\'][:#{name}_match]=='-1' ? 'checked' : '', class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" + 74 | " <%= f.label('All', value: '-1', for: 'q[0][#{name}_match]_-1' ) %>\n" + 75 | "
<%= f.radio_button('q[0][#{name}_match]', '0', checked: @q[\'0\'][:#{name}_match]=='0' ? 'checked' : '', class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" + 76 | " <%= f.label('#{modify_as[:binary][:falsy]}', value: '0', for: 'q[0][#{name}_match]_0') %>\n" + 77 | "
<%= f.radio_button('q[0][#{name}_match]', '1', checked: @q[\'0\'][:#{name}_match]=='1' ? 'checked' : '', class: '#{@layout_strategy.form_checkbox_input_class}') %>\n" + 78 | " <%= f.label('#{modify_as[:binary][:truthy]}', value: '1', for: 'q[0][#{name}_match]_1') %>\n" + 79 | "
" 80 | end 81 | end 82 | 83 | 84 | def where_query_statement 85 | ".where(#{name}_query)" 86 | end 87 | 88 | def load_all_query_statement 89 | if !modify_binary? 90 | "#{name}_query = date_query_constructor(:#{name}, @q['0'][:#{name}_match], @q['0'][:#{name}_search_start], @q['0'][:#{name}_search_end])" 91 | else 92 | "#{name}_query = boolean_modified_datetime_constructor(:#{name}, @q['0'][:#{name}_match])" 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/enum_field.rb: -------------------------------------------------------------------------------- 1 | class EnumField < Field 2 | def spec_setup_and_change_act(which_partial = nil) 3 | # what is the enum name 4 | " list_of_#{enum_type} = #{class_name}.defined_enums['#{name}'].keys \n" + 5 | " " + "new_#{name} = list_of_#{enum_type}[rand(list_of_#{enum_type}.length)].to_s \n" + 6 | ' find("select[name=\'' + singular + '[' + name + ']\'] option[value=\'#{new_' + name + '}\']").select_option' 7 | end 8 | 9 | def enum_type 10 | eval("#{class_name}.columns.select{|x| x.name == '#{name}'}[0].sql_type") 11 | end 12 | 13 | def spec_make_assertion 14 | if(eval("#{class_name}.respond_to?(:#{name}_labels)")) 15 | "expect(page).to have_content(#{singular_class}.#{name}_labels[new_#{name}])" 16 | else 17 | "expect(page).to have_content(new_#{name})" 18 | end 19 | end 20 | 21 | def spec_setup_let_args 22 | super 23 | end 24 | 25 | def spec_list_view_assertion 26 | if(eval("#{class_name}.respond_to?(:#{name}_labels)")) 27 | "expect(page).to have_content(#{class_name}.#{name}_labels[#{singular}#{1}.#{name}])" 28 | else 29 | "expect(page).to have_content(#{singular}1.#{name})" 30 | end 31 | end 32 | 33 | def form_field_output 34 | if @stimmify 35 | col_target = HotGlue.to_camel_case(name.to_s.gsub("_", " ")) 36 | data_attr = ", data: {'#{@stimmify}-target': '#{col_target}'} " 37 | end 38 | 39 | enum_type = eval("#{class_name}.columns.select{|x| x.name == '#{name}'}[0].sql_type") 40 | 41 | if eval("defined? #{class_name}.#{enum_type}_labels") == "method" 42 | enum_definer = "#{class_name}.#{enum_type}_labels" 43 | else 44 | enum_definer = "#{class_name}.defined_enums['#{name}']" 45 | end 46 | 47 | res = "<%= f.collection_select(:#{name}, enum_to_collection_select(#{enum_definer}), :key, :value, {include_blank: true, selected: #{singular}.#{name} }, class: 'form-control' #{data_attr} )%>" 48 | 49 | 50 | if modify_as && modify_as[:enum] == :partials 51 | res << partial_render 52 | end 53 | res 54 | end 55 | 56 | def line_field_output 57 | enum_type = eval("#{class_name}.columns.select{|x| x.name == '#{name}'}[0].sql_type") 58 | 59 | if eval("defined? #{class_name}.#{enum_type}_labels") == "method" 60 | enum_definer = "#{class_name}.#{enum_type}_labels" 61 | else 62 | enum_definer = "#{class_name}.defined_enums['#{name}']" 63 | end 64 | 65 | res = " 66 | <% if #{singular}.#{name}.nil? %> 67 | Missing #{name} 68 | <% else %>" 69 | 70 | if modify_as && modify_as[:enum] == :partials 71 | res << partial_render 72 | else 73 | res << "<%= #{enum_definer}[#{singular}.#{name}.to_sym] %>" 74 | end 75 | 76 | res << "<% end %>" 77 | res 78 | end 79 | 80 | def partial_render 81 | "<% if #{singular}.#{name} %><%= render partial: \"#{namespace + "/" if namespace}#{plural}/\#{#{singular}.#{name}}\", locals: { #{singular}: #{singular} } %><% end %>" 82 | end 83 | 84 | 85 | def form_show_only_output 86 | viewable_output 87 | end 88 | 89 | 90 | def search_field_output 91 | enum_type = eval("#{class_name}.columns.select{|x| x.name == '#{name}'}[0].sql_type") 92 | if eval("defined? #{class_name}.#{enum_type}_labels") == "method" 93 | enum_definer = "#{class_name}.#{enum_type}_labels" 94 | else 95 | enum_definer = "#{class_name}.defined_enums['#{name}']" 96 | end 97 | 98 | "<%= f.collection_select(\'q[0][#{name}_search]\', enum_to_collection_select(#{enum_definer}), :key, :value, {include_blank: true, selected: @q['0']['#{name}_search'] }, class: 'form-control') %>" 99 | end 100 | 101 | 102 | def where_query_statement 103 | ".where(*#{name}_query)" 104 | end 105 | 106 | def load_all_query_statement 107 | "#{name}_query = enum_constructor(:#{name}, @q['0'][:#{name}_search])" 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/float_field.rb: -------------------------------------------------------------------------------- 1 | class FloatField < Field 2 | def spec_setup_and_change_act(which_partial = nil) 3 | " " + "new_#{name} = rand(10) \n" + 4 | " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})" 5 | 6 | end 7 | 8 | 9 | def spec_setup_let_arg 10 | "#{name}: rand(1)*10000" 11 | end 12 | 13 | def form_field_output 14 | field_output(nil, 5) 15 | end 16 | 17 | # def line_field_output 18 | # width = (limit && limit < 40) ? limit : (40) 19 | # 20 | # "<%= #{singular}.#{name} %>" 21 | # end 22 | 23 | def search_field_output 24 | "" 25 | end 26 | 27 | 28 | def where_query_statement 29 | end 30 | 31 | def load_all_query_statement 32 | raise "Float field search not implemented" 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/integer_field.rb: -------------------------------------------------------------------------------- 1 | class IntegerField < Field 2 | def spec_random_data 3 | rand(1...1000) 4 | end 5 | 6 | def spec_setup_and_change_act(which_partial = nil) 7 | if name.to_s.ends_with?("_id") 8 | capybara_block_for_association(name_name: name, which_partial: which_partial) 9 | else 10 | " new_#{name} = rand(10) \n" + 11 | " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})" 12 | end 13 | end 14 | 15 | def spec_make_assertion 16 | "expect(page).to have_content(new_#{name})" 17 | end 18 | 19 | def spec_setup_let_arg 20 | "#{name}: rand(100)" 21 | end 22 | 23 | def spec_list_view_assertion 24 | "expect(page).to have_content(#{singular}#{1}.#{name})" 25 | end 26 | 27 | def form_field_output 28 | " <%= f.text_field :#{name}, value: #{singular}.#{name}, autocomplete: 'off', size: 4, class: 'form-control', type: 'number'" + (form_placeholder_labels ? ", placeholder: '#{name.to_s.humanize}'" : "") + " %>\n " + "\n" 29 | end 30 | 31 | def search_field_output 32 | "
" + 33 | "\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['=', '='], " + 34 | "\n ['≥', '≥'], ['>', '>'], " + 35 | "\n ['≤', '≤'], ['<', '<']], @q[\'0\']['#{name}_match'] ), {} ," + 36 | "\n { class: 'form-control match' } %>"+ 37 | "\n <%= f.text_field 'q[0][#{name}_search]', {value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 4, class: 'form-control', type: 'number'} %>" + 38 | "\n
" 39 | end 40 | 41 | 42 | def where_query_statement 43 | ".where(\"#{name} \#{#{name}_query }\")" 44 | end 45 | 46 | def load_all_query_statement 47 | "#{name}_query = integer_query_constructor(@q['0'][:#{name}_match], @q['0'][:#{name}_search])" 48 | end 49 | 50 | def code_to_reset_match_if_search_is_blank 51 | " @q['0'][:#{name}_match] = '' if @q['0'][:#{name}_search] == ''" 52 | end 53 | 54 | end 55 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/related_set_field.rb: -------------------------------------------------------------------------------- 1 | class RelatedSetField < Field 2 | 3 | attr_accessor :assoc_name, :assoc_class, :assoc 4 | 5 | def initialize( scaffold: , name: ) 6 | super 7 | 8 | @related_set_model = eval("#{class_name}.reflect_on_association(:#{name})") 9 | 10 | if @related_set_model.nil? 11 | raise "You specified a related set #{name} but there is no association on #{singular_class} for #{name}; please add a `has_and_belongs_to_many :#{name}` OR a `has_many :#{name}, through: ...` to the #{singular_class} model" 12 | 13 | # exit_message = "*** Oops: The model #{class_name} is missing an association for :#{assoc_name} or the model #{assoc_name.titlecase} doesn't exist. TODO: Please implement a model for #{assoc_name.titlecase}; or add to #{class_name} `belongs_to :#{assoc_name}`. To make a controller that can read all records, specify with --god." 14 | puts exit_message 15 | raise(HotGlue::Error, exit_message) 16 | end 17 | 18 | @assoc_class = eval(@related_set_model.try(:class_name)) 19 | 20 | name_list = [:name, :to_label, :full_name, :display_name, :email] 21 | 22 | if assoc_class && name_list.collect{ |field| 23 | assoc_class.respond_to?(field.to_s) || assoc_class.instance_methods.include?(field) 24 | }.none? 25 | exit_message = "Oops: Missing a label for `#{assoc_class}`. Can't find any column to use as the display label for the #{@assoc_name} association on the #{class_name} model. TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name 5) email. You can implement any of these directly on your`#{assoc_class}` model (can be database fields or model methods) or alias them to field you want to use as your display label. Then RERUN THIS GENERATOR. (Field used will be chosen based on rank here.)" 26 | raise(HotGlue::Error, exit_message) 27 | end 28 | 29 | end 30 | 31 | 32 | def form_field_output 33 | disabled_syntax = +"" 34 | 35 | if pundit 36 | disabled_syntax << ", {disabled: ! #{class_name}Policy.new(#{auth}, @#{singular}).role_ids_able?}" 37 | end 38 | " <%= f.collection_check_boxes :#{association_ids_method}, #{association_class_name}.all, :id, :label, {}#{disabled_syntax} do |m| %> 39 | <%= m.check_box %> <%= m.label %>
40 | <% end %>" 41 | end 42 | 43 | def association_ids_method 44 | eval("#{class_name}.reflect_on_association(:#{name})").class_name.underscore + "_ids" 45 | end 46 | 47 | def association_class_name 48 | eval("#{class_name}.reflect_on_association(:#{name})").class_name 49 | end 50 | 51 | def viewable_output 52 | "<%= #{singular}.#{name}.collect(&:label).join(\", \") %>" 53 | end 54 | end 55 | 56 | 57 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/string_field.rb: -------------------------------------------------------------------------------- 1 | class StringField < Field 2 | def spec_random_data 3 | FFaker::AnimalUS.common_name 4 | end 5 | 6 | def spec_setup_and_change_act(which_partial = nil) 7 | faker_string = 8 | if name.to_s.include?('email') 9 | "FFaker::Internet.email" 10 | elsif name.to_s.include?('domain') 11 | "FFaker::Internet.domain_name" 12 | elsif name.to_s.include?('ip_address') || name.to_s.ends_with?('_ip') 13 | "FFaker::Internet.ip_v4_address" 14 | else 15 | "FFaker::Movie.title" 16 | end 17 | 18 | return " " + "new_#{name} = #{faker_string} \n" + 19 | " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})" 20 | end 21 | 22 | def spec_setup_let_arg 23 | if name.to_s.include?('email') 24 | "#{name}: FFaker::Internet.email" 25 | elsif name.to_s.include?('domain') 26 | "#{name}: FFaker::Internet.domain_name" 27 | elsif name.to_s.include?('ip_address') || name.to_s.ends_with?('_ip') 28 | "#{name}: FFaker::Internet.ip_v4_address" 29 | else 30 | "#{name}: FFaker::Movie.title" 31 | end 32 | end 33 | 34 | def form_field_output 35 | if sql_type == "varchar" || sql_type == "character varying" 36 | field_output(nil, limit || 40) 37 | else 38 | text_area_output( 65536) 39 | end 40 | end 41 | 42 | # TODO: dry with text_field.rb 43 | def text_result(col, sql_type, limit) 44 | if sql_type == "varchar" 45 | field_output( nil, limit) 46 | else 47 | text_area_output( 65536) 48 | end 49 | end 50 | 51 | def search_field_output 52 | "<%= 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' } %>"+ 53 | "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text' %>" 54 | end 55 | 56 | 57 | def where_query_statement 58 | ".where('#{name} ILIKE ?', #{name}_query)" 59 | end 60 | 61 | def load_all_query_statement 62 | "#{name}_query = string_query_constructor(@q['0'][:#{name}_match], @q['0'][:#{name}_search])" 63 | end 64 | 65 | def code_to_reset_match_if_search_is_blank 66 | " @q['0'][:#{name}_match] = '' if @q['0'][:#{name}_search] == ''" 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/text_field.rb: -------------------------------------------------------------------------------- 1 | class TextField < Field 2 | def spec_random_data 3 | FFaker::AnimalUS.common_name 4 | end 5 | 6 | def spec_setup_and_change_act(which_partial = nil) 7 | " " + "new_#{name} = FFaker::Lorem.paragraphs(1).join("") \n" + 8 | " find(\"[name='#{testing_name}[#{ name.to_s }]']\").fill_in(with: new_#{name.to_s})" 9 | end 10 | 11 | def spec_setup_let_arg 12 | "#{name}: FFaker::Lorem.paragraphs(10).join(" ")" 13 | end 14 | 15 | def form_field_output 16 | if sql_type == "varchar" || sql_type == "character varying" 17 | field_output( nil, limit || 40) 18 | else 19 | text_area_output( 65536, extra_classes: (modify_as == {tinymce: 1} ? " tinymce" : "" )) 20 | end 21 | end 22 | 23 | # TODO: dry with string_field.rb 24 | def text_result( sql_type, limit) 25 | if sql_type == "varchar" 26 | field_output( nil, limit) 27 | else 28 | text_area_output( 65536) 29 | end 30 | end 31 | 32 | 33 | def search_field_output 34 | "<%= 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' } %>"+ 35 | "<%= f.text_field 'q[0][#{name}_search]', value: @q[\'0\'][:#{name}_search], autocomplete: 'off', size: 40, class: 'form-control', type: 'text' %>" 36 | end 37 | 38 | 39 | def where_query_statement 40 | ".where('#{name} ILIKE ?', #{name}_query)" 41 | end 42 | 43 | def load_all_query_statement 44 | "#{name}_query = string_query_constructor(@q['0'][:#{name}_match], @q['0'][:#{name}_search])" 45 | end 46 | 47 | def code_to_reset_match_if_search_is_blank 48 | " @q['0'][:#{name}_match] = '' if @q['0'][:#{name}_search] == ''" 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/time_field.rb: -------------------------------------------------------------------------------- 1 | class TimeField < Field 2 | 3 | def spec_setup_let_arg 4 | "#{name}: Time.current + rand(5000).seconds" 5 | end 6 | 7 | def form_field_output 8 | "<%= time_field_localized(f, :#{name}, #{singular}.#{name}, label: '#{ name.to_s.humanize }') %>" 9 | end 10 | 11 | def line_field_output 12 | "<% unless #{singular}.#{name}.nil? %> 13 | <%= #{singular}.#{name}.in_time_zone(current_timezone).strftime('%l:%M %p ') %> 14 | <% else %> 15 | MISSING 16 | <% end %>" 17 | end 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 | 25 | def spec_make_assertion 26 | "expect(page).to have_content(new_#{name}.strftime('%l:%M %p').strip)" 27 | end 28 | 29 | def spec_list_view_assertion 30 | # "expect(page).to have_content(#{singular}#{1}.#{name})" 31 | end 32 | 33 | def search_field_output 34 | "
"+ 35 | "\n <%= f.select 'q[0][#{name}_match]', options_for_select([['', ''], ['is at exactly', 'is_at_exactly']], @q[\'0\']['#{name}_match']), {} ," + 36 | "\n { class: 'form-control match', 'data-action': 'change->time-range-picker#matchSelection' } %>"+ 37 | "\n <%= time_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-time-range-picker-target': 'start' %>" + 38 | "\n <%= time_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-time-range-picker-target': 'end' %>" + 39 | "\n
" 40 | end 41 | 42 | 43 | def where_query_statement 44 | ".where(*#{name}_query)" 45 | end 46 | 47 | def load_all_query_statement 48 | "#{name}_query = time_query_constructor(:#{name}, @q['0'][:#{name}_match], @q['0'][:#{name}_search_start], @q['0'][:#{name}_search_end])" 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/fields/uuid_field.rb: -------------------------------------------------------------------------------- 1 | class UUIDField < AssociationField 2 | def spec_setup_let_arg 3 | "#{name.to_s.gsub('_id','')}: #{name.to_s.gsub('_id','')}1" 4 | end 5 | 6 | def spec_list_view_assertion 7 | assoc_name = name.to_s.gsub('_id','') 8 | association = eval("#{class_name}.reflect_on_association(:#{assoc_name})") 9 | "expect(page).to have_content(#{singular}#{1}.#{assoc_name}.#{HotGlue.derrive_reference_name(association.class_name)})" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/flash_notices_install_generator.rb: -------------------------------------------------------------------------------- 1 | module HotGlue 2 | class FlashNoticesInstallGenerator < Rails::Generators::Base 3 | source_root File.expand_path('templates', __dir__) 4 | 5 | def filepath_prefix 6 | # todo: inject the context 7 | 'spec/dummy/' if $INTERNAL_SPECS 8 | end 9 | 10 | 11 | def initialize(*args) #:nodoc: 12 | super 13 | copy_file "erb/_flash_notices.erb", "#{'spec/dummy/' if Rails.env.test?}app/views/layouts/_flash_notices.erb" 14 | 15 | end 16 | end 17 | end 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/hot_glue.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module HotGlue 4 | 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/layout_strategy/base.rb: -------------------------------------------------------------------------------- 1 | module LayoutStrategy 2 | class Base 3 | attr_accessor :builder 4 | def initialize(scaffold_builder) 5 | @builder = scaffold_builder 6 | end 7 | 8 | def button_applied_classes; end 9 | def column_classes_for_button_column; ""; end 10 | def button_classes; ""; end 11 | def button_column_style; "" ; end 12 | def button_style ; ""; end 13 | def column_headings_col_style; "" ; end 14 | def column_width; ""; end 15 | def column_classes_for_line_fields; ""; end 16 | def column_classes_for_form_fields; ""; end 17 | def column_classes_for_column_headings; ""; end 18 | def col_width; 100; end 19 | def container_name; ""; end 20 | def downnest_style ; ""; end 21 | def downnest_column_style ; "" ; end 22 | def each_col 23 | return col_width if builder.columns.count == 0 24 | (col_width/(builder.columns.count)).to_i 25 | end 26 | def list_classes; ""; end 27 | def magic_button_classes; ""; end 28 | def row_classes; ""; end 29 | def row_heading_classes; ""; end 30 | def page_begin; '
'; end 31 | def page_end ; '
'; end 32 | def style_with_flex_basis(x); "" ; end 33 | def form_checkbox_input_class; ""; end 34 | def form_checkbox_label_class; ""; end 35 | def form_checkbox_wrapper_class; ""; end 36 | 37 | def search_opening 38 | "" 39 | end 40 | 41 | def form_input_class 42 | "" 43 | end 44 | 45 | def search_closing 46 | "" 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/layout_strategy/bootstrap.rb: -------------------------------------------------------------------------------- 1 | class LayoutStrategy::Bootstrap < LayoutStrategy::Base 2 | def button_classes # column classes 3 | " " + "col-sm-#{builder.layout_object[:columns][:button_columns]}" 4 | end 5 | 6 | def button_applied_classes 7 | "btn btn-sm" 8 | end 9 | 10 | def magic_button_classes 11 | "btn-secondary" 12 | end 13 | 14 | 15 | def column_classes_for_button_column 16 | "col-md-#{builder.layout_object[:buttons][:size]}" 17 | end 18 | 19 | 20 | def column_classes_for_form_fields(size = nil) 21 | "col-md-#{size || builder.layout_object[:columns][:size_each]}" 22 | end 23 | 24 | def column_classes_for_column_headings(size = nil) 25 | column_classes_for_line_fields(size) 26 | end 27 | 28 | def container_name 29 | "container" 30 | end 31 | 32 | def column_classes_for_line_fields(size = nil) 33 | "col-sm-#{size || builder.layout_object[:columns][:size_each]}" 34 | end 35 | 36 | def column_width 37 | builder.layout_object[:columns][:size_each] 38 | end 39 | 40 | def downnest_portal_column_width(downnest) 41 | "col-sm-#{ builder.layout_object[:portals][downnest][:size] }" 42 | end 43 | 44 | def downnest_portal_stacked_column_width 45 | "col-sm-4" 46 | end 47 | 48 | def page_begin 49 | '
' 50 | end 51 | 52 | def row_classes 53 | "row hg-row" 54 | end 55 | 56 | def page_end 57 | '
' 58 | end 59 | 60 | def form_checkbox_input_class 61 | "form-check-input" 62 | end 63 | 64 | def form_checkbox_wrapper_class 65 | "form-check" 66 | end 67 | 68 | def form_checkbox_label_class 69 | "form-check-label" 70 | end 71 | 72 | def form_input_class 73 | "form-control" 74 | end 75 | 76 | def search_opening 77 | '
' 78 | end 79 | 80 | def search_closing 81 | "
" 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/layout_strategy/hot_glue.rb: -------------------------------------------------------------------------------- 1 | class LayoutStrategy::HotGlue < LayoutStrategy::Base 2 | def button_column_style 3 | 'style="flex-basis: 150px' 4 | end 5 | 6 | def button_style 7 | 'style="flex-basis: ' + (100 - (column_width * builder.columns.count)).floor.to_s + '%;"' 8 | end 9 | 10 | def column_width 11 | each_col * builder.columns.count 12 | end 13 | 14 | def column_headings_col_style 15 | " style='flex-basis: #{column_width}%'" 16 | end 17 | 18 | def container_name 19 | "scaffold-container" 20 | end 21 | 22 | def downnest_column_style 23 | 'style="flex-basis: ' + each_downnest_width.to_s + '%;' 24 | end 25 | 26 | def downnest_style 27 | 'style="flex-basis: ' + each_downnest_width.to_s + '%"' 28 | end 29 | 30 | def each_downnest_width 31 | builder.downnest_children.count == 1 ? 33 : (53/builder.downnest_children.count).floor 32 | end 33 | 34 | def list_classes 35 | "scaffold-list" 36 | end 37 | 38 | def row_classes 39 | "scaffold-row" 40 | end 41 | 42 | def column_classes_for_form_fields(size = nil) 43 | "scaffold-cell" 44 | end 45 | 46 | def row_heading_classes 47 | "scaffold-heading-row" 48 | end 49 | def column_classes_for_line_fields(size = nil) 50 | "scaffold-cell" 51 | end 52 | 53 | def column_classes_for_column_headings(size = nil) 54 | "scaffold-cell" 55 | end 56 | 57 | def col_width 58 | downnest_size = case (builder.downnest_children.count) 59 | when 0 60 | downnest_size = 0 61 | when 1 62 | downnest_size = 40 63 | 64 | else 65 | downnest_size = 60 66 | 67 | end 68 | 100 - downnest_size - 5 69 | end 70 | 71 | def page_begin 72 | '
' 73 | end 74 | 75 | def page_end 76 | '
' 77 | end 78 | 79 | def style_with_flex_basis(perc_width) 80 | " style='flex-basis: #{perc_width}%'" 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/layout_strategy/tailwind.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | class LayoutStrategy::Tailwind < LayoutStrategy::Base 4 | def button_classes; ""; end 5 | def button_column_style; "" ; end 6 | def button_style ; ""; end 7 | def column_headings_col_style; "" ; end 8 | def column_width; ""; end 9 | def column_classes_for_line_fields(size = nil); ""; end 10 | def column_classes_for_form_fields(size = nil); ""; end 11 | def column_classes_for_column_headings(size = nil); ""; end 12 | def col_width; 100; end 13 | def container_name; ""; end 14 | def downnest_style ; ""; end 15 | def downnest_column_style ; "" ; end 16 | def each_col 17 | return col_width if builder.columns.count == 0 18 | (col_width/(builder.columns.count)).to_i 19 | end 20 | 21 | def list_classes; "overflow-x-auto w-full"; end 22 | def row_classes; "grid grid-cols-4 gap-x-16 py-5 px-4 text-sm text-gray-700 border-b border-gray-200 dark:border-gray-700"; end 23 | def row_heading_classes; "grid grid-cols-4 gap-x-16 p-4 text-sm font-medium text-gray-900 bg-gray-100 border-t border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700 dark:text-white"; end 24 | def page_begin; '
'; end 25 | def page_end ; '
'; end 26 | def style_with_flex_basis(x); "" ; end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/markup_templates/base.rb: -------------------------------------------------------------------------------- 1 | module HotGlue 2 | class TemplateBase 3 | def initialize(layout_strategy: ) 4 | @layout_strategy = layout_strategy 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/nav_template_generator.rb: -------------------------------------------------------------------------------- 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 | 23 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/set_search_interface_install_generator.rb: -------------------------------------------------------------------------------- 1 | 2 | 3 | module HotGlue 4 | class SetSearchInterfaceInstallGenerator < Rails::Generators::Base 5 | source_root File.expand_path('templates', __dir__) 6 | 7 | def filepath_prefix 8 | # todo: inject the context 9 | 'spec/dummy/' if $INTERNAL_SPECS 10 | end 11 | 12 | def initialize(*args) #:nodoc: 13 | super 14 | 15 | ['date_range_picker','time_range_picker','search_form'].each do |file| 16 | system("./bin/rails generate stimulus #{file.titlecase.gsub(" ", "")}") 17 | copy_file "javascript/#{file}_controller.js", "#{filepath_prefix}app/javascript/controllers/#{file}.js" 18 | puts "HOT GLUE --> copying #{file} stimulus controller into app/javascript/controllers/#{file}.js" 19 | 20 | end 21 | end 22 | end 23 | end 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/base_controller.rb.erb: -------------------------------------------------------------------------------- 1 | class <%= controller_descends_from %> < ApplicationController 2 | end 3 | 4 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/capybara_login.rb: -------------------------------------------------------------------------------- 1 | def login_as(user) 2 | visit '/users/sign_in' 3 | within("#new_user") do 4 | fill_in 'Email', with: user.email 5 | fill_in 'Password', with: 'password' 6 | end 7 | click_button 'Log in' 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/computer_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/lib/generators/hot_glue/templates/computer_code.jpg -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_edit.erb: -------------------------------------------------------------------------------- 1 | <\%= turbo_frame_tag "<%= @namespace %>__#{dom_id(<%= singular %>)}" do %> 2 |
3 | <\% if <%= singular %>.errors.any? %> 4 | <\%= render(partial: "<%= namespace_with_trailing_dash %>errors", locals: {resource: <%= singular %> }) %> 5 | <\% end %> 6 |

Editing <%= singular + " " if @include_object_names %><\%= <%= singular %>.<%= display_class %> %>

7 | <\%= form_with model: <%= singular %>, 8 | <% if @stimmify %> data: {controller: '<%= @stimmify %>' }, 9 | <% end %>url: <%= form_path_edit_helper %><%= ", html: {'data-turbo': false}" if @big_edit %> do |f| %> 10 | <\%= render partial: "<%= namespace_with_trailing_dash + @controller_build_folder + "/" %>form", locals: {:<%= singular %> => <%= singular %>, f: f}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> \%> 11 | <% if @edit_within_form_partial %><\%= render partial: "edit_within_form", locals: {f: f, <%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %> 12 | <\% end %> 13 | <% if @edit_after_form_partial %><\%= render partial: "edit_after_form", locals: {<%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %> 14 |
15 | <\% end %> 16 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_flash_notices.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_frame_tag "flash_notices" do %> 2 | <% unless notice.nil? && alert.nil? %> 3 |
4 |
<%= notice %>
5 |
<%= alert %>
6 | <% if defined?(resource) && resource %> 7 | 12 | <% end %> 13 | 14 |
15 | <% end %> 16 | <% end %> 17 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_form.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= form_fields_html %> 3 | 4 |
5 | <\%= link_to "Cancel", <%= 6 | @nested_set.none? ? path_helper_plural : edit_parent_path_helper %>, {class: "btn btn-secondary"} %><% if @no_field_form %> 7 | <\%= f.hidden_field "_________" %><% end %> 8 | <\%= f.submit "Save", class: "btn btn-primary pull-right" %> 9 |
10 |
11 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_line.erb: -------------------------------------------------------------------------------- 1 | <% if @turbo_streams %><\%= turbo_stream_from <%= singular %> %> 2 | <% end %><\%= turbo_frame_tag "<%= @namespace %>__#{ dom_id(<%= singular %>) }" do %> 3 |
4 | <\%= render partial: '<%= show_path_partial %>', locals: { <%= singular %>: <%= singular %><% if @nested_set.any? %>, <%= @nested_set.collect{|nest_arg| "#{nest_arg[:singular]}: #{ nest_arg[:singular] }"}.join(", ") %>, nested_for: "<%= @nested_set.collect{|nest_arg| nest_arg[:singular] + "-\#{#{nest_arg[:singular]}.id}"}.join("__") %>"<% end %> } %> 5 |
6 | <\% end %> 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_list.erb: -------------------------------------------------------------------------------- 1 | <\%= turbo_frame_tag "<%= @namespace %>__<%= plural %>-list" <%= nested_for_turbo_id_list_constructor %> do %> 2 | 3 |
4 | <% unless @no_list || @no_list_label || (@nested_set.any? && !@nested_set.collect{|x| x[:optional]}.any?) %> 5 | <% unless list_label.nil? %>

6 | <%= list_label %> 7 |

<% end %> 8 | <% end %> 9 | 10 | <% if @new_button_position == 'above' %> 11 | <% unless @no_create %><%= '<%= render partial: "' + ((@namespace+"/" if @namespace) || "") + @controller_build_folder + '/new_button", locals: {}' + @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}} : {})"}.join() + ' %\>'.gsub('\\',"") %>
<% end %> 12 | <% end %> 13 | 14 | <% unless @no_list %> 15 | <% unless @no_list_heading %> 16 |
17 | <%= list_column_headings %> 18 | 19 | <% if @downnest_object.any? && !@big_edit %> 20 | <% if !@stacked_downnesting %> 21 | <%= @layout_strategy.downnest_column_style %> 22 | <% @downnest_object.each do |downnest,i| %> 23 |
24 | 25 | <%= downnest.titleize %> 26 | 27 |
28 | <% end %> 29 | <% else %> 30 |
31 | <%= @layout_strategy.downnest_column_style %> 32 | <% @downnest_object.each do |downnest,i| %> 33 | 34 | <%= downnest.titleize %> 35 | 36 | <% end %> 37 |
38 | <% end %> 39 | <% end %> 40 | 41 | <% if @list_after_each_row_heading_partial %><\%= render partial: "list_after_each_row_heading" %><% end %> 42 | 43 |
> 44 |
45 |
46 | <% end %> 47 | <% if @search %> 48 | <%= @layout_strategy.search_opening %> 49 | <%= @template_builder.search_input_area %> 50 | <%= @layout_strategy.search_closing %> 51 | <% end %> 52 | <\% if <%= plural %>.empty? %> 53 |
54 | None 55 |
56 | <\% end %> 57 | <\% <%= plural %>.each do |<%= singular %>| %> 58 | <\%= render partial: '<%= line_path_partial %>', locals: {<%= singular %>: <%= singular %>} 59 | .merge(defined?(nested_for) ? {nested_for: nested_for} : {}) 60 | <%= @nested_set.collect{|arg| " .merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}} : {})"}.join("\n") %> 61 | %> 62 | <\% end %> 63 | <% if @paginate_per_page_selector %> 64 | <\%= form_with url: '<%= path_helper_plural(top_level: false) %>', method: :get do |f| %> 65 | Show per page 66 | <\%= f.collection_select "per", [10, 25, 100], :to_s, :to_s, {prompt: true, selected: params[:per]}, {onChange: "this.form.submit();"} %> 67 | <\% end %> 68 | <% end %> 69 | <%= @no_paginate ? "" : paginate %> 70 | <% end %> 71 | 72 | 73 | <% if @new_button_position == 'below' %> 74 | <% unless @no_create %><%= '<%= render partial: "' + ((@namespace+"/" if @namespace) || "") + @controller_build_folder + '/new_button", locals: {}' + @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}} : {})"}.join() + ' %\>'.gsub('\\',"") %>
<% end %> 75 | <% end %> 76 |
77 | <\% end %> 78 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_nav.html.erb: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_new_button.erb: -------------------------------------------------------------------------------- 1 | <\%= turbo_frame_tag "<%= @namespace %>__<%= singular %>-new" do %> 2 |
3 | <\%= link_to "<%= @new_button_label %>", <%= new_path_name %>, disable_with: "Loading...", class: "new-<%= singular %>-button btn btn-primary pull-right <%= 'btn-sm' if @nested_set.any? %> " %> 4 |
5 | <\% end %> 6 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_new_form.erb: -------------------------------------------------------------------------------- 1 | <\%= turbo_frame_tag "<%= @namespace %>__<%= singular %>-new" do %> 2 |

3 | <%= @new_form_heading %> 4 |

5 | <\%= form_with model: <%= singular %>, 6 | <% if @stimmify %>data: {controller: '<%= @stimmify %>' }, 7 | <% end %>url: <%= form_path_new_helper %>, method: :post<%= @display_edit_after_create ? ", html: {'data-turbo': false}" : "" %> do |f| \%> 8 | <\%= render partial: "<%= namespace_with_slash + @controller_build_folder %>/form", 9 | locals: { <%= singular %>: <%= singular %>, f: f}<%= @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}}: {})" }.join %> \%> 10 | 11 | <% if @new_within_form_partial %><\%= render partial: "new_within_form", locals: {f: f, <%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %> 12 | <\% end %> 13 | <% if @new_after_form_partial %><\%= render partial: "new_after_form", locals: {<%= singular %>: <%= singular %>}<%= @nested_set.collect{|arg| ".merge(#{arg[:singular]} ? {#{arg[:singular]}: #{arg[:singular]}} : {})" }.join %> %><% end %> 14 | <\% end %> 15 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/_show.erb: -------------------------------------------------------------------------------- 1 | <%= all_line_fields %> 2 | 3 | 4 | 5 | <% if @downnest_children.any? && ! @big_edit %> 6 | <% each_downnest_width = @downnest_children.count == 1 ? 33 : (53/@downnest_children.count).floor %> 7 | <% if @stacked_downnesting %>
<% end %> 8 | <% @downnest_object.each do |downnest, size| %> 9 | <% downnest_object = eval("#{singular_class}.reflect_on_association(:#{downnest})") %> 10 | <% if downnest_object.nil?; raise "no relationship for downnested portal `#{downnest}` found on `#{singular_class}`; please check relationship for has_many :#{downnest}"; end; %> 11 | <% downnest_class = downnest_object.class_name %> 12 | <% downnest_object_name = eval("#{downnest_class}.table_name") %> 13 | <% downnest_style = @layout_strategy.downnest_style %> 14 | <% if !@stacked_downnesting %>
><% end %> 15 | <% if @downnest_shows_headings %> 16 |

17 | <%= downnest_object_name.pluralize.humanize %> 18 |

19 | <% end %> 20 | <\%= render partial: "<%= namespace_with_trailing_dash %><%= downnest_object_name %>/list", locals: { 21 | <%= @singular %>: <%= @singular %>, 22 | <%= downnest_object_name %>: <%= @singular %>.<%= downnest %> 23 | } 24 | .merge({nested_for: "<% if @nested_set.any? %>#{nested_for + "__" if defined?(nested_for)}<% end %><%= @singular %>-#{<%= @singular %>.id}"}) 25 | <%= @nested_set.collect{|arg| ".merge(defined?(#{arg[:singular]}) ? {#{arg[:singular]}: #{arg[:singular]}} : {} )"}.join("\n") %> 26 | \%> 27 | <% if !@stacked_downnesting %>
<% end %> 28 | <% end %> 29 | <% if @stacked_downnesting %>
<% end %> 30 | <% end %> 31 | 32 | 33 | <% if @list_after_each_row_partial %><\%= render partial: "<%= namespace_with_trailing_dash %><%= @controller_build_folder %>/list_after_each_row", locals: {<%= @singular %>: <%= @singular %><% if @nested_set.any? %>, <%= @nested_set.collect{|x| x[:singular] + ": " + x[:singular] }.join(", ") %><% end %>} %> 34 | <% end %> 35 | 36 | <%= @layout_strategy.button_style %> 37 |
> 38 | <%= magic_button_output %> 39 | 40 | <% if destroy_action %> 41 | <\%= form_with url: <%= delete_path_helper %>, html: {data: {'<%= @ujs_syntax ? 'confirm' : 'turbo-confirm' %>': "Are you sure you want to delete #{ <%= @singular + "." + display_class %> }?" }, style: "display: inline-block;"}, method: :delete do |f| %> 42 | <\%= f.submit "Delete".html_safe, class: "delete-<%= singular %>-button btn btn-primary btn-sm" %> 43 | <\% end %> 44 | <% end %> 45 | 46 | <% unless @no_edit %> 47 | <\%= link_to "Edit <% if @button_icons == 'font-awesome' %><% end %>".html_safe, <%= edit_path_helper %>, <% if @big_edit %>'data-turbo' => 'false', <% end %>disable_with: "Loading...", class: "edit-<%= singular %>-button btn btn-primary btn-sm" %> 48 | <% end %> 49 |
50 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/create.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <\% if @<%= singular %>.errors.none? %> 2 | <\%= turbo_stream.replace "<%= @namespace %>__<%= plural %>-list" + <%= nested_for_turbo_nested_constructor %> do %> 3 | <\%= render partial: "list", locals: {<%= plural %>: @<%= plural %><% if @nested_set.any? %>, <%= @nested_set.collect{|arg| "#{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %>, nested_for: '<%= @nested_set.collect{|arg| "\"#{arg[:singular]}-\#{@#{arg[:singular]}.id}\""}.join("__") %>' <% end %> } %> 4 | 5 | <\% end %> 6 | <\% end %> 7 | 8 | <%= turbo_parental_updates %> 9 | 10 | <\%= turbo_stream.replace "<%= @namespace %>__<%= singular %>-new" do %> 11 | <\% if @<%= singular %>.errors.none? %> 12 | <\%= render partial: "new_button", locals: {}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {" + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> %> 13 | <\% else %> 14 | <\%= render partial: "new_form", locals: {<%= singular %>: @<%= singular %>}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {" + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> %> 15 | <\% end %> 16 | <\% end %> 17 | <\%= turbo_stream.replace "flash_notices" do %> 18 | <\%= render partial: "layouts/flash_notices", locals: {resource: @<%= singular %>} %> 19 | <\% end %> 20 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/destroy.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <%= turbo_parental_updates %> 2 | 3 | <\%= turbo_stream.update "<%= @namespace %>__<%= plural %>-list" + <%= nested_for_turbo_nested_constructor %> do %> 4 | <\%= render partial: "list", locals: {<%=plural%>: @<%=plural%> }<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {nested_for: \"" + arg[:singular] + "-\#{@" + arg[:singular] + ".id}\"" + ", " + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> \%> 5 | <\% end %> 6 | 7 | <\%= turbo_stream.update "flash_notices" do %> 8 | <\%= render partial: "layouts/flash_notices", locals: {resource: @<%= singular %>} %> 9 | <\% end %> -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/edit.erb: -------------------------------------------------------------------------------- 1 | <% if @big_edit %> 2 |
3 |
4 |
5 | <\%= link_to "<% if @button_icons == 'font-awesome' %><% end %> Back to list".html_safe, <%= path_helper_plural(true) %> %> 6 | <% end %> 7 | <\%= render partial: "edit", locals: {<%= singular %>: @<%= singular %><%= @nested_set.any? ? ", " + (@nested_set.collect{|x| "#{x[:singular]}: @#{x[:singular]}"}.join(", ") ) : "" %>} %> 8 | <% if @big_edit %> 9 |
10 |
11 |
12 | 13 | 14 | 15 | <% if @downnest_children.any? && @big_edit %> 16 |
17 | 27 | 28 |
29 | <% @downnest_object.each_with_index do |data, index| %> 30 | <% downnest = data[0] %> 31 |
" id="<%= downnest %>-portal" role="tabpanel" aria-labelledby="<%= downnest %>-tab"> 32 | <% downnest_object = eval("#{singular_class}.reflect_on_association(:#{downnest})") %> 33 | <% if downnest_object.nil?; raise "no relationship for downnested portal `#{downnest}` found on `#{singular_class}`; please check relationship for has_many :#{downnest}"; end; %> 34 | <% downnest_class = downnest_object.class_name %> 35 | <% downnest_object_name = eval("#{downnest_class}.table_name") %> 36 | <% downnest_style = @layout_strategy.downnest_style %> 37 | 38 | <\%= render partial: "<%= namespace_with_trailing_dash %><%= downnest_object_name %>/list", locals: {<%= @singular %>: @<%= @singular %>, <%= downnest_object_name %>: @<%= @singular %>.<%= downnest %><% if @nested_set.any? %>, <%= @nested_set.collect{|x| "#{x[:singular]}: @#{x[:singular]}"}.join(", ") %>, nested_for: "<%= @nested_set.collect{|x| "#{x[:singular]}-" + "\#{" + "@#{x[:singular]}.id}"}.join("__") %>__<%= singular %>-#{@<%= @singular %>.id}" <% end %> } \%> 39 |
40 | <% end %> 41 |
42 | 43 |
44 | <% end %> 45 | 46 | 47 | <% end %> 48 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/edit.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <\%= turbo_stream.replace "<%= @namespace %>__#{dom_id(@<%= singular %>)}" do %> 2 | <\%= render 'edit', locals: { <%= singular %>: @<%= singular %><%= @nested_set.collect{|arg| ", #{arg[:singular]}: @#{arg[:singular]} " }.join("") %> } %> 3 | <\% end %> 4 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/index.erb: -------------------------------------------------------------------------------- 1 | <% if @menu_file_exists %><\%= render partial: "<%= namespace_with_trailing_dash %>menu", locals: {active: '<%= plural %>'} %><% end %> 2 | 3 |
4 | <% if include_nav_template %><%= @layout_strategy.page_begin %><\%= render partial: "<%= nav_template %>", locals: {nav: "<%= @plural %>"} %><%= @layout_strategy.page_end %><% end %> 5 | <% if @index_before_list_partial %><\%= render partial: "index_before_list" %><% end %> 6 | 7 | <%= @layout_strategy.page_begin %> 8 | <\%= render partial: '<%= list_path_partial %>', 9 | locals: {<%= plural %>: @<%= plural %>}<%= @nested_set.collect{|arg| ".merge(@" + arg[:singular] + " ? {nested_for: \"" + arg[:singular] + "-\#{@" + arg[:singular] + ".id}\"" + ", " + arg[:singular] + ": @" + arg[:singular] + "} : {})"}.join() %> \%> 10 | <%= @layout_strategy.page_end %> 11 |
12 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/new.erb: -------------------------------------------------------------------------------- 1 | <\%= render partial: "new_form", locals: {<%= singular %>: @<%=singular%>}<%= @nested_set.collect{|arg| ".merge(@#{arg[:singular]} ? {#{arg[:singular]}: @#{arg[:singular]}} : {})" }.join %> %> -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/erb/update.turbo_stream.erb: -------------------------------------------------------------------------------- 1 | <% if !@display_list_after_update %><\%= turbo_stream.replace "<%= @namespace %>__#{dom_id(@<%= singular %>)}" do %> 2 | <\%= render partial: 'line', locals: {<%= singular %>: @<%= singular %>, <%= @nested_set.collect{|arg| " #{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %> } \%> 3 | <\% end %><% else %><\%= turbo_stream.replace "<%= plural %>-list" do %> 4 | <\%= render partial: '<%= list_path_partial %>', locals: {<%= plural %>: @<%= plural %><%= @nested_set.collect{|arg| " #{arg[:singular]}: @#{arg[:singular]}"}.join(", ") %> 5 | \%> 6 | <\% end %> 7 | <% end %> 8 | 9 | <%= turbo_parental_updates %> 10 | 11 | <\%= turbo_stream.update "flash_notices" do %> 12 | <\%= render partial: "layouts/flash_notices", locals: {resource: @<%= singular %>} %> 13 | <\% end %> 14 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/javascript/date_range_picker_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | // Connects to data-controller="date-range-picker" 4 | export default class extends Controller { 5 | 6 | static targets = [ "matchSelector", "start", "end" ]; 7 | connect() { 8 | 9 | console.log("date_range_picker_controller.js connect.....") 10 | } 11 | 12 | matchSelection(event) { 13 | 14 | console.log("date_range_picker_controller.js matchSelection.....") 15 | console.log(event.target.value) 16 | // this.matchSelectorTarget.value = event.target.value 17 | 18 | switch(event.target.value) { 19 | case "is_on": 20 | this.startTarget.disabled = false 21 | this.endTarget.disabled = true 22 | this.endTarget.value = "" 23 | break; 24 | case "is_between": 25 | this.startTarget.disabled = false 26 | this.endTarget.disabled = false 27 | break; 28 | case "is_on_or_after": 29 | this.startTarget.disabled = false 30 | this.endTarget.disabled = true 31 | this.endTarget.value = "" 32 | break; 33 | case "is_before_or_on": 34 | this.startTarget.disabled = true 35 | this.startTarget.value = "" 36 | this.endTarget.disabled = false 37 | break; 38 | case "not_on": 39 | this.startTarget.disabled = false 40 | this.endTarget.disabled = true 41 | this.endTarget.value = "" 42 | break; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/javascript/search_form_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | // Connects to data-controller="search_form" 4 | export default class extends Controller { 5 | static targets = ["clearButton"]; 6 | 7 | connect() { 8 | 9 | if (this.hasClearButtonTarget) { 10 | this.clearButtonTarget.addEventListener("click", (event) => { 11 | event.preventDefault() 12 | this.element.reset() 13 | }) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/javascript/time_range_picker_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | // Connects to data-controller="time-range-picker" 4 | export default class extends Controller { 5 | static targets = [ "matchSelector", "start", "end" ]; 6 | connect() { 7 | console.log("time_range_picker_controller.js connect.....") 8 | } 9 | 10 | matchSelection(event) { 11 | 12 | console.log("time_range_picker_controller.js matchSelection.....") 13 | console.log(event.target.value) 14 | // this.matchSelectorTarget.value = event.target.value 15 | 16 | switch(event.target.value) { 17 | case "is_at": 18 | this.startTarget.disabled = false 19 | this.endTarget.disabled = true 20 | this.endTarget.value = "" 21 | break; 22 | case "is_between": 23 | this.startTarget.disabled = false 24 | this.endTarget.disabled = false 25 | break; 26 | case "is_at_or_after": 27 | this.startTarget.disabled = false 28 | this.endTarget.disabled = true 29 | this.endTarget.value = "" 30 | break; 31 | case "is_before_or_at": 32 | this.startTarget.disabled = true 33 | this.startTarget.value = "" 34 | this.endTarget.disabled = false 35 | break; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/system_spec.rb.erb: -------------------------------------------------------------------------------- 1 | <% item1_addOns = "" 2 | if (eval(@singular_class).instance_methods.include?(display_class.to_s)) 3 | item1_addOns << "#{display_class}: FFaker::Name.name" 4 | end 5 | item1_addOns << ", \n " + @columns_map.collect{|col, col_object| 6 | col_object.spec_setup_let_arg 7 | }.compact.join(", \n ") 8 | %>require 'rails_helper' 9 | 10 | describe 'interaction for <%= controller_class_name %>' do 11 | include HotGlue::ControllerHelper 12 | include ActionView::RecordIdentifier 13 | 14 | <%= @existing_content %> 15 | <% unless @god %>let(:<%= @auth %>) {create(:<%= @auth.gsub('current_', '') %>)}<% end %> 16 | <%= spec_related_column_lets %> 17 | <% unless @self_auth %> let!(:<%= singular %>1) { 18 | <%= singular %> = create(:<%= singular %><%= object_parent_mapping_as_argument_for_specs %> <%= item1_addOns %> ) 19 | <%= @attachments.collect{ |attachment| 20 | " #{singular}.#{ attachment[0].to_s }.attach( 21 | io: File.open('spec/fixtures/glass_button.png'), 22 | filename: 'glass_button.png', 23 | content_type: 'image/png')" 24 | }.join("\n") 25 | %> 26 | <%=singular%>.save! 27 | <%=singular%> 28 | }<% end %> 29 | <%= objest_nest_factory_setup %> <% unless @god || (@existing_content && @existing_content.include?("login_as")) %> 30 | before do 31 | login_as(<%= @auth %>) 32 | end <% end %> <% if any_datetime_fields? %> 33 | let(:testing_timezone) { 34 | Rails.application.config.time_zone 35 | }<% end %> 36 | describe "index" do 37 | it "should show me the list" do 38 | visit <%= path_helper_plural %> 39 | <%= @columns_map.collect{|col, col_object| col_object.spec_list_view_assertion }.join("\n ") %> 40 | end 41 | end 42 | 43 | <% unless @no_create %> describe "new & create" do 44 | it "should create a new <%= singular.titlecase %>" do 45 | visit <%= path_helper_plural %> 46 | click_link "<%= @new_button_label %>" 47 | expect(page).to have_selector(:xpath, './/h3[contains(., "<%= @new_button_label %>")]') 48 | <%= capybara_make_updates(:create) %> 49 | click_button "Save" 50 | expect(page).to have_content("Successfully created") 51 | 52 | <%=" " + @columns_map.map{ |col, col_object| 53 | if @attachments.keys.collect(&:to_sym).include?(col) 54 | elsif @show_only.include?(col) 55 | else 56 | col_object.spec_make_assertion 57 | end 58 | }.compact.join("\n ") 59 | %> 60 | end 61 | end<% end %> 62 | 63 | <% unless @no_edit %> 64 | describe "edit & update" do 65 | it "should return an editable form" do 66 | visit <%= path_helper_plural %> 67 | find("a.edit-<%= singular %>-button[href='/<%= namespace_with_slash %><%= plural %>/#{<%= factory_testing_name %>.id}/edit']").click 68 | 69 | expect(page).to have_content("Editing #{<%= factory_testing_name %>.<%= @display_class %>.squish || "(no name)"}") 70 | <%= capybara_make_updates(:update) %> 71 | click_button "Save" 72 | within("turbo-frame#<%= @namespace %>__#{dom_id(<%= factory_testing_name %>)} ") do 73 | <%= " " + @columns_map.map{ |col, col_object| 74 | if @attachments.keys.collect(&:to_sym).include?(col) 75 | elsif @show_only.include?(col) 76 | else 77 | col_object.spec_make_assertion 78 | end 79 | }.compact.join("\n ") 80 | %> 81 | end 82 | end 83 | end <% end %><% if destroy_action %> 84 | 85 | describe "destroy" do 86 | it "should destroy" do 87 | visit <%= path_helper_plural %> 88 | accept_alert do 89 | find("form[action='<%= namespace_with_dash %>/<%= nested_path %><%= plural %>/#{<%= singular %>1.id}'] > input.delete-<%= singular %>-button").click 90 | end 91 | expect(page).to_not have_content(<%= singular %>1.<%= @display_class %>) 92 | expect(<%= singular_class %>.where(id: <%= singular %>1.id).count).to eq(0) 93 | end 94 | end<% end %> 95 | end 96 | 97 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/themes/hotglue_scaffold_dark_knight.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 3 | background-color: black; 4 | color: white; 5 | 6 | .alert.alert-danger { 7 | padding: 1em; 8 | border: solid 1px crimson; 9 | background-color: #ffd9d9; 10 | color: black; 11 | font-weight: bold; 12 | } 13 | } 14 | 15 | .scaffold-container { 16 | 17 | 18 | 19 | 20 | .cell input { 21 | -webkit-writing-mode: horizontal-tb !important; 22 | text-rendering: auto; 23 | letter-spacing: normal; 24 | word-spacing: normal; 25 | line-height: normal; 26 | text-transform: none; 27 | text-indent: 0px; 28 | text-shadow: none; 29 | display: inline-block; 30 | text-align: start; 31 | appearance: auto; 32 | -webkit-rtl-ordering: logical; 33 | margin: 0em; 34 | border-image: initial; 35 | 36 | display: block; 37 | padding: 0.375rem 0.75rem; 38 | font-size: 1rem; 39 | font-weight: 400; 40 | line-height: 1.5; 41 | 42 | background-clip: padding-box; 43 | border: 1px solid #ced4da; 44 | -webkit-appearance: none; 45 | -moz-appearance: none; 46 | appearance: none; 47 | border-radius: 0.25rem; 48 | transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 49 | color: #212529; 50 | background-clip: padding-box; 51 | border: 1px solid #ced4da; 52 | } 53 | 54 | .scaffold-row { 55 | .scaffold-col-heading { 56 | } 57 | .scaffold-col, .scaffold-col-heading { 58 | 59 | } 60 | 61 | .scaffold-cell { 62 | } 63 | 64 | .scaffold-list { 65 | flex: 0 0 auto; 66 | width: 100%; 67 | } 68 | } 69 | 70 | .container-fluid, .scaffold-container { 71 | width: 100%; 72 | 73 | margin-right: auto; 74 | margin-left: auto; 75 | } 76 | 77 | .row { 78 | display: flex; 79 | --bs-gutter-x: 1.5rem; 80 | --bs-gutter-y: 0; 81 | display: flex; 82 | flex-wrap: wrap; 83 | 84 | margin-top: calc(-1 * var(--bs-gutter-y)); 85 | } 86 | 87 | .btn-primary { 88 | color: #212529; 89 | 90 | background-color: #4D889E; 91 | &:hover { 92 | color: #fff; 93 | border-color: #4D889E; 94 | } 95 | } 96 | 97 | .btn-secondary { 98 | color: white; 99 | 100 | background-color: darkgrey; 101 | &:hover { 102 | color: lightgrey; 103 | border-color: black; 104 | } 105 | } 106 | 107 | .scaffold-list { 108 | input, .btn { 109 | font-size: 0.8rem; 110 | font-weight: 400; 111 | line-height: 1.4; 112 | } 113 | 114 | 115 | .scaffold-list { 116 | input, .btn { 117 | font-size: 0.8rem; 118 | line-height: 1.2; 119 | } 120 | 121 | 122 | 123 | .scaffold-list { 124 | input, .btn { 125 | font-size: 0.55rem; 126 | line-height: 1.0; 127 | } 128 | } 129 | } 130 | } 131 | 132 | 133 | .btn { 134 | display: inline-block; 135 | font-weight: 400; 136 | line-height: 1.5; 137 | color: #212529; 138 | text-align: center; 139 | text-decoration: none; 140 | vertical-align: middle; 141 | cursor: pointer; 142 | -webkit-user-select: none; 143 | -moz-user-select: none; 144 | -ms-user-select: none; 145 | user-select: none; 146 | border: 1px solid transparent; 147 | padding: 0.375rem 0.75rem; 148 | font-size: 1rem; 149 | border-radius: 6px; 150 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 151 | 152 | box-shadow: 0px 0px 12px 2px whitesmoke; 153 | margin-right: 0.6em; 154 | } 155 | 156 | 157 | 158 | } 159 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/themes/hotglue_scaffold_like_bootstrap.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 3 | background-color: white; 4 | color: darkslategrey; 5 | 6 | .alert.alert-danger { 7 | padding: 1em; 8 | border: 1px solid transparent; 9 | color: #721c24; 10 | background-color: #f8d7da; 11 | border-color: #f5c6cb; 12 | } 13 | } 14 | 15 | 16 | .scaffold-container { 17 | .cell input { 18 | -webkit-writing-mode: horizontal-tb !important; 19 | text-rendering: auto; 20 | letter-spacing: normal; 21 | word-spacing: normal; 22 | line-height: normal; 23 | text-transform: none; 24 | text-indent: 0px; 25 | text-shadow: none; 26 | display: inline-block; 27 | text-align: start; 28 | appearance: auto; 29 | -webkit-rtl-ordering: logical; 30 | margin: 0em; 31 | border-image: initial; 32 | 33 | display: block; 34 | padding: 0.375rem 0.75rem; 35 | font-size: 1rem; 36 | font-weight: 400; 37 | line-height: 1.5; 38 | 39 | background-color: #fff; 40 | background-clip: padding-box; 41 | border: 1px solid #ced4da; 42 | -webkit-appearance: none; 43 | -moz-appearance: none; 44 | appearance: none; 45 | border-radius: 0.25rem; 46 | transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 47 | color: #212529; 48 | background-color: #fff; 49 | background-clip: padding-box; 50 | border: 1px solid #ced4da; 51 | } 52 | 53 | .scaffold-row { 54 | .scaffold-col-heading { 55 | } 56 | .scaffold-col, .scaffold-col-heading { 57 | 58 | } 59 | 60 | .scaffold-cell { 61 | } 62 | 63 | .scaffold-list { 64 | flex: 0 0 auto; 65 | width: 100%; 66 | } 67 | } 68 | 69 | .container-fluid, .scaffold-container { 70 | width: 100%; 71 | 72 | margin-right: auto; 73 | margin-left: auto; 74 | } 75 | 76 | .row { 77 | display: flex; 78 | --bs-gutter-x: 1.5rem; 79 | --bs-gutter-y: 0; 80 | display: flex; 81 | flex-wrap: wrap; 82 | 83 | margin-top: calc(-1 * var(--bs-gutter-y)); 84 | } 85 | 86 | .btn-primary { 87 | color: #212529; 88 | 89 | background-color: #0d6efd; 90 | &:hover { 91 | color: #fff; 92 | border-color: #0a58ca; 93 | } 94 | } 95 | 96 | .btn-secondary { 97 | color: white; 98 | 99 | background-color: darkgrey; 100 | &:hover { 101 | color: lightgrey; 102 | border-color: black; 103 | } 104 | } 105 | 106 | .scaffold-list { 107 | input, .btn { 108 | font-size: 0.8rem; 109 | font-weight: 400; 110 | line-height: 1.4; 111 | } 112 | 113 | 114 | .scaffold-list { 115 | input, .btn { 116 | font-size: 0.8rem; 117 | line-height: 1.2; 118 | } 119 | 120 | 121 | 122 | .scaffold-list { 123 | input, .btn { 124 | font-size: 0.55rem; 125 | line-height: 1.0; 126 | } 127 | } 128 | } 129 | } 130 | 131 | 132 | .btn { 133 | display: inline-block; 134 | font-weight: 400; 135 | line-height: 1.5; 136 | color: #212529; 137 | text-align: center; 138 | text-decoration: none; 139 | vertical-align: middle; 140 | cursor: pointer; 141 | -webkit-user-select: none; 142 | -moz-user-select: none; 143 | -ms-user-select: none; 144 | user-select: none; 145 | border: 1px solid transparent; 146 | padding: 0.375rem 0.75rem; 147 | font-size: 1rem; 148 | border-radius: 0.25rem; 149 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 150 | } 151 | 152 | 153 | 154 | } 155 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/themes/hotglue_scaffold_like_los_gatos.scss: -------------------------------------------------------------------------------- 1 | .scaffold-container { 2 | -webkit-font-smoothing: antialiased; 3 | font-family: 'Netflix Sans','Helvetica Neue',Helvetica,Arial,sans-serif; 4 | color: #737373; 5 | 6 | font-size: .875rem; 7 | letter-spacing: .2px; 8 | input[type='submit'] { 9 | background-color: rgba(255,255,255,0); 10 | border: none; 11 | } 12 | 13 | 14 | 15 | 16 | .scaffold-cell { 17 | textarea { 18 | 19 | -webkit-box-sizing: border-box; 20 | box-sizing: border-box; 21 | margin: 1px!important; 22 | padding: 3px 1px 3px 3px; 23 | -webkit-transition: none; 24 | transition: none; 25 | } 26 | 27 | input { 28 | -webkit-writing-mode: horizontal-tb !important; 29 | text-rendering: auto; 30 | letter-spacing: normal; 31 | word-spacing: normal; 32 | line-height: normal; 33 | text-transform: none; 34 | text-indent: 0px; 35 | text-shadow: none; 36 | display: inline-block; 37 | text-align: start; 38 | appearance: auto; 39 | -webkit-rtl-ordering: logical; 40 | margin: 0em; 41 | border-image: initial; 42 | 43 | display: block; 44 | padding: 0.375rem 0.75rem; 45 | font-size: 1rem; 46 | font-weight: 400; 47 | line-height: 1.5; 48 | 49 | background-color: #fff; 50 | background-clip: padding-box; 51 | -webkit-appearance: none; 52 | -moz-appearance: none; 53 | appearance: none; 54 | border: 1px solid #ced4da; 55 | border-radius: 1px; 56 | 57 | transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 58 | color: #212529; 59 | background-color: #fff; 60 | background-clip: padding-box; 61 | 62 | &[type=submit] { 63 | border: none; 64 | border-radius: 0; 65 | } 66 | } 67 | 68 | input, textarea, select { 69 | &:hover { 70 | cursor: pointer; 71 | } 72 | background-color: #fff; 73 | color: #222; 74 | 75 | border: solid 1px #828282; 76 | border-radius: 2px; 77 | 78 | 79 | outline: none !important; 80 | 81 | box-shadow: none; 82 | 83 | &:focus, &:focus-visible { 84 | 85 | outline: none !important; 86 | } 87 | } 88 | } 89 | 90 | 91 | .scaffold-row { 92 | .scaffold-col-heading { 93 | } 94 | .scaffold-col, .scaffold-col-heading { 95 | 96 | } 97 | 98 | .scaffold-cell { 99 | } 100 | 101 | .scaffold-list { 102 | flex: 0 0 auto; 103 | width: 100%; 104 | } 105 | } 106 | 107 | .container-fluid, .scaffold-container { 108 | width: 100%; 109 | 110 | margin-right: auto; 111 | margin-left: auto; 112 | } 113 | 114 | .row { 115 | display: flex; 116 | --bs-gutter-x: 1.5rem; 117 | --bs-gutter-y: 0; 118 | flex-wrap: wrap; 119 | margin-top: calc(-1 * var(--bs-gutter-y)); 120 | } 121 | 122 | .btn-primary { 123 | color: white; 124 | background-color: #e50914; 125 | border-radius: 2px; 126 | &:hover { 127 | color: darkslategrey; 128 | } 129 | } 130 | 131 | .btn-secondary { 132 | color: darkslategrey; 133 | 134 | background-color: white; 135 | &:hover { 136 | color: lightgrey; 137 | } 138 | } 139 | 140 | .scaffold-list { 141 | input, .btn { 142 | font-size: 0.8rem; 143 | font-weight: 400; 144 | line-height: 1.4; 145 | } 146 | 147 | 148 | .scaffold-list { 149 | input, .btn { 150 | font-size: 0.8rem; 151 | line-height: 1.2; 152 | } 153 | 154 | 155 | 156 | .scaffold-list { 157 | input, .btn { 158 | font-size: 0.55rem; 159 | line-height: 1.0; 160 | } 161 | } 162 | } 163 | } 164 | 165 | .btn { 166 | display: inline-block; 167 | font-weight: 400; 168 | line-height: 1.5; 169 | color: #212529; 170 | text-align: center; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | cursor: pointer; 174 | -webkit-user-select: none; 175 | -moz-user-select: none; 176 | -ms-user-select: none; 177 | user-select: none; 178 | padding: 0.375rem 0.75rem; 179 | font-size: 1rem; 180 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/typeahead_controller.rb.erb: -------------------------------------------------------------------------------- 1 | class <%= ((@namespace.titleize.gsub(" ", "") + "::" if @namespace) || "") + @plural.titleize.gsub(" ", "") + "TypeaheadController" %> < <%= controller_descends_from %> 2 | # regenerate this controller with 3 | <% if defined?(RuboCop) %># rubocop:disable Layout/LineLength 4 | <% end %># <%= regenerate_me_code %><% if defined?(RuboCop) %> 5 | # rubocop:enable Layout/LineLength <% end %> 6 | 7 | 8 | <% @nested_set.each do |nest| %>before_action :<%= nest[:singular] %> 9 | def <%= nest[:singular] %> 10 | @<%= nest[:singular] %> ||= current_user.accounts.find(params[:account_id]) 11 | end 12 | <% end %> 13 | 14 | <% nest_chain = [] %> 15 | <% @nested_set.each { |arg| 16 | if @auth_identifier == arg[:singular] 17 | this_scope = auth_object 18 | elsif nest_chain.empty? 19 | this_scope = "#{@auth ? @auth : class_name}.#{arg[:plural]}" 20 | else 21 | this_scope = "#{nest_chain.last}.#{arg[:plural]}" 22 | end 23 | nest_chain << arg 24 | }%> 25 | 26 | def index 27 | <% if @pundit %>authorize <%= @class_name %>, :typeahead? <% end %> 28 | query = params[:query] 29 | @typeahead_identifier = params[:typeahead_identifier] 30 | 31 | 32 | <% if @nested_set.none? %> 33 | @<%= @plural %> = <%= @singular.titleize.gsub(" ", "") %>.where("<%= @search_by.collect{|search| "LOWER(#{search}) LIKE ?" }.join(" OR ") %>", <%= @search_by.collect{|search| "\"%\#{query.downcase}%\"" }.join(", ") %>).limit(10) 34 | <% else %> 35 | <% @nested_set.each do |arg| %> 36 | @<%= @plural %> = <%= @nested_set.last[:singular] %>.<%= @plural %>.where("<%= @search_by.collect{|search| "LOWER(#{search}) LIKE ?" }.join(" OR ") %>", <%= @search_by.collect{|search| "\"%\#{query.downcase}%\"" }.join(", ") %>).limit(10) 37 | <% end %> 38 | <% end %> 39 | render layout: false 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/typeahead_views/_thing.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | 3 | <\%= <%= @singular %>.name %> 4 | 5 |
  • 6 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/typeahead_views/index.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
    6 | 13 |
    14 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/typeahead_views/typeahead.scss: -------------------------------------------------------------------------------- 1 | .typeahead { 2 | 3 | } 4 | 5 | .search__input { 6 | border: 1px solid gray; 7 | border-radius: 2px; 8 | padding: 10px; 9 | font-size: 16px; 10 | box-sizing: border-box; 11 | } 12 | 13 | [data-typeahead-target="results"] { 14 | position: absolute; 15 | } 16 | 17 | .search__results { 18 | border: 1px solid gray; 19 | border-radius: 2px; 20 | border-top: none; 21 | margin: 0; 22 | padding: 0; 23 | list-style-type: none; 24 | top: -1px; 25 | } 26 | 27 | .search__result { 28 | span.search-result-item { 29 | text-decoration: none; 30 | color: black; 31 | padding: 10px; 32 | display: block; 33 | cursor: pointer; 34 | &:hover { 35 | background: #86b7fe; 36 | } 37 | } 38 | } 39 | 40 | .search__result--current, .search__result:hover { 41 | background: #e0e0e0; 42 | } 43 | 44 | .search__result--empty { 45 | padding: 10px; 46 | } 47 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/typeahead_views/typeahead_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | static targets = [ "query", "results", "hiddenFormValue", 5 | "classIdentifier"] 6 | static values = { url: String } 7 | static outlets = [ "typeahead-results" ] 8 | 9 | disconnect() { 10 | this.reset() 11 | } 12 | 13 | fetchResults() { 14 | 15 | 16 | var typeaheadIdentifier = this.classIdentifierTarget.dataset.id 17 | 18 | if(this.query == "") { 19 | this.reset() 20 | return 21 | } 22 | 23 | if(this.query == this.previousQuery) { 24 | return 25 | } 26 | this.previousQuery = this.query 27 | const url = new URL(this.urlValue) 28 | url.searchParams.append("query", this.query) 29 | url.searchParams.append("typeahead_identifier", typeaheadIdentifier) 30 | 31 | this.abortPreviousFetchRequest() 32 | 33 | this.abortController = new AbortController() 34 | 35 | fetch(url, { signal: this.abortController.signal }) 36 | .then(response => response.text()) 37 | .then(html => { 38 | this.resultsTarget.innerHTML = html 39 | }) 40 | .catch(() => {}) 41 | } 42 | 43 | navigateResults(event) { 44 | if(this.hasSearchResultsOutlet) { 45 | this.searchResultsOutlet.navigateResults(event) 46 | } 47 | } 48 | 49 | // private 50 | 51 | reset() { 52 | this.resultsTarget.innerHTML = "" 53 | this.queryTarget.value = "" 54 | this.previousQuery = null 55 | } 56 | 57 | abortPreviousFetchRequest() { 58 | if(this.abortController) { 59 | this.abortController.abort() 60 | } 61 | } 62 | 63 | get query() { 64 | return this.queryTarget.value 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/templates/typeahead_views/typeahead_results_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | const upKey = 38 4 | const downKey = 40 5 | const enterKey = 13 6 | const navigationKeys = [upKey, downKey, enterKey] 7 | 8 | export default class extends Controller { 9 | static classes = [ "current" ] 10 | static targets = [ "result" ] 11 | static outlets = [ "typeahead" ] 12 | 13 | connect() { 14 | this.currentResultIndex = 0 15 | const allElements = this.resultTarget.querySelectorAll(".search-result-item"); 16 | 17 | allElements.forEach((element, index) => { 18 | element.addEventListener("click", () => { 19 | // Call the searchItemClicked member function when the element is clicked 20 | this.searchItemClicked(element, index); 21 | }); 22 | }) 23 | this.selectCurrentResult() 24 | } 25 | 26 | searchItemClicked(element, index) { 27 | const result_value = element.dataset.value; 28 | const result_id = element.dataset.id; 29 | 30 | // how to pass this to the search controller, set the field value and clear out the search 31 | 32 | this.typeaheadOutlets.forEach(outlet => { 33 | outlet.hiddenFormValueTarget.value = result_id; 34 | outlet.queryTarget.value = result_value; 35 | }) 36 | 37 | this.resultTarget.innerHTML = ""; 38 | } 39 | 40 | navigateResults(event) { 41 | if(!navigationKeys.includes(event.keyCode)) { 42 | return 43 | } 44 | 45 | event.preventDefault() 46 | 47 | switch(event.keyCode) { 48 | case downKey: 49 | this.selectNextResult() 50 | break; 51 | case upKey: 52 | this.selectPreviousResult() 53 | break; 54 | case enterKey: 55 | this.goToSelectedResult() 56 | break; 57 | } 58 | } 59 | 60 | // private 61 | 62 | selectCurrentResult() { 63 | this.resultTargets.forEach((element, index) => { 64 | element.classList.toggle(this.currentClass, index == this.currentResultIndex) 65 | }) 66 | } 67 | 68 | selectNextResult() { 69 | if(this.currentResultIndex < this.resultTargets.length - 1) { 70 | this.currentResultIndex++ 71 | this.selectCurrentResult() 72 | } 73 | } 74 | 75 | selectPreviousResult() { 76 | if(this.currentResultIndex > 0) { 77 | this.currentResultIndex-- 78 | this.selectCurrentResult() 79 | } 80 | } 81 | 82 | goToSelectedResult() { 83 | this.resultTargets[this.currentResultIndex].firstElementChild.click() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/generators/hot_glue/typeahead_install_generator.rb: -------------------------------------------------------------------------------- 1 | module HotGlue 2 | class TypeaheadInstallGenerator < 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 | 15 | copy_file "typeahead_views/typeahead.scss", "#{'spec/dummy/' if $INTERNAL_SPECS}app/assets/stylesheets/typeahead.scss" 16 | 17 | 18 | try = ["application.scss", "application.bootstrap.scss"] 19 | try.each do |filename| 20 | main_scss_file = "#{'spec/dummy/' if $INTERNAL_SPECS}app/assets/stylesheets/#{filename}" 21 | if File.exist?(main_scss_file) 22 | insert_into_file main_scss_file do 23 | "@import 'typeahead';\n" 24 | end 25 | puts "Inserted @import 'typeahead'; into #{main_scss_file}" 26 | break 27 | else 28 | # puts "Could not find #{main_scss_file}. Please add the following line to your main scss file: @import 'typeahead';" 29 | end 30 | end 31 | copy_file "typeahead_views/typeahead_controller.js", "#{'spec/dummy/' if $INTERNAL_SPECS}app/javascript/controllers/typeahead_controller.js" 32 | copy_file "typeahead_views/typeahead_results_controller.js", "#{'spec/dummy/' if $INTERNAL_SPECS}app/javascript/controllers/typeahead_results_controller.js" 33 | 34 | system("bin/rails stimulus:manifest:update") 35 | end 36 | end 37 | end 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /lib/hot-glue.rb: -------------------------------------------------------------------------------- 1 | require "hotglue/engine" 2 | require 'kaminari' 3 | 4 | module HotGlue 5 | module TemplateBuilders 6 | 7 | end 8 | 9 | 10 | class Error < StandardError 11 | end 12 | 13 | 14 | def self.to_camel_case(str) 15 | words = str.split(/[^a-zA-Z0-9]/) # split by non-alphanumeric characters 16 | return '' if words.empty? 17 | 18 | first_word = words.first.downcase 19 | rest_words = words[1..-1].map { |w| w.capitalize } 20 | 21 | first_word + rest_words.join 22 | end 23 | 24 | 25 | def self.construct_downnest_object(input) 26 | res = input.split(",").map { |child| 27 | child_name = child.gsub("+","") 28 | extra_size = child.count("+") 29 | {child_name => 4+extra_size} 30 | } 31 | Hash[*res.collect{|hash| hash.collect{|key,value| [key,value].flatten}.flatten}.flatten] 32 | end 33 | 34 | def self.optionalized_ternary(namespace: nil, 35 | target:, 36 | nested_set:, 37 | modifier: "", 38 | with_params: false, 39 | top_level: false, 40 | put_form: false, 41 | instance_last_item: false ) 42 | instance_sym = top_level ? "@" : "" 43 | 44 | 45 | if nested_set.nil? || nested_set.empty? 46 | return modifier + "#{(namespace + '_') if namespace}#{target}_path" + (("(#{instance_sym}#{target})" if put_form) || "") 47 | elsif nested_set[0][:optional] == false 48 | 49 | res = modifier + ((namespace + "_" if namespace) || "") + nested_set.collect{|x| 50 | x[:singular] + "_" 51 | }.join() + target + "_path" + (("(#{nested_set.collect{ 52 | |x| instance_sym + x[:singular] }.join(",") 53 | }#{ put_form ? ',' + (instance_last_item ? "@" : instance_sym) + target : '' })") || "") 54 | 55 | res 56 | 57 | else 58 | # copy the first item, make a ternery in this cycle, and recursively move to both the 59 | # is present path and the is optional path 60 | 61 | nonoptional = nested_set[0].dup 62 | nonoptional[:optional] = false 63 | rest_of_nest = nested_set[1..-1] 64 | 65 | is_present_path = HotGlue.optionalized_ternary( 66 | namespace: namespace, 67 | target: target, 68 | modifier: modifier, 69 | top_level: top_level, 70 | with_params: with_params, 71 | put_form: put_form, 72 | nested_set: [nonoptional, *rest_of_nest]) 73 | 74 | is_missing_path = HotGlue.optionalized_ternary( 75 | namespace: namespace, 76 | target: target, 77 | modifier: modifier, 78 | top_level: top_level, 79 | with_params: with_params, 80 | put_form: put_form, 81 | nested_set: rest_of_nest ) 82 | return "defined?(#{instance_sym + nested_set[0][:singular]}2) ? #{is_present_path} : #{is_missing_path}" 83 | end 84 | end 85 | 86 | def self.derrive_reference_name(thing_as_string) 87 | assoc_class = eval(thing_as_string) 88 | 89 | if assoc_class.new.respond_to?("name") 90 | display_column = "name" 91 | elsif assoc_class.new.respond_to?("to_label") 92 | display_column = "to_label" 93 | elsif assoc_class.new.respond_to?("full_name") 94 | display_column = "full_name" 95 | elsif assoc_class.new.respond_to?("display_name") 96 | display_column = "display_name" 97 | elsif assoc_class.new.respond_to?("email") 98 | display_column = "email" 99 | end 100 | display_column 101 | end 102 | 103 | 104 | end 105 | 106 | -------------------------------------------------------------------------------- /lib/hotglue/engine.rb: -------------------------------------------------------------------------------- 1 | module HotGlue 2 | class Engine < ::Rails::Engine 3 | 4 | 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/hotglue/version.rb: -------------------------------------------------------------------------------- 1 | module HotGlue 2 | class Version 3 | CURRENT = '0.6.18' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /script/clean_generated_code: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm dummy/app/controllers/ghis_controller.rb 3 | rm dummy/app/controllers/abcs_controller.rb 4 | rm dummy/app/controllers/dfgs_controller.rb 5 | rm dummy/app/controllers/cantelopes_controller.rb 6 | rm dummy/app/controllers/jkls_controller.rb 7 | rm dummy/app/controllers/users_controller.rb 8 | rm dummy/app/controllers/humans_controller.rb 9 | rm dummy/app/controllers/pets_controller.rb 10 | 11 | 12 | rm -rf dummy/app/views/cantelopes/* 13 | rm -rf dummy/app/views/jkls/* 14 | rm -rf dummy/app/views/ghis/* 15 | rm -rf dummy/app/views/abcs/* 16 | rm -rf dummy/app/views/dfgs/* 17 | rm -rf dummy/app/views/users/* 18 | rm -rf dummy/app/views/humans/* 19 | rm -rf dummy/app/views/pets/* 20 | 21 | /bin/rm -rf dummy/spec/features/ 22 | 23 | 24 | # !("application_controller.rb"|"welcome_controller.rb") 25 | #find dummy/app/views/ -type f ! -name "application.html.erb" ! -name "welcome/index.erb" -delete 26 | -------------------------------------------------------------------------------- /script/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clean_generated_code() { 4 | ./clean_generated_code 5 | } 6 | 7 | script/clean_generated_code 8 | 9 | echo "Integration test suite for Hot Glue..." 10 | echo "these specs generate several controllers & specs in the dummy app" 11 | echo "and then run the generated specs to test the generated code" 12 | 13 | cd dummy 14 | 15 | echo "Setting up database..." 16 | 17 | bundle install 18 | rake db:drop db:create db:migrate db:test:prepare 19 | 20 | echo "Generating all code for testing..." 21 | bin/rails generate hot_glue:scaffold Abc --gd 22 | 23 | bin/rails generate hot_glue:scaffold Ghi --gd 24 | 25 | bin/rails generate hot_glue:scaffold Dfg --auth=current_user 26 | 27 | bin/rails generate hot_glue:scaffold Fruits::Cantelope --gd 28 | 29 | bin/rails generate hot_glue:scaffold Pet --gd 30 | 31 | bin/rails generate hot_glue:scaffold Human --gd 32 | 33 | 34 | # why is only this one failing on CI 35 | 36 | 37 | # 38 | # 1) interaction for UsersController edit & update should return an editable form 39 | # Failure/Error: find("a.edit-user-button[href='/users/#{current_user.id}/edit']").click 40 | # 41 | # Capybara::ElementNotFound: 42 | # Unable to find css "a.edit-user-button[href='/users/14/edit']" 43 | # # ./spec/features/users_behavior_spec.rb:31:in `block (3 levels) in ' 44 | # 45 | #Finished in 10.76 seconds (files took 2.69 seconds to load) 46 | #26 examples, 1 failure 47 | 48 | #bin/rails generate hot_glue:scaffold User --no-create --self-auth 49 | 50 | 51 | #rails generate hot_glue:scaffold Jkl --gd 52 | #rails generate hot_glue:scaffold Appointment --hawk=pets --gd 53 | #rails generate hot_glue:scaffold Family --gd 54 | 55 | # TODO: test these 56 | # --update-show-only 57 | # --alt-lookup-foreign-keys 58 | # --factory-creation 59 | 60 | rspec || exit 61 | 62 | cd ../ 63 | 64 | script/clean_generated_code 65 | 66 | -------------------------------------------------------------------------------- /spec/dummy/app/models/abc.rb: -------------------------------------------------------------------------------- 1 | class Abc < ApplicationRecord 2 | 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/models/dfg.rb: -------------------------------------------------------------------------------- 1 | class Dfg < ApplicationRecord 2 | # can't use DEF as it is a Ruby Keyword 3 | 4 | belongs_to :user 5 | belongs_to :cantelope, class_name: "Fruits::Cantelope" 6 | 7 | has_many :ghis 8 | 9 | 10 | end 11 | -------------------------------------------------------------------------------- /spec/dummy/app/policies/dfg_policy.rb: -------------------------------------------------------------------------------- 1 | class DfgPolicy < ApplicationPolicy 2 | def initialize(user, record) 3 | @user = user 4 | @record = record 5 | end 6 | 7 | def name_able? 8 | @record.sent_at.nil? 9 | end 10 | 11 | def update? 12 | if !@user.is_admin? 13 | return false 14 | elsif record.name_changed? && !name_able? 15 | record.errors.add(:name, "cannot be changed.") 16 | return false 17 | else 18 | return true 19 | end 20 | end 21 | 22 | class Scope < Scope 23 | attr_reader :user, :scope 24 | def initialize(user, scope) 25 | @user = user 26 | @scope = scope 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/dummy/config/hot_glue.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :layout: hotglue 3 | :markup: erb 4 | :default_boolean_display: radio 5 | -------------------------------------------------------------------------------- /spec/dummy/spec/files/computer_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hot-glue-for-rails/hot-glue/caf4d1ebfb456fdccfcfc5f1213aef2e0062cd52/spec/dummy/spec/files/computer_code.jpg -------------------------------------------------------------------------------- /spec/dummy/spec/support/capybara_login.rb: -------------------------------------------------------------------------------- 1 | def login_as(user) 2 | visit '/users/sign_in' 3 | within("#new_user") do 4 | fill_in 'Email', with: user.email 5 | fill_in 'Password', with: 'password' 6 | end 7 | click_button 'Log in' 8 | end 9 | -------------------------------------------------------------------------------- /spec/lib/generators/hot_glue/direct_upload_install_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | 4 | describe HotGlue::DirectUploadInstallGenerator do 5 | # include GeneratorsTestHelper 6 | 7 | def setup 8 | 9 | end 10 | 11 | def teardown 12 | 13 | end 14 | 15 | 16 | describe "importmap" do 17 | 18 | describe "when app/javascript/application.js is empty" do 19 | before(:each) do 20 | File.write("spec/dummy/app/javascript/application.js", "") 21 | end 22 | # PASSES LOCALLY FAILS ON GITHUB ACTIONS 23 | # it "installs adds active storage to application.js " do 24 | # Rails::Generators.invoke("hot_glue:direct_upload_install") 25 | # res = File.read("spec/dummy/app/javascript/application.js") 26 | # expect(res).to include("//= require activestorage") 27 | # end 28 | end 29 | 30 | 31 | describe "when app/javascript/application.js already contains //= require activestorage" do 32 | before(:each) do 33 | File.write("spec/dummy/app/javascript/application.js", "//= require activestorage") 34 | end 35 | # PASSES LOCALLY, FAILS ON GITHUB ACTION 36 | # it "installs adds active storage to application.js " do 37 | # Rails::Generators.invoke("hot_glue:direct_upload_install") 38 | # res = File.read("spec/dummy/app/javascript/application.js") 39 | # expect(res.scan(/(?=#{"//= require activestorage"})/).count).to be(1) 40 | # end 41 | end 42 | end 43 | 44 | describe "jsbundling" do 45 | 46 | end 47 | end 48 | 49 | 50 | -------------------------------------------------------------------------------- /spec/lib/generators/hot_glue/dropzone_install_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | 4 | describe HotGlue::DropzoneInstallGenerator do 5 | after(:each) do 6 | File.delete("spec/dummy/app/assets/stylesheets/application.scss") if File.exists?("spec/dummy/app/assets/stylesheets/application.scss") 7 | File.delete("spec/dummy/app/assets/stylesheets/application.bootstrap.scss") if File.exists?("spec/dummy/app/assets/stylesheets/application.bootstrap.scss") 8 | end 9 | 10 | describe "bootstrap" do 11 | before(:each) do 12 | File.write("spec/dummy/app/assets/stylesheets/application.bootstrap.scss", "") 13 | end 14 | # PASSES LOCALLY, FAILS ON GITHUB ACTIONS 15 | # it "installs adds active storage to application.js " do 16 | # Rails::Generators.invoke("hot_glue:dropzone_install") 17 | # res = File.read("spec/dummy/app/javascript/controllers/dropzone_controller.js") 18 | # expect(res).to include("Dropzone") 19 | # 20 | # css = File.read("spec/dummy/app/assets/stylesheets/application.bootstrap.scss") 21 | # expect(css).to include("@import \"dropzone/dist/dropzone\"") 22 | # expect(css).to include("@import \"dropzone/dist/basic\"") 23 | # end 24 | end 25 | 26 | describe "no bootstrap" do 27 | before(:each) do 28 | File.write("spec/dummy/app/assets/stylesheets/application.scss", "") 29 | end 30 | # PASSES LOCALLY, FAILS ON GITHUB ACTIONS 31 | # it "installs adds active storage to application.js " do 32 | # Rails::Generators.invoke("hot_glue:dropzone_install") 33 | # res = File.read("spec/dummy/app/javascript/controllers/dropzone_controller.js") 34 | # expect(res).to include("Dropzone") 35 | # 36 | # css = File.read("spec/dummy/app/assets/stylesheets/application.scss") 37 | # expect(res).to include("import Dropzone from \"dropzone\";") 38 | # end 39 | end 40 | 41 | 42 | # PASSES LOCALLY, FAILS ON GITHUB ACTIONS 43 | # describe "can't detect stylesheet" do 44 | # before(:each) do 45 | # 46 | # end 47 | # it "installs adds active storage to application.js " do 48 | # expect{ 49 | # Rails::Generators.invoke("hot_glue:dropzone_install") 50 | # }.to raise_exception(HotGlue::Error) 51 | # end 52 | # end 53 | 54 | describe "stylesheet already contains dropzone" do 55 | before(:each) do 56 | File.write("spec/dummy/app/assets/stylesheets/application.scss", "@import \"dropzone/dist/dropzone\"") 57 | end 58 | # PASSES LOCALLY, FAILS ON GITHUB ACTIONS 59 | # it "installs adds active storage to application.js " do 60 | # Rails::Generators.invoke("hot_glue:dropzone_install") 61 | # res = File.read("spec/dummy/app/javascript/controllers/dropzone_controller.js") 62 | # expect(res).to include("Dropzone") 63 | # 64 | # css = File.read("spec/dummy/app/assets/stylesheets/application.scss") 65 | # 66 | # expect(res.scan(/(?=#{"import Dropzone from \"dropzone\";"})/).count).to be(1) 67 | # end 68 | end 69 | end 70 | 71 | 72 | -------------------------------------------------------------------------------- /spec/lib/generators/hot_glue/install_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | 4 | describe HotGlue::InstallGenerator do 5 | # include GeneratorsTestHelper 6 | 7 | def setup 8 | 9 | end 10 | 11 | def teardown 12 | end 13 | 14 | 15 | describe "css strategies" do 16 | it "should install correctly with bootstrap " do 17 | Rails::Generators.invoke("hot_glue:install", ["Ghi", "--layout=bootstrap"]) 18 | res = File.read("spec/dummy/config/hot_glue.yml") 19 | end 20 | 21 | it "should install correctly with tailwind " do 22 | Rails::Generators.invoke("hot_glue:install", ["Ghi", "--layout=tailwind"]) 23 | end 24 | end 25 | 26 | describe "with a theme " do 27 | it "should install correctly with hotglue layout syntax" do 28 | response = Rails::Generators.invoke("hot_glue:install --theme=dark_knight") 29 | 30 | end 31 | end 32 | end 33 | 34 | 35 | -------------------------------------------------------------------------------- /spec/lib/helpers/hot_glue_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper.rb' 2 | 3 | describe "HotGlueHelper" do 4 | 5 | class FakeController 6 | include HotGlueHelper 7 | end 8 | it "#enum_to_collection_select should take a hash" do 9 | controller = FakeController.new 10 | 11 | controller.enum_to_collection_select({4 => "a"}) 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/lib/hot_glue/field_factory_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe FieldFactory do 4 | it "should make a new filed " do 5 | ff = FieldFactory.new(type: :integer, 6 | name: "how_many", 7 | generator: OpenStruct.new({ 8 | modify_as: {}, 9 | display_as: {}, 10 | default_boolean_display: 'radio', 11 | form_labels_position: nil, 12 | singular_class: "Hgi", 13 | layout_strategy: nil, 14 | form_placeholder_labels: false, 15 | ownership_field: nil, 16 | hawk_keys: nil, 17 | auth: nil, 18 | attachment_data: nil, 19 | sample_file_path: nil, 20 | class_name: "Abc", 21 | alt_lookups: {}, 22 | singular: "abc", 23 | update_show_only: [], 24 | attachments: {} 25 | })) 26 | 27 | expect(ff.field).to be_a(IntegerField) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/lib/hot_glue/hot_glue_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe HotGlue do 4 | 5 | describe "#construct_downnest_object" do 6 | 7 | it "should work when passed nothing" do 8 | res = HotGlue.construct_downnest_object("") 9 | expect(res).to eq({}) 10 | end 11 | 12 | it "should work when passed one downnested" do 13 | res = HotGlue.construct_downnest_object("abc") 14 | expect(res).to eq({"abc"=>4}) 15 | end 16 | 17 | it "should work when passed two downnested" do 18 | res = HotGlue.construct_downnest_object("abc,def") 19 | expect(res).to eq({"abc"=>4,"def"=>4}) 20 | end 21 | 22 | it "should work when passed two downnested" do 23 | res = HotGlue.construct_downnest_object("abc+,def") 24 | expect(res).to eq({"abc"=>5,"def"=>4}) 25 | end 26 | 27 | it "should work when passed two downnested" do 28 | res = HotGlue.construct_downnest_object("abc,def+") 29 | expect(res).to eq({"abc"=>4,"def"=>5}) 30 | end 31 | end 32 | 33 | 34 | 35 | describe "#optionalized_ternary" do 36 | 37 | describe "when the first parent is optional" do 38 | it "should work for a one-level nest" do 39 | target = 'invoices' 40 | nested_set = [ 41 | {singular: 'account', optional: true, plural: 'accounts'} 42 | ] 43 | 44 | result = HotGlue.optionalized_ternary(target: 'invoices', 45 | nested_set: nested_set, 46 | namespace: "admin") 47 | # expect(result).to eq("defined?(account) ? admin_account_invoices_path : admin_invoices_path") 48 | end 49 | end 50 | 51 | describe "for non-optional" do 52 | it "should work for a one-level nest" do 53 | target = 'invoices' 54 | nested_set = [ 55 | {singular: 'account', optional: false, plural: 'accounts'} 56 | ] 57 | 58 | result = HotGlue.optionalized_ternary(target: 'invoices', 59 | nested_set: nested_set, 60 | namespace: "admin") 61 | expect(result).to eq("admin_account_invoices_path(account)") 62 | end 63 | 64 | 65 | it "should work for a two-level nest" do 66 | 67 | end 68 | 69 | it "should work for a three-level nest" do 70 | 71 | end 72 | end 73 | end 74 | 75 | 76 | describe "for two level optional" do 77 | 78 | end 79 | 80 | describe "with top level" do 81 | 82 | end 83 | 84 | describe "namespace" do 85 | 86 | end 87 | 88 | describe "modifier" do 89 | 90 | end 91 | 92 | describe "with_params" do 93 | 94 | end 95 | 96 | describe "top_level" do 97 | 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /spec/lib/hot_glue/version_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper.rb" 2 | 3 | 4 | describe HotGlue::Version do 5 | before(:each) do 6 | @version = HotGlue::Version::CURRENT 7 | end 8 | 9 | it "should be a major- syntax" do 10 | @version = HotGlue::Version::CURRENT 11 | split = @version.split(".").collect(&:to_i) 12 | expect(split[0]).to be_kind_of(Numeric) 13 | end 14 | it "should be a point- syntax" do 15 | split = @version.split(".").collect(&:to_i) 16 | expect(split[1]).to be_kind_of(Numeric) 17 | end 18 | 19 | it "should be a minor- syntax" do 20 | split = @version.split(".").collect(&:to_i) 21 | expect(split[2]).to be_kind_of(Numeric) 22 | end 23 | 24 | it "should be before version 1 " do 25 | split = @version.split(".").collect(&:to_i) 26 | expect(split[0]).to be(0) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/lib/hot_glue_spec.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'spec_helper.rb' 3 | 4 | 5 | 6 | describe HotGlue do 7 | context("#configure") do 8 | it "should assert true" do 9 | expect(true).to be(true) 10 | end 11 | end 12 | end -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require 'spec_helper' 3 | require 'byebug' 4 | 5 | 6 | # require File.expand_path('../../config/environment', __FILE__) 7 | require 'rspec/rails' 8 | # require 'database_cleaner' 9 | require 'factory_bot' 10 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 11 | 12 | ActiveRecord::Migration.maintain_test_schema! 13 | 14 | RSpec.configure do |config| 15 | 16 | config.include FactoryBot::Syntax::Methods 17 | 18 | # config.use_transactional_fixtures = false 19 | 20 | # config.before(:suite) do 21 | # DatabaseCleaner.clean_with(:truncation) 22 | # end 23 | # 24 | config.before(:each) do 25 | $INTERNAL_SPECS = true 26 | end 27 | 28 | # config.before(:each, js: true) do 29 | # DatabaseCleaner.strategy = :truncation 30 | # end 31 | # config.before(:each) do 32 | # DatabaseCleaner.start 33 | # end 34 | 35 | config.after(:each) do 36 | $INTERNAL_SPECS = false 37 | end 38 | 39 | # config.before(:all) do 40 | # DatabaseCleaner.start 41 | # end 42 | # config.after(:all) do 43 | # DatabaseCleaner.clean 44 | # end 45 | config.infer_spec_type_from_file_location! 46 | end 47 | 48 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= "test" 2 | # require 'byebug' 3 | 4 | require 'simplecov' 5 | require 'simplecov-rcov' 6 | class SimpleCov::Formatter::MergedFormatter 7 | def format(result) 8 | SimpleCov::Formatter::HTMLFormatter.new.format(result) 9 | SimpleCov::Formatter::RcovFormatter.new.format(result) 10 | end 11 | end 12 | SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter 13 | SimpleCov.start 'rails' do 14 | add_filter "/vendor/" 15 | add_filter "/test/" 16 | add_filter "/dummy/" 17 | add_filter "lib/hotglue/version.rb" 18 | add_filter "lib/generators/hot_glue/templates/capybara_login.rb" 19 | end 20 | 21 | require "rails/all" 22 | require 'rails/generators' 23 | require './lib/hot-glue.rb' 24 | 25 | # require the gem's core code 26 | Dir["./lib/**/*.rb"].each do |x| 27 | require(x) 28 | end 29 | 30 | require_relative "../dummy/config/application.rb" 31 | require "rspec/rails" 32 | Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 33 | 34 | # https://stackoverflow.com/questions/76319469/when-i-switch-my-rails-open-source-engine-to-postgres-for-testing-i-get-cant 35 | # 36 | # begin 37 | Dummy::Application.initialize! 38 | # rescue 39 | # end 40 | 41 | 42 | RSpec.configure do |config| 43 | config.infer_spec_type_from_file_location! 44 | config.order = :random 45 | config.use_transactional_fixtures = true 46 | 47 | config.expect_with :rspec do |expectations| 48 | expectations.syntax = :expect 49 | end 50 | 51 | config.mock_with :rspec do |mocks| 52 | mocks.syntax = :expect 53 | end 54 | 55 | config.before { 56 | restore_default_warning_free_config 57 | } 58 | end 59 | 60 | def restore_default_warning_free_config 61 | 62 | end 63 | --------------------------------------------------------------------------------