From cec89271dd0828f61e432851f7dc5be916575737 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Tue, 26 Nov 2024 05:33:39 -0500 Subject: [PATCH 1/9] rails 7 initial commit --- .dockerignore | 48 ++ .gitattributes | 9 + .github/dependabot.yml | 12 + .github/workflows/ci.yml | 90 ++++ .gitignore | 59 +++ .rubocop.yml | 8 + .ruby-version | 1 + Dockerfile | 69 +++ Gemfile | 68 +++ Gemfile.lock | 425 ++++++++++++++++++ README.md | 24 + Rakefile | 6 + app/admin/dashboard.rb | 33 ++ app/assets/config/manifest.js | 4 + app/assets/images/.keep | 0 app/assets/javascripts/active_admin.js | 1 + app/assets/stylesheets/active_admin.scss | 17 + app/assets/stylesheets/application.css | 15 + app/channels/application_cable/channel.rb | 4 + app/channels/application_cable/connection.rb | 4 + app/controllers/application_controller.rb | 4 + app/controllers/concerns/.keep | 0 app/helpers/application_helper.rb | 2 + app/javascript/application.js | 3 + app/javascript/controllers/application.js | 9 + .../controllers/hello_controller.js | 7 + app/javascript/controllers/index.js | 4 + app/jobs/application_job.rb | 7 + app/mailers/application_mailer.rb | 4 + app/models/api_key.rb | 2 + app/models/application_record.rb | 3 + app/models/authentication.rb | 2 + app/models/concerns/.keep | 0 app/models/gift_card.rb | 2 + app/models/person.rb | 2 + app/views/layouts/application.html.erb | 23 + app/views/layouts/mailer.html.erb | 13 + app/views/layouts/mailer.text.erb | 1 + app/views/pwa/manifest.json.erb | 22 + app/views/pwa/service-worker.js | 26 ++ bin/brakeman | 7 + bin/bundle | 109 +++++ bin/docker-entrypoint | 13 + bin/importmap | 4 + bin/rails | 4 + bin/rake | 4 + bin/rubocop | 8 + bin/setup | 37 ++ config.ru | 6 + config/application.rb | 27 ++ config/boot.rb | 4 + config/cable.yml | 10 + config/credentials.yml.enc | 1 + config/database.yml | 22 + config/environment.rb | 5 + config/environments/development.rb | 81 ++++ config/environments/production.rb | 105 +++++ config/environments/test.rb | 67 +++ config/importmap.rb | 7 + config/initializers/active_admin.rb | 352 +++++++++++++++ config/initializers/assets.rb | 12 + .../initializers/content_security_policy.rb | 25 ++ .../initializers/filter_parameter_logging.rb | 8 + config/initializers/inflections.rb | 16 + config/initializers/permissions_policy.rb | 13 + config/locales/en.yml | 31 ++ config/puma.rb | 34 ++ config/routes.rb | 15 + config/storage.yml | 34 ++ .../20241126092131_create_gift_cards.rb | 13 + db/migrate/20241126092151_create_people.rb | 10 + .../20241126092204_create_authentications.rb | 13 + db/migrate/20241126092217_create_api_keys.rb | 10 + ...092230_add_certificate_id_to_gift_cards.rb | 5 + ...1126094659_create_active_admin_comments.rb | 16 + db/schema.rb | 65 +++ db/seeds.rb | 9 + lib/assets/.keep | 0 lib/tasks/.keep | 0 log/.keep | 0 public/404.html | 67 +++ public/406-unsupported-browser.html | 66 +++ public/422.html | 67 +++ public/500.html | 66 +++ public/icon.png | Bin 0 -> 5599 bytes public/icon.svg | 3 + public/robots.txt | 1 + storage/.keep | 0 test/application_system_test_case.rb | 5 + .../application_cable/connection_test.rb | 13 + test/controllers/.keep | 0 test/fixtures/api_keys.yml | 9 + test/fixtures/authentications.yml | 15 + test/fixtures/files/.keep | 0 test/fixtures/gift_cards.yml | 15 + test/fixtures/people.yml | 9 + test/helpers/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/models/api_key_test.rb | 7 + test/models/authentication_test.rb | 7 + test/models/gift_card_test.rb | 7 + test/models/person_test.rb | 7 + test/system/.keep | 0 test/test_helper.rb | 15 + tmp/.keep | 0 tmp/pids/.keep | 0 tmp/storage/.keep | 0 vendor/.keep | 0 vendor/javascript/.keep | 0 111 files changed, 2614 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitattributes create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .rubocop.yml create mode 100644 .ruby-version create mode 100644 Dockerfile create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 README.md create mode 100644 Rakefile create mode 100644 app/admin/dashboard.rb create mode 100644 app/assets/config/manifest.js create mode 100644 app/assets/images/.keep create mode 100644 app/assets/javascripts/active_admin.js create mode 100644 app/assets/stylesheets/active_admin.scss create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/channels/application_cable/channel.rb create mode 100644 app/channels/application_cable/connection.rb create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/concerns/.keep create mode 100644 app/helpers/application_helper.rb create mode 100644 app/javascript/application.js create mode 100644 app/javascript/controllers/application.js create mode 100644 app/javascript/controllers/hello_controller.js create mode 100644 app/javascript/controllers/index.js create mode 100644 app/jobs/application_job.rb create mode 100644 app/mailers/application_mailer.rb create mode 100644 app/models/api_key.rb create mode 100644 app/models/application_record.rb create mode 100644 app/models/authentication.rb create mode 100644 app/models/concerns/.keep create mode 100644 app/models/gift_card.rb create mode 100644 app/models/person.rb create mode 100644 app/views/layouts/application.html.erb create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb create mode 100644 app/views/pwa/manifest.json.erb create mode 100644 app/views/pwa/service-worker.js create mode 100755 bin/brakeman create mode 100755 bin/bundle create mode 100755 bin/docker-entrypoint create mode 100755 bin/importmap create mode 100755 bin/rails create mode 100755 bin/rake create mode 100755 bin/rubocop create mode 100755 bin/setup create mode 100644 config.ru create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/cable.yml create mode 100644 config/credentials.yml.enc create mode 100644 config/database.yml create mode 100644 config/environment.rb create mode 100644 config/environments/development.rb create mode 100644 config/environments/production.rb create mode 100644 config/environments/test.rb create mode 100644 config/importmap.rb create mode 100644 config/initializers/active_admin.rb create mode 100644 config/initializers/assets.rb create mode 100644 config/initializers/content_security_policy.rb create mode 100644 config/initializers/filter_parameter_logging.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/permissions_policy.rb create mode 100644 config/locales/en.yml create mode 100644 config/puma.rb create mode 100644 config/routes.rb create mode 100644 config/storage.yml create mode 100644 db/migrate/20241126092131_create_gift_cards.rb create mode 100644 db/migrate/20241126092151_create_people.rb create mode 100644 db/migrate/20241126092204_create_authentications.rb create mode 100644 db/migrate/20241126092217_create_api_keys.rb create mode 100644 db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb create mode 100644 db/migrate/20241126094659_create_active_admin_comments.rb create mode 100644 db/schema.rb create mode 100644 db/seeds.rb create mode 100644 lib/assets/.keep create mode 100644 lib/tasks/.keep create mode 100644 log/.keep create mode 100644 public/404.html create mode 100644 public/406-unsupported-browser.html create mode 100644 public/422.html create mode 100644 public/500.html create mode 100644 public/icon.png create mode 100644 public/icon.svg create mode 100644 public/robots.txt create mode 100644 storage/.keep create mode 100644 test/application_system_test_case.rb create mode 100644 test/channels/application_cable/connection_test.rb create mode 100644 test/controllers/.keep create mode 100644 test/fixtures/api_keys.yml create mode 100644 test/fixtures/authentications.yml create mode 100644 test/fixtures/files/.keep create mode 100644 test/fixtures/gift_cards.yml create mode 100644 test/fixtures/people.yml create mode 100644 test/helpers/.keep create mode 100644 test/integration/.keep create mode 100644 test/mailers/.keep create mode 100644 test/models/.keep create mode 100644 test/models/api_key_test.rb create mode 100644 test/models/authentication_test.rb create mode 100644 test/models/gift_card_test.rb create mode 100644 test/models/person_test.rb create mode 100644 test/system/.keep create mode 100644 test/test_helper.rb create mode 100644 tmp/.keep create mode 100644 tmp/pids/.keep create mode 100644 tmp/storage/.keep create mode 100644 vendor/.keep create mode 100644 vendor/javascript/.keep diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd7190b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,48 @@ +# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. + +# Ignore git directory. +/.git/ +/.gitignore + +# Ignore bundler config. +/.bundle + +# Ignore all environment files (except templates). +/.env* +!/.env*.erb + +# Ignore all default key files. +/config/master.key +/config/credentials/*.key + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/.keep + +# Ignore storage (uploaded files in development and any SQLite databases). +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/.keep + +# Ignore assets. +/node_modules/ +/app/assets/builds/* +!/app/assets/builds/.keep +/public/assets + +# Ignore CI service files. +/.github + +# Ignore development files +/.devcontainer + +# Ignore Docker-related files +/.dockerignore +/Dockerfile* diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8dc4323 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored +config/credentials/*.yml.enc diff=rails_credentials +config/credentials.yml.enc diff=rails_credentials diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f0527e6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..00af91f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,90 @@ +name: CI + +on: + pull_request: + push: + branches: [ main ] + +jobs: + scan_ruby: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Scan for common Rails security vulnerabilities using static analysis + run: bin/brakeman --no-pager + + scan_js: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Scan for security vulnerabilities in JavaScript dependencies + run: bin/importmap audit + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Lint code for consistent style + run: bin/rubocop -f github + + test: + runs-on: ubuntu-latest + + # services: + # redis: + # image: redis + # ports: + # - 6379:6379 + # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 + steps: + - name: Install packages + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y google-chrome-stable curl libjemalloc2 libvips sqlite3 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Run tests + env: + RAILS_ENV: test + # REDIS_URL: redis://localhost:6379/0 + run: bin/rails db:test:prepare test test:system + + - name: Keep screenshots from failed system tests + uses: actions/upload-artifact@v4 + if: failure() + with: + name: screenshots + path: ${{ github.workspace }}/tmp/screenshots + if-no-files-found: ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52fe601 --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore the database +/db/*.sqlite3 +/db/*.sqlite3-* + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + +# Ignore uploaded files in development. +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/ +!/tmp/storage/.keep + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key + +# Configs +vendor/ruby +vendor/bundle +vendor/cache +.env.* + +# Personal notes +notes.txt + +# CSV dumps +/*.csv + +.DS_Store +.idea +coverage + +# secret file that was encrypted for travis +bin/deploy/erd-service-account.json + +/app/assets/builds/* +!/app/assets/builds/.keep + +/node_modules diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..f9d86d4 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,8 @@ +# Omakase Ruby styling for Rails +inherit_gem: { rubocop-rails-omakase: rubocop.yml } + +# Overwrite or add rules to create your own house style +# +# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]` +# Layout/SpaceInsideArrayLiteralBrackets: +# Enabled: false diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..e391e18 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-3.3.6 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..492a06c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,69 @@ +# syntax = docker/dockerfile:1 + +# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand: +# docker build -t my-app . +# docker run -d -p 80:80 -p 443:443 --name my-app -e RAILS_MASTER_KEY= my-app + +# Make sure RUBY_VERSION matches the Ruby version in .ruby-version +ARG RUBY_VERSION=3.3.6 +FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base + +# Rails app lives here +WORKDIR /rails + +# Install base packages +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Set production environment +ENV RAILS_ENV="production" \ + BUNDLE_DEPLOYMENT="1" \ + BUNDLE_PATH="/usr/local/bundle" \ + BUNDLE_WITHOUT="development" + +# Throw-away build stage to reduce size of final image +FROM base AS build + +# Install packages needed to build gems +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -y build-essential git pkg-config && \ + rm -rf /var/lib/apt/lists /var/cache/apt/archives + +# Install application gems +COPY Gemfile Gemfile.lock ./ +RUN bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ + bundle exec bootsnap precompile --gemfile + +# Copy application code +COPY . . + +# Precompile bootsnap code for faster boot times +RUN bundle exec bootsnap precompile app/ lib/ + +# Precompiling assets for production without requiring secret RAILS_MASTER_KEY +RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile + + + + +# Final stage for app image +FROM base + +# Copy built artifacts: gems, application +COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" +COPY --from=build /rails /rails + +# Run and own only the runtime files as a non-root user for security +RUN groupadd --system --gid 1000 rails && \ + useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ + chown -R rails:rails db log storage tmp +USER 1000:1000 + +# Entrypoint prepares the database. +ENTRYPOINT ["/rails/bin/docker-entrypoint"] + +# Start the server by default, this can be overwritten at runtime +EXPOSE 3000 +CMD ["./bin/rails", "server"] diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..a91f5e4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,68 @@ +source "https://rubygems.org" + +# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" +gem "rails", "~> 7.2.2" +# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] +gem "sprockets-rails" +gem "sassc-rails" + +gem "pg" +gem "dotenv" + +# Use the Puma web server [https://github.com/puma/puma] +gem "puma", ">= 5.0" +# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] +gem "importmap-rails" +# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] +gem "stimulus-rails" +# Build JSON APIs with ease [https://github.com/rails/jbuilder] +gem "jbuilder" +# Use Redis adapter to run Action Cable in production +# gem "redis", ">= 4.0.1" + +# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] +# gem "kredis" + +# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] +# gem "bcrypt", "~> 3.1.7" + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem "tzinfo-data", platforms: %i[ windows jruby ] + +# Reduces boot times through caching; required in config/boot.rb +gem "bootsnap", require: false + +# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] +# gem "image_processing", "~> 1.2" + +group :development, :test do + # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem + gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" + + # Static analysis for security vulnerabilities [https://brakemanscanner.org/] + gem "brakeman", require: false + + # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] + gem "rubocop-rails-omakase", require: false +end + +group :development do + # Use console on exceptions pages [https://github.com/rails/web-console] + gem "web-console" + + gem "standard" + + gem "dotenv-rails" + + gem "pry-byebug" + gem "pry-remote" + gem "pry-stack_explorer" +end + +group :test do + # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] + gem "capybara" + gem "selenium-webdriver" +end + +gem "activeadmin", "~> 3.2" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..22ca93d --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,425 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + actionmailer (7.2.2) + actionpack (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activesupport (= 7.2.2) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2) + actionview (= 7.2.2) + activesupport (= 7.2.2) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2) + actionpack (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2) + activesupport (= 7.2.2) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activeadmin (3.2.5) + arbre (~> 1.2, >= 1.2.1) + csv + formtastic (>= 3.1) + formtastic_i18n (>= 0.4) + inherited_resources (~> 1.7) + jquery-rails (>= 4.2) + kaminari (>= 1.2.1) + railties (>= 6.1) + ransack (>= 4.0) + activejob (7.2.2) + activesupport (= 7.2.2) + globalid (>= 0.3.6) + activemodel (7.2.2) + activesupport (= 7.2.2) + activerecord (7.2.2) + activemodel (= 7.2.2) + activesupport (= 7.2.2) + timeout (>= 0.4.0) + activestorage (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activesupport (= 7.2.2) + marcel (~> 1.0) + activesupport (7.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + arbre (1.7.0) + activesupport (>= 3.0.0) + ruby2_keywords (>= 0.0.2) + ast (2.4.2) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.8) + bindex (0.8.1) + binding_of_caller (1.0.1) + debug_inspector (>= 1.2.0) + bootsnap (1.18.4) + msgpack (~> 1.2) + brakeman (6.2.2) + racc + builder (3.3.0) + byebug (11.1.3) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + coderay (1.1.3) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + crass (1.0.6) + csv (3.3.0) + date (3.4.0) + debug (1.9.2) + irb (~> 1.10) + reline (>= 0.3.8) + debug_inspector (1.2.0) + dotenv (3.1.4) + dotenv-rails (3.1.4) + dotenv (= 3.1.4) + railties (>= 6.1) + drb (2.2.1) + erubi (1.13.0) + ffi (1.17.0-aarch64-linux-gnu) + ffi (1.17.0-aarch64-linux-musl) + ffi (1.17.0-arm-linux-gnu) + ffi (1.17.0-arm-linux-musl) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86-linux-gnu) + ffi (1.17.0-x86-linux-musl) + ffi (1.17.0-x86_64-darwin) + ffi (1.17.0-x86_64-linux-gnu) + ffi (1.17.0-x86_64-linux-musl) + formtastic (5.0.0) + actionpack (>= 6.0.0) + formtastic_i18n (0.7.0) + globalid (1.2.1) + activesupport (>= 6.1) + has_scope (0.8.2) + actionpack (>= 5.2) + activesupport (>= 5.2) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + importmap-rails (2.0.3) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) + inherited_resources (1.14.0) + actionpack (>= 6.0) + has_scope (>= 0.6) + railties (>= 6.0) + responders (>= 2) + io-console (0.7.2) + irb (1.14.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + jquery-rails (4.6.0) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + json (2.8.2) + kaminari (1.2.2) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) + actionview + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + logger (1.6.1) + loofah (2.23.1) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.2) + method_source (1.1.0) + mini_mime (1.1.5) + minitest (5.25.2) + msgpack (1.7.5) + net-imap (0.5.1) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.4) + nokogiri (1.16.7-aarch64-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm-linux) + racc (~> 1.4) + nokogiri (1.16.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86-linux) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.16.7-x86_64-linux) + racc (~> 1.4) + parallel (1.26.3) + parser (3.3.6.0) + ast (~> 2.4.1) + racc + pg (1.5.9) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.10.1) + byebug (~> 11.0) + pry (>= 0.13, < 0.15) + pry-remote (0.1.8) + pry (~> 0.9) + slop (~> 3.0) + pry-stack_explorer (0.6.1) + binding_of_caller (~> 1.0) + pry (~> 0.13) + psych (5.2.0) + stringio + public_suffix (6.0.1) + puma (6.5.0) + nio4r (~> 2.0) + racc (1.8.1) + rack (3.1.8) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails (7.2.2) + actioncable (= 7.2.2) + actionmailbox (= 7.2.2) + actionmailer (= 7.2.2) + actionpack (= 7.2.2) + actiontext (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activemodel (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) + bundler (>= 1.15.0) + railties (= 7.2.2) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + rake (13.2.1) + ransack (4.2.1) + activerecord (>= 6.1.5) + activesupport (>= 6.1.5) + i18n + rdoc (6.8.1) + psych (>= 4.0.0) + regexp_parser (2.9.2) + reline (0.5.11) + io-console (~> 0.5) + responders (3.1.1) + actionpack (>= 5.2) + railties (>= 5.2) + rexml (3.3.9) + rubocop (1.68.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.36.1) + parser (>= 3.3.1.0) + rubocop-minitest (0.36.0) + rubocop (>= 1.61, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-performance (1.22.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.27.0) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.52.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails-omakase (1.0.0) + rubocop + rubocop-minitest + rubocop-performance + rubocop-rails + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + securerandom (0.3.2) + selenium-webdriver (4.27.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + slop (3.6.0) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) + sprockets (>= 3.0.0) + standard (1.42.1) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.68.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.5) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.5.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.22.0) + stimulus-rails (1.3.4) + railties (>= 6.0.0) + stringio (3.1.2) + thor (1.3.2) + tilt (2.4.0) + timeout (0.4.2) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.6.0) + useragent (0.16.10) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) + bindex (>= 0.4.0) + railties (>= 6.0.0) + websocket (1.2.11) + websocket-driver (0.7.6) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.7.1) + +PLATFORMS + aarch64-linux + aarch64-linux-gnu + aarch64-linux-musl + arm-linux + arm-linux-gnu + arm-linux-musl + arm64-darwin + x86-linux + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + activeadmin (~> 3.2) + bootsnap + brakeman + capybara + debug + dotenv + dotenv-rails + importmap-rails + jbuilder + pg + pry-byebug + pry-remote + pry-stack_explorer + puma (>= 5.0) + rails (~> 7.2.2) + rubocop-rails-omakase + sassc-rails + selenium-webdriver + sprockets-rails + standard + stimulus-rails + tzinfo-data + web-console + +BUNDLED WITH + 2.5.22 diff --git a/README.md b/README.md new file mode 100644 index 0000000..7db80e4 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..9a5ea73 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative "config/application" + +Rails.application.load_tasks diff --git a/app/admin/dashboard.rb b/app/admin/dashboard.rb new file mode 100644 index 0000000..3824c62 --- /dev/null +++ b/app/admin/dashboard.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true +ActiveAdmin.register_page "Dashboard" do + menu priority: 1, label: proc { I18n.t("active_admin.dashboard") } + + content title: proc { I18n.t("active_admin.dashboard") } do + div class: "blank_slate_container", id: "dashboard_default_message" do + span class: "blank_slate" do + span I18n.t("active_admin.dashboard_welcome.welcome") + small I18n.t("active_admin.dashboard_welcome.call_to_action") + end + end + + # Here is an example of a simple dashboard with columns and panels. + # + # columns do + # column do + # panel "Recent Posts" do + # ul do + # Post.recent(5).map do |post| + # li link_to(post.title, admin_post_path(post)) + # end + # end + # end + # end + + # column do + # panel "Info" do + # para "Welcome to ActiveAdmin." + # end + # end + # end + end # content +end diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 0000000..ddd546a --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,4 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/javascripts/active_admin.js b/app/assets/javascripts/active_admin.js new file mode 100644 index 0000000..d2b66c5 --- /dev/null +++ b/app/assets/javascripts/active_admin.js @@ -0,0 +1 @@ +//= require active_admin/base diff --git a/app/assets/stylesheets/active_admin.scss b/app/assets/stylesheets/active_admin.scss new file mode 100644 index 0000000..41c27b3 --- /dev/null +++ b/app/assets/stylesheets/active_admin.scss @@ -0,0 +1,17 @@ +// Sass variable overrides must be declared before loading up Active Admin's styles. +// +// To view the variables that Active Admin provides, take a look at +// `app/assets/stylesheets/active_admin/mixins/_variables.scss` in the +// Active Admin source. +// +// For example, to change the sidebar width: +// $sidebar-width: 242px; + +// Active Admin's got SASS! +@import "active_admin/mixins"; +@import "active_admin/base"; + +// Overriding any non-variable Sass must be done after the fact. +// For example, to change the default status-tag color: +// +// .status_tag { background: #6090DB; } diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 0000000..288b9ab --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 0000000..0ff5442 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 0000000..0d95db2 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,4 @@ +class ApplicationController < ActionController::Base + # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. + allow_browser versions: :modern +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 0000000..de6be79 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/javascript/application.js b/app/javascript/application.js new file mode 100644 index 0000000..0d7b494 --- /dev/null +++ b/app/javascript/application.js @@ -0,0 +1,3 @@ +// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails +import "@hotwired/turbo-rails" +import "controllers" diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js new file mode 100644 index 0000000..1213e85 --- /dev/null +++ b/app/javascript/controllers/application.js @@ -0,0 +1,9 @@ +import { Application } from "@hotwired/stimulus" + +const application = Application.start() + +// Configure Stimulus development experience +application.debug = false +window.Stimulus = application + +export { application } diff --git a/app/javascript/controllers/hello_controller.js b/app/javascript/controllers/hello_controller.js new file mode 100644 index 0000000..5975c07 --- /dev/null +++ b/app/javascript/controllers/hello_controller.js @@ -0,0 +1,7 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.element.textContent = "Hello World!" + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js new file mode 100644 index 0000000..1156bf8 --- /dev/null +++ b/app/javascript/controllers/index.js @@ -0,0 +1,4 @@ +// Import and register all your controllers from the importmap via controllers/**/*_controller +import { application } from "controllers/application" +import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" +eagerLoadControllersFrom("controllers", application) diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 0000000..d394c3d --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,7 @@ +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 0000000..3c34c81 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: "from@example.com" + layout "mailer" +end diff --git a/app/models/api_key.rb b/app/models/api_key.rb new file mode 100644 index 0000000..f44edba --- /dev/null +++ b/app/models/api_key.rb @@ -0,0 +1,2 @@ +class ApiKey < ApplicationRecord +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 0000000..b63caeb --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + primary_abstract_class +end diff --git a/app/models/authentication.rb b/app/models/authentication.rb new file mode 100644 index 0000000..a3c8f98 --- /dev/null +++ b/app/models/authentication.rb @@ -0,0 +1,2 @@ +class Authentication < ApplicationRecord +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/app/models/gift_card.rb b/app/models/gift_card.rb new file mode 100644 index 0000000..1b11df6 --- /dev/null +++ b/app/models/gift_card.rb @@ -0,0 +1,2 @@ +class GiftCard < ApplicationRecord +end diff --git a/app/models/person.rb b/app/models/person.rb new file mode 100644 index 0000000..a8b1b85 --- /dev/null +++ b/app/models/person.rb @@ -0,0 +1,2 @@ +class Person < ApplicationRecord +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 0000000..13f3e77 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,23 @@ + + + + <%= content_for(:title) || "Familylife Gift Cards" %> + + + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= yield :head %> + + + + + + <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> + <%= javascript_importmap_tags %> + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000..3aac900 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000..37f0bdd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb new file mode 100644 index 0000000..a685c35 --- /dev/null +++ b/app/views/pwa/manifest.json.erb @@ -0,0 +1,22 @@ +{ + "name": "FamilylifeGiftCards", + "icons": [ + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "/icon.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + } + ], + "start_url": "/", + "display": "standalone", + "scope": "/", + "description": "FamilylifeGiftCards.", + "theme_color": "red", + "background_color": "red" +} diff --git a/app/views/pwa/service-worker.js b/app/views/pwa/service-worker.js new file mode 100644 index 0000000..b3a13fb --- /dev/null +++ b/app/views/pwa/service-worker.js @@ -0,0 +1,26 @@ +// Add a service worker for processing Web Push notifications: +// +// self.addEventListener("push", async (event) => { +// const { title, options } = await event.data.json() +// event.waitUntil(self.registration.showNotification(title, options)) +// }) +// +// self.addEventListener("notificationclick", function(event) { +// event.notification.close() +// event.waitUntil( +// clients.matchAll({ type: "window" }).then((clientList) => { +// for (let i = 0; i < clientList.length; i++) { +// let client = clientList[i] +// let clientPath = (new URL(client.url)).pathname +// +// if (clientPath == event.notification.data.path && "focus" in client) { +// return client.focus() +// } +// } +// +// if (clients.openWindow) { +// return clients.openWindow(event.notification.data.path) +// } +// }) +// ) +// }) diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 0000000..ace1c9b --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 0000000..50da5fd --- /dev/null +++ b/bin/bundle @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "rubygems" + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV["BUNDLER_VERSION"] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../Gemfile", __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_requirement + @bundler_requirement ||= + env_var_version || + cli_arg_version || + bundler_requirement_for(lockfile_version) + end + + def bundler_requirement_for(version) + return "#{Gem::Requirement.default}.a" unless version + + bundler_gem_version = Gem::Version.new(version) + + bundler_gem_version.approximate_recommendation + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + 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}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint new file mode 100755 index 0000000..840d093 --- /dev/null +++ b/bin/docker-entrypoint @@ -0,0 +1,13 @@ +#!/bin/bash -e + +# Enable jemalloc for reduced memory usage and latency. +if [ -z "${LD_PRELOAD+x}" ] && [ -f /usr/lib/*/libjemalloc.so.2 ]; then + export LD_PRELOAD="$(echo /usr/lib/*/libjemalloc.so.2)" +fi + +# If running the rails server then create or migrate existing database +if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then + ./bin/rails db:prepare +fi + +exec "${@}" diff --git a/bin/importmap b/bin/importmap new file mode 100755 index 0000000..36502ab --- /dev/null +++ b/bin/importmap @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby + +require_relative "../config/application" +require "importmap/commands" diff --git a/bin/rails b/bin/rails new file mode 100755 index 0000000..efc0377 --- /dev/null +++ b/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/bin/rake b/bin/rake new file mode 100755 index 0000000..4fbf10b --- /dev/null +++ b/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "rake" +Rake.application.run diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 0000000..40330c0 --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..1d5687b --- /dev/null +++ b/bin/setup @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +require "fileutils" + +APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "familylife-gift-cards" + +def system!(*args) + system(*args, exception: true) +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. + + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! "bin/rails db:prepare" + + puts "\n== Removing old logs and tempfiles ==" + system! "bin/rails log:clear tmp:clear" + + puts "\n== Restarting application server ==" + system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..4a3c09a --- /dev/null +++ b/config.ru @@ -0,0 +1,6 @@ +# This file is used by Rack-based servers to start the application. + +require_relative "config/environment" + +run Rails.application +Rails.application.load_server diff --git a/config/application.rb b/config/application.rb new file mode 100644 index 0000000..01f415f --- /dev/null +++ b/config/application.rb @@ -0,0 +1,27 @@ +require_relative "boot" + +require "rails/all" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module FamilylifeGiftCards + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.2 + + # Please, add to the `ignore` list any other `lib` subdirectories that do + # not contain `.rb` files, or that should not be reloaded or eager loaded. + # Common ones are `templates`, `generators`, or `middleware`, for example. + config.autoload_lib(ignore: %w[assets tasks]) + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..988a5dd --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,4 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000..18e52ff --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: familylife_gift_cards_production diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc new file mode 100644 index 0000000..8cce492 --- /dev/null +++ b/config/credentials.yml.enc @@ -0,0 +1 @@ +7hktJmm7uXG0AWe6Wr7UwvW2IKi6x1ZTp5mTVON59xJKVNzphfD+Tg/1ctYqCVE+nvmJctPlJLoN7A5oWXQf6sYFSRqCZb9YIoV0xgrUNFFYK60bJVffHO7Krt9oouTcYNPNGSj4RBs7w6shgDiNSw+AwIFA04I6p1UGQ7nhCY8DbGtvtrkGqUsdsnFUCHhPrFxUpvoLtdAYm9dkthptUzZ8V5lsqXq1sPzXqwfGlRYeKOPy8Sv9U5FWvQTAR2sIFskXbJ/fic2wtr+9OkK5jO8nPisUQztA3/3nF3DcSvuEe1bAbPc9a/j9hbt/Ry0GWlTBiGdmGklK8T237o1mlUkxn98LFQqGEz+retY958Fr4C0oCt9i2NLP1q5RYviR5i2b+4Puvq03XPfWCSlL1mSFFzJK--HvGnt/JC5frH4xxW--sHbxfxnGRYggwiYEJzhJSg== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..c17ddc6 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,22 @@ +default: &default + adapter: postgresql + database: <%= ENV['DB_ENV_POSTGRESQL_DB'] %> + encoding: "utf8" + host: <%= ENV['DB_PORT_5432_TCP_ADDR'] %> + username: <%= ENV['DB_ENV_POSTGRESQL_USER'] %> + password: <%= ENV['DB_ENV_POSTGRESQL_PASS'] %> + reconnect: true + pool: <%= ENV['DB_ENV_POSTGRESQL_POOL'] || 25 %> + +staging: + <<: *default + +development: + <<: *default + +production: + <<: *default + +test: + <<: *default + database: <%= ENV['DB_ENV_POSTGRESQL_DB_TEST'] || 'cap_test' %> diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 0000000..cac5315 --- /dev/null +++ b/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative "application" + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb new file mode 100644 index 0000000..9b67360 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,81 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.enable_reloading = true + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable server timing. + config.server_timing = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Don't care if the mailer can't send. + config.action_mailer.raise_delivery_errors = false + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Highlight code that enqueued background job in logs. + config.active_job.verbose_enqueue_logs = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + config.action_view.annotate_rendered_view_with_filenames = true + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true + + # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. + # config.generators.apply_rubocop_autocorrect_after_generate! +end diff --git a/config/environments/production.rb b/config/environments/production.rb new file mode 100644 index 0000000..67b4816 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,105 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.enable_reloading = false + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment + # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. + # config.public_file_server.enabled = false + + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fall back to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Mount Action Cable outside main process or domain. + # config.action_cable.mount_path = nil + # config.action_cable.url = "wss://example.com/cable" + # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] + + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. + # config.assume_ssl = true + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = true + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT by default + config.logger = ActiveSupport::Logger.new(STDOUT) + .tap { |logger| logger.formatter = ::Logger::Formatter.new } + .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # "info" includes generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). If you + # want to log everything, set the level to "debug". + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment). + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "familylife_gift_cards_production" + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + # Ignore bad email addresses and do not raise email delivery errors. + # Set this to true and configure the email server for immediate delivery to raise delivery errors. + # config.action_mailer.raise_delivery_errors = false + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + + # Only use :id for inspections in production. + config.active_record.attributes_for_inspect = [ :id ] + + # Enable DNS rebinding protection and other `Host` header attacks. + # config.hosts = [ + # "example.com", # Allow requests from example.com + # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` + # ] + # Skip DNS rebinding protection for the default health check endpoint. + # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 0000000..0c616a1 --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,67 @@ +require "active_support/core_ext/integer/time" + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false + + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # loading is working properly before deploying your code. + config.eager_load = ENV["CI"].present? + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.cache_store = :null_store + + # Render exception templates for rescuable exceptions and raise for other exceptions. + config.action_dispatch.show_exceptions = :rescuable + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Store uploaded files on the local file system in a temporary directory. + config.active_storage.service = :test + + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. + config.action_mailer.perform_caching = false + + # Tell Action Mailer not to deliver emails to the real world. + # The :test delivery method accumulates sent emails in the + # ActionMailer::Base.deliveries array. + config.action_mailer.delivery_method = :test + + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: "www.example.com" } + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/config/importmap.rb b/config/importmap.rb new file mode 100644 index 0000000..909dfc5 --- /dev/null +++ b/config/importmap.rb @@ -0,0 +1,7 @@ +# Pin npm packages by running ./bin/importmap + +pin "application" +pin "@hotwired/turbo-rails", to: "turbo.min.js" +pin "@hotwired/stimulus", to: "stimulus.min.js" +pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" +pin_all_from "app/javascript/controllers", under: "controllers" diff --git a/config/initializers/active_admin.rb b/config/initializers/active_admin.rb new file mode 100644 index 0000000..f8f22c8 --- /dev/null +++ b/config/initializers/active_admin.rb @@ -0,0 +1,352 @@ +ActiveAdmin.setup do |config| + # == Site Title + # + # Set the title that is displayed on the main layout + # for each of the active admin pages. + # + config.site_title = "Familylife Gift Cards" + + # Set the link url for the title. For example, to take + # users to your main site. Defaults to no link. + # + # config.site_title_link = "/" + + # Set an optional image to be displayed for the header + # instead of a string (overrides :site_title) + # + # Note: Aim for an image that's 21px high so it fits in the header. + # + # config.site_title_image = "logo.png" + + # == Load Paths + # + # By default Active Admin files go inside app/admin/. + # You can change this directory. + # + # eg: + # config.load_paths = [File.join(Rails.root, 'app', 'ui')] + # + # Or, you can also load more directories. + # Useful when setting namespaces with users that are not your main AdminUser entity. + # + # eg: + # config.load_paths = [ + # File.join(Rails.root, 'app', 'admin'), + # File.join(Rails.root, 'app', 'cashier') + # ] + + # == Default Namespace + # + # Set the default namespace each administration resource + # will be added to. + # + # eg: + # config.default_namespace = :hello_world + # + # This will create resources in the HelloWorld module and + # will namespace routes to /hello_world/* + # + # To set no namespace by default, use: + # config.default_namespace = false + # + # Default: + # config.default_namespace = :admin + # + # You can customize the settings for each namespace by using + # a namespace block. For example, to change the site title + # within a namespace: + # + # config.namespace :admin do |admin| + # admin.site_title = "Custom Admin Title" + # end + # + # This will ONLY change the title for the admin section. Other + # namespaces will continue to use the main "site_title" configuration. + + # == User Authentication + # + # Active Admin will automatically call an authentication + # method in a before filter of all controller actions to + # ensure that there is a currently logged in admin user. + # + # This setting changes the method which Active Admin calls + # within the application controller. + # config.authentication_method = :authenticate_admin_user! + + # == User Authorization + # + # Active Admin will automatically call an authorization + # method in a before filter of all controller actions to + # ensure that there is a user with proper rights. You can use + # CanCanAdapter or make your own. Please refer to documentation. + # config.authorization_adapter = ActiveAdmin::CanCanAdapter + + # In case you prefer Pundit over other solutions you can here pass + # the name of default policy class. This policy will be used in every + # case when Pundit is unable to find suitable policy. + # config.pundit_default_policy = "MyDefaultPunditPolicy" + + # If you wish to maintain a separate set of Pundit policies for admin + # resources, you may set a namespace here that Pundit will search + # within when looking for a resource's policy. + # config.pundit_policy_namespace = :admin + + # You can customize your CanCan Ability class name here. + # config.cancan_ability_class = "Ability" + + # You can specify a method to be called on unauthorized access. + # This is necessary in order to prevent a redirect loop which happens + # because, by default, user gets redirected to Dashboard. If user + # doesn't have access to Dashboard, he'll end up in a redirect loop. + # Method provided here should be defined in application_controller.rb. + # config.on_unauthorized_access = :access_denied + + # == Current User + # + # Active Admin will associate actions with the current + # user performing them. + # + # This setting changes the method which Active Admin calls + # (within the application controller) to return the currently logged in user. + # config.current_user_method = :current_admin_user + + # == Logging Out + # + # Active Admin displays a logout link on each screen. These + # settings configure the location and method used for the link. + # + # This setting changes the path where the link points to. If it's + # a string, the strings is used as the path. If it's a Symbol, we + # will call the method to return the path. + # + # Default: + config.logout_link_path = :destroy_admin_user_session_path + + # This setting changes the http method used when rendering the + # link. For example :get, :delete, :put, etc.. + # + # Default: + # config.logout_link_method = :get + + # == Root + # + # Set the action to call for the root path. You can set different + # roots for each namespace. + # + # Default: + # config.root_to = 'dashboard#index' + + # == Admin Comments + # + # This allows your users to comment on any resource registered with Active Admin. + # + # You can completely disable comments: + # config.comments = false + # + # You can change the name under which comments are registered: + # config.comments_registration_name = 'AdminComment' + # + # You can change the order for the comments and you can change the column + # to be used for ordering: + # config.comments_order = 'created_at ASC' + # + # You can disable the menu item for the comments index page: + # config.comments_menu = false + # + # You can customize the comment menu: + # config.comments_menu = { parent: 'Admin', priority: 1 } + + # == Batch Actions + # + # Enable and disable Batch Actions + # + config.batch_actions = true + + # == Controller Filters + # + # You can add before, after and around filters to all of your + # Active Admin resources and pages from here. + # + # config.before_action :do_something_awesome + + # == Attribute Filters + # + # You can exclude possibly sensitive model attributes from being displayed, + # added to forms, or exported by default by ActiveAdmin + # + config.filter_attributes = [:encrypted_password, :password, :password_confirmation] + + # == Localize Date/Time Format + # + # Set the localize format to display dates and times. + # To understand how to localize your app with I18n, read more at + # https://guides.rubyonrails.org/i18n.html + # + # You can run `bin/rails runner 'puts I18n.t("date.formats")'` to see the + # available formats in your application. + # + config.localize_format = :long + + # == Setting a Favicon + # + # config.favicon = 'favicon.ico' + + # == Meta Tags + # + # Add additional meta tags to the head element of active admin pages. + # + # Add tags to all pages logged in users see: + # config.meta_tags = { author: 'My Company' } + + # By default, sign up/sign in/recover password pages are excluded + # from showing up in search engine results by adding a robots meta + # tag. You can reset the hash of meta tags included in logged out + # pages: + # config.meta_tags_for_logged_out_pages = {} + + # == Removing Breadcrumbs + # + # Breadcrumbs are enabled by default. You can customize them for individual + # resources or you can disable them globally from here. + # + # config.breadcrumb = false + + # == Create Another Checkbox + # + # Create another checkbox is disabled by default. You can customize it for individual + # resources or you can enable them globally from here. + # + # config.create_another = true + + # == Register Stylesheets & Javascripts + # + # We recommend using the built in Active Admin layout and loading + # up your own stylesheets / javascripts to customize the look + # and feel. + # + # To load a stylesheet: + # config.register_stylesheet 'my_stylesheet.css' + # + # You can provide an options hash for more control, which is passed along to stylesheet_link_tag(): + # config.register_stylesheet 'my_print_stylesheet.css', media: :print + # + # To load a javascript file: + # config.register_javascript 'my_javascript.js' + + # == CSV options + # + # Set the CSV builder separator + # config.csv_options = { col_sep: ';' } + # + # Force the use of quotes + # config.csv_options = { force_quotes: true } + + # == Menu System + # + # You can add a navigation menu to be used in your application, or configure a provided menu + # + # To change the default utility navigation to show a link to your website & a logout btn + # + # config.namespace :admin do |admin| + # admin.build_menu :utility_navigation do |menu| + # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank } + # admin.add_logout_button_to_menu menu + # end + # end + # + # If you wanted to add a static menu item to the default menu provided: + # + # config.namespace :admin do |admin| + # admin.build_menu :default do |menu| + # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: "_blank" } + # end + # end + + # == Download Links + # + # You can disable download links on resource listing pages, + # or customize the formats shown per namespace/globally + # + # To disable/customize for the :admin namespace: + # + # config.namespace :admin do |admin| + # + # # Disable the links entirely + # admin.download_links = false + # + # # Only show XML & PDF options + # admin.download_links = [:xml, :pdf] + # + # # Enable/disable the links based on block + # # (for example, with cancan) + # admin.download_links = proc { can?(:view_download_links) } + # + # end + + # == Pagination + # + # Pagination is enabled by default for all resources. + # You can control the default per page count for all resources here. + # + # config.default_per_page = 30 + # + # You can control the max per page count too. + # + # config.max_per_page = 10_000 + + # == Filters + # + # By default the index screen includes a "Filters" sidebar on the right + # hand side with a filter for each attribute of the registered model. + # You can enable or disable them for all resources here. + # + # config.filters = true + # + # By default the filters include associations in a select, which means + # that every record will be loaded for each association (up + # to the value of config.maximum_association_filter_arity). + # You can enabled or disable the inclusion + # of those filters by default here. + # + # config.include_default_association_filters = true + + # config.maximum_association_filter_arity = 256 # default value of :unlimited will change to 256 in a future version + # config.filter_columns_for_large_association = [ + # :display_name, + # :full_name, + # :name, + # :username, + # :login, + # :title, + # :email, + # ] + # config.filter_method_for_large_association = '_start' + + # == Head + # + # You can add your own content to the site head like analytics. Make sure + # you only pass content you trust. + # + # config.head = ''.html_safe + + # == Footer + # + # By default, the footer shows the current Active Admin version. You can + # override the content of the footer here. + # + # config.footer = 'my custom footer text' + + # == Sorting + # + # By default ActiveAdmin::OrderClause is used for sorting logic + # You can inherit it with own class and inject it for all resources + # + # config.order_clause = MyOrderClause + + # == Webpacker + # + # By default, Active Admin uses Sprocket's asset pipeline. + # You can switch to using Webpacker here. + # + # config.use_webpacker = true +end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 0000000..bd5bcd2 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,12 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = "1.0" + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w[ admin.js admin.css ] diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 0000000..b3076b3 --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,25 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000..c010b83 --- /dev/null +++ b/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb new file mode 100644 index 0000000..3860f65 --- /dev/null +++ b/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym "RESTful" +# end diff --git a/config/initializers/permissions_policy.rb b/config/initializers/permissions_policy.rb new file mode 100644 index 0000000..7db3b95 --- /dev/null +++ b/config/initializers/permissions_policy.rb @@ -0,0 +1,13 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide HTTP permissions policy. For further +# information see: https://developers.google.com/web/updates/2018/06/feature-policy + +# Rails.application.config.permissions_policy do |policy| +# policy.camera :none +# policy.gyroscope :none +# policy.microphone :none +# policy.usb :none +# policy.fullscreen :self +# policy.payment :self, "https://secure.example.com" +# end diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 0000000..6c349ae --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,31 @@ +# Files in the config/locales directory are used for internationalization and +# are automatically loaded by Rails. If you want to use locales other than +# English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more about the API, please read the Rails Internationalization guide +# at https://guides.rubyonrails.org/i18n.html. +# +# Be aware that YAML interprets the following case-insensitive strings as +# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings +# must be quoted to be interpreted as strings. For example: +# +# en: +# "yes": yup +# enabled: "ON" + +en: + hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..03c166f --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,34 @@ +# This configuration file will be evaluated by Puma. The top-level methods that +# are invoked here are part of Puma's configuration DSL. For more information +# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. + +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch("PORT", 3000) + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart + +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..2d82e1c --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,15 @@ +Rails.application.routes.draw do + ActiveAdmin.routes(self) + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. + # Can be used by load balancers and uptime monitors to verify that the app is live. + get "up" => "rails/health#show", as: :rails_health_check + + # Render dynamic PWA files from app/views/pwa/* + get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + + # Defines the root path route ("/") + # root "posts#index" +end diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000..4942ab6 --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket-<%= Rails.env %> + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket-<%= Rails.env %> + +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/migrate/20241126092131_create_gift_cards.rb b/db/migrate/20241126092131_create_gift_cards.rb new file mode 100644 index 0000000..273ade8 --- /dev/null +++ b/db/migrate/20241126092131_create_gift_cards.rb @@ -0,0 +1,13 @@ +class CreateGiftCards < ActiveRecord::Migration[7.2] + def change + create_table :gift_cards do |t| + t.datetime :expiration_date + t.integer :registrations_available + t.string :associated_product + t.decimal :certificate_value + t.string :gl_code + + t.timestamps + end + end +end diff --git a/db/migrate/20241126092151_create_people.rb b/db/migrate/20241126092151_create_people.rb new file mode 100644 index 0000000..6fa6505 --- /dev/null +++ b/db/migrate/20241126092151_create_people.rb @@ -0,0 +1,10 @@ +class CreatePeople < ActiveRecord::Migration[7.2] + def change + create_table :people do |t| + t.string :first_name + t.string :last_name + + t.timestamps + end + end +end diff --git a/db/migrate/20241126092204_create_authentications.rb b/db/migrate/20241126092204_create_authentications.rb new file mode 100644 index 0000000..ad0a4c0 --- /dev/null +++ b/db/migrate/20241126092204_create_authentications.rb @@ -0,0 +1,13 @@ +class CreateAuthentications < ActiveRecord::Migration[7.2] + def change + create_table :authentications do |t| + t.integer :person_id + t.string :provider + t.string :uid + t.string :token + t.string :username + + t.timestamps + end + end +end diff --git a/db/migrate/20241126092217_create_api_keys.rb b/db/migrate/20241126092217_create_api_keys.rb new file mode 100644 index 0000000..29e797f --- /dev/null +++ b/db/migrate/20241126092217_create_api_keys.rb @@ -0,0 +1,10 @@ +class CreateApiKeys < ActiveRecord::Migration[7.2] + def change + create_table :api_keys do |t| + t.string :access_token + t.string :user + + t.timestamps + end + end +end diff --git a/db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb b/db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb new file mode 100644 index 0000000..6f1f706 --- /dev/null +++ b/db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb @@ -0,0 +1,5 @@ +class AddCertificateIdToGiftCards < ActiveRecord::Migration[7.2] + def change + add_column :gift_cards, :certificate_id, :string + end +end diff --git a/db/migrate/20241126094659_create_active_admin_comments.rb b/db/migrate/20241126094659_create_active_admin_comments.rb new file mode 100644 index 0000000..73e3e00 --- /dev/null +++ b/db/migrate/20241126094659_create_active_admin_comments.rb @@ -0,0 +1,16 @@ +class CreateActiveAdminComments < ActiveRecord::Migration[7.2] + def self.up + create_table :active_admin_comments do |t| + t.string :namespace + t.text :body + t.references :resource, polymorphic: true + t.references :author, polymorphic: true + t.timestamps + end + add_index :active_admin_comments, [:namespace] + end + + def self.down + drop_table :active_admin_comments + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..ee52688 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,65 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.2].define(version: 2024_11_26_094659) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "active_admin_comments", force: :cascade do |t| + t.string "namespace" + t.text "body" + t.string "resource_type" + t.bigint "resource_id" + t.string "author_type" + t.bigint "author_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author" + t.index ["namespace"], name: "index_active_admin_comments_on_namespace" + t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource" + end + + create_table "api_keys", force: :cascade do |t| + t.string "access_token" + t.string "user" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "authentications", force: :cascade do |t| + t.integer "person_id" + t.string "provider" + t.string "uid" + t.string "token" + t.string "username" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "gift_cards", force: :cascade do |t| + t.datetime "expiration_date" + t.integer "registrations_available" + t.string "associated_product" + t.decimal "certificate_value" + t.string "gl_code" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "certificate_id" + end + + create_table "people", force: :cascade do |t| + t.string "first_name" + t.string "last_name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end +end diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 0000000..4fbd6ed --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,9 @@ +# This file should ensure the existence of records required to run the application in every environment (production, +# development, test). The code here should be idempotent so that it can be executed at any point in every environment. +# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). +# +# Example: +# +# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name| +# MovieGenre.find_or_create_by!(name: genre_name) +# end diff --git a/lib/assets/.keep b/lib/assets/.keep new file mode 100644 index 0000000..e69de29 diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 0000000..e69de29 diff --git a/log/.keep b/log/.keep new file mode 100644 index 0000000..e69de29 diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..2be3af2 --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

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

