Browse Source

basics copied from original repo

undefined
Richard Cagle 4 months ago
parent
commit
5dce016657
100 changed files with 1404 additions and 142 deletions
  1. 78
    0
      .rubocop.yml
  2. 30
    22
      Gemfile
  3. 62
    1
      Gemfile.lock
  4. 35
    24
      README.md
  5. 3
    1
      Rakefile
  6. 10
    0
      app/assets/stylesheets/_fonts.scss
  7. 25
    0
      app/assets/stylesheets/_vendor_customizations.scss
  8. 0
    15
      app/assets/stylesheets/application.css
  9. 7
    0
      app/assets/stylesheets/application.scss
  10. 105
    0
      app/assets/stylesheets/layout.scss
  11. 184
    0
      app/assets/stylesheets/theme-oldschool.scss
  12. 2
    0
      app/channels/application_cable/channel.rb
  13. 2
    0
      app/channels/application_cable/connection.rb
  14. 3
    0
      app/components/welcome_back_message_component.html.erb
  15. 7
    0
      app/components/welcome_back_message_component.rb
  16. 3
    0
      app/components/welcome_login_message_component.html.erb
  17. 6
    0
      app/components/welcome_login_message_component.rb
  18. 6
    0
      app/controllers/account_controller.rb
  19. 2
    0
      app/controllers/application_controller.rb
  20. 20
    0
      app/controllers/game_messages_controller.rb
  21. 38
    0
      app/controllers/games_controller.rb
  22. 6
    0
      app/controllers/lobby_controller.rb
  23. 4
    0
      app/controllers/main_controller.rb
  24. 9
    0
      app/helpers/alert_helper.rb
  25. 8
    0
      app/helpers/game_helper.rb
  26. 13
    0
      app/javascript/cables/cable.js
  27. 42
    0
      app/javascript/controllers/game_message_controller.js
  28. 4
    1
      app/javascript/packs/application.js
  29. 37
    0
      app/javascript/src/_globals.js
  30. 9
    0
      app/javascript/src/alerts.js
  31. 28
    0
      app/javascript/src/game.js
  32. 33
    0
      app/javascript/src/lobby.js
  33. 24
    0
      app/javascript/src/navigation-hotkeys.js
  34. 2
    0
      app/jobs/application_job.rb
  35. 4
    2
      app/mailers/application_mailer.rb
  36. 2
    0
      app/models/application_record.rb
  37. 6
    0
      app/models/character.rb
  38. 6
    0
      app/models/character_inventory_item.rb
  39. 5
    0
      app/models/friend_request.rb
  40. 4
    0
      app/models/game.rb
  41. 6
    0
      app/models/game_message.rb
  42. 4
    0
      app/models/inventory_item.rb
  43. 4
    0
      app/models/monster.rb
  44. 7
    0
      app/models/user.rb
  45. 5
    0
      app/models/user_friend.rb
  46. 0
    0
      app/views/account/index.html.erb
  47. 8
    0
      app/views/clearance_mailer/change_password.html.erb
  48. 5
    0
      app/views/clearance_mailer/change_password.text.erb
  49. 10
    0
      app/views/game_messages/_message.html.erb
  50. 2
    0
      app/views/game_messages/create.js.erb
  51. 8
    0
      app/views/games/_form.html.erb
  52. 13
    0
      app/views/games/_list.html.erb
  53. 3
    0
      app/views/games/components/_message.html.erb
  54. 0
    0
      app/views/games/index.html.erb
  55. 5
    0
      app/views/games/index.js.erb
  56. 12
    0
      app/views/games/new.html.erb
  57. 17
    0
      app/views/games/show.html.erb
  58. 7
    0
      app/views/layouts/_footer.html.erb
  59. 17
    0
      app/views/layouts/_nav.html.erb
  60. 20
    5
      app/views/layouts/application.html.erb
  61. 42
    0
      app/views/lobby/index.html.erb
  62. 34
    0
      app/views/main/index.html.erb
  63. 3
    0
      app/views/passwords/create.html.erb
  64. 18
    0
      app/views/passwords/edit.html.erb
  65. 16
    0
      app/views/passwords/new.html.erb
  66. 22
    0
      app/views/sessions/_form.html.erb
  67. 6
    0
      app/views/sessions/new.html.erb
  68. 9
    0
      app/views/users/_form.html.erb
  69. 15
    0
      app/views/users/new.html.erb
  70. 25
    21
      bin/bundle
  71. 3
    1
      bin/rails
  72. 3
    1
      bin/rake
  73. 2
    0
      bin/setup
  74. 1
    0
      bin/spring
  75. 10
    9
      bin/webpack
  76. 10
    9
      bin/webpack-dev-server
  77. 7
    7
      bin/yarn
  78. 3
    1
      config.ru
  79. 5
    2
      config/application.rb
  80. 5
    3
      config/boot.rb
  81. 3
    1
      config/environment.rb
  82. 6
    2
      config/environments/development.rb
  83. 7
    5
      config/environments/production.rb
  84. 6
    2
      config/environments/test.rb
  85. 1
    0
      config/initializers/application_controller_renderer.rb
  86. 4
    2
      config/initializers/assets.rb
  87. 1
    0
      config/initializers/backtrace_silencers.rb
  88. 7
    0
      config/initializers/clearance.rb
  89. 1
    0
      config/initializers/content_security_policy.rb
  90. 2
    0
      config/initializers/cookies_serializer.rb
  91. 2
    0
      config/initializers/filter_parameter_logging.rb
  92. 1
    0
      config/initializers/inflections.rb
  93. 1
    0
      config/initializers/mime_types.rb
  94. 3
    0
      config/initializers/theme.rb
  95. 2
    0
      config/initializers/wrap_parameters.rb
  96. 64
    0
      config/locales/clearance.en.yml
  97. 6
    4
      config/puma.rb
  98. 19
    1
      config/routes.rb
  99. 2
    0
      config/spring.rb
  100. 0
    0
      db/migrate/20200906212251_create_users.rb

+ 78
- 0
.rubocop.yml View File

@@ -0,0 +1,78 @@
require:
- rubocop-rails
- rubocop-performance

AllCops:
NewCops: enable

Layout/EmptyLinesAroundAccessModifier:
EnforcedStyle: only_before

Layout/ExtraSpacing:
AllowForAlignment: false

Layout/IndentationConsistency:
EnforcedStyle: indented_internal_methods

Layout/LineLength:
Enabled: false

Metrics:
Enabled: false

Style/AccessorGrouping:
Enabled: false

