72 | <%= render(FieldsetHeaderComponent.new(
73 | title: "Change password",
74 | description: "Update your password associated with your account."
75 | )) %>
76 |
77 |
78 | <%= render partial: "passwords/form" %>
79 |
80 |
81 |
82 |
83 | <%= render(FieldsetHeaderComponent.new(
84 | title: "Log out other sessions",
85 | description: "You can manage your sessions to log out across any of your other devices."
86 | )) %>
87 |
88 |
95 | <%= render(FieldsetHeaderComponent.new(
96 | title: "Delete account",
97 | description: "No longer want to use our service? You can delete your account here. This action is not reversible. All information related to this account will be deleted permanently."
98 | )) %>
99 |
100 |
103 |
If this was you, carry on. We could notify you about sign-ins from this device again.
14 |
15 |
If you don't recognize this device, someone else may have accessed your account. You should immediately <%= link_to "change your password", new_identity_password_reset_url %>.
16 |
17 |
Tip: It's a good idea to periodically review all of the <%= link_to "devices and sessions", sessions_url %> in your account for suspicious activity.
18 |
19 |
20 |
21 |
Have questions or need help? Just reply to this email and our support team will help you sort it out.
This is to confirm that <%= @user.email %> is the email you want to use on your account. If you ever lose your password, that's where we'll email a reset link.
4 |
5 |
You must hit the link below to confirm that you received this email.
6 |
7 | <%= link_to "Yes, use this email for my account", edit_identity_email_verification_url(sid: @signed_id) %>
8 |
9 |
10 |
11 |
Have questions or need help? Just reply to this email and our support team will help you sort it out.
Can't remember your password for <%= @user.email %>? That's OK, it happens. Just hit the link below to set a new one.
4 |
5 |
<%= link_to "Reset my password", edit_identity_password_reset_url(sid: @signed_id) %>
6 |
7 |
If you did not request a password reset you can safely ignore this email, it expires in 20 minutes. Only someone with access to this email account can reset your password.
8 |
9 |
10 |
11 |
Have questions or need help? Just reply to this email and our support team will help you sort it out.
12 |
--------------------------------------------------------------------------------
/bin/brakeman:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | ARGV.unshift("--ensure-latest")
6 |
7 | load Gem.bin_path("brakeman", "brakeman")
8 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new do
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34 | bundler_version = $1
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../../Gemfile", __FILE__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_requirement
64 | @bundler_requirement ||=
65 | env_var_version || cli_arg_version ||
66 | bundler_requirement_for(lockfile_version)
67 | end
68 |
69 | def bundler_requirement_for(version)
70 | return "#{Gem::Requirement.default}.a" unless version
71 |
72 | bundler_gem_version = Gem::Version.new(version)
73 |
74 | requirement = bundler_gem_version.approximate_recommendation
75 |
76 | return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0")
77 |
78 | requirement += ".a" if bundler_gem_version.prerelease?
79 |
80 | requirement
81 | end
82 |
83 | def load_bundler!
84 | ENV["BUNDLE_GEMFILE"] ||= gemfile
85 |
86 | activate_bundler
87 | end
88 |
89 | def activate_bundler
90 | gem_error = activation_error_handling do
91 | gem "bundler", bundler_requirement
92 | end
93 | return if gem_error.nil?
94 | require_error = activation_error_handling do
95 | require "bundler/version"
96 | end
97 | return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
98 | warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
99 | exit 42
100 | end
101 |
102 | def activation_error_handling
103 | yield
104 | nil
105 | rescue StandardError, LoadError => e
106 | e
107 | end
108 | end
109 |
110 | m.load_bundler!
111 |
112 | if m.invoked_as_script?
113 | load Gem.bin_path("bundler", "bundle")
114 | end
115 |
--------------------------------------------------------------------------------
/bin/dev:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | if ! gem list foreman -i --silent; then
4 | echo "Installing foreman..."
5 | gem install foreman
6 | fi
7 |
8 | exec foreman start -f Procfile.dev "$@"
9 |
--------------------------------------------------------------------------------
/bin/importmap:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require_relative "../config/application"
4 | require "importmap/commands"
5 |
--------------------------------------------------------------------------------
/bin/jobs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require_relative "../config/environment"
4 | require "solid_queue/cli"
5 |
6 | SolidQueue::Cli.start(ARGV)
7 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path("../config/application", __dir__)
3 | require_relative "../config/boot"
4 | require "rails/commands"
5 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative "../config/boot"
3 | require "rake"
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/bin/rspec:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'rspec' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "pathname"
12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13 | Pathname.new(__FILE__).realpath)
14 |
15 | bundle_binstub = File.expand_path("../bundle", __FILE__)
16 |
17 | if File.file?(bundle_binstub)
18 | if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19 | load(bundle_binstub)
20 | else
21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23 | end
24 | end
25 |
26 | require "rubygems"
27 | require "bundler/setup"
28 |
29 | load Gem.bin_path("rspec-core", "rspec")
30 |
--------------------------------------------------------------------------------
/bin/rubocop:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | # explicit rubocop config increases performance slightly while avoiding config confusion.
6 | ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
7 |
8 | load Gem.bin_path("rubocop", "rubocop")
9 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | APP_ROOT = File.expand_path("..", __dir__)
5 |
6 | def system!(*args)
7 | system(*args, exception: true)
8 | end
9 |
10 | FileUtils.chdir APP_ROOT do
11 | # This script is a way to set up or update your development environment automatically.
12 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
13 | # Add necessary setup steps to this file.
14 |
15 | puts "== Installing dependencies =="
16 | system("bundle check") || system!("bundle install")
17 |
18 | # puts "\n== Copying sample files =="
19 | # unless File.exist?("config/database.yml")
20 | # FileUtils.cp "config/database.yml.sample", "config/database.yml"
21 | # end
22 |
23 | puts "\n== Preparing database =="
24 | system! "bin/rails db:prepare"
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! "bin/rails log:clear tmp:clear"
28 |
29 | unless ARGV.include?("--skip-server")
30 | puts "\n== Starting development server =="
31 | STDOUT.flush # flush the output before exec(2) so that it displays
32 | exec "bin/dev"
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/bin/thrust:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "rubygems"
3 | require "bundler/setup"
4 |
5 | load Gem.bin_path("thruster", "thrust")
6 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails"
4 | # Pick the frameworks you want:
5 | require "active_model/railtie"
6 | require "active_job/railtie"
7 | require "active_record/railtie"
8 | require "active_storage/engine"
9 | require "action_controller/railtie"
10 | require "action_mailer/railtie"
11 | require "action_mailbox/engine"
12 | require "action_text/engine"
13 | require "action_view/railtie"
14 | require "action_cable/engine"
15 | # require "rails/test_unit/railtie"
16 |
17 | # Require the gems listed in Gemfile, including any gems
18 | # you've limited to :test, :development, or :production.
19 | Bundler.require(*Rails.groups)
20 |
21 | module RapidRubyStarter
22 | class Application < Rails::Application
23 | # Initialize configuration defaults for originally generated Rails version.
24 | config.load_defaults 7.2
25 |
26 | # Please, add to the `ignore` list any other `lib` subdirectories that do
27 | # not contain `.rb` files, or that should not be reloaded or eager loaded.
28 | # Common ones are `templates`, `generators`, or `middleware`, for example.
29 | config.autoload_lib(ignore: %w[assets tasks])
30 |
31 | # Configuration for the application, engines, and railties goes here.
32 | #
33 | # These settings can be overridden in specific environments using the files
34 | # in config/environments, which are processed later.
35 | #
36 | # config.time_zone = "Central Time (US & Canada)"
37 | # config.eager_load_paths << Rails.root.join("extras")
38 |
39 | # Set Active::Job backend
40 | config.active_job.queue_adapter = :solid_queue
41 | config.mission_control.jobs.base_controller_class = "Admin::BaseController"
42 | config.active_storage.queues.analysis = :default
43 | config.active_storage.queues.purge = :default
44 | config.active_storage.queues.mirror = :default
45 |
46 | config.generators do |g|
47 | g.skip_routes true
48 | g.helper false
49 | g.assets false
50 | g.test_framework :rspec, fixture: false
51 | g.helper_specs false
52 | g.controller_specs false
53 | g.system_tests false
54 | g.view_specs false
55 | end
56 |
57 | # GZip all responses
58 | config.middleware.use Rack::Deflater
59 |
60 | # Fix tailwind/administrate issue
61 | config.assets.css_compressor = nil
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | # Async adapter only works within the same process, so for manually triggering cable updates from a console,
2 | # and seeing results in the browser, you must do so from the web console (running inside the dev process),
3 | # not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
4 | # to make the web console appear.
5 | development:
6 | adapter: solid_cable
7 | polling_interval: 0.1.seconds
8 | message_retention: 1.day
9 |
10 | test:
11 | adapter: test
12 |
13 | production:
14 | adapter: solid_cable
15 | polling_interval: 0.1.seconds
16 | message_retention: 1.day
17 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite. Versions 3.8.0 and up are supported.
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem "sqlite3"
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Make code changes take effect immediately without server restart.
7 | config.enable_reloading = true
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable server timing.
16 | config.server_timing = true
17 |
18 | # Enable/disable Action Controller caching. By default Action Controller caching is disabled.
19 | # Run rails dev:cache to toggle Action Controller caching.
20 | if Rails.root.join("tmp/caching-dev.txt").exist?
21 | config.action_controller.perform_caching = true
22 | config.action_controller.enable_fragment_cache_logging = true
23 | config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" }
24 | else
25 | config.action_controller.perform_caching = false
26 | end
27 |
28 | # Change to :null_store to avoid any caching.
29 | config.cache_store = :memory_store
30 |
31 | # Store uploaded files on the local file system (see config/storage.yml for options).
32 | config.active_storage.service = :local
33 |
34 | # Don't care if the mailer can't send.
35 | config.action_mailer.raise_delivery_errors = false
36 |
37 | # Make template changes take effect immediately.
38 | config.action_mailer.perform_caching = false
39 |
40 | # Set localhost to be used by links generated in mailer templates.
41 | config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
42 |
43 | # Print deprecation notices to the Rails logger.
44 | config.active_support.deprecation = :log
45 |
46 | # Raise an error on page load if there are pending migrations.
47 | config.active_record.migration_error = :page_load
48 |
49 | # Highlight code that triggered database queries in logs.
50 | config.active_record.verbose_query_logs = true
51 |
52 | # Append comments with runtime information tags to SQL queries in logs.
53 | config.active_record.query_log_tags_enabled = true
54 |
55 | # Highlight code that enqueued background job in logs.
56 | config.active_job.verbose_enqueue_logs = true
57 |
58 | # Raises error for missing translations.
59 | # config.i18n.raise_on_missing_translations = true
60 |
61 | # Annotate rendered view with file names.
62 | config.action_view.annotate_rendered_view_with_filenames = true
63 |
64 | # Uncomment if you wish to allow Action Cable access from any origin.
65 | # config.action_cable.disable_request_forgery_protection = true
66 |
67 | # Raise error when a before_action's only/except options reference missing actions.
68 | config.action_controller.raise_on_missing_callback_actions = true
69 |
70 | config.action_mailer.delivery_method = :letter_opener
71 |
72 | # Hotwire Spark
73 | config.hotwire.spark.enabled = true
74 | config.hotwire.spark.logging = true
75 | config.hotwire.spark.html_reload_method = :morph
76 | config.hotwire.spark.html_paths << "app/components"
77 | end
78 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.enable_reloading = false
8 |
9 | # Eager load code on boot for better performance and memory savings (ignored by Rake tasks).
10 | config.eager_load = true
11 |
12 | # Full error reports are disabled.
13 | config.consider_all_requests_local = false
14 |
15 | # Turn on fragment caching in view templates.
16 | config.action_controller.perform_caching = true
17 |
18 | # Cache assets for far-future expiry since they are all digest stamped.
19 | config.public_file_server.headers = { "cache-control" => "public, max-age=#{1.year.to_i}" }
20 |
21 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
22 | # config.asset_host = "http://assets.example.com"
23 |
24 | # Store uploaded files on the local file system (see config/storage.yml for options).
25 | config.active_storage.service = :local
26 |
27 | # Assume all access to the app is happening through a SSL-terminating reverse proxy.
28 | config.assume_ssl = true
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | config.force_ssl = true
32 |
33 | # Skip http-to-https redirect for the default health check endpoint.
34 | # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } }
35 |
36 | # Log to STDOUT with the current request id as a default log tag.
37 | config.log_tags = [ :request_id ]
38 | config.logger = ActiveSupport::TaggedLogging.logger(STDOUT)
39 |
40 | # Change to "debug" to log everything (including potentially personally-identifiable information!)
41 | config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
42 |
43 | # Prevent health checks from clogging up the logs.
44 | config.silence_healthcheck_path = "/up"
45 |
46 | # Don't log any deprecations.
47 | config.active_support.report_deprecations = false
48 |
49 | # Replace the default in-process memory cache store with a durable alternative.
50 | # config.cache_store = :mem_cache_store
51 |
52 | # Replace the default in-process and non-durable queuing backend for Active Job.
53 | # config.active_job.queue_adapter = :resque
54 |
55 | # Ignore bad email addresses and do not raise email delivery errors.
56 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
57 | # config.action_mailer.raise_delivery_errors = false
58 |
59 | # Set host to be used by links generated in mailer templates.
60 | config.action_mailer.default_url_options = { host: "example.com" }
61 |
62 | # Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit.
63 | # config.action_mailer.smtp_settings = {
64 | # user_name: Rails.application.credentials.dig(:smtp, :user_name),
65 | # password: Rails.application.credentials.dig(:smtp, :password),
66 | # address: "smtp.example.com",
67 | # port: 587,
68 | # authentication: :plain
69 | # }
70 |
71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
72 | # the I18n.default_locale when a translation cannot be found).
73 | config.i18n.fallbacks = true
74 |
75 | # Do not dump schema after migrations.
76 | config.active_record.dump_schema_after_migration = false
77 |
78 | # Only use :id for inspections in production.
79 | config.active_record.attributes_for_inspect = [ :id ]
80 |
81 | # Enable DNS rebinding protection and other `Host` header attacks.
82 | # config.hosts = [
83 | # "example.com", # Allow requests from example.com
84 | # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
85 | # ]
86 | #
87 | # Skip DNS rebinding protection for the default health check endpoint.
88 | # config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
89 | end
90 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # The test environment is used exclusively to run your application's
2 | # test suite. You never need to work with it otherwise. Remember that
3 | # your test database is "scratch space" for the test suite and is wiped
4 | # and recreated between test runs. Don't rely on the data there!
5 |
6 | Rails.application.configure do
7 | # Settings specified here will take precedence over those in config/application.rb.
8 |
9 | # While tests run files are not watched, reloading is not necessary.
10 | config.enable_reloading = false
11 |
12 | # Eager loading loads your entire application. When running a single test locally,
13 | # this is usually not necessary, and can slow down your test suite. However, it's
14 | # recommended that you enable it in continuous integration systems to ensure eager
15 | # loading is working properly before deploying your code.
16 | config.eager_load = ENV["CI"].present?
17 |
18 | # Configure public file server for tests with cache-control for performance.
19 | config.public_file_server.headers = { "cache-control" => "public, max-age=3600" }
20 |
21 | # Show full error reports.
22 | config.consider_all_requests_local = true
23 | config.cache_store = :null_store
24 |
25 | # Render exception templates for rescuable exceptions and raise for other exceptions.
26 | config.action_dispatch.show_exceptions = :rescuable
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory.
32 | config.active_storage.service = :test
33 |
34 | # Tell Action Mailer not to deliver emails to the real world.
35 | # The :test delivery method accumulates sent emails in the
36 | # ActionMailer::Base.deliveries array.
37 | config.action_mailer.delivery_method = :test
38 |
39 | # Set host to be used by links generated in mailer templates.
40 | config.action_mailer.default_url_options = { host: "example.com" }
41 |
42 | # Print deprecation notices to the stderr.
43 | config.active_support.deprecation = :stderr
44 |
45 | # Raises error for missing translations.
46 | # config.i18n.raise_on_missing_translations = true
47 |
48 | # Annotate rendered view with file names.
49 | # config.action_view.annotate_rendered_view_with_filenames = true
50 |
51 | # Raise error when a before_action's only/except options reference missing actions.
52 | config.action_controller.raise_on_missing_callback_actions = true
53 |
54 | # URLs
55 | # routes.default_url_options = { host: "rapidrubystarter.test" }
56 | # config.action_controller.default_url_options = { host: "rapidrubystarter.test" }
57 |
58 | config.action_mailer.default_url_options = { host: "rapidrubystarter.test" }
59 |
60 | config.active_job.queue_adapter = :test
61 | end
62 |
--------------------------------------------------------------------------------
/config/importmap.rb:
--------------------------------------------------------------------------------
1 | # Pin npm packages by running ./bin/importmap
2 |
3 | pin "application"
4 | pin "@hotwired/turbo-rails", to: "turbo.min.js"
5 | pin "@hotwired/stimulus", to: "stimulus.min.js"
6 | pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
7 | pin "stimulus-use" # @0.52.3
8 | pin_all_from "app/javascript/controllers", under: "controllers"
9 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = "1.0"
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy.
4 | # See the Securing Rails Applications Guide for more information:
5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header
6 |
7 | # Rails.application.configure do
8 | # config.content_security_policy do |policy|
9 | # policy.default_src :self, :https
10 | # policy.font_src :self, :https, :data
11 | # policy.img_src :self, :https, :data
12 | # policy.object_src :none
13 | # policy.script_src :self, :https
14 | # policy.style_src :self, :https
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 | #
19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles.
20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
21 | # config.content_security_policy_nonce_directives = %w(script-src style-src)
22 | #
23 | # # Report violations without enforcing the policy.
24 | # # config.content_security_policy_report_only = true
25 | # end
26 |
--------------------------------------------------------------------------------
/config/initializers/exception_notification.rb:
--------------------------------------------------------------------------------
1 | if Rails.env.production?
2 | require "exception_notification/rails"
3 |
4 | ExceptionNotification.configure do |config|
5 | # Ignore additional exception types.
6 | # ActiveRecord::RecordNotFound, Mongoid::Errors::DocumentNotFound, AbstractController::ActionNotFound and ActionController::RoutingError are already added.
7 | config.ignored_exceptions += %w[
8 | ActionDispatch::RemoteIp::IpSpoofAttackError
9 | ActionController::InvalidAuthenticityToken
10 | ActionController::RoutingError
11 | ActiveRecord::RecordNotFound
12 | ActionView::MissingTemplate
13 | Stripe::CardError
14 | URI::InvalidURIError
15 | OpenSSL::SSL::SSLError
16 | OutgoingWebhookResponseError
17 | ]
18 |
19 | # Adds a condition to decide when an exception must be ignored or not.
20 | # The ignore_if method can be invoked multiple times to add extra conditions.
21 | config.ignore_if do |exception, options|
22 | !Rails.env.production? && !Rails.env.staging?
23 | end
24 |
25 | # Ignore exceptions generated by crawlers
26 | # config.ignore_crawlers %w{Googlebot bingbot}
27 |
28 | # Notifiers =================================================================
29 |
30 | if (slack_exceptions_webhook_url = Rails.application.credentials.slack_exceptions_webhook_url)
31 | # Email notifier sends notifications by email.
32 | config.add_notifier :slack, {
33 | webhook_url: slack_exceptions_webhook_url,
34 | username: "Rapid Ruby error (#{Rails.env})",
35 | channel: "#rr-errors",
36 | additional_parameters: {
37 | mrkdwn: true
38 | }
39 | }
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
4 | # Use this to limit dissemination of sensitive information.
5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
6 | Rails.application.config.filter_parameters += [
7 | :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc
8 | ]
9 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, "\\1en"
8 | # inflect.singular /^(ox)en/i, "\\1"
9 | # inflect.irregular "person", "people"
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym "RESTful"
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/new_framework_defaults_8_0.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file eases your Rails 8.0 framework defaults upgrade.
4 | #
5 | # Uncomment each configuration one by one to switch to the new default.
6 | # Once your application is ready to run with all new defaults, you can remove
7 | # this file and set the `config.load_defaults` to `8.0`.
8 | #
9 | # Read the Guide for Upgrading Ruby on Rails for more info on each option.
10 | # https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
11 |
12 | ###
13 | # Specifies whether `to_time` methods preserve the UTC offset of their receivers or preserves the timezone.
14 | # If set to `:zone`, `to_time` methods will use the timezone of their receivers.
15 | # If set to `:offset`, `to_time` methods will use the UTC offset.
16 | # If `false`, `to_time` methods will convert to the local system UTC offset instead.
17 | #++
18 | # Rails.application.config.active_support.to_time_preserves_timezone = :zone
19 |
20 | ###
21 | # When both `If-Modified-Since` and `If-None-Match` are provided by the client
22 | # only consider `If-None-Match` as specified by RFC 7232 Section 6.
23 | # If set to `false` both conditions need to be satisfied.
24 | #++
25 | # Rails.application.config.action_dispatch.strict_freshness = true
26 |
27 | ###
28 | # Set `Regexp.timeout` to `1`s by default to improve security over Regexp Denial-of-Service attacks.
29 | #++
30 | # Regexp.timeout = 1
31 |
--------------------------------------------------------------------------------
/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide HTTP permissions policy. For further
4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy
5 |
6 | # Rails.application.config.permissions_policy do |policy|
7 | # policy.camera :none
8 | # policy.gyroscope :none
9 | # policy.microphone :none
10 | # policy.usb :none
11 | # policy.fullscreen :self
12 | # policy.payment :self, "https://secure.example.com"
13 | # end
14 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t "hello"
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t("hello") %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # "true": "foo"
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # This configuration file will be evaluated by Puma. The top-level methods that
2 | # are invoked here are part of Puma's configuration DSL. For more information
3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
4 | #
5 | # Puma starts a configurable number of processes (workers) and each process
6 | # serves each request in a thread from an internal thread pool.
7 | #
8 | # You can control the number of workers using ENV["WEB_CONCURRENCY"]. You
9 | # should only set this value when you want to run 2 or more workers. The
10 | # default is already 1.
11 | #
12 | # The ideal number of threads per worker depends both on how much time the
13 | # application spends waiting for IO operations and on how much you wish to
14 | # prioritize throughput over latency.
15 | #
16 | # As a rule of thumb, increasing the number of threads will increase how much
17 | # traffic a given process can handle (throughput), but due to CRuby's
18 | # Global VM Lock (GVL) it has diminishing returns and will degrade the
19 | # response time (latency) of the application.
20 | #
21 | # The default is set to 3 threads as it's deemed a decent compromise between
22 | # throughput and latency for the average Rails application.
23 | #
24 | # Any libraries that use a connection pool or another resource pool should
25 | # be configured to provide at least as many connections as the number of
26 | # threads. This includes Active Record's `pool` parameter in `database.yml`.
27 | threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
28 | threads threads_count, threads_count
29 |
30 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
31 | port ENV.fetch("PORT", 3000)
32 |
33 | # Allow puma to be restarted by `bin/rails restart` command.
34 | plugin :tmp_restart
35 |
36 | # Run the Solid Queue supervisor inside of Puma for single-server deployments
37 | plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
38 |
39 | # Specify the PID file. Defaults to tmp/pids/server.pid in development.
40 | # In other environments, only set the PID file if requested.
41 | pidfile ENV["PIDFILE"] if ENV["PIDFILE"]
42 |
--------------------------------------------------------------------------------
/config/queue.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | dispatchers:
3 | - polling_interval: 1
4 | batch_size: 500
5 | workers:
6 | - queues: "*"
7 | threads: 3
8 | processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %>
9 | polling_interval: 0.1
10 |
11 | development:
12 | <<: *default
13 |
14 | test:
15 | <<: *default
16 |
17 | production:
18 | <<: *default
19 |
--------------------------------------------------------------------------------
/config/recurring.yml:
--------------------------------------------------------------------------------
1 | # production:
2 | # periodic_cleanup:
3 | # class: CleanSoftDeletedRecordsJob
4 | # queue: background
5 | # args: [ 1000, { batch_size: 500 } ]
6 | # schedule: every hour
7 | # periodic_command:
8 | # command: "SoftDeletedRecord.due.delete_all"
9 | # priority: 2
10 | # schedule: at 5am every day
11 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | namespace :admin do
3 | resources :sessions
4 | resources :teams
5 | resources :team_users
6 | resources :users do
7 | member do
8 | post :masquerade
9 | post :reverse_masquerade
10 | end
11 | end
12 |
13 | root to: "users#index"
14 | end
15 | # Authentication routes
16 | get "sign_in", to: "sessions#new"
17 | post "sign_in", to: "sessions#create"
18 | get "sign_up", to: "registrations#new"
19 | post "sign_up", to: "registrations#create"
20 | resources :sessions, only: [:index, :show, :destroy]
21 | resource :password, only: [:update]
22 | namespace :identity do
23 | resource :account, only: [:show, :update], controller: :account
24 | resource :email, only: [:update]
25 | resource :email_verification, only: [:edit, :create]
26 | resource :password_reset, only: [:new, :edit, :create, :update]
27 | end
28 |
29 | # Solid Queue UI
30 | mount MissionControl::Jobs::Engine, at: "/admin/jobs"
31 |
32 | # App routes
33 | resources :lessons, only: :index
34 |
35 | root "pages#home"
36 | end
37 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket-<%= Rails.env %>
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket-<%= Rails.env %>
23 |
24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name-<%= Rails.env %>
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/config/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme')
2 |
3 | module.exports = {
4 | content: [
5 | './public/*.html',
6 | './app/helpers/**/*.rb',
7 | './app/javascript/**/*.js',
8 | './app/views/**/*.{erb,haml,html,slim}',
9 | 'app/components/**/*.{erb,rb}',
10 | ],
11 | theme: {
12 | extend: {
13 | fontFamily: {
14 | sans: ['Inter var', ...defaultTheme.fontFamily.sans],
15 | },
16 | },
17 | },
18 | plugins: [
19 | require('@tailwindcss/forms'),
20 | require('@tailwindcss/aspect-ratio'),
21 | require('@tailwindcss/typography'),
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/db/migrate/20230205103754_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :users do |t|
4 | t.string :first_name, null: false
5 | t.string :last_name, null: false
6 | t.string :email, null: false
7 | t.string :password_digest, null: false
8 |
9 | t.boolean :verified, null: false, default: false
10 |
11 | t.timestamps
12 | end
13 |
14 | add_index :users, :email, unique: true
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/db/migrate/20230205103755_create_sessions.rb:
--------------------------------------------------------------------------------
1 | class CreateSessions < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :sessions do |t|
4 | t.references :user, null: false, foreign_key: true
5 | t.string :user_agent
6 | t.string :ip_address
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/db/migrate/20230205103756_create_email_verification_tokens.rb:
--------------------------------------------------------------------------------
1 | class CreateEmailVerificationTokens < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :email_verification_tokens do |t|
4 | t.references :user, null: false, foreign_key: true
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20230205103757_create_password_reset_tokens.rb:
--------------------------------------------------------------------------------
1 | class CreatePasswordResetTokens < ActiveRecord::Migration[7.0]
2 | def change
3 | create_table :password_reset_tokens do |t|
4 | t.references :user, null: false, foreign_key: true
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/db/migrate/20240820163037_add_service_name_to_active_storage_blobs.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20190112182829)
2 | class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0]
3 | def up
4 | return unless table_exists?(:active_storage_blobs)
5 |
6 | unless column_exists?(:active_storage_blobs, :service_name)
7 | add_column :active_storage_blobs, :service_name, :string
8 |
9 | if configured_service = ActiveStorage::Blob.service.name
10 | ActiveStorage::Blob.unscoped.update_all(service_name: configured_service)
11 | end
12 |
13 | change_column :active_storage_blobs, :service_name, :string, null: false
14 | end
15 | end
16 |
17 | def down
18 | return unless table_exists?(:active_storage_blobs)
19 |
20 | remove_column :active_storage_blobs, :service_name
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/db/migrate/20240820163038_create_active_storage_variant_records.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20191206030411)
2 | class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0]
3 | def change
4 | return unless table_exists?(:active_storage_blobs)
5 |
6 | # Use Active Record's configured type for primary key
7 | create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t|
8 | t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type
9 | t.string :variation_digest, null: false
10 |
11 | t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
12 | t.foreign_key :active_storage_blobs, column: :blob_id
13 | end
14 | end
15 |
16 | private
17 | def primary_key_type
18 | config = Rails.configuration.generators
19 | config.options[config.orm][:primary_key_type] || :primary_key
20 | end
21 |
22 | def blobs_primary_key_type
23 | pkey_name = connection.primary_key(:active_storage_blobs)
24 | pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name }
25 | pkey_column.bigint? ? :bigint : pkey_column.type
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/db/migrate/20240820163039_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20211119233751)
2 | class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0]
3 | def change
4 | return unless table_exists?(:active_storage_blobs)
5 |
6 | change_column_null(:active_storage_blobs, :checksum, true)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/db/migrate/20241026134459_create_teams.rb:
--------------------------------------------------------------------------------
1 | class CreateTeams < ActiveRecord::Migration[7.2]
2 | def change
3 | create_table :teams do |t|
4 | t.string :name, null: false
5 |
6 | t.timestamps
7 | end
8 |
9 | create_table :team_users do |t|
10 | t.references :team, null: false, foreign_key: true
11 | t.references :user, null: false, foreign_key: true
12 | t.string :role, null: false, default: "owner"
13 |
14 | t.timestamps
15 | end
16 |
17 | add_reference :users, :team, null: false, foreign_key: true
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/db/migrate/20241120200338_install_solid_cable.rb:
--------------------------------------------------------------------------------
1 | class InstallSolidCable < ActiveRecord::Migration[7.2]
2 | def change
3 | create_table "solid_cable_messages", force: :cascade do |t|
4 | t.binary "channel", limit: 1024, null: false
5 | t.binary "payload", limit: 536870912, null: false
6 | t.datetime "created_at", null: false
7 | t.integer "channel_hash", limit: 8, null: false
8 | t.index ["channel"], name: "index_solid_cable_messages_on_channel"
9 | t.index ["channel_hash"], name: "index_solid_cable_messages_on_channel_hash"
10 | t.index ["created_at"], name: "index_solid_cable_messages_on_created_at"
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20241124153040_install_solid_queue.rb:
--------------------------------------------------------------------------------
1 | class InstallSolidQueue < ActiveRecord::Migration[7.2]
2 | def change
3 | create_table "solid_queue_blocked_executions", force: :cascade do |t|
4 | t.bigint "job_id", null: false
5 | t.string "queue_name", null: false
6 | t.integer "priority", default: 0, null: false
7 | t.string "concurrency_key", null: false
8 | t.datetime "expires_at", null: false
9 | t.datetime "created_at", null: false
10 | t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release"
11 | t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance"
12 | t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
13 | end
14 |
15 | create_table "solid_queue_claimed_executions", force: :cascade do |t|
16 | t.bigint "job_id", null: false
17 | t.bigint "process_id"
18 | t.datetime "created_at", null: false
19 | t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
20 | t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
21 | end
22 |
23 | create_table "solid_queue_failed_executions", force: :cascade do |t|
24 | t.bigint "job_id", null: false
25 | t.text "error"
26 | t.datetime "created_at", null: false
27 | t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true
28 | end
29 |
30 | create_table "solid_queue_jobs", force: :cascade do |t|
31 | t.string "queue_name", null: false
32 | t.string "class_name", null: false
33 | t.text "arguments"
34 | t.integer "priority", default: 0, null: false
35 | t.string "active_job_id"
36 | t.datetime "scheduled_at"
37 | t.datetime "finished_at"
38 | t.string "concurrency_key"
39 | t.datetime "created_at", null: false
40 | t.datetime "updated_at", null: false
41 | t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id"
42 | t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name"
43 | t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at"
44 | t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering"
45 | t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting"
46 | end
47 |
48 | create_table "solid_queue_pauses", force: :cascade do |t|
49 | t.string "queue_name", null: false
50 | t.datetime "created_at", null: false
51 | t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true
52 | end
53 |
54 | create_table "solid_queue_processes", force: :cascade do |t|
55 | t.string "kind", null: false
56 | t.datetime "last_heartbeat_at", null: false
57 | t.bigint "supervisor_id"
58 | t.integer "pid", null: false
59 | t.string "hostname"
60 | t.text "metadata"
61 | t.datetime "created_at", null: false
62 | t.string "name", null: false
63 | t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at"
64 | t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
65 | t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id"
66 | end
67 |
68 | create_table "solid_queue_ready_executions", force: :cascade do |t|
69 | t.bigint "job_id", null: false
70 | t.string "queue_name", null: false
71 | t.integer "priority", default: 0, null: false
72 | t.datetime "created_at", null: false
73 | t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true
74 | t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all"
75 | t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue"
76 | end
77 |
78 | create_table "solid_queue_recurring_executions", force: :cascade do |t|
79 | t.bigint "job_id", null: false
80 | t.string "task_key", null: false
81 | t.datetime "run_at", null: false
82 | t.datetime "created_at", null: false
83 | t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
84 | t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
85 | end
86 |
87 | create_table "solid_queue_recurring_tasks", force: :cascade do |t|
88 | t.string "key", null: false
89 | t.string "schedule", null: false
90 | t.string "command", limit: 2048
91 | t.string "class_name"
92 | t.text "arguments"
93 | t.string "queue_name"
94 | t.integer "priority", default: 0
95 | t.boolean "static", default: true, null: false
96 | t.text "description"
97 | t.datetime "created_at", null: false
98 | t.datetime "updated_at", null: false
99 | t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true
100 | t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static"
101 | end
102 |
103 | create_table "solid_queue_scheduled_executions", force: :cascade do |t|
104 | t.bigint "job_id", null: false
105 | t.string "queue_name", null: false
106 | t.integer "priority", default: 0, null: false
107 | t.datetime "scheduled_at", null: false
108 | t.datetime "created_at", null: false
109 | t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
110 | t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all"
111 | end
112 |
113 | create_table "solid_queue_semaphores", force: :cascade do |t|
114 | t.string "key", null: false
115 | t.integer "value", default: 1, null: false
116 | t.datetime "expires_at", null: false
117 | t.datetime "created_at", null: false
118 | t.datetime "updated_at", null: false
119 | t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at"
120 | t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value"
121 | t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true
122 | end
123 |
124 | add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
125 | add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
126 | add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
127 | add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
128 | add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
129 | add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
130 | end
131 | end
132 |
--------------------------------------------------------------------------------
/db/migrate/20241124153608_add_admin_to_users.rb:
--------------------------------------------------------------------------------
1 | class AddAdminToUsers < ActiveRecord::Migration[7.2]
2 | def change
3 | add_column :users, :admin, :boolean, default: false, null: false
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20241124161131_create_active_storage_tables.active_storage.rb:
--------------------------------------------------------------------------------
1 | # This migration comes from active_storage (originally 20170806125915)
2 | class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
3 | def change
4 | # Use Active Record's configured type for primary and foreign keys
5 | primary_key_type, foreign_key_type = primary_and_foreign_key_types
6 |
7 | create_table :active_storage_blobs, id: primary_key_type do |t|
8 | t.string :key, null: false
9 | t.string :filename, null: false
10 | t.string :content_type
11 | t.text :metadata
12 | t.string :service_name, null: false
13 | t.bigint :byte_size, null: false
14 | t.string :checksum
15 |
16 | if connection.supports_datetime_with_precision?
17 | t.datetime :created_at, precision: 6, null: false
18 | else
19 | t.datetime :created_at, null: false
20 | end
21 |
22 | t.index [ :key ], unique: true
23 | end
24 |
25 | create_table :active_storage_attachments, id: primary_key_type do |t|
26 | t.string :name, null: false
27 | t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28 | t.references :blob, null: false, type: foreign_key_type
29 |
30 | if connection.supports_datetime_with_precision?
31 | t.datetime :created_at, precision: 6, null: false
32 | else
33 | t.datetime :created_at, null: false
34 | end
35 |
36 | t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37 | t.foreign_key :active_storage_blobs, column: :blob_id
38 | end
39 |
40 | create_table :active_storage_variant_records, id: primary_key_type do |t|
41 | t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42 | t.string :variation_digest, null: false
43 |
44 | t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45 | t.foreign_key :active_storage_blobs, column: :blob_id
46 | end
47 | end
48 |
49 | private
50 | def primary_and_foreign_key_types
51 | config = Rails.configuration.generators
52 | setting = config.options[config.orm][:primary_key_type]
53 | primary_key_type = setting || :primary_key
54 | foreign_key_type = setting || :bigint
55 | [ primary_key_type, foreign_key_type ]
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/db/migrate/20241124165139_add_masquerade_to_sessions.rb:
--------------------------------------------------------------------------------
1 | class AddMasqueradeToSessions < ActiveRecord::Migration[8.0]
2 | def change
3 | add_reference :sessions, :admin_user, null: true, foreign_key: { to_table: :users }
4 | add_column :sessions, :masquerade_at, :datetime
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }])
7 | # Character.create(name: "Luke", movie: movies.first)
8 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/lib/tasks/.keep
--------------------------------------------------------------------------------
/lib/tasks/admin.rake:
--------------------------------------------------------------------------------
1 | namespace :admin do
2 | desc "Creates a user account"
3 | task create_user: :environment do
4 | puts "First name:"
5 | first_name = STDIN.gets.strip
6 | puts "Last name:"
7 | last_name = STDIN.gets.strip
8 | puts "Email:"
9 | email = STDIN.gets.strip
10 | puts "Password (characters will be hidden):"
11 | password = STDIN.noecho(&:gets).strip
12 |
13 | user = User.new(
14 | first_name: first_name,
15 | last_name: last_name,
16 | email: email,
17 | password: password,
18 | verified: true,
19 | admin: true
20 | )
21 |
22 | user.team = Team.new(name: "#{user.first_name}’s Team")
23 |
24 | if user.save(validate: false)
25 | user.team.users << user
26 | puts "User created with email: #{email}"
27 | else
28 | puts "Creation failed, please try again"
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/log/.keep
--------------------------------------------------------------------------------
/public/400.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | The server cannot process the request due to a client error (400 Bad Request)
8 |
9 |
10 |
11 |
12 |
13 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.
The page you were looking for doesn’t exist. You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.
The change you wanted was rejected. Maybe you tried to change something you didn’t have access to. If you’re the application owner check the logs for more information.
We’re sorry, but something went wrong. If you’re the application owner check the logs for more information.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/public/favicon.ico
--------------------------------------------------------------------------------
/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/public/icon.png
--------------------------------------------------------------------------------
/public/icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/spec/components/admin/masquerade_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails_helper"
4 |
5 | RSpec.describe Admin::MasqueradeComponent, type: :component do
6 | it "renders a masquerade banner if masquerading" do
7 | user = double(:user, email: "test@example.com")
8 | admin_user = double(:admin_user, email: "admin@example.com")
9 | session = double(:session, masquerading?: true, user: user, admin_user: admin_user)
10 |
11 | render_inline(described_class.new(session: session))
12 |
13 | expect(page).to have_text("You are masquerading as #{session.user.email}")
14 | end
15 |
16 | it "doesn't render if not masquerading" do
17 | session = double(:session, masquerading?: false)
18 |
19 | render_inline(described_class.new(session: session))
20 |
21 | expect(page).to have_no_text("You are masquerading as")
22 | end
23 |
24 | it "doesn't render if session is blank" do
25 | session = nil
26 |
27 | render_inline(described_class.new(session: session))
28 |
29 | expect(page).to have_no_text("You are masquerading as")
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/components/avatar_component_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe AvatarComponent, type: :component do
4 | let(:user) { users(:pete) }
5 |
6 | it "renders with default settings" do
7 | render_inline(described_class.new(user: user))
8 | end
9 |
10 | it "renders at :thumb size" do
11 | render_inline(described_class.new(user: user, size: :thumb))
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/components/fieldset_header_component_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe FieldsetHeaderComponent, type: :component do
4 | subject(:component) { described_class.new(title: "Title", description: "Description") }
5 |
6 | it "renders the component" do
7 | render_inline(component)
8 |
9 | expect(page).to have_text("Title")
10 | expect(page).to have_text("Description")
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/components/user_dropdown_component_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails_helper"
4 |
5 | RSpec.describe UserDropdownComponent, type: :component do
6 | let(:user) { users(:pete) }
7 | let(:session) { Session.create!(user: user) }
8 |
9 | it "renders something useful" do
10 | render_inline(described_class.new(current_user: user, current_session: session))
11 |
12 | expect(page).to have_content(user.email)
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/features/identity/emails_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Identity::EmailsTest", type: :feature do
4 | let!(:user) { sign_in_as(users(:lazaro_nixon)) }
5 |
6 | it "updates the email" do
7 | click_on "Manage account", match: :first
8 |
9 | within("#change_email_form") do
10 | fill_in "New email", with: "new_email@hey.com"
11 | click_on "Save changes"
12 | end
13 |
14 | assert_text "Your email has been changed"
15 | end
16 |
17 | it "sends a verification email" do
18 | user.update! verified: false
19 |
20 | click_on "Manage account", match: :first
21 | assert_current_path "/identity/account"
22 | click_on "Re-send verification email"
23 |
24 | assert_text "We sent a verification email to your email address"
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/features/identity/password_resets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Identity::PasswordResetsTest", type: :feature do
4 | before do
5 | @user = users(:lazaro_nixon)
6 | @sid = @user.password_reset_tokens.create.signed_id(expires_in: 20.minutes)
7 | end
8 |
9 | it "sending a password reset email" do
10 | visit sign_in_url
11 | click_on "Forgot your password?"
12 |
13 | fill_in "Email", with: @user.email
14 | click_on "Send password reset email"
15 |
16 | assert_text "Check your email for reset instructions"
17 | end
18 |
19 | it "updating password" do
20 | visit edit_identity_password_reset_url(sid: @sid)
21 |
22 | fill_in "New password", with: "Secret6*4*2*"
23 | fill_in "Confirm new password", with: "Secret6*4*2*"
24 | click_on "Save changes"
25 |
26 | assert_text "Your password was reset successfully. Please sign in"
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/features/passwords_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "PasswordsTest", type: :feature do
4 | before { sign_in_as(users(:lazaro_nixon)) }
5 |
6 | it "updating the password" do
7 | click_on "Manage account", match: :first
8 |
9 | within("#change_password_form") do
10 | fill_in "Current password", with: "Secret1*3*5*"
11 | fill_in "New password", with: "Secret6*4*2*"
12 | fill_in "Confirm new password", with: "Secret6*4*2*"
13 | click_on "Save changes"
14 | end
15 |
16 | assert_text "Your password has been changed"
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/spec/features/registrations_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "RegistrationsTest", type: :feature do
4 | it "allows signing up" do
5 | visit sign_up_url
6 |
7 | fill_in "First name", with: "Jane"
8 | fill_in "Last name", with: "Nixon"
9 | fill_in "Email", with: "janenixon@hey.com"
10 | fill_in "Password", with: "Secret6*4*2*"
11 | fill_in "Password confirmation", with: "Secret6*4*2*"
12 | check "terms_and_conditions"
13 | within ".form-actions" do
14 | click_on "Sign up"
15 | end
16 |
17 | assert_text "Welcome! You have signed up successfully"
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/features/sessions_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "SessionsTest", type: :feature do
4 | before do
5 | @user = users(:lazaro_nixon)
6 | end
7 |
8 | it "visiting the index" do
9 | sign_in_as @user
10 |
11 | click_on "Manage account", match: :first
12 | click_on "Manage devices and sessions", match: :first
13 | assert_selector "h1", text: "Sessions"
14 | end
15 |
16 | it "signing in" do
17 | visit sign_in_url
18 | fill_in "Email", with: @user.email
19 | fill_in "Password", with: "Secret1*3*5*"
20 | click_on "Sign in"
21 |
22 | assert_text "Signed in successfully"
23 | end
24 |
25 | it "signing out" do
26 | sign_in_as @user
27 |
28 | click_on "Log out", match: :first
29 | assert_text "That session has been logged out"
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/fixtures/team_users.yml:
--------------------------------------------------------------------------------
1 | pete_team_user:
2 | user: pete
3 | team: petes_team
4 | role: owner
5 | lazaro_nixon_team_user:
6 | user: lazaro_nixon
7 | team: lazaro_nixon_team
8 | role: owner
9 |
--------------------------------------------------------------------------------
/spec/fixtures/teams.yml:
--------------------------------------------------------------------------------
1 | petes_team:
2 | name: Pete's Team
3 |
4 | lazaro_nixon_team:
5 | name: Lazaro's Team
6 |
--------------------------------------------------------------------------------
/spec/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 | pete:
3 | first_name: Pete
4 | last_name: Hawkins
5 | email: pete@rapidruby.com
6 | password_digest: <%= BCrypt::Password.create("Secret1*3*5*") %>
7 | verified: true
8 | admin: true
9 | team: petes_team
10 | lazaro_nixon:
11 | first_name: "Lazaro"
12 | last_name: "Nixon"
13 | email: lazaronixon@hotmail.com
14 | password_digest: <%= BCrypt::Password.create("Secret1*3*5*") %>
15 | verified: true
16 | team: lazaro_nixon_team
17 |
--------------------------------------------------------------------------------
/spec/mailers/session_mailer_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe SessionMailer, type: :mailer do
4 | let(:session) { users(:lazaro_nixon).sessions.create! }
5 |
6 | it "#signed_in_notification" do
7 | mail = SessionMailer.with(session: session).signed_in_notification
8 | expect(mail.subject).to eq("New sign-in to your account")
9 | expect(mail.to.first).to eq(session.user.email)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/mailers/user_mailer_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe UserMailer, type: :mailer do
4 | let(:user) { users(:lazaro_nixon) }
5 |
6 | it "#password_reset" do
7 | mail = UserMailer.with(user: user).password_reset
8 | expect(mail.subject).to eq("Reset your password")
9 | expect(mail.to.first).to eq(user.email)
10 | end
11 |
12 | it "#email_verification" do
13 | mail = UserMailer.with(user: user).email_verification
14 | expect(mail.subject).to eq("Verify your email")
15 | expect(mail.to.first).to eq(user.email)
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/models/concerns/avatarable_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Avatarable, type: :model do
4 | let(:user) { users(:pete) }
5 |
6 | describe "#letters_svg" do
7 | it "returns an svg of initials" do
8 | svg = user.letters_svg
9 | expect(svg).to include("PH")
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/spec/models/session_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Session, type: :model do
4 | describe "associations" do
5 | it { should belong_to(:user) }
6 | it { should belong_to(:admin_user).optional }
7 | end
8 |
9 | describe "#masquerade_as!" do
10 | let(:admin_user) { users(:pete) }
11 | let(:normal_user) { users(:lazaro_nixon) }
12 |
13 | before do
14 | admin_user.update!(admin: true)
15 | end
16 |
17 | it "masquerades as the given user" do
18 | session = Session.create!(user: admin_user)
19 | expect(session.masquerading?).to be(false)
20 |
21 | session.masquerade_as!(normal_user)
22 |
23 | expect(session.admin_user).to eq(admin_user)
24 | expect(session.user).to eq(normal_user)
25 | expect(session.masquerading?).to be(true)
26 |
27 | session.reverse_masquerade!
28 |
29 | expect(session.admin_user).to be_nil
30 | expect(session.user).to eq(admin_user)
31 | expect(session.masquerading?).to be(false)
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/spec/models/team_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe Team, type: :model do
4 | it { is_expected.to have_many :team_users }
5 | it { is_expected.to have_many :users }
6 | end
7 |
--------------------------------------------------------------------------------
/spec/models/team_user_spec.rb:
--------------------------------------------------------------------------------
1 | require 'rails_helper'
2 |
3 | RSpec.describe TeamUser, type: :model do
4 | it { is_expected.to belong_to :user }
5 | it { is_expected.to belong_to :team }
6 | end
7 |
--------------------------------------------------------------------------------
/spec/models/user_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe User, type: :model do
4 | it { is_expected.to have_many :email_verification_tokens }
5 | it { is_expected.to have_many :password_reset_tokens }
6 | it { is_expected.to have_many :sessions }
7 | it { is_expected.to have_many :team_users }
8 | it { is_expected.to have_many :teams }
9 | it { is_expected.to belong_to :team }
10 |
11 | it { is_expected.to validate_presence_of :first_name }
12 | it { is_expected.to validate_presence_of :last_name }
13 | it { is_expected.to validate_presence_of :email }
14 |
15 | describe "#name" do
16 | it "concatenates first and last name" do
17 | user = User.new(first_name: "John", last_name: "Doe")
18 | expect(user.name).to eq("John Doe")
19 | end
20 | end
21 |
22 | describe "#obfuscated_name" do
23 | it "concatenates first name and the first letter of last name" do
24 | user = User.new(first_name: "John", last_name: "Doe")
25 | expect(user.obfuscated_name).to eq("John D.")
26 | end
27 |
28 | it "uses only the first name if last name is empty" do
29 | user = User.new(first_name: "John", last_name: nil)
30 | expect(user.obfuscated_name).to eq("John")
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | # This file is copied to spec/ when you run 'rails generate rspec:install'
2 | require 'spec_helper'
3 | ENV['RAILS_ENV'] ||= 'test'
4 | require_relative '../config/environment'
5 | # Prevent database truncation if the environment is production
6 | abort("The Rails environment is running in production mode!") if Rails.env.production?
7 | require 'rspec/rails'
8 | # Add additional requires below this line. Rails is not loaded until this point!
9 | require "shoulda/matchers"
10 | require "view_component/test_helpers"
11 |
12 | Shoulda::Matchers.configure do |config|
13 | config.integrate do |with|
14 | with.test_framework :rspec
15 | with.library :rails
16 | end
17 | end
18 |
19 | # Requires supporting ruby files with custom matchers and macros, etc, in
20 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
21 | # run as spec files by default. This means that files in spec/support that end
22 | # in _spec.rb will both be required and run as specs, causing the specs to be
23 | # run twice. It is recommended that you do not name files matching this glob to
24 | # end with _spec.rb. You can configure this pattern with the --pattern
25 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
26 | #
27 | # The following line is provided for convenience purposes. It has the downside
28 | # of increasing the boot-up time by auto-requiring all files in the support
29 | # directory. Alternatively, in the individual `*_spec.rb` files, manually
30 | # require only the support files necessary.
31 | #
32 | Dir[Rails.root.join("spec", "support", "**", "*.rb")].sort.each { |f| require f }
33 |
34 | # Checks for pending migrations and applies them before tests are run.
35 | # If you are not using ActiveRecord, you can remove these lines.
36 | begin
37 | ActiveRecord::Migration.maintain_test_schema!
38 | rescue ActiveRecord::PendingMigrationError => e
39 | abort e.to_s.strip
40 | end
41 | RSpec.configure do |config|
42 | config.include ViewComponent::TestHelpers, type: :component
43 | config.include Capybara::RSpecMatchers, type: :component
44 |
45 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
46 | config.fixture_paths = ["#{::Rails.root}/spec/fixtures"]
47 | config.global_fixtures = :all
48 |
49 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
50 | # examples within a transaction, remove the following line or assign false
51 | # instead of true.
52 | config.use_transactional_fixtures = true
53 |
54 | # You can uncomment this line to turn off ActiveRecord support entirely.
55 | # config.use_active_record = false
56 |
57 | # RSpec Rails can automatically mix in different behaviours to your tests
58 | # based on their file location, for example enabling you to call `get` and
59 | # `post` in specs under `spec/controllers`.
60 | #
61 | # You can disable this behaviour by removing the line below, and instead
62 | # explicitly tag your specs with their type, e.g.:
63 | #
64 | # RSpec.describe UsersController, type: :controller do
65 | # # ...
66 | # end
67 | #
68 | # The different available types are documented in the features, such as in
69 | # https://relishapp.com/rspec/rspec-rails/docs
70 | config.infer_spec_type_from_file_location!
71 |
72 | # Filter lines from Rails gems in backtraces.
73 | config.filter_rails_from_backtrace!
74 | # arbitrary gems may also be filtered via:
75 | # config.filter_gems_from_backtrace("gem name")
76 | end
77 |
--------------------------------------------------------------------------------
/spec/requests/admin/masquerade_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Admin Masquerade as another user", type: :request do
4 | let(:admin_user) { users(:pete) }
5 | let(:normal_user) { users(:lazaro_nixon) }
6 |
7 | before do
8 | sign_in(admin_user)
9 | end
10 |
11 | describe "POST /admin/users/:id/masquerade" do
12 | it "masquerades as the given user and can reverse masquerade" do
13 | post masquerade_admin_user_path(normal_user)
14 | expect(response).to redirect_to(root_path)
15 | follow_redirect!
16 | expect(response.body).to include("You are masquerading as #{normal_user.email}")
17 | expect(response.body).to include("Return to admin")
18 |
19 | post reverse_masquerade_admin_user_path(admin_user)
20 | expect(response).to redirect_to(admin_users_path)
21 | follow_redirect!
22 | expect(response.body).not_to include("You are masquerading as #{normal_user.email}")
23 | expect(response.body).to include("Users")
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/requests/identity/account_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Identity::AccountController, type: :request do
4 | let(:user) { users(:pete) }
5 | before { sign_in(user) }
6 |
7 | describe "#show" do
8 | it "should load without errors" do
9 | get identity_account_url
10 | expect(response).to have_http_status(:success)
11 | end
12 | end
13 |
14 | describe "#update" do
15 | it "should load without errors" do
16 | patch identity_account_url, params: {
17 | user: {
18 | first_name: "New first name",
19 | last_name: "New last name"
20 | }
21 | }
22 | expect(response).to redirect_to(identity_account_url)
23 | expect(user.reload.name).to eq("New first name New last name")
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/spec/requests/identity/email_verifications_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Identity::EmailVerificationsController, type: :request do
4 | let(:user) { users(:lazaro_nixon) }
5 | before do
6 | sign_in(user)
7 | user.update! verified: false
8 | end
9 |
10 | describe "#create" do
11 | it "should send a verification email" do
12 | perform_enqueued_jobs do
13 | post identity_email_verification_url
14 |
15 | expect(last_email.to.first).to eq(user.email)
16 | expect(last_email.subject).to eq("Verify your email")
17 |
18 | expect(response).to redirect_to(identity_account_path)
19 | end
20 | end
21 | end
22 |
23 | describe "#edit" do
24 | it "should verify email" do
25 | sid = user.email_verification_tokens.create.signed_id(expires_in: 2.days)
26 |
27 | get edit_identity_email_verification_url(sid: sid, email: user.email)
28 | expect(response).to redirect_to(identity_account_path)
29 | end
30 |
31 | it "should not verify email with expired token" do
32 | sid_exp = user.email_verification_tokens.create.signed_id(expires_in: 0.minutes)
33 |
34 | get edit_identity_email_verification_url(sid: sid_exp, email: user.email)
35 |
36 | expect(response).to redirect_to(identity_account_url)
37 | expect(flash[:alert]).to eq("That email verification link is invalid")
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/requests/identity/emails_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Identity::EmailsController, type: :request do
4 | let(:user) { users(:lazaro_nixon) }
5 | before { sign_in(user) }
6 |
7 | describe "#update" do
8 | it "should update email" do
9 | patch identity_email_url, params: { email: "new_email@hey.com" }
10 | expect(response).to redirect_to(root_url)
11 | end
12 |
13 | it "responds with a turbo_stream when validation fails" do
14 | patch identity_email_url, params: { email: "new_email" }
15 |
16 | expect(response).to have_http_status(:success)
17 | assert_select("turbo-stream[action='replace'][target='change_email_form']", 1)
18 | expect(response.body).to include("Email is invalid")
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/requests/identity/password_resets_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe Identity::PasswordResetsController, type: :request do
4 | let(:user) { users(:lazaro_nixon) }
5 |
6 | describe "#new" do
7 | it "should get new" do
8 | get new_identity_password_reset_url
9 | expect(response).to have_http_status(:success)
10 | end
11 | end
12 |
13 | describe "#edit" do
14 | it "should get edit" do
15 | sid = user.password_reset_tokens.create.signed_id(expires_in: 20.minutes)
16 |
17 | get edit_identity_password_reset_url(sid: sid)
18 | expect(response).to have_http_status(:success)
19 | end
20 | end
21 |
22 | describe "#create" do
23 | it "should send a password reset email" do
24 | perform_enqueued_jobs do
25 | post identity_password_reset_url, params: { email: user.email }
26 |
27 | expect(last_email.to.first).to eq(user.email)
28 | expect(last_email.subject).to eq("Reset your password")
29 |
30 | expect(response).to redirect_to(sign_in_url)
31 | end
32 | end
33 |
34 | it "should not send a password reset email to a nonexistent email" do
35 | post identity_password_reset_url, params: { email: "invalid_email@hey.com" }
36 |
37 | expect(last_email).to be_nil
38 |
39 | expect(response).to redirect_to(new_identity_password_reset_url)
40 | expect(flash[:alert]).to eq("You can't reset your password until you verify your email")
41 | end
42 |
43 | it "should not send a password reset email to a unverified email" do
44 | user.update! verified: false
45 |
46 | post identity_password_reset_url, params: { email: user.email }
47 |
48 | expect(last_email).to be_nil
49 |
50 | expect(response).to redirect_to(new_identity_password_reset_url)
51 | expect(flash[:alert]).to eq("You can't reset your password until you verify your email")
52 | end
53 | end
54 |
55 | describe "#update" do
56 | it "should update password" do
57 | sid = user.password_reset_tokens.create.signed_id(expires_in: 20.minutes)
58 |
59 | patch identity_password_reset_url, params: { sid: sid, password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }
60 | expect(response).to redirect_to(sign_in_url)
61 | end
62 |
63 | it "should not update password with expired token" do
64 | sid_exp = user.password_reset_tokens.create.signed_id(expires_in: 0.minutes)
65 |
66 | patch identity_password_reset_url, params: { sid: sid_exp, password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }
67 | expect(response).to redirect_to(new_identity_password_reset_url)
68 | expect(flash[:alert]).to eq("That password reset link is invalid")
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/spec/requests/pages_requests_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe PagesController, type: :request do
4 | describe "#home" do
5 | it "renders the home page" do
6 | get "/"
7 | expect(response).to have_http_status(:success)
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/requests/passwords_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe PasswordsController, type: :request do
4 | let(:user) { users(:lazaro_nixon) }
5 | before { sign_in(user) }
6 |
7 | describe "#update" do
8 | it "should update password" do
9 | patch password_url, params: { current_password: "Secret1*3*5*", password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }
10 | expect(response).to redirect_to(identity_account_url)
11 | end
12 |
13 | it "should not update password with wrong current password" do
14 | patch password_url, params: { current_password: "SecretWrong1*3", password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }
15 |
16 | expect(response).to redirect_to(identity_account_url)
17 | expect(flash[:alert]).to eq("The current password you entered is incorrect")
18 | end
19 |
20 | it "should respond with a turbo stream when there are errors" do
21 | patch password_url, params: { format: :turbo_stream, current_password: "Secret1*3*5*", password: "dont", password_confirmation: "match" }
22 |
23 | expect(response).to have_http_status(:success)
24 | assert_select("turbo-stream[action='replace'][target='change_password_form']", 1)
25 | expect(response.body).to include("Password is invalid")
26 | end
27 |
28 | it "redirects to manage account when current_passowrd is invalid" do
29 | patch password_url, params: { format: :turbo_stream, current_password: "bad_password", password: "dont", password_confirmation: "match" }
30 |
31 | expect(response).to redirect_to(identity_account_url)
32 | expect(flash[:alert]).to eq("The current password you entered is incorrect")
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/requests/registrations_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe RegistrationsController, type: :request do
4 | describe "#new" do
5 | it "should get new" do
6 | get sign_up_url
7 | expect(response).to have_http_status(:success)
8 | end
9 | end
10 |
11 | describe "#create" do
12 | it "signs up the user and creates a team" do
13 | expect do
14 | post sign_up_url, params: { first_name: "New", last_name: "User", email: "newuser@hey.com", password: "Secret1*3*5*", password_confirmation: "Secret1*3*5*" }
15 | end.to change(User, :count).by(1)
16 | .and change(Team, :count).by(1)
17 |
18 | expect(response).to redirect_to(root_path)
19 | expect(User.last.team.name).to eq("New’s Team")
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/requests/sessions_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe SessionsController, type: :request do
4 | let(:user) { users(:lazaro_nixon) }
5 |
6 | describe "#index" do
7 | it "should get index" do
8 | sign_in(user)
9 |
10 | get sessions_url
11 | expect(response).to have_http_status(:success)
12 | end
13 | end
14 |
15 | describe "#new" do
16 | it "should get new" do
17 | get sign_in_url
18 | expect(response).to have_http_status(:success)
19 | end
20 | end
21 |
22 | describe "#create" do
23 | it "should sign in" do
24 | perform_enqueued_jobs do
25 | post sign_in_url, params: { email: user.email, password: "Secret1*3*5*" }
26 |
27 | expect(response).to redirect_to(root_path)
28 |
29 | get root_url
30 | expect(response).to have_http_status(:success)
31 |
32 | expect(last_email.to.first).to eq(user.email)
33 | expect(last_email.subject).to eq("New sign-in to your account")
34 | end
35 | end
36 |
37 | it "should not sign in with wrong credentials" do
38 | post sign_in_url, params: { email: user.email, password: "SecretWrong1*3" }
39 | expect(response).to redirect_to(sign_in_url(email_hint: user.email))
40 | expect(flash[:alert]).to eq("That email or password is incorrect")
41 | end
42 | end
43 |
44 | describe "#destroy" do
45 | it "should sign out" do
46 | sign_in(user)
47 |
48 | delete session_url(user.sessions.last)
49 | expect(response).to redirect_to(sessions_url)
50 |
51 | follow_redirect!
52 | assert_redirected_to sign_in_url
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/spec/services/disposable_email_service_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe DisposableEmailService do
4 | describe "#disposable?(email)" do
5 | it "returns false when the email domain is NOT disposable" do
6 | expect(described_class.disposable?("hi@rapidruby.com")).to be(false)
7 | end
8 |
9 | it "returns true when the email domain is disposable" do
10 | expect(described_class.disposable?("info@zzz-xxx.com")).to be(true)
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/services/markdown_service_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe MarkdownService do
4 | describe ".to_html" do
5 | it "converts markdown to HTML" do
6 | html = described_class.to_html("# Hello")
7 | expect(html).to eq("
Hello
\n")
8 | end
9 |
10 | it "adds target: _blank and rel: noopener to links" do
11 | html = described_class.to_html("[Google](https://google.com)")
12 | expect(html).to eq('
' + "\n")
13 | end
14 |
15 | it "handles nil" do
16 | html = described_class.to_html(nil)
17 | expect(html).to eq("")
18 | end
19 |
20 | it "handles invalid input like integers" do
21 | expect { described_class.to_html(42) }.to raise_error(ArgumentError, "Input must be a string or nil")
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3 | # The generated `.rspec` file contains `--require spec_helper` which will cause
4 | # this file to always be loaded, without a need to explicitly require it in any
5 | # files.
6 | #
7 | # Given that it is always loaded, you are encouraged to keep this file as
8 | # light-weight as possible. Requiring heavyweight dependencies from this file
9 | # will add to the boot time of your test suite on EVERY test run, even for an
10 | # individual file that may not need all of that loaded. Instead, consider making
11 | # a separate helper file that requires the additional dependencies and performs
12 | # the additional setup, and require it from the spec files that actually need
13 | # it.
14 | #
15 | # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16 | RSpec.configure do |config|
17 | # rspec-expectations config goes here. You can use an alternate
18 | # assertion/expectation library such as wrong or the stdlib/minitest
19 | # assertions if you prefer.
20 | config.expect_with :rspec do |expectations|
21 | # This option will default to `true` in RSpec 4. It makes the `description`
22 | # and `failure_message` of custom matchers include text for helper methods
23 | # defined using `chain`, e.g.:
24 | # be_bigger_than(2).and_smaller_than(4).description
25 | # # => "be bigger than 2 and smaller than 4"
26 | # ...rather than:
27 | # # => "be bigger than 2"
28 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29 | end
30 |
31 | # rspec-mocks config goes here. You can use an alternate test double
32 | # library (such as bogus or mocha) by changing the `mock_with` option here.
33 | config.mock_with :rspec do |mocks|
34 | # Prevents you from mocking or stubbing a method that does not exist on
35 | # a real object. This is generally recommended, and will default to
36 | # `true` in RSpec 4.
37 | mocks.verify_partial_doubles = true
38 | end
39 |
40 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41 | # have no way to turn it off -- the option exists only for backwards
42 | # compatibility in RSpec 3). It causes shared context metadata to be
43 | # inherited by the metadata hash of host groups and examples, rather than
44 | # triggering implicit auto-inclusion in groups with matching metadata.
45 | config.shared_context_metadata_behavior = :apply_to_host_groups
46 |
47 | # The settings below are suggested to provide a good initial experience
48 | # with RSpec, but feel free to customize to your heart's content.
49 | =begin
50 | # This allows you to limit a spec run to individual examples or groups
51 | # you care about by tagging them with `:focus` metadata. When nothing
52 | # is tagged with `:focus`, all examples get run. RSpec also provides
53 | # aliases for `it`, `describe`, and `context` that include `:focus`
54 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55 | config.filter_run_when_matching :focus
56 |
57 | # Allows RSpec to persist some state between runs in order to support
58 | # the `--only-failures` and `--next-failure` CLI options. We recommend
59 | # you configure your source control system to ignore this file.
60 | config.example_status_persistence_file_path = "spec/examples.txt"
61 |
62 | # Limits the available syntax to the non-monkey patched syntax that is
63 | # recommended. For more details, see:
64 | # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
65 | config.disable_monkey_patching!
66 |
67 | # Many RSpec users commonly either run the entire suite or an individual
68 | # file, and it's useful to allow more verbose output when running an
69 | # individual spec file.
70 | if config.files_to_run.one?
71 | # Use the documentation formatter for detailed output,
72 | # unless a formatter has already been configured
73 | # (e.g. via a command-line flag).
74 | config.default_formatter = "doc"
75 | end
76 |
77 | # Print the 10 slowest examples and example groups at the
78 | # end of the spec run, to help surface which specs are running
79 | # particularly slow.
80 | config.profile_examples = 10
81 |
82 | # Run specs in random order to surface order dependencies. If you find an
83 | # order dependency and want to debug it, you can fix the order by providing
84 | # the seed, which is printed after each run.
85 | # --seed 1234
86 | config.order = :random
87 |
88 | # Seed global randomization in this process using the `--seed` CLI option.
89 | # Setting this allows you to use `--seed` to deterministically reproduce
90 | # test failures related to randomization by passing the same `--seed` value
91 | # as the one that triggered the failure.
92 | Kernel.srand config.seed
93 | =end
94 | end
95 |
--------------------------------------------------------------------------------
/spec/support/capybara_config.rb:
--------------------------------------------------------------------------------
1 | require "capybara/rspec"
2 | require "capybara/cuprite"
3 |
4 | Capybara.javascript_driver = :cuprite
5 | Capybara.register_driver(:cuprite) do |app|
6 | Capybara::Cuprite::Driver.new(app, window_size: [1200, 800], inspector: ENV["INSPECTOR"])
7 | end
8 |
--------------------------------------------------------------------------------
/spec/support/mail_helper.rb:
--------------------------------------------------------------------------------
1 | module MailHelper
2 | def reset_emails!
3 | ActionMailer::Base.deliveries.clear
4 | end
5 |
6 | def last_email
7 | ActionMailer::Base.deliveries.last
8 | end
9 |
10 | def delivered_emails
11 | ActionMailer::Base.deliveries
12 | end
13 | end
14 |
15 | RSpec.configure do |config|
16 | config.include MailHelper
17 | config.before(:each) { reset_emails! }
18 | end
19 |
--------------------------------------------------------------------------------
/spec/support/request_helper.rb:
--------------------------------------------------------------------------------
1 | module RequestHelper
2 | def sign_in(user)
3 | post(sign_in_url, params: { email: user.email, password: "Secret1*3*5*" }); user
4 | end
5 | end
6 |
7 | RSpec.configure do |config|
8 | config.include RequestHelper, type: :request
9 | config.before(:each, type: :request) { host! "rapidrubystarter.test" }
10 | end
11 |
--------------------------------------------------------------------------------
/spec/support/system_test_helper.rb:
--------------------------------------------------------------------------------
1 | module SystemTestHelper
2 | def sign_in_as(user)
3 | visit sign_in_url
4 | fill_in :email, with: user.email
5 | fill_in :password, with: "Secret1*3*5*"
6 | click_on "Sign in"
7 |
8 | assert_current_path root_path
9 | user
10 | end
11 |
12 | def open_debug!
13 | page.driver.debug(binding)
14 | end
15 | end
16 |
17 | RSpec.configure do |config|
18 | config.include SystemTestHelper, type: :feature
19 | end
20 |
--------------------------------------------------------------------------------
/storage/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/storage/.keep
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/tmp/.keep
--------------------------------------------------------------------------------
/tmp/pids/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/tmp/pids/.keep
--------------------------------------------------------------------------------
/tmp/storage/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/tmp/storage/.keep
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/vendor/.keep
--------------------------------------------------------------------------------
/vendor/javascript/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rapidruby/rapid-ruby-starter/8b6700c7f9e974da31c728d7da167fffd2939faa/vendor/javascript/.keep
--------------------------------------------------------------------------------