+
+

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

+
+ + diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html new file mode 100644 index 0000000..7cf1e16 --- /dev/null +++ b/public/406-unsupported-browser.html @@ -0,0 +1,66 @@ + + + + Your browser is not supported (406) + + + + + + +
+
+

Your browser is not supported.

+

Please upgrade your browser to continue.

+
+
+ + diff --git a/public/422.html b/public/422.html new file mode 100644 index 0000000..c08eac0 --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

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

+
+

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

+
+ + diff --git a/public/500.html b/public/500.html new file mode 100644 index 0000000..78a030a --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

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

+
+ + diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f3b5abcbde91cf6d7a6a26e514eb7e30f476f950 GIT binary patch literal 5599 zcmeHL-D}fO6hCR_taXJlzs3}~RuB=Iujyo=i*=1|1FN%E=zNfMTjru|Q<6v{J{U!C zBEE}?j6I3sz>fzN!6}L_BKjcuASk~1;Dg|U_@d{g?V8mM`~#9U+>>*Ezw>c(PjYWA z4(;!cgge6k5E&d$G5`S-0}!Ik>CV(0Y#1}s-v_gAHhja2=W1?nBAte9D2HG<(+)uj z!5=W4u*{VKMw#{V@^NNs4TClr!FAA%ID-*gc{R%CFKEzG<6gm*9s_uy)oMGW*=nJf zw{(Mau|2FHfXIv6C0@Wk5k)F=3jo1srV-C{pl&k&)4_&JjYrnbJiul}d0^NCSh(#7h=F;3{|>EU>h z6U8_p;^wK6mAB(1b92>5-HxJ~V}@3?G`&Qq-TbJ2(&~-HsH6F#8mFaAG(45eT3VPO zM|(Jd<+;UZs;w>0Qw}0>D%{~r{uo_Fl5_Bo3ABWi zWo^j^_T3dxG6J6fH8X)$a^%TJ#PU!=LxF=#Fd9EvKx_x>q<(KY%+y-08?kN9dXjXK z**Q=yt-FTU*13ouhCdqq-0&;Ke{T3sQU9IdzhV9LhQIpq*P{N)+}|Mh+a-VV=x?R} c>%+pvTcMWshj-umO}|qP?%A)*_KlqT3uEqhU;qFB literal 0 HcmV?d00001 diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..78307cc --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..c19f78a --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/storage/.keep b/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 0000000..cee29fd --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] +end diff --git a/test/channels/application_cable/connection_test.rb b/test/channels/application_cable/connection_test.rb new file mode 100644 index 0000000..6340bf9 --- /dev/null +++ b/test/channels/application_cable/connection_test.rb @@ -0,0 +1,13 @@ +require "test_helper" + +module ApplicationCable + class ConnectionTest < ActionCable::Connection::TestCase + # test "connects with cookies" do + # cookies.signed[:user_id] = 42 + # + # connect + # + # assert_equal connection.user_id, "42" + # end + end +end diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/api_keys.yml b/test/fixtures/api_keys.yml new file mode 100644 index 0000000..5a8674a --- /dev/null +++ b/test/fixtures/api_keys.yml @@ -0,0 +1,9 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + access_token: MyString + user: MyString + +two: + access_token: MyString + user: MyString diff --git a/test/fixtures/authentications.yml b/test/fixtures/authentications.yml new file mode 100644 index 0000000..4d0715c --- /dev/null +++ b/test/fixtures/authentications.yml @@ -0,0 +1,15 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + person_id: 1 + provider: MyString + uid: MyString + token: MyString + username: MyString + +two: + person_id: 1 + provider: MyString + uid: MyString + token: MyString + username: MyString diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/gift_cards.yml b/test/fixtures/gift_cards.yml new file mode 100644 index 0000000..3ddaad3 --- /dev/null +++ b/test/fixtures/gift_cards.yml @@ -0,0 +1,15 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + expiration_date: 2024-11-26 04:21:31 + registrations_available: 1 + associated_product: MyString + certificate_value: 9.99 + gl_code: MyString + +two: + expiration_date: 2024-11-26 04:21:31 + registrations_available: 1 + associated_product: MyString + certificate_value: 9.99 + gl_code: MyString diff --git a/test/fixtures/people.yml b/test/fixtures/people.yml new file mode 100644 index 0000000..7da5ff7 --- /dev/null +++ b/test/fixtures/people.yml @@ -0,0 +1,9 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + first_name: MyString + last_name: MyString + +two: + first_name: MyString + last_name: MyString diff --git a/test/helpers/.keep b/test/helpers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/models/api_key_test.rb b/test/models/api_key_test.rb new file mode 100644 index 0000000..6dc74dc --- /dev/null +++ b/test/models/api_key_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ApiKeyTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/authentication_test.rb b/test/models/authentication_test.rb new file mode 100644 index 0000000..bb67860 --- /dev/null +++ b/test/models/authentication_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class AuthenticationTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/gift_card_test.rb b/test/models/gift_card_test.rb new file mode 100644 index 0000000..30317c0 --- /dev/null +++ b/test/models/gift_card_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class GiftCardTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/person_test.rb b/test/models/person_test.rb new file mode 100644 index 0000000..357c9af --- /dev/null +++ b/test/models/person_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PersonTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/system/.keep b/test/system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..0c22470 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,15 @@ +ENV["RAILS_ENV"] ||= "test" +require_relative "../config/environment" +require "rails/test_help" + +module ActiveSupport + class TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... + end +end diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tmp/pids/.keep b/tmp/pids/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tmp/storage/.keep b/tmp/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/javascript/.keep b/vendor/javascript/.keep new file mode 100644 index 0000000..e69de29 From 061d7d52a34bf4c3538ff2c9d3968a3c1f72b1a6 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Tue, 26 Nov 2024 05:39:06 -0500 Subject: [PATCH 2/9] update build.sh and .ruby-version to what rails_8 was --- .ruby-version | 2 +- build.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 build.sh diff --git a/.ruby-version b/.ruby-version index e391e18..9c25013 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-3.3.6 +3.3.6 diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..d107393 --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +docker buildx build $DOCKER_ARGS \ + --build-arg SIDEKIQ_CREDS=$SIDEKIQ_CREDS \ + --build-arg RUBY_VERSION=$(cat .ruby-version) \ + . +rc=$? + +if [ $rc -ne 0 ]; then + echo -e "Docker build failed" + exit $rc +fi From b2eca4edd0ec0818c209920d9575e317fd8200fc Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Mon, 2 Dec 2024 03:19:32 -0500 Subject: [PATCH 3/9] WIP building out admin view to create gift cards --- .gitignore | 2 + Gemfile | 3 + Gemfile.lock | 18 +++ app/admin/gift_card_types.rb | 18 +++ app/admin/gift_cards.rb | 19 +++ app/admin/issuances.rb | 59 +++++++++ app/assets/javascripts/active_admin.js | 1 + app/assets/stylesheets/active_admin.scss | 1 + app/controllers/application_controller.rb | 9 ++ app/models/api_key.rb | 7 + app/models/authentication.rb | 7 + app/models/gift_card.rb | 10 ++ app/models/gift_card_type.rb | 15 +++ app/models/issuance.rb | 122 ++++++++++++++++++ app/models/person.rb | 11 ++ config/initializers/active_admin.rb | 4 +- config/initializers/activeadmin_addons.rb | 12 ++ config/locales/en.yml | 6 +- .../20241126092131_create_gift_cards.rb | 1 + ...092230_add_certificate_id_to_gift_cards.rb | 5 - .../20241126110902_create_gift_card_types.rb | 15 +++ db/migrate/20241126111253_create_issuances.rb | 16 +++ ..._allocated_certificate_ids_to_issuances.rb | 5 + ...241202051127_add_numbering_to_issuances.rb | 5 + ...202065714_add_issuance_id_to_gift_cards.rb | 5 + db/schema.rb | 32 ++++- test/fixtures/gift_card_types.yml | 19 +++ test/fixtures/issuances.yml | 19 +++ test/models/gift_card_type_test.rb | 7 + test/models/issuance_test.rb | 7 + 30 files changed, 450 insertions(+), 10 deletions(-) create mode 100644 app/admin/gift_card_types.rb create mode 100644 app/admin/gift_cards.rb create mode 100644 app/admin/issuances.rb create mode 100644 app/models/gift_card_type.rb create mode 100644 app/models/issuance.rb create mode 100644 config/initializers/activeadmin_addons.rb delete mode 100644 db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb create mode 100644 db/migrate/20241126110902_create_gift_card_types.rb create mode 100644 db/migrate/20241126111253_create_issuances.rb create mode 100644 db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb create mode 100644 db/migrate/20241202051127_add_numbering_to_issuances.rb create mode 100644 db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb create mode 100644 test/fixtures/gift_card_types.yml create mode 100644 test/fixtures/issuances.yml create mode 100644 test/models/gift_card_type_test.rb create mode 100644 test/models/issuance_test.rb diff --git a/.gitignore b/.gitignore index 52fe601..b99ebed 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,5 @@ bin/deploy/erd-service-account.json !/app/assets/builds/.keep /node_modules + +.byebug_history diff --git a/Gemfile b/Gemfile index a91f5e4..19a115d 100644 --- a/Gemfile +++ b/Gemfile @@ -66,3 +66,6 @@ group :test do end gem "activeadmin", "~> 3.2" +gem "activeadmin_addons" + +gem "aasm" diff --git a/Gemfile.lock b/Gemfile.lock index 22ca93d..f0f51a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,8 @@ GEM remote: https://rubygems.org/ specs: + aasm (5.5.0) + concurrent-ruby (~> 1.0) actioncable (7.2.2) actionpack (= 7.2.2) activesupport (= 7.2.2) @@ -45,6 +47,7 @@ GEM erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) + active_material (2.1.2) activeadmin (3.2.5) arbre (~> 1.2, >= 1.2.1) csv @@ -55,6 +58,14 @@ GEM kaminari (>= 1.2.1) railties (>= 6.1) ransack (>= 4.0) + activeadmin_addons (1.10.1) + active_material + railties + redcarpet + require_all + sassc + sassc-rails + xdan-datetimepicker-rails (~> 2.5.1) activejob (7.2.2) activesupport (= 7.2.2) globalid (>= 0.3.6) @@ -284,9 +295,11 @@ GEM i18n rdoc (6.8.1) psych (>= 4.0.0) + redcarpet (3.6.0) regexp_parser (2.9.2) reline (0.5.11) io-console (~> 0.5) + require_all (3.0.0) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) @@ -376,6 +389,9 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xdan-datetimepicker-rails (2.5.4) + jquery-rails + rails (>= 3.2.16) xpath (3.2.0) nokogiri (~> 1.8) zeitwerk (2.7.1) @@ -397,7 +413,9 @@ PLATFORMS x86_64-linux-musl DEPENDENCIES + aasm activeadmin (~> 3.2) + activeadmin_addons bootsnap brakeman capybara diff --git a/app/admin/gift_card_types.rb b/app/admin/gift_card_types.rb new file mode 100644 index 0000000..68dcf3c --- /dev/null +++ b/app/admin/gift_card_types.rb @@ -0,0 +1,18 @@ +ActiveAdmin.register GiftCardType do + + # See permitted parameters documentation: + # https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters + # + # Uncomment all parameters which should be permitted for assignment + # + permit_params :label, :numbering, :contact, :prod_id, :isbn, :gl_acct, :department_number + # + # or + # + # permit_params do + # permitted = [:label, :numbering, :contact, :prod_id, :isbn, :gl_acct, :department_number] + # permitted << :other if params[:action] == 'create' && current_user.admin? + # permitted + # end + +end diff --git a/app/admin/gift_cards.rb b/app/admin/gift_cards.rb new file mode 100644 index 0000000..5de3599 --- /dev/null +++ b/app/admin/gift_cards.rb @@ -0,0 +1,19 @@ +ActiveAdmin.register GiftCard do + + # See permitted parameters documentation: + # https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters + # + # Uncomment all parameters which should be permitted for assignment + # + # permit_params :expiration_date, :registrations_available, :associated_product, :certificate_value, :gl_code, :certificate + # + # or + # + # permit_params do + # permitted = [:expiration_date, :registrations_available, :associated_product, :certificate_value, :gl_code, :certificate] + # permitted << :other if params[:action] == 'create' && current_user.admin? + # permitted + # end + + actions :all, :except => [:new] +end diff --git a/app/admin/issuances.rb b/app/admin/issuances.rb new file mode 100644 index 0000000..8cea3c7 --- /dev/null +++ b/app/admin/issuances.rb @@ -0,0 +1,59 @@ +ActiveAdmin.register Issuance do + actions :all, except: [:edit, :destroy] + + # See permitted parameters documentation: + # https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters + # + # Uncomment all parameters which should be permitted for assignment + # + permit_params :status, :gift_card_amount, :card_amount, :quantity, :begin_use_date, :end_use_date, :expiration_date, :gift_card_type_id + # + # or + # + # permit_params do + # permitted = [:status, :initiator_id, :card_amount, :quantity, :begin_use_date, :end_use_date, :expiration_date] + # permitted << :other if params[:action] == 'create' && current_user.admin? + # permitted + # end + + index do + [:initiator, :created_at, :gift_card_type, :card_amount, :quantity, :begin_use_date, :end_use_date, :expiration_date] + end + + form do |f| + f.semantic_errors + + inputs do + input :gift_card_type + input :card_amount + input :quantity + input :begin_use_date, as: :date_time_picker + input :end_use_date, as: :date_time_picker + input :expiration_date, as: :date_time_picker + + actions do + byebug + if params[:action] == "new" + submit_tag "Preview", action: :create + elsif params[:action] == "edit" && issuance.preview? + submit_tag "Issue Gift Cards", action: :create + end + end + end + end + + action_item only: :show do + if issuance.preview? + link_to 'Issue Gift Cards', issue_admin_issuance_path(issuance), method: :put, data: { confirm: 'Are you sure?' } + end + end + + member_action :issue, method: :put do + resource.issue! + redirect_to admin_issuance_path(resource), notice: "Gift cards issued!" + end + + before_create do |issuance| + issuance.initiator = Person.first # TODO current_user + end +end diff --git a/app/assets/javascripts/active_admin.js b/app/assets/javascripts/active_admin.js index d2b66c5..b656ec2 100644 --- a/app/assets/javascripts/active_admin.js +++ b/app/assets/javascripts/active_admin.js @@ -1 +1,2 @@ //= require active_admin/base +//= require activeadmin_addons/all diff --git a/app/assets/stylesheets/active_admin.scss b/app/assets/stylesheets/active_admin.scss index 41c27b3..8ef502e 100644 --- a/app/assets/stylesheets/active_admin.scss +++ b/app/assets/stylesheets/active_admin.scss @@ -1,3 +1,4 @@ +@import 'activeadmin_addons/all'; // Sass variable overrides must be declared before loading up Active Admin's styles. // // To view the variables that Active Admin provides, take a look at diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0d95db2..1235efc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,13 @@ class ApplicationController < ActionController::Base # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. allow_browser versions: :modern + + def current_admin_user + # TODO replace with okta user + Person.first + end + + def destroy_admin_user_session_path + "TODO" + end end diff --git a/app/models/api_key.rb b/app/models/api_key.rb index f44edba..8e4096e 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -1,2 +1,9 @@ class ApiKey < ApplicationRecord + def self.ransackable_attributes(auth_object = nil) + [] + end + + def self.ransackable_associations(auth_object = nil) + [] + end end diff --git a/app/models/authentication.rb b/app/models/authentication.rb index a3c8f98..4b77c46 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,2 +1,9 @@ class Authentication < ApplicationRecord + def self.ransackable_attributes(auth_object = nil) + [] + end + + def self.ransackable_associations(auth_object = nil) + [] + end end diff --git a/app/models/gift_card.rb b/app/models/gift_card.rb index 1b11df6..427dbac 100644 --- a/app/models/gift_card.rb +++ b/app/models/gift_card.rb @@ -1,2 +1,12 @@ class GiftCard < ApplicationRecord + belongs_to :issuance + + def self.ransackable_attributes(auth_object = nil) + [ :certificate, :expiration_date, :registrations_available, :associated_product, + :certificate_value, :gl_code, :created_at, :updated_at, :issuance_id ] + end + + def self.ransackable_associations(auth_object = nil) + %w(issuance) + end end diff --git a/app/models/gift_card_type.rb b/app/models/gift_card_type.rb new file mode 100644 index 0000000..80e4092 --- /dev/null +++ b/app/models/gift_card_type.rb @@ -0,0 +1,15 @@ +class GiftCardType < ApplicationRecord + validates :numbering, presence: true, format: { with: /\d*x+\d*/ } + + def to_s + label + end + + def self.ransackable_attributes(auth_object = nil) + [:label, :numbering, :contact, :prod_id, :isbn, :gl_acct, :department_number] + end + + def self.ransackable_associations(auth_object = nil) + [] + end +end diff --git a/app/models/issuance.rb b/app/models/issuance.rb new file mode 100644 index 0000000..54c17c4 --- /dev/null +++ b/app/models/issuance.rb @@ -0,0 +1,122 @@ +=begin + create_table "gift_cards", force: :cascade do |t| + t.integer "certificate" + t.datetime "expiration_date" + t.integer "registrations_available" + t.string "associated_product" + t.decimal "certificate_value" + t.string "gl_code" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "issuances", force: :cascade do |t| + t.string "status" + t.integer "initiator_id" + t.decimal "card_amount" + t.integer "quantity" + t.datetime "begin_use_date" + t.datetime "end_use_date" + t.datetime "expiration_date" + t.integer "gift_card_type_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "allocated_certificates" + t.string "numbering" + end + +=end + +class Issuance < ApplicationRecord + include AASM + + belongs_to :initiator, class_name: "Person" + belongs_to :gift_card_type + has_many :gift_cards + + validates :card_amount, numericality: true + validates :quantity, numericality: { only_integer: true } + + aasm column: :status do + state :configure, initial: true + state :preview + state :issued + + event :preview do + transitions from: :configure, to: :preview, after: :allocate_certificates + end + + event :issue do + transitions from: :preview, to: :issued, after: :create_gift_cards + transitions from: :issued, to: :issued + end + end + + before_save do + unless issued? + # keep a copy of numbering for posterity if still previewing + self.numbering = gift_card_type.numbering + end + end + + after_create do + preview! + end + + def to_s + if preview? + "Gift Card Issuance (Preview)" + elsif issued? + "Gift Card Issuance by #{initiator.full_name} at #{created_at}" + end + end + + def create_gift_cards + byebug + allocated_certificates.split(", ").each do |certificate| + gift_card = gift_cards.where(certificate: certificate).first_or_initialize + gift_card.expiration_date = expiration_date + end + end + + def numbering_regex + # add brackets around the x's so that it can be extracted, and use \d instead of x + @numbering_regex ||= numbering.gsub(/x+/) { |xs| "(#{xs.gsub("x", "\\d")})" } + end + + def allocate_certificates + largest_existing_certificate =~ numbering_regex + next_number = $1.to_i + + allocated_certificates = [] + quantity.times do + certificate = numbering.gsub(/x+/) { |xs| next_number.to_s.rjust(xs.length, "0") } + allocated_certificates << certificate + next_number += 1 + end + + self.update(allocated_certificates: allocated_certificates.join(", ")) + end + + def largest_existing_certificate + + # look for all numbers that match numbering + existing_matching_certificates = GiftCard.all.where("certificate::text LIKE ?", numbering_regex).pluck(:certificate) + + # pulling all allocated certificate ids instead of a rergex isn't ideal, but there shouldn't be many, if any, times there + # are previewed gift cards issuances while another one is being previewed + existing_matching_certificates += Issuance.preview.pluck(:allocated_certificates).find_all do |certificate| + certificate =~ numbering_regex + end + + existing_matching_certificates.collect(&:to_i).max + end + + def self.ransackable_attributes(auth_object = nil) + %w(status created_at updated_at initiator_id gift_card_type_id card_amount quantity allocated_certificates numbering gift_cards_id) + end + + def self.ransackable_associations(auth_object = nil) + %w(initiator gift_card_type gift_cards) + end +end diff --git a/app/models/person.rb b/app/models/person.rb index a8b1b85..fa0c41d 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -1,2 +1,13 @@ class Person < ApplicationRecord + def self.ransackable_attributes(auth_object = nil) + [:first_name, :last_name] + end + + def self.ransackable_associations(auth_object = nil) + [] + end + + def full_name + "#{first_name} #{last_name}" + end end diff --git a/config/initializers/active_admin.rb b/config/initializers/active_admin.rb index f8f22c8..048c77e 100644 --- a/config/initializers/active_admin.rb +++ b/config/initializers/active_admin.rb @@ -108,7 +108,7 @@ # # This setting changes the method which Active Admin calls # (within the application controller) to return the currently logged in user. - # config.current_user_method = :current_admin_user + config.current_user_method = :current_admin_user # == Logging Out # @@ -120,7 +120,7 @@ # will call the method to return the path. # # Default: - config.logout_link_path = :destroy_admin_user_session_path + # config.logout_link_path = :destroy_admin_user_session_path # This setting changes the http method used when rendering the # link. For example :get, :delete, :put, etc.. diff --git a/config/initializers/activeadmin_addons.rb b/config/initializers/activeadmin_addons.rb new file mode 100644 index 0000000..ac9ea44 --- /dev/null +++ b/config/initializers/activeadmin_addons.rb @@ -0,0 +1,12 @@ +ActiveadminAddons.setup do |config| + # Change to "default" if you want to use ActiveAdmin's default select control. + # config.default_select = "select2" + + # Set default options for DateTimePickerInput. The options you can provide are the same as in + # xdan's datetimepicker library (https://github.com/xdan/datetimepicker/tree/2.5.4). Yo need to + # pass a ruby hash, avoid camelCase keys. For example: use min_date instead of minDate key. + # config.datetime_picker_default_options = {} + + # Set DateTimePickerInput input format. This if for backend (Ruby) + # config.datetime_picker_input_format = "%Y-%m-%d %H:%M" +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6c349ae..c166c64 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -28,4 +28,8 @@ # enabled: "ON" en: - hello: "Hello world" + flash: + admin: + issuances: + create: + notice: "Please review the allocated gift card ids and click \"Issue Gift Cards\" on the right to create the gift cards. Please be patient as creating the gift cards can take a moment." diff --git a/db/migrate/20241126092131_create_gift_cards.rb b/db/migrate/20241126092131_create_gift_cards.rb index 273ade8..3590a4a 100644 --- a/db/migrate/20241126092131_create_gift_cards.rb +++ b/db/migrate/20241126092131_create_gift_cards.rb @@ -1,6 +1,7 @@ class CreateGiftCards < ActiveRecord::Migration[7.2] def change create_table :gift_cards do |t| + t.integer :certificate, limit: 8 t.datetime :expiration_date t.integer :registrations_available t.string :associated_product diff --git a/db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb b/db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb deleted file mode 100644 index 6f1f706..0000000 --- a/db/migrate/20241126092230_add_certificate_id_to_gift_cards.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddCertificateIdToGiftCards < ActiveRecord::Migration[7.2] - def change - add_column :gift_cards, :certificate_id, :string - end -end diff --git a/db/migrate/20241126110902_create_gift_card_types.rb b/db/migrate/20241126110902_create_gift_card_types.rb new file mode 100644 index 0000000..62adde0 --- /dev/null +++ b/db/migrate/20241126110902_create_gift_card_types.rb @@ -0,0 +1,15 @@ +class CreateGiftCardTypes < ActiveRecord::Migration[7.2] + def change + create_table :gift_card_types do |t| + t.string :label + t.string :numbering + t.string :contact + t.string :prod_id + t.string :isbn + t.string :gl_acct + t.string :department_number + + t.timestamps + end + end +end diff --git a/db/migrate/20241126111253_create_issuances.rb b/db/migrate/20241126111253_create_issuances.rb new file mode 100644 index 0000000..2c15665 --- /dev/null +++ b/db/migrate/20241126111253_create_issuances.rb @@ -0,0 +1,16 @@ +class CreateIssuances < ActiveRecord::Migration[7.2] + def change + create_table :issuances do |t| + t.string :status + t.integer :initiator_id + t.decimal :card_amount + t.integer :quantity + t.datetime :begin_use_date + t.datetime :end_use_date + t.datetime :expiration_date + t.integer :gift_card_type_id + + t.timestamps + end + end +end diff --git a/db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb b/db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb new file mode 100644 index 0000000..4c7eb64 --- /dev/null +++ b/db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb @@ -0,0 +1,5 @@ +class AddAllocatedCertificateIdsToIssuances < ActiveRecord::Migration[7.2] + def change + add_column :issuances, :allocated_certificates, :text + end +end diff --git a/db/migrate/20241202051127_add_numbering_to_issuances.rb b/db/migrate/20241202051127_add_numbering_to_issuances.rb new file mode 100644 index 0000000..c612c9c --- /dev/null +++ b/db/migrate/20241202051127_add_numbering_to_issuances.rb @@ -0,0 +1,5 @@ +class AddNumberingToIssuances < ActiveRecord::Migration[7.2] + def change + add_column :issuances, :numbering, :string + end +end diff --git a/db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb b/db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb new file mode 100644 index 0000000..a52f85e --- /dev/null +++ b/db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb @@ -0,0 +1,5 @@ +class AddIssuanceIdToGiftCards < ActiveRecord::Migration[7.2] + def change + add_column :gift_cards, :issuance_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index ee52688..11ce8bb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_11_26_094659) do +ActiveRecord::Schema[7.2].define(version: 2024_12_02_065714) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -45,7 +45,20 @@ t.datetime "updated_at", null: false end + create_table "gift_card_types", force: :cascade do |t| + t.string "label" + t.string "numbering" + t.string "contact" + t.string "prod_id" + t.string "isbn" + t.string "gl_acct" + t.string "department_number" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "gift_cards", force: :cascade do |t| + t.bigint "certificate" t.datetime "expiration_date" t.integer "registrations_available" t.string "associated_product" @@ -53,7 +66,22 @@ t.string "gl_code" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "certificate_id" + t.integer "issuance_id" + end + + create_table "issuances", force: :cascade do |t| + t.string "status" + t.integer "initiator_id" + t.decimal "card_amount" + t.integer "quantity" + t.datetime "begin_use_date" + t.datetime "end_use_date" + t.datetime "expiration_date" + t.integer "gift_card_type_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "numbering" + t.text "allocated_certificates" end create_table "people", force: :cascade do |t| diff --git a/test/fixtures/gift_card_types.yml b/test/fixtures/gift_card_types.yml new file mode 100644 index 0000000..8b8b9ec --- /dev/null +++ b/test/fixtures/gift_card_types.yml @@ -0,0 +1,19 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + label: MyString + numbering: MyString + contact: MyString + prod_id: MyString + isbn: MyString + gl_acct: MyString + department_number: MyString + +two: + label: MyString + numbering: MyString + contact: MyString + prod_id: MyString + isbn: MyString + gl_acct: MyString + department_number: MyString diff --git a/test/fixtures/issuances.yml b/test/fixtures/issuances.yml new file mode 100644 index 0000000..a7c3356 --- /dev/null +++ b/test/fixtures/issuances.yml @@ -0,0 +1,19 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + state: MyString + initiator_id: 1 + card_amount: 9.99 + quantity: 1 + begin_use_date: 2024-11-26 06:12:53 + end_use_date: 2024-11-26 06:12:53 + expiration_date: 2024-11-26 06:12:53 + +two: + state: MyString + initiator_id: 1 + card_amount: 9.99 + quantity: 1 + begin_use_date: 2024-11-26 06:12:53 + end_use_date: 2024-11-26 06:12:53 + expiration_date: 2024-11-26 06:12:53 diff --git a/test/models/gift_card_type_test.rb b/test/models/gift_card_type_test.rb new file mode 100644 index 0000000..9d0c7cf --- /dev/null +++ b/test/models/gift_card_type_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class GiftCardTypeTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/issuance_test.rb b/test/models/issuance_test.rb new file mode 100644 index 0000000..261f884 --- /dev/null +++ b/test/models/issuance_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class IssuanceTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From c6bc74abcabf8547dab38e560f65c9dcd526e514 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Mon, 2 Dec 2024 05:27:59 -0500 Subject: [PATCH 4/9] WIP generating gift cards able to generate now and proper auth on the admin for issuances - no editing issuance after gift cards are created --- .gitignore | 2 + app/admin/issuances.rb | 49 ++++++++++++++----- app/helpers/application_helper.rb | 3 ++ app/models/custom_authorization.rb | 13 +++++ app/models/issuance.rb | 38 ++++++++------ config/initializers/active_admin.rb | 2 +- db/migrate/20241126111253_create_issuances.rb | 6 ++- ..._allocated_certificate_ids_to_issuances.rb | 5 -- ...241202051127_add_numbering_to_issuances.rb | 5 -- db/schema.rb | 8 +-- 10 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 app/models/custom_authorization.rb delete mode 100644 db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb delete mode 100644 db/migrate/20241202051127_add_numbering_to_issuances.rb diff --git a/.gitignore b/.gitignore index b99ebed..454e54c 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ bin/deploy/erd-service-account.json /node_modules .byebug_history +*.zip +*.csv diff --git a/app/admin/issuances.rb b/app/admin/issuances.rb index 8cea3c7..70bca39 100644 --- a/app/admin/issuances.rb +++ b/app/admin/issuances.rb @@ -1,5 +1,5 @@ ActiveAdmin.register Issuance do - actions :all, except: [:edit, :destroy] + #actions :all, except: [:edit, :destroy] # See permitted parameters documentation: # https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters @@ -17,7 +17,22 @@ # end index do - [:initiator, :created_at, :gift_card_type, :card_amount, :quantity, :begin_use_date, :end_use_date, :expiration_date] + id_column + column :created_at + column :gift_card_type + column :creator + column :issuer + number_column :card_amount, as: :currency, unit: "$" + column :quantity + column :min_certificate do |issuance| + issuance.allocated_certificates.split(", ").first + end + column :max_certificate do |issuance| + issuance.allocated_certificates.split(", ").last + end + column :begin_use_date + column :end_use_date + column :expiration_date end form do |f| @@ -32,28 +47,40 @@ input :expiration_date, as: :date_time_picker actions do - byebug - if params[:action] == "new" - submit_tag "Preview", action: :create + unless issuance.issued? + f.action :submit, as: :button, label: "Save and Preview Issuance" + end + #f.action :cancel, as: :link, label: 'Cancel', class: 'cancel-link' + cancel_link +=begin + if %w(new create).include?(params[:action]) + submit_tag(:create, "Preview Issuance") elsif params[:action] == "edit" && issuance.preview? - submit_tag "Issue Gift Cards", action: :create + submit_tag(:update, "Preview Issuance") end + cancel_link +=end end end end - action_item only: :show do - if issuance.preview? - link_to 'Issue Gift Cards', issue_admin_issuance_path(issuance), method: :put, data: { confirm: 'Are you sure?' } - end +=begin + action_item :destroy, only: :show, if: -> { resource.preview? } do + link_to 'Delete Issuance', issue_admin_issuance_path(issuance), method: :destroy, data: { confirm: 'Are you sure?' } + end +=end + + action_item :issue, only: :show, if: -> { resource.preview? } do + link_to 'Issue Gift Cards', issue_admin_issuance_path(issuance), method: :put, data: { confirm: 'Are you sure?' } end member_action :issue, method: :put do resource.issue! + resource.update(issuer_id: current_admin_user.id, issued_at: Time.now) redirect_to admin_issuance_path(resource), notice: "Gift cards issued!" end before_create do |issuance| - issuance.initiator = Person.first # TODO current_user + issuance.creator = current_admin_user end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be79..ce706a1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,5 @@ module ApplicationHelper + def destroy_admin_user_session_path + "HERE" + end end diff --git a/app/models/custom_authorization.rb b/app/models/custom_authorization.rb new file mode 100644 index 0000000..59dfde9 --- /dev/null +++ b/app/models/custom_authorization.rb @@ -0,0 +1,13 @@ +class CustomAuthorization < ActiveAdmin::AuthorizationAdapter + def authorized?(action, subject = nil) + return true if action == :read + + case subject + when Issuance + !subject.issued? # can't do anything on issuances that already happened + else + true + end + end +end + diff --git a/app/models/issuance.rb b/app/models/issuance.rb index 54c17c4..f5074da 100644 --- a/app/models/issuance.rb +++ b/app/models/issuance.rb @@ -30,9 +30,10 @@ class Issuance < ApplicationRecord include AASM - belongs_to :initiator, class_name: "Person" + belongs_to :creator, class_name: "Person" + belongs_to :issuer, class_name: "Person", optional: true belongs_to :gift_card_type - has_many :gift_cards + has_many :gift_cards validates :card_amount, numericality: true validates :quantity, numericality: { only_integer: true } @@ -43,7 +44,7 @@ class Issuance < ApplicationRecord state :issued event :preview do - transitions from: :configure, to: :preview, after: :allocate_certificates + transitions from: :configure, to: :preview end event :issue do @@ -53,9 +54,10 @@ class Issuance < ApplicationRecord end before_save do - unless issued? + unless issued? # keep a copy of numbering for posterity if still previewing self.numbering = gift_card_type.numbering + set_allocated_certificates end end @@ -67,25 +69,29 @@ def to_s if preview? "Gift Card Issuance (Preview)" elsif issued? - "Gift Card Issuance by #{initiator.full_name} at #{created_at}" + "Gift Card Issuance by #{issuer.full_name} at #{created_at}" end end def create_gift_cards byebug allocated_certificates.split(", ").each do |certificate| - gift_card = gift_cards.where(certificate: certificate).first_or_initialize + gift_card = gift_cards.where(certificate: certificate).first_or_initialize gift_card.expiration_date = expiration_date end end + def numbering_regex_str + numbering.gsub(/x+/) { |xs| "(#{xs.gsub("x", "\\d")})" } + end + def numbering_regex # add brackets around the x's so that it can be extracted, and use \d instead of x - @numbering_regex ||= numbering.gsub(/x+/) { |xs| "(#{xs.gsub("x", "\\d")})" } + @numbering_regex ||= /#{numbering_regex_str}/ end - def allocate_certificates - largest_existing_certificate =~ numbering_regex + def set_allocated_certificates + largest_existing_certificate.to_s =~ numbering_regex next_number = $1.to_i allocated_certificates = [] @@ -95,28 +101,28 @@ def allocate_certificates next_number += 1 end - self.update(allocated_certificates: allocated_certificates.join(", ")) + self.allocated_certificates = allocated_certificates.join(", ") end def largest_existing_certificate # look for all numbers that match numbering - existing_matching_certificates = GiftCard.all.where("certificate::text LIKE ?", numbering_regex).pluck(:certificate) + existing_matching_certificates = GiftCard.all.where("certificate::text LIKE ?", numbering_regex_str).pluck(:certificate) # pulling all allocated certificate ids instead of a rergex isn't ideal, but there shouldn't be many, if any, times there # are previewed gift cards issuances while another one is being previewed - existing_matching_certificates += Issuance.preview.pluck(:allocated_certificates).find_all do |certificate| - certificate =~ numbering_regex - end + existing_matching_certificates += Issuance.preview.pluck(:allocated_certificates).collect do |allocated_certificates| + allocated_certificates.split(", ").find_all { |certificate| certificate =~ numbering_regex } + end.flatten existing_matching_certificates.collect(&:to_i).max end def self.ransackable_attributes(auth_object = nil) - %w(status created_at updated_at initiator_id gift_card_type_id card_amount quantity allocated_certificates numbering gift_cards_id) + %w(status created_at updated_at creator_id issuer_id gift_card_type_id card_amount quantity allocated_certificates numbering gift_cards_id) end def self.ransackable_associations(auth_object = nil) - %w(initiator gift_card_type gift_cards) + %w(creator issuer gift_card_type gift_cards) end end diff --git a/config/initializers/active_admin.rb b/config/initializers/active_admin.rb index 048c77e..85c4cc7 100644 --- a/config/initializers/active_admin.rb +++ b/config/initializers/active_admin.rb @@ -79,7 +79,7 @@ # method in a before filter of all controller actions to # ensure that there is a user with proper rights. You can use # CanCanAdapter or make your own. Please refer to documentation. - # config.authorization_adapter = ActiveAdmin::CanCanAdapter + config.authorization_adapter = "CustomAuthorization" # In case you prefer Pundit over other solutions you can here pass # the name of default policy class. This policy will be used in every diff --git a/db/migrate/20241126111253_create_issuances.rb b/db/migrate/20241126111253_create_issuances.rb index 2c15665..476973a 100644 --- a/db/migrate/20241126111253_create_issuances.rb +++ b/db/migrate/20241126111253_create_issuances.rb @@ -2,13 +2,17 @@ class CreateIssuances < ActiveRecord::Migration[7.2] def change create_table :issuances do |t| t.string :status - t.integer :initiator_id + t.integer :creator_id + t.integer :issuer_id t.decimal :card_amount t.integer :quantity t.datetime :begin_use_date t.datetime :end_use_date t.datetime :expiration_date t.integer :gift_card_type_id + t.text :allocated_certificates + t.string :numbering + t.datetime :issued_at t.timestamps end diff --git a/db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb b/db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb deleted file mode 100644 index 4c7eb64..0000000 --- a/db/migrate/20241202051016_add_allocated_certificate_ids_to_issuances.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddAllocatedCertificateIdsToIssuances < ActiveRecord::Migration[7.2] - def change - add_column :issuances, :allocated_certificates, :text - end -end diff --git a/db/migrate/20241202051127_add_numbering_to_issuances.rb b/db/migrate/20241202051127_add_numbering_to_issuances.rb deleted file mode 100644 index c612c9c..0000000 --- a/db/migrate/20241202051127_add_numbering_to_issuances.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddNumberingToIssuances < ActiveRecord::Migration[7.2] - def change - add_column :issuances, :numbering, :string - end -end diff --git a/db/schema.rb b/db/schema.rb index 11ce8bb..e34f938 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -71,17 +71,19 @@ create_table "issuances", force: :cascade do |t| t.string "status" - t.integer "initiator_id" + t.integer "creator_id" + t.integer "issuer_id" t.decimal "card_amount" t.integer "quantity" t.datetime "begin_use_date" t.datetime "end_use_date" t.datetime "expiration_date" t.integer "gift_card_type_id" + t.text "allocated_certificates" + t.string "numbering" + t.datetime "issued_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.string "numbering" - t.text "allocated_certificates" end create_table "people", force: :cascade do |t| From 0f498ed6fee123d6a850b106dfafd21c2dd0bbf0 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Mon, 2 Dec 2024 06:23:54 -0500 Subject: [PATCH 5/9] properly check existing ids with regex various other fixes and progress towards issuing and displaying gift cards --- app/admin/issuances.rb | 1 + app/assets/stylesheets/active_admin.scss | 4 ++++ app/models/gift_card.rb | 5 ++--- app/models/issuance.rb | 17 +++++++++-------- db/migrate/20241126092131_create_gift_cards.rb | 3 ++- ...41202065714_add_issuance_id_to_gift_cards.rb | 5 ----- db/schema.rb | 6 +++--- 7 files changed, 21 insertions(+), 20 deletions(-) delete mode 100644 db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb diff --git a/app/admin/issuances.rb b/app/admin/issuances.rb index 70bca39..07c1a2b 100644 --- a/app/admin/issuances.rb +++ b/app/admin/issuances.rb @@ -18,6 +18,7 @@ index do id_column + tag_column :status column :created_at column :gift_card_type column :creator diff --git a/app/assets/stylesheets/active_admin.scss b/app/assets/stylesheets/active_admin.scss index 8ef502e..6af7e57 100644 --- a/app/assets/stylesheets/active_admin.scss +++ b/app/assets/stylesheets/active_admin.scss @@ -16,3 +16,7 @@ // For example, to change the default status-tag color: // // .status_tag { background: #6090DB; } + +.status_tag.issued { + background: black; +} diff --git a/app/models/gift_card.rb b/app/models/gift_card.rb index 427dbac..feb81b9 100644 --- a/app/models/gift_card.rb +++ b/app/models/gift_card.rb @@ -2,11 +2,10 @@ class GiftCard < ApplicationRecord belongs_to :issuance def self.ransackable_attributes(auth_object = nil) - [ :certificate, :expiration_date, :registrations_available, :associated_product, - :certificate_value, :gl_code, :created_at, :updated_at, :issuance_id ] + %w(certificate expiration_date registrations_available associated_product certificate_value gl_code created_at updated_at issuance_id) end def self.ransackable_associations(auth_object = nil) - %w(issuance) + %w(issuance issuance_id) end end diff --git a/app/models/issuance.rb b/app/models/issuance.rb index f5074da..8b75394 100644 --- a/app/models/issuance.rb +++ b/app/models/issuance.rb @@ -69,12 +69,11 @@ def to_s if preview? "Gift Card Issuance (Preview)" elsif issued? - "Gift Card Issuance by #{issuer.full_name} at #{created_at}" + "Issuance by #{issuer.full_name} #{created_at}" end end def create_gift_cards - byebug allocated_certificates.split(", ").each do |certificate| gift_card = gift_cards.where(certificate: certificate).first_or_initialize gift_card.expiration_date = expiration_date @@ -91,8 +90,7 @@ def numbering_regex end def set_allocated_certificates - largest_existing_certificate.to_s =~ numbering_regex - next_number = $1.to_i + next_number = largest_existing_number_in_certificate + 1 allocated_certificates = [] quantity.times do @@ -104,10 +102,10 @@ def set_allocated_certificates self.allocated_certificates = allocated_certificates.join(", ") end - def largest_existing_certificate - + # largest number in certificates represented in x's in format, for all certificates matching format + def largest_existing_number_in_certificate # look for all numbers that match numbering - existing_matching_certificates = GiftCard.all.where("certificate::text LIKE ?", numbering_regex_str).pluck(:certificate) + existing_matching_certificates = GiftCard.all.where("certificate ~* ?", numbering_regex_str).pluck(:certificate) # pulling all allocated certificate ids instead of a rergex isn't ideal, but there shouldn't be many, if any, times there # are previewed gift cards issuances while another one is being previewed @@ -115,7 +113,10 @@ def largest_existing_certificate allocated_certificates.split(", ").find_all { |certificate| certificate =~ numbering_regex } end.flatten - existing_matching_certificates.collect(&:to_i).max + existing_matching_certificates.collect{ |certificate| + certificate =~ numbering_regex + $1.to_i + }.max || 1 end def self.ransackable_attributes(auth_object = nil) diff --git a/db/migrate/20241126092131_create_gift_cards.rb b/db/migrate/20241126092131_create_gift_cards.rb index 3590a4a..8128582 100644 --- a/db/migrate/20241126092131_create_gift_cards.rb +++ b/db/migrate/20241126092131_create_gift_cards.rb @@ -1,7 +1,8 @@ class CreateGiftCards < ActiveRecord::Migration[7.2] def change create_table :gift_cards do |t| - t.integer :certificate, limit: 8 + t.integer :issuance_id + t.string :certificate t.datetime :expiration_date t.integer :registrations_available t.string :associated_product diff --git a/db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb b/db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb deleted file mode 100644 index a52f85e..0000000 --- a/db/migrate/20241202065714_add_issuance_id_to_gift_cards.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AddIssuanceIdToGiftCards < ActiveRecord::Migration[7.2] - def change - add_column :gift_cards, :issuance_id, :integer - end -end diff --git a/db/schema.rb b/db/schema.rb index e34f938..7f25982 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_12_02_065714) do +ActiveRecord::Schema[7.2].define(version: 2024_11_26_111253) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -58,7 +58,8 @@ end create_table "gift_cards", force: :cascade do |t| - t.bigint "certificate" + t.integer "issuance_id" + t.string "certificate" t.datetime "expiration_date" t.integer "registrations_available" t.string "associated_product" @@ -66,7 +67,6 @@ t.string "gl_code" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.integer "issuance_id" end create_table "issuances", force: :cascade do |t| From f159eb71a6fa488172723fa6447ffb21a8972aef Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Tue, 3 Dec 2024 15:28:16 -0500 Subject: [PATCH 6/9] more tweaks and import script store gift card type on each gift card --- Gemfile | 2 + Gemfile.lock | 3 + app/models/gift_card.rb | 5 +- app/models/gift_card_type.rb | 2 + app/models/issuance.rb | 13 +- .../20241126092131_create_gift_cards.rb | 1 + db/schema.rb | 1 + import.rb | 136 ++++++++++++++++++ lib/has_numbering.rb | 10 ++ 9 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 import.rb create mode 100644 lib/has_numbering.rb diff --git a/Gemfile b/Gemfile index 19a115d..45d6ce8 100644 --- a/Gemfile +++ b/Gemfile @@ -69,3 +69,5 @@ gem "activeadmin", "~> 3.2" gem "activeadmin_addons" gem "aasm" + +gem "activerecord-import" diff --git a/Gemfile.lock b/Gemfile.lock index f0f51a9..5af6688 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -75,6 +75,8 @@ GEM activemodel (= 7.2.2) activesupport (= 7.2.2) timeout (>= 0.4.0) + activerecord-import (1.8.1) + activerecord (>= 4.2) activestorage (7.2.2) actionpack (= 7.2.2) activejob (= 7.2.2) @@ -416,6 +418,7 @@ DEPENDENCIES aasm activeadmin (~> 3.2) activeadmin_addons + activerecord-import bootsnap brakeman capybara diff --git a/app/models/gift_card.rb b/app/models/gift_card.rb index feb81b9..1f3336d 100644 --- a/app/models/gift_card.rb +++ b/app/models/gift_card.rb @@ -1,11 +1,12 @@ class GiftCard < ApplicationRecord belongs_to :issuance + belongs_to :gift_card_type def self.ransackable_attributes(auth_object = nil) - %w(certificate expiration_date registrations_available associated_product certificate_value gl_code created_at updated_at issuance_id) + %w(certificate expiration_date registrations_available associated_product certificate_value gl_code created_at updated_at issuance_id gift_card_type_id) end def self.ransackable_associations(auth_object = nil) - %w(issuance issuance_id) + %w(issuance issuance_id gift_card_type gift_card_type_id) end end diff --git a/app/models/gift_card_type.rb b/app/models/gift_card_type.rb index 80e4092..797c103 100644 --- a/app/models/gift_card_type.rb +++ b/app/models/gift_card_type.rb @@ -1,4 +1,6 @@ class GiftCardType < ApplicationRecord + include HasNumbering + validates :numbering, presence: true, format: { with: /\d*x+\d*/ } def to_s diff --git a/app/models/issuance.rb b/app/models/issuance.rb index 8b75394..a37255e 100644 --- a/app/models/issuance.rb +++ b/app/models/issuance.rb @@ -29,6 +29,7 @@ class Issuance < ApplicationRecord include AASM + include HasNumbering belongs_to :creator, class_name: "Person" belongs_to :issuer, class_name: "Person", optional: true @@ -77,18 +78,12 @@ def create_gift_cards allocated_certificates.split(", ").each do |certificate| gift_card = gift_cards.where(certificate: certificate).first_or_initialize gift_card.expiration_date = expiration_date + gift_card.gift_card_type = gift_card_type + gift_card.issuance = self + gift_card.save! end end - def numbering_regex_str - numbering.gsub(/x+/) { |xs| "(#{xs.gsub("x", "\\d")})" } - end - - def numbering_regex - # add brackets around the x's so that it can be extracted, and use \d instead of x - @numbering_regex ||= /#{numbering_regex_str}/ - end - def set_allocated_certificates next_number = largest_existing_number_in_certificate + 1 diff --git a/db/migrate/20241126092131_create_gift_cards.rb b/db/migrate/20241126092131_create_gift_cards.rb index 8128582..7b3d478 100644 --- a/db/migrate/20241126092131_create_gift_cards.rb +++ b/db/migrate/20241126092131_create_gift_cards.rb @@ -2,6 +2,7 @@ class CreateGiftCards < ActiveRecord::Migration[7.2] def change create_table :gift_cards do |t| t.integer :issuance_id + t.integer :gift_card_type_id t.string :certificate t.datetime :expiration_date t.integer :registrations_available diff --git a/db/schema.rb b/db/schema.rb index 7f25982..d858b07 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -59,6 +59,7 @@ create_table "gift_cards", force: :cascade do |t| t.integer "issuance_id" + t.integer "gift_card_type_id" t.string "certificate" t.datetime "expiration_date" t.integer "registrations_available" diff --git a/import.rb b/import.rb new file mode 100644 index 0000000..4facf94 --- /dev/null +++ b/import.rb @@ -0,0 +1,136 @@ +#=> GiftCardType(id: integer, label: string, numbering: string, contact: string, prod_id: string, isbn: string, gl_acct: string, department_number: string, created_at: datetime, updated_at: datetime) + +GiftCardType.delete_all + +t = GiftCardType.where(label: "Regular Rate (Green) Paid Gift Card").first_or_initialize +t.numbering = "3500xxxxx0" +t.prod_id = "CER21842" +t.isbn = "978-1602005761" +t.save! + +t = GiftCardType.where(label: "Half Price (Blue) Paid Gift Card").first_or_initialize +t.numbering = "1750xxxxx0" +t.prod_id = "CER21841" +t.isbn = "978-1602006676" +t.save! + +t = GiftCardType.where(label: "Marketing Department Card").first_or_initialize +t.numbering = "4240xxxxx0" +t.gl_acct = "63301" +t.department_number = "4240-15999" +t.contact = "Taylor" +t.save! + +t = GiftCardType.where(label: "Donor Dept. Department Card").first_or_initialize +t.numbering = "3210xxxxx0" +t.gl_acct = "63301" +t.department_number = "3210-38100" +t.contact = "Quinton/Gene" +t.save! + +t = GiftCardType.where(label: "Partners Department Card").first_or_initialize +t.numbering = "3215xxxxx0" +t.gl_acct = "63301" +t.department_number = "3215-38116" +t.contact = "Quinton/Gene" +t.save! + +t = GiftCardType.where(label: "President Department Card").first_or_initialize +t.numbering = "1100xxxxx0" +t.gl_acct = "63301" +t.department_number = "1100-50050" +t.contact = "Brian Borger" +t.save! + +t = GiftCardType.where(label: "Field Core Department Card").first_or_initialize +t.numbering = "4710xxxxx0" +t.gl_acct = "63301" +t.department_number = "4710-15999" +t.contact = "Brandon" +t.save! + +t = GiftCardType.where(label: "Innovative Events Department Card").first_or_initialize +t.numbering = "4228xxxxx0" +t.gl_acct = "63301" +t.department_number = "4228-14400" +t.contact = "Tim Bell/Tanya" +t.save! + +t = GiftCardType.where(label: "Speaker Dept. Card").first_or_initialize +t.numbering = "1470xxxxx0" +t.gl_acct = "63301" +t.department_number = "1470-34998" +t.contact = "Jennifer Abbott" +t.save! + +t = GiftCardType.where(label: "Corporate Department Card").first_or_initialize +t.numbering = "4241xxxx0" +t.gl_acct = "63301" +t.department_number = "4241-45100" +t.contact = "Glen Flagerstrom" +t.save! + +t = GiftCardType.where(label: "Station Relations Card").first_or_initialize +t.numbering = "1420xxxxx0" +t.gl_acct = "63301" +t.department_number = "1420-34001" +t.contact = "Maddison Villafane" +t.save! + +t = GiftCardType.where(label: "Unknown").first_or_initialize +t.numbering = "xxxxx" +t.contact = "This is for initial import cards with a certificate id that doesn't match any gift card numbering" +t.save! + +#=> GiftCardType(id: integer, label: string, numbering: string, contact: string, prod_id: string, isbn: string, gl_acct: string, department_number: string, created_at: datetime, updated_at: datetime) + +#=> GiftCard(id: integer, certificate: integer, expiration_date: datetime, registrations_available: integer, associated_product: string, certificate_value: decimal, gl_code: string, created_at: datetime, updated_at: datetime, issuance_id: integer) + +total = `wc -l "FL_EventCertificate_202411261112.csv"`.split(" ").first.to_i +unknown_gct = GiftCardType.last + +system = Person.where(first_name: "Initial", last_name: "Import").first_or_create + +GiftCard.delete_all +Issuance.delete_all + +initial_issuances = GiftCardType.all.collect do |gct| + issuance = Issuance.where(creator_id: system, issuer_id: system, gift_card_type: gct).first_or_create + issuance.card_amount = 0 + issuance.quantity = 0 + issuance.save! + [ gct, issuance ] +end.to_h + +all_types = GiftCardType.all +batch = [] + +i = 0 +CSV.foreach("FL_EventCertificate_202411261112.csv", headers: true) do |row| + puts("[#{i += 1}/#{total}, #{(i / total.to_f * 100).round(2)}%]") + + # ex: # + # + gct = all_types.detect{ |gtt| gtt.numbering_regex.match(row["certificateId"]) } || unknown_gct + issuance = initial_issuances[gct] + + #gc = GiftCard.where(certificate_id: row["certificateId"]).first_or_initialize + gc = GiftCard.new + gc.certificate = row["certificateId"] + gc.issuance = issuance + gc.gift_card_type = gct + gc.expiration_date = DateTime.parse(row["expirationDate"]) + gc.registrations_available = row["numberRegistrations"].to_i + gc.certificate_value = row["certificateValue"].to_d + gc.gl_code = row["glCode"].to_d + gc.created_at = row["addDate"].to_d + gc.updated_at = row["modifiedDate"].to_d + gc.associated_product = row["associatedProduct"].to_d + + batch << gc + + if batch.length >= 1000 + GiftCard.import(batch) + batch = [] + end +end diff --git a/lib/has_numbering.rb b/lib/has_numbering.rb new file mode 100644 index 0000000..2a2d383 --- /dev/null +++ b/lib/has_numbering.rb @@ -0,0 +1,10 @@ +module HasNumbering + def numbering_regex_str + numbering.gsub(/x+/) { |xs| "(#{xs.gsub("x", "\\d")})" } + end + + def numbering_regex + # add brackets around the x's so that it can be extracted, and use \d instead of x + @numbering_regex ||= /#{numbering_regex_str}/ + end +end From e7f4f86e2359831a8df9889d5caf907e3f8a31c5 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Tue, 3 Dec 2024 19:25:49 -0500 Subject: [PATCH 7/9] fix numbering bug --- import.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/import.rb b/import.rb index 4facf94..b960a21 100644 --- a/import.rb +++ b/import.rb @@ -64,7 +64,7 @@ t.save! t = GiftCardType.where(label: "Corporate Department Card").first_or_initialize -t.numbering = "4241xxxx0" +t.numbering = "4241xxxxx0" t.gl_acct = "63301" t.department_number = "4241-45100" t.contact = "Glen Flagerstrom" From 4221e321cc8b3575186092ac97211f53acc3e6b8 Mon Sep 17 00:00:00 2001 From: Andrew Roth Date: Wed, 4 Dec 2024 10:20:09 -0500 Subject: [PATCH 8/9] add back workflow for build-deploy-ecs --- .github/workflows/build-deploy-ecs.yml | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/build-deploy-ecs.yml diff --git a/.github/workflows/build-deploy-ecs.yml b/.github/workflows/build-deploy-ecs.yml new file mode 100644 index 0000000..9a90cb6 --- /dev/null +++ b/.github/workflows/build-deploy-ecs.yml @@ -0,0 +1,37 @@ +name: Build & Deploy ECS + +on: + push: + branches: + # Automatically build and deploy master and staging. Additional branches may be added. + # - main + - staging + workflow_dispatch: + # Allows manual build and deploy of any branch/ref + inputs: + auto-deploy: + type: boolean + description: Deploy image after building? + required: true + default: 'false' + +jobs: + # Build and push container image to ECR. Builds occur in the project repository. + build: + name: Build + uses: CruGlobal/.github/.github/workflows/build-ecs.yml@v1 + + # Triggers an ECS deployment in https://github.com/CruGlobal/cru-deploy/actions. + # All deployments happen in the cru-deploy repo. + deploy: + name: Deploy + runs-on: ubuntu-latest + needs: build + if: github.event_name != 'workflow_dispatch' || github.event.inputs.auto-deploy == 'true' + steps: + - uses: CruGlobal/.github/actions/trigger-deploy@v1 + with: + github-token: ${{ secrets.CRU_DEPLOY_GITHUB_TOKEN }} + project-name: ${{ needs.build.outputs.project-name }} + environment: ${{ needs.build.outputs.environment }} + build-number: ${{ needs.build.outputs.build-number }} From 3596d15af74e76d2fd89c5b55b5ac0539dafb733 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:37:42 +0000 Subject: [PATCH 9/9] Bump importmap-rails from 2.0.3 to 2.1.0 Bumps [importmap-rails](https://github.com/rails/importmap-rails) from 2.0.3 to 2.1.0. - [Release notes](https://github.com/rails/importmap-rails/releases) - [Commits](https://github.com/rails/importmap-rails/compare/v2.0.3...v2.1.0) --- updated-dependencies: - dependency-name: importmap-rails dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5af6688..a0495b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -137,7 +137,7 @@ GEM dotenv (= 3.1.4) railties (>= 6.1) drb (2.2.1) - erubi (1.13.0) + erubi (1.13.1) ffi (1.17.0-aarch64-linux-gnu) ffi (1.17.0-aarch64-linux-musl) ffi (1.17.0-arm-linux-gnu) @@ -158,7 +158,7 @@ GEM activesupport (>= 5.2) i18n (1.14.6) concurrent-ruby (~> 1.0) - importmap-rails (2.0.3) + importmap-rails (2.1.0) actionpack (>= 6.0.0) activesupport (>= 6.0.0) railties (>= 6.0.0) @@ -167,8 +167,8 @@ GEM has_scope (>= 0.6) railties (>= 6.0) responders (>= 2) - io-console (0.7.2) - irb (1.14.1) + io-console (0.8.0) + irb (1.14.3) rdoc (>= 4.0.0) reline (>= 0.4.2) jbuilder (2.13.0) @@ -193,7 +193,7 @@ GEM kaminari-core (1.2.2) language_server-protocol (3.17.0.3) lint_roller (1.1.0) - logger (1.6.1) + logger (1.6.4) loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -206,7 +206,7 @@ GEM matrix (0.4.2) method_source (1.1.0) mini_mime (1.1.5) - minitest (5.25.2) + minitest (5.25.4) msgpack (1.7.5) net-imap (0.5.1) date @@ -218,17 +218,17 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.4) - nokogiri (1.16.7-aarch64-linux) + nokogiri (1.17.2-aarch64-linux) racc (~> 1.4) - nokogiri (1.16.7-arm-linux) + nokogiri (1.17.2-arm-linux) racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.17.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86-linux) + nokogiri (1.17.2-x86-linux) racc (~> 1.4) - nokogiri (1.16.7-x86_64-darwin) + nokogiri (1.17.2-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.17.2-x86_64-linux) racc (~> 1.4) parallel (1.26.3) parser (3.3.6.0) @@ -247,7 +247,8 @@ GEM pry-stack_explorer (0.6.1) binding_of_caller (~> 1.0) pry (~> 0.13) - psych (5.2.0) + psych (5.2.2) + date stringio public_suffix (6.0.1) puma (6.5.0) @@ -278,9 +279,9 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) railties (7.2.2) actionpack (= 7.2.2) activesupport (= 7.2.2) @@ -295,11 +296,11 @@ GEM activerecord (>= 6.1.5) activesupport (>= 6.1.5) i18n - rdoc (6.8.1) + rdoc (6.10.0) psych (>= 4.0.0) redcarpet (3.6.0) regexp_parser (2.9.2) - reline (0.5.11) + reline (0.6.0) io-console (~> 0.5) require_all (3.0.0) responders (3.1.1) @@ -345,7 +346,7 @@ GEM sprockets (> 3.0) sprockets-rails tilt - securerandom (0.3.2) + securerandom (0.4.1) selenium-webdriver (4.27.0) base64 (~> 0.2) logger (~> 1.4) @@ -381,7 +382,7 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.6.0) - useragent (0.16.10) + useragent (0.16.11) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0)