Style/AndOr:
Enabled: false

Style/ClassAndModuleChildren:
Enabled: false

Style/ConditionalAssignment:
Enabled: false

Style/Documentation:
Enabled: false

Style/EmptyMethod:
Enabled: false

Style/FormatStringToken:
Enabled: false

Style/GuardClause:
Enabled: false

Style/HashEachMethods:
Enabled: true

Style/HashTransformKeys:
Enabled: true

Style/HashTransformValues:
Enabled: true

Style/IfInsideElse:
Enabled: false

Style/IfUnlessModifier:
Enabled: false

Style/NumericLiterals:
Enabled: false

Style/SafeNavigation:
Enabled: false

Style/StringLiterals:
EnforcedStyle: double_quotes

Style/StringLiteralsInInterpolation:
EnforcedStyle: double_quotes

Style/SymbolArray:
Enabled: false

Style/WordArray:
Enabled: false

+ 30
- 22
Gemfile View File

@@ -1,54 +1,62 @@
source 'https://rubygems.org'
# frozen_string_literal: true

source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.6'
ruby "2.6.6"

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3', '>= 6.0.3.2'
gem "rails", "~> 6.0.3", ">= 6.0.3.2"
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
gem "pg", ">= 0.18", "< 2.0"
# Use Puma as the app server
gem 'puma', '~> 4.1'
gem "puma", "~> 4.1"
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
gem "sass-rails", ">= 6"
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
gem "webpacker", "~> 4.0"
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
gem "turbolinks", "~> 5"
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
gem "jbuilder", "~> 2.7"
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
gem "redis", "~> 4.0"
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
gem "bcrypt", "~> 3.1.7"

# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
gem "bootsnap", ">= 1.4.2", require: false

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem "byebug", platforms: %i[mri mingw x64_mingw]
end

group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '~> 3.2'
gem "listen", "~> 3.2"
gem "web-console", ">= 3.3.0"
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
gem "spring"
gem "spring-watcher-listen", "~> 2.0.0"
end

group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
gem "capybara", ">= 2.15"
gem "selenium-webdriver"
# Easy installation and use of web drivers to run system tests with browsers
gem 'webdrivers'
gem "webdrivers"
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem "clearance", "~> 2.3"
gem "rubocop", "~> 0.90.0"
gem "rubocop-performance", "~> 1.8"
gem "rubocop-rails", "~> 2.8"
gem "view_component", "~> 2.18"

gem "better_errors", "~> 2.7"
gem "binding_of_caller", "~> 0.8.0"

+ 62
- 1
Gemfile.lock View File

@@ -58,7 +58,18 @@ GEM
zeitwerk (~> 2.2, >= 2.2.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
argon2 (2.0.2)
ffi (~> 1.9)
ffi-compiler (>= 0.1)
ast (2.4.1)
bcrypt (3.1.16)
better_errors (2.7.1)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
bindex (0.8.1)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
bootsnap (1.4.8)
msgpack (~> 1.0)
builder (3.2.4)
@@ -72,10 +83,25 @@ GEM
regexp_parser (~> 1.5)
xpath (~> 3.2)
childprocess (3.0.0)
clearance (2.3.0)
actionmailer (>= 5.0)
activemodel (>= 5.0)
activerecord (>= 5.0)
argon2 (~> 2.0, >= 2.0.2)
bcrypt (>= 3.1.1)
email_validator (~> 2.0)
railties (>= 5.0)
coderay (1.1.3)
concurrent-ruby (1.1.7)
crass (1.0.6)
debug_inspector (0.0.3)
email_validator (2.0.1)
activemodel
erubi (1.9.0)
ffi (1.13.1)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.8.5)
@@ -101,6 +127,9 @@ GEM
nio4r (2.5.2)
nokogiri (1.10.10)
mini_portile2 (~> 2.4.0)
parallel (1.19.2)
parser (2.7.1.4)
ast (~> 2.4.1)
pg (1.2.3)
public_suffix (4.0.6)
puma (4.3.6)
@@ -136,11 +165,32 @@ GEM
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
rainbow (3.0.0)
rake (13.0.1)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.2.1)
regexp_parser (1.7.1)
rexml (3.2.4)
rubocop (0.90.0)
parallel (~> 1.10)
parser (>= 2.7.1.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.7)
rexml
rubocop-ast (>= 0.3.0, < 1.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.3.0)
parser (>= 2.7.1.4)
rubocop-performance (1.8.0)
rubocop (>= 0.87.0)
rubocop-rails (2.8.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 0.87.0)
ruby-progressbar (1.10.1)
rubyzip (2.3.0)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
@@ -174,6 +224,9 @@ GEM
turbolinks-source (5.2.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
unicode-display_width (1.7.0)
view_component (2.18.1)
activesupport (>= 5.0.0, < 7.0)
web-console (4.0.4)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
@@ -198,20 +251,28 @@ PLATFORMS
ruby

DEPENDENCIES
bcrypt (~> 3.1.7)
better_errors (~> 2.7)
binding_of_caller (~> 0.8.0)
bootsnap (>= 1.4.2)
byebug
capybara (>= 2.15)
clearance (~> 2.3)
jbuilder (~> 2.7)
listen (~> 3.2)
pg (>= 0.18, < 2.0)
puma (~> 4.1)
rails (~> 6.0.3, >= 6.0.3.2)
redis (~> 4.0)
rubocop (~> 0.90.0)
rubocop-performance (~> 1.8)
rubocop-rails (~> 2.8)
sass-rails (>= 6)
selenium-webdriver
spring
spring-watcher-listen (~> 2.0.0)
turbolinks (~> 5)
tzinfo-data
view_component (~> 2.18)
web-console (>= 3.3.0)
webdrivers
webpacker (~> 4.0)

+ 35
- 24
README.md View File

@@ -1,24 +1,35 @@
# README

This README would normally document whatever steps are necessary to get the
application up and running.

Things you may want to cover:

* Ruby version

* System dependencies

* Configuration

* Database creation

* Database initialization

* How to run the test suite

* Services (job queues, cache servers, search engines, etc.)

* Deployment instructions

* ...
# Super Text Adventure
_Graphics are for peasants..._

```
_,. _,. _,. _,.
,` -.) ,` -.) ,` -.) ,` -.)
( _/-\\-._ ( _/-\\-._ ( _/-\\-._ ( _/-\\-._
/,|`--._,-^| , /,|`--._,-^| , /,|`--._,-^| , /,|`--._,-^| ,
\_| |`-._/|| ,'| \_| |`-._/|| ,'| \_| |`-._/|| ,'| \_| |`-._/|| ,'|
| `-, / | / / | `-, / | / / | `-, / | / / | `-, / | / /
| || | / / | || | / / | || | / / | || | / /
`r-._||/ __ / / `r-._||/ __ / / `r-._||/ __ / / `r-._||/ __ / /
__,-<_ )`-/ `./ / __,-<_ )`-/ `./ / __,-<_ )`-/ `./ / __,-<_ )`-/ `./ /
' \ `---' \ / / ' \ `---' \ / / ' \ `---' \ / / ' \ `---' \ / /
| |./ / | |./ / | |./ / | |./ /
/ // / / // / / // / / // /
\_/' \ |/ / \_/' \ |/ / \_/' \ |/ / \_/' \ |/ /
| | _,^-'/ / | | _,^-'/ / | | _,^-'/ / | | _,^-'/ /
| , `` (\/ /_ | , `` (\/ /_ | , `` (\/ /_ | , `` (\/ /_
\,.->._ \X-=/^ \,.->._ \X-=/^ \,.->._ \X-=/^ \,.->._ \X-=/^
( / `-._//^` ( / `-._//^` ( / `-._//^` ( / `-._//^`
`Y-.____(__} `Y-.____(__} `Y-.____(__} `Y-.____(__}
| {__) | {__) | {__) | {__)
() () () ()

source: https://www.asciiart.eu/people/occupations/knights
```

This is a web-based game that attempts to capture some of the nostalgia from the old text-based adventure games (e.g. Zork).

Most of these old games pitted you against a text interpreter and the cunning mind of the game designer! Super Text Adventure replaces the latter with a real person.

Essentially it's a glorified chat app, but the idea is to provide the dynamic nature of a DnD session with the graphical limitations of a text-based adventure.

This is a Rails app that utilizes ActionCable and [ViewComponents](https://github.com/github/view_component). Please enjoy.

+ 3
- 1
Rakefile View File

@@ -1,6 +1,8 @@
# frozen_string_literal: true

# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require_relative 'config/application'
require_relative "config/application"

Rails.application.load_tasks

+ 10
- 0
app/assets/stylesheets/_fonts.scss View File

@@ -0,0 +1,10 @@
@import url('https://fonts.googleapis.com/css?family=VT323&display=swap');

@font-face {
font-family: "c64";
src: url('/fonts/C64_Pro_Mono.woff2') format('woff2'),
url('/fonts/C64_Pro_Mono.woff') format('woff'),
url('/fonts/C64_Pro_Mono.ttf') format('truetype'),
url('/fonts/C64_Pro_Mono.eot') format('embedded-opentype'),
url('/fonts/C64_Pro_Mono.svg') format('svg');
}

+ 25
- 0
app/assets/stylesheets/_vendor_customizations.scss View File

@@ -0,0 +1,25 @@
$footer-padding: 1rem 0rem 1rem;

pre {
background: transparent !important;
}

.mx-auto {
margin: 0 auto;
}

.fit-content {
width: fit-content;
}

.fixed-top {
position: fixed;
top: 0;
width: 100%;
}

.fixed-bottom {
position: fixed;
bottom: 0;
width: 100%;
}

+ 0
- 15
app/assets/stylesheets/application.css View File

@@ -1,15 +0,0 @@
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/

+ 7
- 0
app/assets/stylesheets/application.scss View File

@@ -0,0 +1,7 @@
@import '_fonts';
@import '_vendor_customizations';

@import 'bulma/bulma.sass';
@import 'layout';

@import 'theme-oldschool'

+ 105
- 0
app/assets/stylesheets/layout.scss View File

@@ -0,0 +1,105 @@
html {
overflow: hidden;
}

nav {
max-height: 56px;
}

.app-container {
margin-top: 56px;
}

.width-fit-content {
width: fit-content;
width: -moz-fit-content;
}

.sign-out {
position: absolute;
top: 10px;
right: 10px;
opacity: 0.6;

.hidden-text {
opacity: 0;
vertical-align: bottom;
padding-top: 5px;
}

&:hover {
opacity: 1;
text-decoration: none;

.hidden-text {
opacity: 0.4;
}
}

a:hover {
text-decoration: none;
}
}

.games-container {
min-height: 200px;
.games-loading {
display: none;
}

.games-list {
.game-list-item {
border-bottom: dashed 2px;
min-height: 50px;
}
}

&.loading {
.games-loading {
display: block;
}

.games-list {
display: none;
}
}

.games-loading {
width: 100%;
height: 100%;
}
}

.game-container {
height: calc(100vh - 100px);

#message-container {
height: calc(100vh - 190px);
padding-bottom: 150px;
overflow-y: scroll;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */

&::-webkit-scrollbar { /* WebKit */
width: 0;
height: 0;
}

.game-message-user-divider {
border-top: 2px dashed;
opacity: 0.3;
}
}

#message-form {
bottom: 0px;
width: 100%;
left: 4px;
textarea {
border: none;
border-top: solid;
min-height: 90px;
}
}
}

+ 184
- 0
app/assets/stylesheets/theme-oldschool.scss View File

@@ -0,0 +1,184 @@
$oldschool-font-color: #0cbf0c;
$oldschool-bg-color: #292922;

html, body {
font-family: 'c64', monospace;
}

body.oldschool {
color: $oldschool-font-color;
background-color: $oldschool-bg-color;

.oldschool-text-color {
color: $oldschool-font-color;
}

.border-container {
border: dashed 2px $oldschool-font-color;
}

.section-header {
text-align: center;
text-decoration: underline;
padding: 2px 0px;
}

background-image: radial-gradient(
rgba(0, 150, 0, 0.13), $oldschool-bg-color 120%
);
height: 100vh;

&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: repeating-linear-gradient(
0deg,
rgba(black, 0.15),
rgba(black, 0.15) 1px,
transparent 1px,
transparent 2px
);
pointer-events: none;
}

a {
color: $oldschool-font-color;

&:hover {
color: lighten( $oldschool-font-color, 10% );
}
}

select, input, textarea, .btn {
background-color: transparent;
border: solid 2px $oldschool-font-color;
caret-color: $oldschool-font-color;
box-shadow: none;
color: $oldschool-font-color;

&:focus{
background-color: transparent;
border-color: $oldschool-font-color;
outline: 0;
box-shadow: none;
color: $oldschool-font-color;
}
}

&.form-control:focus{
background-color: transparent;
border-color: $oldschool-font-color;
outline: 0;
box-shadow: none;
color: $oldschool-font-color;
}

.btn:hover {
color: lighten($oldschool-font-color, 20%);
background-color: lighten($oldschool-bg-color, 5%);
}

.btn {
transition: none;
padding-top: 3px;
padding-bottom: 1px;
text-transform: uppercase;
}

.btn:focus, button:focus {
outline: none;
}

.form-check input {
position: absolute;
z-index: -1;
opacity: 0;
}

.form-check label {
padding-left: 5px;
}

.form-check.form-check-inline label {
padding-left: 25px;
}

.form-check label::before {
position: absolute;
top: .2rem;
left: 0;
display: block;
width: 1rem;
height: 1rem;
pointer-events: none;
content: "";
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: transparent;
border: solid 1px $oldschool-font-color;
}

.form-check label::after {
position: absolute;
top: .25rem;
left: 0;
display: block;
width: 1rem;
height: 1rem;
content: "";
background-repeat: no-repeat;
background-position: center center;
background-size: 50% 50%;
}

.form-check input:checked~label::before {
background-color: #0fbf09;
}

.games-container {
.games-list {
.game-list-item:hover {
background-color: darken($oldschool-font-color, 20%);
cursor: pointer;
}

a {
&:hover {
text-decoration: none;
}
}
}
}

nav {
background-color: #282e21;
}

footer {
border-top: dashed 2px $oldschool-font-color;
background-color: #282e21;

.application-hotkey {
display: inline-block;
margin-right: 10px;
.hotkey {
color: $oldschool-font-color;
font-size: 18px;
background-color: darken($oldschool-font-color, 10%);
padding: 2px;
}

.hotkey-label {
opacity: 0.6;
margin-left: 0;
}
}
}
}

+ 2
- 0
app/channels/application_cable/channel.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

module ApplicationCable
class Channel < ActionCable::Channel::Base
end

+ 2
- 0
app/channels/application_cable/connection.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

module ApplicationCable
class Connection < ActionCable::Connection::Base
end

+ 3
- 0
app/components/welcome_back_message_component.html.erb View File

@@ -0,0 +1,3 @@
<%= link_to @link do %>
Start Thine Adventure, <%= @username %>
<% end %>

+ 7
- 0
app/components/welcome_back_message_component.rb View File

@@ -0,0 +1,7 @@
class WelcomeBackMessageComponent < ViewComponent::Base

def initialize(link:, username:)
@link = link
@username = username
end
end

+ 3
- 0
app/components/welcome_login_message_component.html.erb View File

@@ -0,0 +1,3 @@
<%= link_to @link, class: 'login-button' do %>
Loggeth In
<% end %>

+ 6
- 0
app/components/welcome_login_message_component.rb View File

@@ -0,0 +1,6 @@
class WelcomeLoginMessageComponent < ViewComponent::Base

def initialize(link:)
@link = link
end
end

+ 6
- 0
app/controllers/account_controller.rb View File

@@ -0,0 +1,6 @@
class AccountController < ApplicationController
before_action :require_login
def index
end
end

+ 2
- 0
app/controllers/application_controller.rb View File

@@ -1,2 +1,4 @@
class ApplicationController < ActionController::Base
include Clearance::Controller
protect_from_forgery with: :exception
end

+ 20
- 0
app/controllers/game_messages_controller.rb View File

@@ -0,0 +1,20 @@
class GameMessagesController < ApplicationController
def create
previous_message_username = GameMessage.where(game_id: game_message_params[:game_id]).last.user.username
@game_message = GameMessage.create game_message_params.merge(user_id: current_user.id)

@show_username = @game_message.user.username != previous_message_username

message_script_template = render_to_string template: "game_messages/create.js.erb", layout: false

ActionCable.server.broadcast("game_message:#{game_message_params[:game_id]}", script: message_script_template)

head :ok
end

private

def game_message_params
params.require(:game_message).permit(:body, :game_id, :meta)
end
end

+ 38
- 0
app/controllers/games_controller.rb View File

@@ -0,0 +1,38 @@
class GamesController < ApplicationController
before_action :require_login
def index
@games = []
if params[:my_games].present? || params[:friendly_games].present?
@games = current_user.games if params[:my_games].present?
@games += current_user.friends.map(&:games).flatten
else
@games = Game.all
end

respond_to :js
end

def show
@game = Game.find(params[:id])
end

def new
@game = Game.new
end

def create
@game = Game.create game_params.merge(created_by: current_user.id)
@game.games_users.create user_id: current_user.id, role: "dm"

flash[:success] = "Game Created Successfully! You can now prepare content and invite players."

redirect_to game_path @game
end

private

def game_params
params.require(:game).permit([:id, :name, :mode, :description, :max_players, :is_friends_only])
end
end

+ 6
- 0
app/controllers/lobby_controller.rb View File

@@ -0,0 +1,6 @@
class LobbyController < ApplicationController
before_action :require_login
def index
end
end

+ 4
- 0
app/controllers/main_controller.rb View File

@@ -0,0 +1,4 @@
class MainController < ApplicationController
def index
end
end

+ 9
- 0
app/helpers/alert_helper.rb View File

@@ -0,0 +1,9 @@
module AlertHelper
def alert_type(type)
{
error: "is-danger",
alert: "is-warning",
notice: "is-info"
}[type.to_sym] || "is-#{type}"
end
end

+ 8
- 0
app/helpers/game_helper.rb View File

@@ -0,0 +1,8 @@
module GameHelper
def options_for_game_mode
[
["Classic", :classic],
["Free-Form", :freeform]
]
end
end

+ 13
- 0
app/javascript/cables/cable.js View File

@@ -0,0 +1,13 @@
import { createConsumer } from "@rails/actioncable"

let consumer;

function createChannel(...args) {
if (!consumer) {
consumer = createConsumer();
}

return consumer.subscriptions.create(...args);
}

export default createChannel;

+ 42
- 0
app/javascript/controllers/game_message_controller.js View File

@@ -0,0 +1,42 @@
import { Controller } from "stimulus"
import { cspNonce } from '@rails/ujs';
import createChannel from "cables/cable";

export default class extends Controller {
static targets = [ "game_message" ]

initialize() {
let GameMessageController = this;
this.GameMessageChannel = createChannel(
{
channel: "GameMessageChannel",
game_id: document.getElementById('game').getAttribute('data-game-id')
},
{
connected() {
GameMessageController.listen()
},
received(data) {
let script = document.createElement('script');
script.setAttribute('nonce', cspNonce());
script.text = data.script;
document.head.appendChild(script).parentNode.removeChild(script);
}
}
);
}

connect() {
this.listen()
}

disconnect() {
this.GameMessageChannel.perform('unfollow')
}

listen() {
if (this.GameMessageChannel) {
this.GameMessageChannel.perform('follow')
}
}
}

+ 4
- 1
app/javascript/packs/application.js View File

@@ -4,7 +4,6 @@
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

@@ -17,3 +16,7 @@ require("channels")
// const imagePath = (name) => images(name, true)

import "controllers"


const files = require.context('../src', true, /\.js$/);
files.keys().forEach(files);

+ 37
- 0
app/javascript/src/_globals.js View File

@@ -0,0 +1,37 @@
import { extend } from 'lodash';

// http://youmightnotneedjquery.com/#ready
let ready = function(fn) {
if (document.readyState != 'loading'){
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}

// https://codepen.io/rileyjshaw/pen/ABzsc
let loading = function (el, i = 0) {
if(!el) return false;
let seq = [
'Loading',
'=Loading=',
'==Loading==',
'-==Loading==-',
'--==Loading==--',
'--== Loading ==--',
'--== Loading ==--',
'--==Loading==--',
'-==Loading==-',
'==Loading==',
'=Loading=',
'Loading',
]

el.innerText = seq[i %= seq.length];
return setTimeout(loading, 150, el, ++i);
};

extend(window, {
ready,
loading
});

+ 9
- 0
app/javascript/src/alerts.js View File

@@ -0,0 +1,9 @@
ready(function(){
let dismissibles = document.getElementsByClassName('close-notification');

for (var i=0, len=dismissibles.length|0; i<len; i=i+1|0) {
dismissibles[i].addEventListener('click', function(e){
e.target.closest('.notification').parentNode.removeChild(e.target.closest('.notification'));
});
}
});

+ 28
- 0
app/javascript/src/game.js View File

@@ -0,0 +1,28 @@
import { ajax } from '@rails/ujs';
import qs from 'qs';

function submit_game_message(e){
let evtobj = window.event ? event : e
if(!evtobj.shiftKey && evtobj.keyCode == 13){
ajax({
type: 'POST',
url: `/game_messages`,
data: qs.stringify({
game_message: {
body: document.getElementById('game-message-input').value,
game_id: document.getElementById('game').getAttribute('data-game-id'),
meta: {}
}
}),
dataType: 'json',
});
document.getElementById('game-message-input').value = '';
return false;
}
}

ready(function(){
if(document.getElementById('game')){
document.getElementById('game-message-input').onkeydown = submit_game_message;
}
});

+ 33
- 0
app/javascript/src/lobby.js View File

@@ -0,0 +1,33 @@
import { ajax } from '@rails/ujs';
import qs from 'qs';

let fetch_games_list = function(){
let filters = {}
let filter_triggers = document.getElementsByClassName('filter-trigger')
for (var i=0, len=filter_triggers.length|0; i<len; i=i+1|0) {
let filter_input = filter_triggers[i].querySelector('input');
if(filter_input.checked){
filters[filter_input.name] = 1;
}
}

ajax({
type: 'GET',
url: `/games`,
data: qs.stringify(filters),
dataType: 'script',
});
}

ready(function(){
if(document.getElementsByClassName('lobby-container').length > 0){
loading(document.getElementsByClassName('games-loading')[0]);

fetch_games_list();

let filter_triggers = document.getElementsByClassName('filter-trigger')
for (var i=0, len=filter_triggers.length|0; i<len; i=i+1|0) {
filter_triggers[i].querySelector('input').addEventListener('change', fetch_games_list);
}
}
});

+ 24
- 0
app/javascript/src/navigation-hotkeys.js View File

@@ -0,0 +1,24 @@
function key_press(e) {
let evtobj = window.event ? event : e
if(evtobj.ctrlKey){
switch(evtobj.keyCode){
case 72:
window.location.href = '/'
break;
case 70:
window.location.href = '/friends'
break;
case 67:
window.location.href = '/characters'
break
case 71:
window.location.href = '/lobby'
break
case 83:
window.location.href = '/settings'
break
}
}
}

document.onkeydown = key_press;

+ 2
- 0
app/jobs/application_job.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

class ApplicationJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked

+ 4
- 2
app/mailers/application_mailer.rb View File

@@ -1,4 +1,6 @@
# frozen_string_literal: true

class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
default from: "from@example.com"
layout "mailer"
end

+ 2
- 0
app/models/application_record.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

+ 6
- 0
app/models/character.rb View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true

class Character < ApplicationRecord
belongs_to :user
belongs_to :game
end

+ 6
- 0
app/models/character_inventory_item.rb View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true

class CharacterInventoryItem < ApplicationRecord
belongs_to :character
belongs_to :inventory_item
end

+ 5
- 0
app/models/friend_request.rb View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true

class FriendRequest < ApplicationRecord
belongs_to :user
end

+ 4
- 0
app/models/game.rb View File

@@ -0,0 +1,4 @@
# frozen_string_literal: true

class Game < ApplicationRecord
end

+ 6
- 0
app/models/game_message.rb View File

@@ -0,0 +1,6 @@
# frozen_string_literal: true

class GameMessage < ApplicationRecord
belongs_to :game
belongs_to :user
end

+ 4
- 0
app/models/inventory_item.rb View File

@@ -0,0 +1,4 @@
# frozen_string_literal: true

class InventoryItem < ApplicationRecord
end

+ 4
- 0
app/models/monster.rb View File

@@ -0,0 +1,4 @@
# frozen_string_literal: true

class Monster < ApplicationRecord
end

+ 7
- 0
app/models/user.rb View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true

class User < ApplicationRecord
include Clearance::User

include Clearance::User
end

+ 5
- 0
app/models/user_friend.rb View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true

class UserFriend < ApplicationRecord
belongs_to :user
end

+ 0
- 0
app/views/account/index.html.erb View File


+ 8
- 0
app/views/clearance_mailer/change_password.html.erb View File

@@ -0,0 +1,8 @@
<p><%= t(".opening") %></p>

<p>
<%= link_to t(".link_text", default: "Change my password"),
edit_user_password_url(@user, token: @user.confirmation_token) %>
</p>

<p><%= t(".closing") %></p>

+ 5
- 0
app/views/clearance_mailer/change_password.text.erb View File

@@ -0,0 +1,5 @@
<%= t(".opening") %>

<%= edit_user_password_url(@user, token: @user.confirmation_token) %>

<%= t(".closing") %>

+ 10
- 0
app/views/game_messages/_message.html.erb View File

@@ -0,0 +1,10 @@
<div class="game-message" data-usermname="<%= message.user.username %>">
<% if show_username %>
<div class="py-1 game-message-user-divider"></div>
<span class="font-weight-bold">
<%= message.user.username.upcase %>:
</span><br>
<% else %>
<% end %>
<div class="pl-4"><%= message.body %></div>
</div>

+ 2
- 0
app/views/game_messages/create.js.erb View File

@@ -0,0 +1,2 @@
var message_html = document.createRange().createContextualFragment("<%= escape_javascript(render('message', message: @game_message, show_username: @show_username)) %>");
document.getElementById('message-container').appendChild(message_html);

+ 8
- 0
app/views/games/_form.html.erb View File

@@ -0,0 +1,8 @@
<%= simple_form_for game do |f| %>
<%= f.input :name, label: 'Game Name' %>
<%= f.input :mode, collection: options_for_game_mode, selected: :classic, as: :radio_buttons %>
<%= f.input :description %>
<%= f.input :max_players, value: 4 %>
<%= f.input :is_friends_only, label: 'Friends Only?' %>
<%= f.submit 'Starteth Thine Game', class: 'btn w-100' %>
<% end %>

+ 13
- 0
app/views/games/_list.html.erb View File

@@ -0,0 +1,13 @@
<% if games.present? %>
<% games.uniq.each do |game| %>
<%= link_to game_path(game) do %>
<div class="game-list-item" attr-id="<%= game.id %>">
<img src="/scroll.svg" width="45" class="float-right mr-2">
<span class="game-name"><%= game.name %></span><br>
<span><small><%= game.description %></small></span>
</div>
<% end %>
<% end %>
<% else %>
<div class="text-center pt-5"> --== No Games Found ==--</div>
<% end %>

+ 3
- 0
app/views/games/components/_message.html.erb View File

@@ -0,0 +1,3 @@
<div class="game-message">
Game Message Stuff
</div>

+ 0
- 0
app/views/games/index.html.erb View File


+ 5
- 0
app/views/games/index.js.erb View File

@@ -0,0 +1,5 @@
var list_html = '<%= escape_javascript(render('list', games: @games)) %>';

document.getElementsByClassName('games-list')[0].innerHTML = list_html;

document.getElementsByClassName('games-container')[0].classList.remove('loading');

+ 12
- 0
app/views/games/new.html.erb View File

@@ -0,0 +1,12 @@
<h4 class="text-center w-100">Create a Game</h4>

<div class="container">
<%= simple_form_for @game do |f| %>
<%= f.input :name, label: 'Game Name' %>
<%= f.input :mode, collection: options_for_game_mode, selected: :classic, as: :radio_buttons %>
<%= f.input :description %>
<%= f.input :max_players, value: 4 %>
<%= f.input :is_friends_only, label: 'Friends Only?' %>
<%= f.submit 'Starteth Thine Game', class: 'btn w-100' %>
<% end %>
</div>

+ 17
- 0
app/views/games/show.html.erb View File

@@ -0,0 +1,17 @@
<div data-game-id="<%= @game.id %>" id="game" class="game-container row">
<div class="col-md-8 border-container border-right-0 border-bottom-0 position-relative">
<div data-controller="game-message" id="message-container">
<% current_username = nil %>
<% @game.game_messages.each do |message| %>
<%= render "game_messages/message", { message: message, show_username: (current_username != message.user.username) } %>
<% current_username = message.user.username %>
<% end %>
</div>
<div id="message-form" class="position-absolute">
<textarea class="form-control" id="game-message-input"></textarea>
</div>
</div>
<div class="col-md-4 border-container border-bottom-0">
<div id="sidebar"></div>
</div>
</div>

+ 7
- 0
app/views/layouts/_footer.html.erb View File

@@ -0,0 +1,7 @@
<footer class="footer fixed-bottom p-1">
<span class="application-hotkey"><span class="hotkey">^H</span><span class="hotkey-label">ome</span></span>
<span class="application-hotkey"><span class="hotkey">^F</span><span class="hotkey-label">riends</span></span>
<span class="application-hotkey"><span class="hotkey">^C</span><span class="hotkey-label">haracters</span></span>
<span class="application-hotkey"><span class="hotkey">^G</span><span class="hotkey-label">ames</span></span>
<span class="application-hotkey"><span class="hotkey">^S</span><span class="hotkey-label">ettings</span></span>
</footer>

+ 17
- 0
app/views/layouts/_nav.html.erb View File

@@ -0,0 +1,17 @@
<nav class="navbar text-center fixed-top">
<a class="navbar-brand mx-auto" href="/">
<img src="/shield.svg" width="25" height="30" alt="">
<p class="is-size-4 pl-2 pr-2 pt-2">
Super Text Adventure
</p>
<img src="/sword.svg" width="30" alt="">
</a>

<% unless current_user.nil? %>
<div class="float-right sign-out">
<%= link_to destroy_user_session_path, method: :delete, data: { confirm: 'Signeth Out?' } do %>
<span class="hidden-text"><small>Sign Out</small></span> <img src="/sign-out.svg" width="25" height="25">
<% end %>
</div>
<% end %>
</nav>

+ 20
- 5
app/views/layouts/application.html.erb View File

@@ -1,15 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<title>SuperTextAdventure</title>
<title>Super Text Adventure</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
</head>

<body>
<%= yield %>
<body class="<%= APP_THEME %>">
<%= render 'layouts/nav' %>

<% unless flash.empty? %>
<% flash.each do |type, message| %>
<div class="notification <%= alert_type(type) %>" role="alert">
<%= message %>
<button class="delete close-notification"></button>
</div>
<% end %>
<% end %>

<div class="app-container p-2">
<%= yield %>
</div>

<%= render 'layouts/footer' %>
</body>
</html>

+ 42
- 0
app/views/lobby/index.html.erb View File

@@ -0,0 +1,42 @@
<div class="lobby-container px-4">
<h4 class="text-center w-100">Lobby</h4>
<div class="row">
<div class="col-md-8 border-container">
<div class="lobby-filters w-100 py-2">
<div class="form-check form-check-inline filter-trigger">
<input class="form-check-input boolean optional" type="checkbox" value="1" name="friendly_games" id="friendly_games">
<label class="form-check-label boolean optional" for="friendly_games">Friend's Games</label>
</div>
<div class="form-check form-check-inline filter-trigger">
<input class="form-check-input boolean optional" type="checkbox" value="1" name="my_games" id="my_games">
<label class="form-check-label boolean optional" for="my_games">My Games</label>
</div>
<div class="d-inline-block">
<%= link_to new_game_path, class: "btn btn-sm text-uppercase" do %>
New Game +
<% end %>
</div>
</div>
<div class="games-container loading w-100 border-container border-left-0 border-right-0 border-bottom-0">
<div class="games-list"></div>
<div class="games-loading text-center pt-5 loader"></div>
</div>
</div>

<div class="col-md-4 border-container border-left-0">
<div class="friends-containter">
<p class="section-header">Friends</p>

<% if current_user.friends.present? %>
<% current_user.friends.each do |friend| %>
<div class="friend-list-item">
<span><%= friend.username %></span>
</div>
<% end %>
<% else %>
<p class="text-center pt-5"><strong>THOU HAST NO FRIENDS!</strong></p>
<% end %>
</div>
</div>
</div>
</div>

+ 34
- 0
app/views/main/index.html.erb View File

@@ -0,0 +1,34 @@

<div class="container">
<div class="fit-content mx-auto pl-5">
<pre class="oldschool-text-color font-weight-bold">
_,.
,` -.)
( _/-\\-._
/,|`--._,-^| ,
\_| |`-._/|| ,'|
| `-, / | / /
| || | / /
`r-._||/ __ / /
__,-<_ )`-/ `./ /
' \ `---' \ / /
| |./ /
/ // /
\_/' \ |/ /
| | _,^-'/ /
| , `` (\/ /_
\,.->._ \X-=/^
( / `-._//^`
`Y-.____(__}
| {__)
()
</pre>
</div>
<div class="has-text-centered">
<% if signed_in? %>
<%= render WelcomeBackMessageComponent.new(link: lobby_index_path, username: current_user.username) %>
<% else %>
<%= render WelcomeLoginMessageComponent.new(link: sign_in_path) %>
<% end %>
</div>
</div>

+ 3
- 0
app/views/passwords/create.html.erb View File

@@ -0,0 +1,3 @@
<div id="clearance" class="password-reset">
<p><%= t(".description") %></p>
</div>

+ 18
- 0
app/views/passwords/edit.html.erb View File

@@ -0,0 +1,18 @@
<div id="clearance" class="password-reset">
<h2><%= t(".title") %></h2>

<p><%= t(".description") %></p>

<%= form_for :password_reset,
url: user_password_path(@user, token: @user.confirmation_token),
html: { method: :put } do |form| %>
<div class="password-field">
<%= form.label :password %>
<%= form.password_field :password %>
</div>

<div class="submit-field">
<%= form.submit %>
</div>
<% end %>
</div>

+ 16
- 0
app/views/passwords/new.html.erb View File

@@ -0,0 +1,16 @@
<div id="clearance" class="password-reset">
<h2><%= t(".title") %></h2>

<p><%= t(".description") %></p>

<%= form_for :password, url: passwords_path do |form| %>
<div class="text-field">
<%= form.label :email %>
<%= form.text_field :email, type: 'email' %>
</div>

<div class="submit-field">
<%= form.submit %>
</div>
<% end %>
</div>

+ 22
- 0
app/views/sessions/_form.html.erb View File

@@ -0,0 +1,22 @@
<%= form_for :session, url: session_path do |form| %>
<div class="text-field">
<%= form.label :email %>
<%= form.text_field :email, type: 'email' %>
</div>

<div class="password-field">
<%= form.label :password %>
<%= form.password_field :password %>
</div>

<div class="submit-field">
<%= form.submit %>
</div>

<div class="other-links">
<% if Clearance.configuration.allow_sign_up? %>
<%= link_to t(".sign_up"), sign_up_path %>
<% end %>
<%= link_to t(".forgot_password"), new_password_path %>
</div>
<% end %>

+ 6
- 0
app/views/sessions/new.html.erb View File

@@ -0,0 +1,6 @@
<div id="clearance" class="sign-in">
<h2><%= t(".title") %></h2>

<%= render partial: '/sessions/form' %>

</div>

+ 9
- 0
app/views/users/_form.html.erb View File

@@ -0,0 +1,9 @@
<div class="text-field">
<%= form.label :email %>
<%= form.text_field :email, type: 'email' %>
</div>

<div class="password-field">
<%= form.label :password %>
<%= form.password_field :password %>
</div>

+ 15
- 0
app/views/users/new.html.erb View File

@@ -0,0 +1,15 @@
<div id="clearance" class="sign-up">
<h2><%= t(".title") %></h2>

<%= form_for @user do |form| %>
<%= render partial: '/users/form', object: form %>

<div class="submit-field">
<%= form.submit %>
</div>

<div class="other-links">
<%= link_to t(".sign_in"), sign_in_path %>
</div>
<% end %>
</div>

+ 25
- 21
bin/bundle View File

@@ -8,46 +8,46 @@
# this file is here to facilitate running it.
#

require "rubygems"
require 'rubygems'

m = Module.new do
module_function

def invoked_as_script?
File.expand_path($0) == File.expand_path(__FILE__)
File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
end

def env_var_version
ENV["BUNDLER_VERSION"]
ENV['BUNDLER_VERSION']
end

def cli_arg_version
return unless invoked_as_script? # don't want to hijack other binstubs
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update`

bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
bundler_version = a
end
bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
bundler_version = $1

bundler_version = Regexp.last_match(1)
update_index = i
end
bundler_version
end

def gemfile
gemfile = ENV["BUNDLE_GEMFILE"]
gemfile = ENV['BUNDLE_GEMFILE']
return gemfile if gemfile && !gemfile.empty?

File.expand_path("../../Gemfile", __FILE__)
File.expand_path('../Gemfile', __dir__)
end

def lockfile
lockfile =
case File.basename(gemfile)
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
@@ -55,15 +55,17 @@ m = Module.new do

def lockfile_version
return unless File.file?(lockfile)

lockfile_contents = File.read(lockfile)
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/

Regexp.last_match(1)
end

def bundler_version
@bundler_version ||=
env_var_version || cli_arg_version ||
lockfile_version
lockfile_version
end

def bundler_requirement
@@ -73,28 +75,32 @@ m = Module.new do

requirement = bundler_gem_version.approximate_recommendation

return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0')

requirement += ".a" if bundler_gem_version.prerelease?
requirement += '.a' if bundler_gem_version.prerelease?

requirement
end

def load_bundler!
ENV["BUNDLE_GEMFILE"] ||= gemfile
ENV['BUNDLE_GEMFILE'] ||= gemfile

activate_bundler
end

def activate_bundler
gem_error = activation_error_handling do
gem "bundler", bundler_requirement
gem 'bundler', bundler_requirement
end
return if gem_error.nil?

require_error = activation_error_handling do
require "bundler/version"
require 'bundler/version'
end
if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
return
end
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
exit 42
end
@@ -109,6 +115,4 @@ end

m.load_bundler!

if m.invoked_as_script?
load Gem.bin_path("bundler", "bundle")
end
load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?

+ 3
- 1
bin/rails View File

@@ -1,6 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

begin
load File.expand_path('../spring', __FILE__)
load File.expand_path('spring', __dir__)
rescue LoadError => e
raise unless e.message.include?('spring')
end

+ 3
- 1
bin/rake View File

@@ -1,6 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

begin
load File.expand_path('../spring', __FILE__)
load File.expand_path('spring', __dir__)
rescue LoadError => e
raise unless e.message.include?('spring')
end

+ 2
- 0
bin/setup View File

@@ -1,4 +1,6 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'fileutils'

# path to your application root.

+ 1
- 0
bin/spring View File

@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# This file loads Spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.

+ 10
- 9
bin/webpack View File

@@ -1,18 +1,19 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] || 'development'
ENV['NODE_ENV'] ||= 'development'

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)

require "bundler/setup"
require 'bundler/setup'

require "webpacker"
require "webpacker/webpack_runner"
require 'webpacker'
require 'webpacker/webpack_runner'

APP_ROOT = File.expand_path("..", __dir__)
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
Webpacker::WebpackRunner.run(ARGV)
end

+ 10
- 9
bin/webpack-dev-server View File

@@ -1,18 +1,19 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"] ||= "development"
ENV['RAILS_ENV'] ||= ENV['RACK_ENV'] || 'development'
ENV['NODE_ENV'] ||= 'development'

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)

require "bundler/setup"
require 'bundler/setup'

require "webpacker"
require "webpacker/dev_server_runner"
require 'webpacker'
require 'webpacker/dev_server_runner'

APP_ROOT = File.expand_path("..", __dir__)
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
Webpacker::DevServerRunner.run(ARGV)
end

+ 7
- 7
bin/yarn View File

@@ -1,11 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
begin
exec "yarnpkg", *ARGV
rescue Errno::ENOENT
$stderr.puts "Yarn executable was not detected in the system."
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1
end
exec 'yarnpkg', *ARGV
rescue Errno::ENOENT
warn 'Yarn executable was not detected in the system.'
warn 'Download Yarn at https://yarnpkg.com/en/docs/install'
exit 1
end

+ 3
- 1
config.ru View File

@@ -1,5 +1,7 @@
# frozen_string_literal: true

# This file is used by Rack-based servers to start the application.

require_relative 'config/environment'
require_relative "config/environment"

run Rails.application

+ 5
- 2
config/application.rb View File

@@ -1,6 +1,9 @@
require_relative 'boot'
# frozen_string_literal: true

require 'rails/all'
require_relative "boot"

require "view_component/engine"
require "rails/all"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.

+ 5
- 3
config/boot.rb View File

@@ -1,4 +1,6 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
# frozen_string_literal: true

require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

require "bundler/setup" # Set up gems listed in the Gemfile.
require "bootsnap/setup" # Speed up boot time by caching expensive operations.

+ 3
- 1
config/environment.rb View File

@@ -1,5 +1,7 @@
# frozen_string_literal: true

# Load the Rails application.
require_relative 'application'
require_relative "application"

# Initialize the Rails application.
Rails.application.initialize!

+ 6
- 2
config/environments/development.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.

@@ -14,13 +16,13 @@ Rails.application.configure do

# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
if Rails.root.join("tmp/caching-dev.txt").exist?
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true

config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}"
"Cache-Control" => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
@@ -36,6 +38,8 @@ Rails.application.configure do

config.action_mailer.perform_caching = false

config.action_mailer.default_url_options = { host: "localhost:3000" }

# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log


+ 7
- 5
config/environments/production.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.

@@ -11,7 +13,7 @@ Rails.application.configure do
config.eager_load = true

# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.consider_all_requests_local = false
config.action_controller.perform_caching = true

# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
@@ -20,7 +22,7 @@ Rails.application.configure do

# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?

# Compress CSS using a preprocessor.
# config.assets.css_compressor = :sass
@@ -51,7 +53,7 @@ Rails.application.configure do
config.log_level = :debug

# Prepend all log lines with the following tags.
config.log_tags = [ :request_id ]
config.log_tags = [:request_id]

# Use a different cache store in production.
# config.cache_store = :mem_cache_store
@@ -81,9 +83,9 @@ Rails.application.configure do
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')

if ENV["RAILS_LOG_TO_STDOUT"].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger = ActiveSupport::Logger.new($stdout)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
config.logger = ActiveSupport::TaggedLogging.new(logger)
end

# Do not dump schema after migrations.

+ 6
- 2
config/environments/test.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

# The test environment is used exclusively to run your application's
# test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
@@ -17,11 +19,11 @@ Rails.application.configure do
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
"Cache-Control" => "public, max-age=#{1.hour.to_i}"
}

# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.cache_store = :null_store

@@ -36,6 +38,8 @@ Rails.application.configure do

config.action_mailer.perform_caching = false

config.action_mailer.default_url_options = { host: "localhost:3000" }

# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.

+ 1
- 0
config/initializers/application_controller_renderer.rb View File

@@ -1,3 +1,4 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.

# ActiveSupport::Reloader.to_prepare do

+ 4
- 2
config/initializers/assets.rb View File

@@ -1,12 +1,14 @@
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.version = "1.0"

# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
Rails.application.config.assets.paths << Rails.root.join("node_modules")

# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets

+ 1
- 0
config/initializers/backtrace_silencers.rb View File

@@ -1,3 +1,4 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.

# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.

+ 7
- 0
config/initializers/clearance.rb View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true

Clearance.configure do |config|
config.mailer_sender = "reply@example.com"
config.rotate_csrf_on_sign_in = true
config.routes = false
end

+ 1
- 0
config/initializers/content_security_policy.rb View File

@@ -1,3 +1,4 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.

# Define an application-wide content security policy

+ 2
- 0
config/initializers/cookies_serializer.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Specify a serializer for the signed and encrypted cookie jars.

+ 2
- 0
config/initializers/filter_parameter_logging.rb View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true

# Be sure to restart your server when you modify this file.

# Configure sensitive parameters which will be filtered from the log file.