├── .browserslistrc ├── .cloud66 └── manifest.yml ├── .env.example ├── .erdconfig ├── .eslintrc ├── .gitattributes ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .nvmrc ├── .postcss-sorting.json ├── .pryrc.example ├── .rspec ├── .rubocop.yml ├── .ruby-version ├── .scss-lint.yml ├── .semaphore ├── heroku.yml └── semaphore.yml ├── .slim-lint.yml ├── Brewfile ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── Procfile ├── README.md ├── Rakefile ├── app.json ├── app ├── assets │ ├── images │ │ └── .gitkeep │ ├── javascripts │ │ └── application.js │ ├── stylesheets │ │ ├── application.scss │ │ ├── components │ │ │ ├── all.scss │ │ │ ├── container.scss │ │ │ ├── forms.scss │ │ │ ├── navigation.scss │ │ │ └── notifications.scss │ │ ├── core │ │ │ ├── all.scss │ │ │ ├── foundation_and_overrides.scss │ │ │ └── settings.scss │ │ ├── emails │ │ │ └── core │ │ │ │ ├── all.scss │ │ │ │ └── settings.scss │ │ └── mailer.scss │ └── templates │ │ └── .keep ├── constraints │ └── ip_whitelist_constraint.rb ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ ├── authentication.rb │ │ ├── authorization.rb │ │ └── bullet_helper.rb │ ├── pages_controller.rb │ └── users │ │ └── registrations_controller.rb ├── decorators │ ├── application_decorator.rb │ ├── paginating_decorator.rb │ └── user_decorator.rb ├── interactors │ └── .keep ├── javascript │ └── packs │ │ └── application.js ├── jobs │ └── application_job.rb ├── mailers │ ├── application_mailer.rb │ └── devise_mailer.rb ├── models │ ├── application_record.rb │ └── user.rb ├── policies │ └── application_policy.rb ├── sanitizers │ └── user │ │ └── parameter_sanitizer.rb ├── uploaders │ └── avatar_uploader.rb └── views │ ├── application │ ├── _footer.html.slim │ ├── _messages.html.slim │ ├── _navigation.html.slim │ ├── _navigation_main.html.slim │ └── _navigation_user.html.slim │ ├── devise_mailer │ ├── confirmation_instructions.html.inky │ ├── confirmation_instructions.text.erb │ ├── reset_password_instructions.html.inky │ └── reset_password_instructions.text.erb │ ├── kaminari │ ├── _first_page.html.slim │ ├── _gap.html.slim │ ├── _last_page.html.slim │ ├── _next_page.html.slim │ ├── _page.html.slim │ ├── _paginator.html.slim │ └── _prev_page.html.slim │ ├── layouts │ ├── application.html.slim │ └── mailer.html.slim │ ├── pages │ └── home.html.slim │ └── users │ ├── confirmations │ └── new.html.slim │ ├── passwords │ ├── edit.html.slim │ └── new.html.slim │ ├── registrations │ ├── edit.html.slim │ └── new.html.slim │ ├── sessions │ └── new.html.slim │ └── shared │ └── _links.html.slim ├── babel.config.js ├── bin ├── autospec ├── brakeman ├── bundle ├── bundle-audit ├── guard ├── quality ├── rails ├── rake ├── rspec ├── rubocop ├── scss-lint ├── server ├── setup ├── slim-lint ├── spring ├── test ├── webpack ├── webpack-dev-server └── yarn ├── config.ru ├── config ├── application.rb ├── autoprefixer.yml ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── auth_basic.rb │ ├── backtrace_silencers.rb │ ├── bullet.rb │ ├── canonical_host.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── cors.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── generators.rb │ ├── google_analytics.rb │ ├── health_check.rb │ ├── inflections.rb │ ├── meta_tags.rb │ ├── mime_types.rb │ ├── mini_profiler.rb │ ├── premailer_rails.rb │ ├── rollbar.rb │ ├── secure_headers.rb │ ├── session_store.rb │ ├── shrine.rb │ ├── simple_form.rb │ ├── slim.rb │ └── wrap_parameters.rb ├── locales │ ├── en.yml │ ├── flash.en.yml │ ├── models │ │ └── user.en.yml │ ├── simple_form.en.yml │ └── time.en.yml ├── newrelic.yml ├── puma.rb ├── routes.rb ├── secrets.yml ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db ├── migrate │ ├── 20100713113845_devise_create_users.rb │ └── 20200610083210_add_avatar_data_to_users.rb ├── schema.rb ├── seeds.rb └── seeds │ └── development │ └── all.seeds.rb ├── doc └── README_TEMPLATE.md ├── lib └── tasks │ ├── .gitkeep │ └── assets.rake ├── package.json ├── postcss.config.js ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── spec ├── factories │ ├── sequences.rb │ └── users.rb ├── features │ ├── user │ │ ├── account │ │ │ ├── cancel_spec.rb │ │ │ └── update_spec.rb │ │ ├── avatar │ │ │ └── update_spec.rb │ │ └── sign_out_spec.rb │ └── visitor │ │ ├── password_reset_spec.rb │ │ ├── resend_confirmation_email_spec.rb │ │ ├── sign_in_spec.rb │ │ └── sign_up_spec.rb ├── javascript │ └── setupTests.js ├── mailers │ └── previews │ │ └── devise_mailer_preview.rb ├── models │ └── user_spec.rb ├── rails_helper.rb ├── spec_helper.rb └── support │ ├── bullet.rb │ ├── capybara.rb │ ├── database_cleaner.rb │ ├── factory_girl.rb │ ├── feature_helpers.rb │ ├── features │ └── shared_contexts │ │ └── user_signed_in.rb │ ├── fixtures │ └── avatar.jpg │ ├── formulaic.rb │ ├── maintain_test_schema.rb │ ├── matchers │ └── .keep │ ├── shared_examples │ └── .keep │ ├── shoulda_matchers.rb │ └── warden.rb └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.cloud66/manifest.yml: -------------------------------------------------------------------------------- 1 | production: 2 | rails: 3 | server: 4 | unique_name: app 5 | configuration: 6 | asset_pipeline_precompile: true 7 | do_initial_db_schema_load: true 8 | environment_variables: 9 | MAILER_SENDER_ADDRESS: noreply@example.com 10 | HOST: example.com 11 | SMTP_DOMAIN: example.com 12 | SMTP_ADDRESS: smtp.sendgrid.net 13 | SMTP_PORT: 587 14 | SMTP_USERNAME: 15 | SMTP_PASSWORD: 16 | 17 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Port to serve application 2 | PORT=5000 3 | 4 | # Host name of the application 5 | HOST=lvh.me 6 | 7 | # Enabled origins 8 | CORS_ORIGINS=127.0.0.1:5000,localhost:5000,lvh.me:5000 9 | 10 | # Current environment 11 | RACK_ENV=development 12 | 13 | # Maximum sign in tries before account lock 14 | MAX_SIGNIN_ATTEMPTS = 5 15 | 16 | # Base secret key 17 | SECRET_KEY_BASE=development_secret 18 | 19 | # Static files serving 20 | RAILS_SERVE_STATIC_FILES=true 21 | 22 | # Specify assets server host name, eg.: d2oek0c5zwe48d.cloudfront.net 23 | # ASSET_HOST=d2oek0c5zwe48d.cloudfront.net 24 | 25 | # Database name prefix 26 | # DATABASE_NAME=paste_database_prefix_here 27 | 28 | # Set Rollbar key for the app 29 | # ROLLBAR_ACCESS_TOKEN=your_key_here 30 | 31 | # We send devise email using this "from" address 32 | MAILER_SENDER_ADDRESS=noreply@example.com 33 | 34 | # Enable basic auth to close the app from unauthorized viewers 35 | # AUTH_BASIC_REALM=your_application_name 36 | # AUTH_BASIC_PASS=your_password_here 37 | 38 | # Set single hostname 39 | # CANONICAL_HOST=paste_single_hostname_here 40 | 41 | # Google analytics 42 | # GA_TRACKER=your_ga_tracker_code_here 43 | 44 | # comma separated list of IP adresses 45 | # IP_WHITELIST=127.0.0.1 46 | -------------------------------------------------------------------------------- /.erdconfig: -------------------------------------------------------------------------------- 1 | # more about rails-erd config file you can find 2 | # here: https://github.com/voormedia/rails-erd#configuration 3 | # and here: http://voormedia.github.io/rails-erd/customise.html 4 | 5 | filename: doc/entity-relationship 6 | filetype: png 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "extends": [ 6 | "airbnb", 7 | "prettier" 8 | ], 9 | "globals": { 10 | "App": true, 11 | "document": true, 12 | "localStorage": true, 13 | "window": true, 14 | "navigator": true, 15 | "fetch": true 16 | }, 17 | "parser": "babel-eslint" 18 | } 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | CHANGELOG merge=union 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | A brief description of the pull request. 4 | 5 | https://www.pivotaltracker.com/story/show/[story-id] 6 | 7 | # Test plan 8 | 9 | List of steps to manually test introduced functionality: 10 | 11 | * Go to Application 12 | * Sign in 13 | * ... 14 | 15 | # Review notes 16 | 17 | While reviewing pull-request (especially when it's your pull-request), 18 | please make sure that: 19 | 20 | - you understand what problem is solved by PR and how is it solved 21 | - new tests are in place, no redundant tests 22 | - DB schema changes reflect new migrations 23 | - newly introduces DB fields have indexes and constraints 24 | - routes are RESTful, no useless routes 25 | - there are no missed files (migrations, view templates) 26 | - required ENV variables added and described in `.env.example` and added to Heroku 27 | - associated Heroku review app works correctly with introduced changes 28 | 29 | # Deploy notes 30 | 31 | Notes regarding deployment the contained body of work. 32 | These should note any db migrations, ENV variables, services, etc. 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ._* 2 | .bundle 3 | .env 4 | .pryrc 5 | .sass-cache 6 | coverage/* 7 | db/*.sqlite3 8 | doc/**/* 9 | log/*.log 10 | public/uploads 11 | rerun.txt 12 | tmp/* 13 | vendor/bundle 14 | vendor/cache 15 | vendor/ruby 16 | /public/assets 17 | /public/packs 18 | /public/packs-test 19 | /node_modules 20 | /yarn-error.log 21 | yarn-debug.log* 22 | .yarn-integrity 23 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v12.18.0 2 | -------------------------------------------------------------------------------- /.postcss-sorting.json: -------------------------------------------------------------------------------- 1 | { 2 | "empty-lines-between-media-rules": 1, 3 | "empty-lines-between-children-rules": 1, 4 | 5 | "sort-order": [ 6 | ["$variable"], 7 | ["@include", "@extend"], 8 | [ 9 | "display", 10 | "position", 11 | "top", 12 | "right", 13 | "bottom", 14 | "left", 15 | "flex", 16 | "flex-basis", 17 | "flex-direction", 18 | "flex-flow", 19 | "flex-grow", 20 | "flex-shrink", 21 | "flex-wrap", 22 | "align-content", 23 | "align-items", 24 | "align-self", 25 | "justify-content", 26 | "order", 27 | "width", 28 | "min-width", 29 | "max-width", 30 | "height", 31 | "min-height", 32 | "max-height", 33 | "margin", 34 | "margin-top", 35 | "margin-right", 36 | "margin-bottom", 37 | "margin-left", 38 | "padding", 39 | "padding-top", 40 | "padding-right", 41 | "padding-bottom", 42 | "padding-left", 43 | "float", 44 | "clear", 45 | "columns", 46 | "column-gap", 47 | "column-fill", 48 | "column-rule", 49 | "column-span", 50 | "column-count", 51 | "column-width", 52 | "transform", 53 | "transform-box", 54 | "transform-origin", 55 | "transform-style", 56 | "transition", 57 | "transition-delay", 58 | "transition-duration", 59 | "transition-property", 60 | "transition-timing-function", 61 | "border", 62 | "border-top", 63 | "border-right", 64 | "border-bottom", 65 | "border-left", 66 | "border-width", 67 | "border-top-width", 68 | "border-right-width", 69 | "border-bottom-width", 70 | "border-left-width", 71 | "border-style", 72 | "border-top-style", 73 | "border-right-style", 74 | "border-bottom-style", 75 | "border-left-style", 76 | "border-radius", 77 | "border-top-left-radius", 78 | "border-top-right-radius", 79 | "border-bottom-left-radius", 80 | "border-bottom-right-radius", 81 | "border-color", 82 | "border-top-color", 83 | "border-right-color", 84 | "border-bottom-color", 85 | "border-left-color", 86 | "outline", 87 | "outline-color", 88 | "outline-offset", 89 | "outline-style", 90 | "outline-width", 91 | "background", 92 | "background-attachment", 93 | "background-color", 94 | "background-image", 95 | "background-repeat", 96 | "background-position", 97 | "background-size", 98 | "color", 99 | "font", 100 | "font-family", 101 | "font-size", 102 | "font-smoothing", 103 | "font-style", 104 | "font-variant", 105 | "font-weight", 106 | "letter-spacing", 107 | "line-height", 108 | "list-style", 109 | "text-align", 110 | "text-decoration", 111 | "text-indent", 112 | "text-overflow", 113 | "text-rendering", 114 | "text-shadow", 115 | "text-transform", 116 | "text-wrap", 117 | "text-size-adjust", 118 | "white-space", 119 | "word-spacing", 120 | "border-collapse", 121 | "border-spacing", 122 | "box-shadow", 123 | "caption-side", 124 | "content", 125 | "cursor", 126 | "empty-cells", 127 | "opacity", 128 | "overflow", 129 | "quotes", 130 | "speak", 131 | "table-layout", 132 | "vertical-align", 133 | "visibility", 134 | "z-index", 135 | "appearance" 136 | ], 137 | [">child"], 138 | ["@include breakpoint"], 139 | ["@atrule"] 140 | ] 141 | } 142 | -------------------------------------------------------------------------------- /.pryrc.example: -------------------------------------------------------------------------------- 1 | Pry.commands.alias_command 'c', 'continue' 2 | Pry.commands.alias_command 's', 'step' 3 | Pry.commands.alias_command 'n', 'next' 4 | Pry.commands.alias_command 'f', 'finish' 5 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --require spec_helper 3 | --profile 2 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-rails 3 | - rubocop-rspec 4 | 5 | Rails: 6 | Enabled: true 7 | 8 | AllCops: 9 | TargetRubyVersion: 2.7 10 | DisplayCopNames: true 11 | Exclude: 12 | - bin/**/* 13 | - config/initializers/secure_headers.rb 14 | - db/**/* 15 | - node_modules/**/* 16 | - vendor/**/* 17 | - tmp/**/* 18 | 19 | Capybara/FeatureMethods: 20 | EnabledMethods: 21 | - background 22 | - feature 23 | - scenario 24 | 25 | # Rails 26 | 27 | Rails/FilePath: 28 | EnforcedStyle: arguments 29 | 30 | Rails/UnknownEnv: 31 | Environments: 32 | - development 33 | - test 34 | - staging 35 | - production 36 | 37 | Rails/HasAndBelongsToMany: 38 | Enabled: false 39 | 40 | Rails/OutputSafety: 41 | Enabled: false 42 | 43 | # RSpec 44 | 45 | RSpec/MultipleExpectations: 46 | Enabled: false 47 | 48 | RSpec/ExampleLength: 49 | Enabled: false 50 | 51 | RSpec/HookArgument: 52 | EnforcedStyle: each 53 | 54 | RSpec/RepeatedDescription: 55 | Enabled: false 56 | 57 | RSpec/MessageSpies: 58 | EnforcedStyle: receive 59 | 60 | # Style 61 | 62 | Style/AndOr: 63 | Enabled: false 64 | 65 | Style/Documentation: 66 | Enabled: false 67 | 68 | Style/MethodCalledOnDoEndBlock: 69 | Enabled: true 70 | 71 | Style/CollectionMethods: 72 | Enabled: true 73 | 74 | Style/SymbolArray: 75 | Enabled: true 76 | 77 | Style/StringLiterals: 78 | EnforcedStyle: double_quotes 79 | ConsistentQuotesInMultiline: true 80 | 81 | Style/EmptyMethod: 82 | EnforcedStyle: expanded 83 | SupportedStyles: 84 | - compact 85 | - expanded 86 | 87 | Style/FrozenStringLiteralComment: 88 | Enabled: false 89 | 90 | Style/StringMethods: 91 | Enabled: true 92 | 93 | Style/NestedTernaryOperator: 94 | Enabled: false 95 | 96 | # Layout 97 | 98 | Layout/ParameterAlignment: 99 | EnforcedStyle: with_fixed_indentation 100 | SupportedStyles: 101 | - with_first_parameter 102 | - with_fixed_indentation 103 | 104 | Layout/EndAlignment: 105 | EnforcedStyleAlignWith: variable 106 | SupportedStylesAlignWith: 107 | - keyword 108 | - variable 109 | 110 | Layout/LineLength: 111 | Max: 120 112 | 113 | # Metrics 114 | 115 | Metrics/MethodLength: 116 | Max: 16 117 | 118 | Metrics/BlockLength: 119 | Enabled: false 120 | 121 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.1 2 | -------------------------------------------------------------------------------- /.scss-lint.yml: -------------------------------------------------------------------------------- 1 | scss_files: "app/assets/stylesheets/**/*.scss" 2 | 3 | exclude: "app/assets/stylesheets/core/settings.scss" 4 | 5 | linters: 6 | NameFormat: 7 | enabled: true 8 | allow_leading_underscore: true 9 | convention: "[^A-Z]" 10 | 11 | StringQuotes: 12 | style: double_quotes 13 | 14 | PropertySortOrder: 15 | order: 16 | - display 17 | - position 18 | - top 19 | - right 20 | - bottom 21 | - left 22 | - flex 23 | - flex-basis 24 | - flex-direction 25 | - flex-flow 26 | - flex-grow 27 | - flex-shrink 28 | - flex-wrap 29 | - align-content 30 | - align-items 31 | - align-self 32 | - justify-content 33 | - order 34 | - width 35 | - min-width 36 | - max-width 37 | - height 38 | - min-height 39 | - max-height 40 | - margin 41 | - margin-top 42 | - margin-right 43 | - margin-bottom 44 | - margin-left 45 | - padding 46 | - padding-top 47 | - padding-right 48 | - padding-bottom 49 | - padding-left 50 | - float 51 | - clear 52 | - columns 53 | - column-gap 54 | - column-fill 55 | - column-rule 56 | - column-span 57 | - column-count 58 | - column-width 59 | - transform 60 | - transform-box 61 | - transform-origin 62 | - transform-style 63 | - transition 64 | - transition-delay 65 | - transition-duration 66 | - transition-property 67 | - transition-timing-function 68 | - border 69 | - border-top 70 | - border-right 71 | - border-bottom 72 | - border-left 73 | - border-width 74 | - border-top-width 75 | - border-right-width 76 | - border-bottom-width 77 | - border-left-width 78 | - border-style 79 | - border-top-style 80 | - border-right-style 81 | - border-bottom-style 82 | - border-left-style 83 | - border-radius 84 | - border-top-left-radius 85 | - border-top-right-radius 86 | - border-bottom-left-radius 87 | - border-bottom-right-radius 88 | - border-color 89 | - border-top-color 90 | - border-right-color 91 | - border-bottom-color 92 | - border-left-color 93 | - outline 94 | - outline-color 95 | - outline-offset 96 | - outline-style 97 | - outline-width 98 | - background 99 | - background-attachment 100 | - background-color 101 | - background-image 102 | - background-repeat 103 | - background-position 104 | - background-size 105 | - color 106 | - font 107 | - font-family 108 | - font-size 109 | - font-smoothing 110 | - font-style 111 | - font-variant 112 | - font-weight 113 | - letter-spacing 114 | - line-height 115 | - list-style 116 | - text-align 117 | - text-decoration 118 | - text-indent 119 | - text-overflow 120 | - text-rendering 121 | - text-shadow 122 | - text-transform 123 | - text-size-adjust 124 | - text-wrap 125 | - white-space 126 | - word-spacing 127 | - border-collapse 128 | - border-spacing 129 | - box-shadow 130 | - caption-side 131 | - content 132 | - cursor 133 | - empty-cells 134 | - opacity 135 | - overflow 136 | - quotes 137 | - speak 138 | - table-layout 139 | - vertical-align 140 | - visibility 141 | - z-index 142 | - appearance 143 | -------------------------------------------------------------------------------- /.semaphore/heroku.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Deploy to Heroku 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu1804 7 | 8 | blocks: 9 | - name: Deploy 10 | task: 11 | secrets: 12 | - name: rails-base-secrets 13 | 14 | prologue: 15 | commands: 16 | # For deploying to Heroku, it is required that you use checkout 17 | # with the --use-cache option in order 18 | # to avoid the shallow clone of your GitHub repository. 19 | - checkout --use-cache 20 | 21 | # Setup SSH key from secrets 22 | - ssh-keyscan -H heroku.com >> ~/.ssh/known_hosts 23 | - chmod 600 ~/.ssh/id_rsa_semaphore_heroku 24 | - ssh-add ~/.ssh/id_rsa_semaphore_heroku 25 | - git config --global url.ssh://git@heroku.com/.insteadOf https://git.heroku.com/ 26 | 27 | jobs: 28 | - name: Deploy to Production 29 | commands: 30 | - git remote add heroku https://git.heroku.com/fs-rails-base.git 31 | - git push heroku -f $SEMAPHORE_GIT_BRANCH:master 32 | - heroku run rails db:migrate 33 | -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Rails Base 3 | 4 | agent: 5 | machine: 6 | type: e1-standard-2 7 | os_image: ubuntu1804 8 | 9 | execution_time_limit: 10 | hours: 1 11 | 12 | auto_cancel: 13 | queued: 14 | when: 'true' 15 | 16 | fail_fast: 17 | stop: 18 | when: 'true' 19 | 20 | global_job_config: 21 | env_vars: 22 | - name: RAILS_ENV 23 | value: 'test' 24 | - name: DATABASE_URL 25 | value: 'postgresql://postgres@localhost/test?encoding=utf8' 26 | - name: BUNDLE_ARGS 27 | value: '--deployment --without development staging production --jobs 4' 28 | 29 | prologue: 30 | commands: 31 | - checkout 32 | - cache restore 33 | - cp .env.example .env 34 | 35 | blocks: 36 | - name: Setup 37 | task: 38 | jobs: 39 | - name: Bundle 40 | commands: 41 | - bundle install ${BUNDLE_ARGS} 42 | - cache store 43 | 44 | - name: Quality 45 | task: 46 | jobs: 47 | - name: Quality 48 | commands: 49 | - bundle install ${BUNDLE_ARGS} --local 50 | - bin/quality 51 | 52 | - name: Test 53 | task: 54 | prologue: 55 | commands: 56 | - nvm use 57 | - sem-version ruby 2.7.1 58 | - bundle install ${BUNDLE_ARGS} --local 59 | - sem-service start postgres 60 | - bin/rails db:setup 61 | - bin/rails assets:precompile 62 | 63 | jobs: 64 | - name: Unit 65 | commands: 66 | - bin/rspec --tag ~type:feature 67 | 68 | - name: Features 69 | commands: 70 | - bin/rspec --tag type:feature 71 | 72 | promotions: 73 | - name: Deploy to Heroku 74 | pipeline_file: heroku.yml 75 | 76 | # Continuous deployment from master branch 77 | auto_promote_on: 78 | - result: passed 79 | branch: 80 | - master 81 | -------------------------------------------------------------------------------- /.slim-lint.yml: -------------------------------------------------------------------------------- 1 | skip_frontmatter: false 2 | 3 | linters: 4 | CommentControlStatement: 5 | enabled: true 6 | 7 | ConsecutiveControlStatements: 8 | enabled: true 9 | max_consecutive: 2 10 | 11 | EmptyControlStatment: 12 | enabled: true 13 | 14 | ExplicitDiv: 15 | enabled: true 16 | 17 | LineLength: 18 | enabled: true 19 | max: 120 20 | 21 | RuboCop: 22 | enabled: true 23 | # These cops are incredibly noisy since the Ruby we extract from Slim 24 | # templates isn't well-formatted, so we ignore them. 25 | ignored_cops: 26 | - Layout/TrailingEmptyLines 27 | - Lint/EndAlignment 28 | - Layout/ArgumentAlignment 29 | - Lint/Void 30 | - Layout/IndentationWidth 31 | 32 | TagCase: 33 | enabled: true 34 | 35 | TrailingWhitespace: 36 | enabled: true 37 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew "graphviz" 2 | brew "node@12" 3 | brew "postgresql" 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## Unreleased 4 | - Updated Rails version to 5.2.4.3 5 | - Updated most important gems (pundit, draper etc) to fresh versions 6 | - Updated rack-mini-profiler to version 1.1.3 7 | - Ruby updated to version 2.5.7 8 | - Replace gem [metamagic](https://github.com/lassebunk/metamagic) to [meta-tags](https://github.com/kpumuk/meta-tags). Using for CEO. 9 | - Remove `qt` from Brewfile and dependencies list 10 | - Upgrade Ruby to 2.3.3, Nokogiri to 1.7.1 11 | - Upgrade [decent exposure](https://github.com/hashrocket/decent_exposure) to 3.0 12 | - Email previews for `DeviseMailer` at http://lvh.me:5000/rails/mailers 13 | - Update foundation to 6 version 14 | - Change csscomb & scss-lint configs 15 | - Upgrade [rack-mini-profiler gem](https://github.com/MiniProfiler/rack-mini-profiler) to 0.10.1 to avoid [vulnerability](https://github.com/MiniProfiler/rack-mini-profiler/commit/4273771d65f1a7411e3ef5843329308d0e2d257c) 16 | - Minor improvements in newrelick.yml 17 | - Update Ruby to 2.3.1 18 | - Update bin/setup script to clean old log files and tmp directory 19 | - Update [nokogiri gem](https://github.com/sparklemotion/nokogiri) 20 | - Update [rails_best_practices gem](https://github.com/railsbp/rails_best_practices) 21 | - Update [email_spec gem](https://github.com/email-spec/email-spec) to avoid flaky specs based on encoded html entities 22 | - Upgrade [rails](https://github.com/rails/rails) to 5.0.2, draper to 3.0.0.pre1 23 | - Add [guard-rspec](https://github.com/guard/guard-rspec) to spead up TDD 24 | - Add [bullet](https://github.com/flyerhzm/bullet) helper 25 | - Replace [thetubyracer](https://github.com/cowboyd/therubyracer) with [mini-racer](https://github.com/discourse/mini_racer) 26 | - Remove foreman and save logs to STDOUT 27 | - Remove capybara-webkit and install [poltergeist](https://github.com/teampoltergeist/poltergeist) 28 | - Remove [skim](https://github.com/appjudo/skim) 29 | 30 | ## 1.9 - 2016-04-22 31 | 32 | - Introduce [Rspec-Its](https://github.com/rspec/rspec-its) 33 | - Configure `.gitattributes` to avoid merge conflicts due to `CHANGELOG` 34 | - Upgrade rubocop to 0.39.0, rubocop-rspec to 1.4.1, active_link_to to 1.0.3, faker to 1.6.3, slim_lint to 0.7.2 35 | 36 | ## 1.8 - 2016-03-22 37 | 38 | - Upgrade Rails to 4.2.6, Ruby to 2.3.0, Rollbar to 2.8.3, Spring to 1.6.4 39 | - Remove [fuubar](https://github.com/thekompanee/fuubar) 40 | - Show 2 slowest specs during specs run [#415](https://github.com/fs/rails-base/pull/415) [#410](https://github.com/fs/rails-base/pull/410/files) 41 | - Update [devise gem](https://github.com/plataformatec/devise) 42 | - Removed wrong underfield hint on sign_up page 43 | - Fix flash messages at new password and email confirmation sending 44 | - Install [health_check](https://github.com/ianheggie/health_check) 45 | - Fix alerts closing button on iOs devices 46 | - Update [simple_form gem](https://github.com/plataformatec/simple_form) 47 | - User factory generate confirmed user by default 48 | - Update [nokogiri gem](https://github.com/sparklemotion/nokogiri) 49 | 50 | ## 1.7 - 2015-11-27 51 | 52 | - Introduce [Draper](https://github.com/drapergem/draper) for presenting models into views 53 | - Move Rack::MiniProfiler authorization to initializers 54 | - Add ability to configure SMTP Mailer options not only with SendGrid via `config/initializers/mailer.rb` 55 | - [Remove `app_config`](https://github.com/fs/rails-base/pull/342) shortcut and use `ENV` exclusively 56 | - Turning on Partial Double Verification for Rspec 57 | - Replace [rails_12factor](https://github.com/heroku/rails_12factor) with [rails_stdout_logging](https://github.com/heroku/rails_stdout_logging) 58 | - Update [foundation-rails gem](https://github.com/zurb/foundation-rails) 59 | - Update [rollbar gem](https://github.com/rollbar/rollbar-gem) 60 | - Add gem [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler) for displaying speed badge for every html page. 61 | - Add configuration for [NewRelic](https://devcenter.heroku.com/articles/newrelic) Heroku-addon 62 | - Implement the dynamic database name 63 | 64 | ## 1.6 - 2015-08-28 65 | 66 | - Switch web server from [Thin](https://github.com/macournoyer/thin) to [Puma](https://github.com/puma/puma) 67 | - Update [Uglifier](https://github.com/lautis/uglifier) version up to 2.7.2 68 | - Add [Rubocop-Rspec](https://github.com/nevir/rubocop-rspec) for reporting violations of Ruby style guide in specs 69 | - Add [Slim-Lint](https://github.com/sds/slim-lint) for reporting violations of Ruby style guide in `.slim` templates 70 | - Update Ruby to 2.2.3 71 | - Improve assets caching on Heroku: headers, gzipping, Cloudfront 72 | - Move Rack::CanonicalHost and Rack::Auth::Basic configuration to initializers 73 | - Support [Heroku Review Apps](https://devcenter.heroku.com/articles/github-integration#review-apps) 74 | - Update [rails](https://github.com/rails/rails) version up to 4.2.3 75 | - Add [Rails ERD](https://github.com/voormedia/rails-erd) gem for generating a diagram based on application's AR models 76 | 77 | ## 1.5 - 2015-08-14 78 | 79 | - Replace [title](https://github.com/calebthompson/title) gem with [metamagic](https://github.com/lassebunk/metamagic) 80 | 81 | ## 1.4 - 2015-07-31 82 | 83 | - Add gem [rack-canonical-host](https://github.com/tylerhunt/rack-canonical-host) for the definition a single host name as the canonical host for your application. 84 | - Add Google Analytics 85 | - Add ability to close app from unauthorized viewers, see: https://github.com/fs/rails-base/pull/302/files 86 | 87 | ## 1.3 - 2015-07-03 88 | 89 | - Deprecate using i18n for submits, see: https://github.com/fs/rails-base/commit/ed4e55992e671cb3c9281cd10a4f5c26e7f1c02d 90 | - Fix wrong titles and submit labels on devise pages after submit 91 | - Fix `rails_best_practices` invocation from `bin/quality`, `config/rails_best_practices.yml` was ignored before. 92 | - Introduce [CSSComb](https://github.com/csscomb/csscomb.js). For more details see: https://github.com/fs/rails-base/pull/284 93 | - Add `Brewfile` to track application dependencies. 94 | 95 | ## 1.2 - 2015-06-19 96 | 97 | - Update the following gems: `rails`, `jquery-rails`, `rack`, `web-console`. It fixes the security vulnerabilities, see http://weblog.rubyonrails.org/2015/6/16/Rails-3-2-22-4-1-11-and-4-2-2-have-been-released-and-more/ 98 | 99 | ## 1.1 - 2015-06-05 100 | 101 | - Add [Coffeelint](https://github.com/zmbush/coffeelint-ruby) to keep Coffeescript code clean and consistent 102 | - Force double quotes everywhere: SCSS, Ruby. For more details see: https://github.com/fs/rails-base/pull/274 103 | - Add [SCSS-Lint](https://github.com/brigade/scss-lint) for reporting violations of SCSS coding conventions 104 | - Style Kaminari pagination to make it look like [Foundation's pagination](http://foundation.zurb.com/docs/v/4.3.2/components/pagination.html) 105 | - Add [Spring](https://github.com/rails/spring) for fast Rails actions via pre-loading 106 | 107 | ## 1.0 - 2015-05-18 108 | 109 | - Upgrade Ruby to 2.2.2 110 | - Upgrade Rails to 4.2.1 111 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 1. Fork the project. 4 | 2. Setup it on your machine with `bin/setup`. 5 | 3. Make sure the tests pass: `bin/test`. 6 | 4. Make your change. Add tests for your change when necessary. Make the tests pass: `bin/test`. 7 | 5. Mention your changes in "Unreleased" section of `CHANGELOG.md` 8 | 6. Push to your fork and [submit a pull request](https://help.github.com/articles/creating-a-pull-request/) 9 | (bonus points for topic branches). 10 | 11 | ## Releases 12 | 13 | We release new versions of Rails Base fortnightly on Fridays. 14 | 15 | We do not strictly follow [Semantic Versioning](http://semver.org/) as we use only `MAJOR`/`MINOR` increments: 16 | 17 | * Increment the `MAJOR` version when you make incompatible API changes. 18 | For example, upgrading Rails from 3.x to 4.x. 19 | * Increment the `MINOR` version when you make any other changes. 20 | That includes backwards-compatible changes and bug fixes. 21 | 22 | When ready to release: 23 | 24 | 1. Make sure that tests are green. 25 | 2. Update the changelog with the new version and commit it with message "release ". 26 | 3. Tag the release by running `git tag v`. Push the tag: `git push --tags`. 27 | 4. Verify that everything was pushed correctly on the Github: https://github.com/fs/rails-base/releases 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | ruby "2.7.1" 4 | 5 | gem "pg" 6 | 7 | gem "rails", "~> 6.0.3" 8 | 9 | # assets 10 | gem "autoprefixer-rails" 11 | gem "aws-sdk-s3" 12 | gem "foundation-icons-sass-rails" 13 | gem "foundation-rails" 14 | gem "image_processing" 15 | gem "premailer-rails" 16 | gem "sass-rails", "~> 6.0.0" 17 | gem "uglifier", ">= 2.7.2" 18 | gem "webpacker" 19 | 20 | # views 21 | gem "active_link_to" 22 | gem "inky-rb", require: "inky" 23 | gem "meta-tags" 24 | gem "simple_form" 25 | gem "slim" 26 | 27 | # all other gems 28 | gem "action_policy" 29 | gem "bootsnap" 30 | gem "decent_decoration" 31 | gem "decent_exposure" 32 | gem "devise" 33 | gem "draper" 34 | gem "flamegraph" 35 | gem "google-analytics-rails" 36 | gem "health_check" 37 | gem "interactor" 38 | gem "kaminari" 39 | gem "memory_profiler" 40 | gem "puma" 41 | gem "rack-canonical-host" 42 | gem "rack-cors" 43 | gem "rack-mini-profiler", require: false, git: "https://github.com/MiniProfiler/rack-mini-profiler.git" 44 | gem "responders" 45 | gem "rollbar" 46 | gem "secure_headers" 47 | gem "seedbank" 48 | gem "shrine" 49 | gem "stackprof" 50 | 51 | group :staging, :production do 52 | gem "newrelic_rpm" 53 | end 54 | 55 | group :test do 56 | gem "capybara" 57 | gem "capybara-email" 58 | gem "database_cleaner" 59 | gem "formulaic" 60 | gem "guard-rspec" 61 | gem "launchy" 62 | gem "rspec-its" 63 | gem "selenium-webdriver" 64 | gem "shoulda-matchers" 65 | gem "terminal-notifier-guard" 66 | gem "webmock", require: false 67 | end 68 | 69 | group :development, :test do 70 | gem "awesome_print" 71 | gem "brakeman", require: false 72 | gem "bullet" 73 | gem "bundler-audit", require: false 74 | gem "byebug" 75 | gem "dotenv-rails" 76 | gem "factory_bot_rails" 77 | gem "faker" 78 | gem "rspec-rails" 79 | gem "rubocop", require: false 80 | gem "rubocop-rails", require: false 81 | gem "rubocop-rspec", require: false 82 | gem "scss_lint", require: false 83 | gem "slim_lint", require: false 84 | end 85 | 86 | group :development do 87 | gem "letter_opener" 88 | gem "rails-erd" 89 | gem "slim-rails" 90 | gem "spring" 91 | gem "spring-commands-rspec" 92 | gem "spring-watcher-listen" 93 | gem "web-console" 94 | end 95 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/MiniProfiler/rack-mini-profiler.git 3 | revision: aef9b2308a96be9be2236005d854a0e1d47cad00 4 | specs: 5 | rack-mini-profiler (2.3.1) 6 | rack (>= 1.2.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | action_policy (0.5.7) 12 | ruby-next-core (>= 0.11.0) 13 | actioncable (6.0.3.7) 14 | actionpack (= 6.0.3.7) 15 | nio4r (~> 2.0) 16 | websocket-driver (>= 0.6.1) 17 | actionmailbox (6.0.3.7) 18 | actionpack (= 6.0.3.7) 19 | activejob (= 6.0.3.7) 20 | activerecord (= 6.0.3.7) 21 | activestorage (= 6.0.3.7) 22 | activesupport (= 6.0.3.7) 23 | mail (>= 2.7.1) 24 | actionmailer (6.0.3.7) 25 | actionpack (= 6.0.3.7) 26 | actionview (= 6.0.3.7) 27 | activejob (= 6.0.3.7) 28 | mail (~> 2.5, >= 2.5.4) 29 | rails-dom-testing (~> 2.0) 30 | actionpack (6.0.3.7) 31 | actionview (= 6.0.3.7) 32 | activesupport (= 6.0.3.7) 33 | rack (~> 2.0, >= 2.0.8) 34 | rack-test (>= 0.6.3) 35 | rails-dom-testing (~> 2.0) 36 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 37 | actiontext (6.0.3.7) 38 | actionpack (= 6.0.3.7) 39 | activerecord (= 6.0.3.7) 40 | activestorage (= 6.0.3.7) 41 | activesupport (= 6.0.3.7) 42 | nokogiri (>= 1.8.5) 43 | actionview (6.0.3.7) 44 | activesupport (= 6.0.3.7) 45 | builder (~> 3.1) 46 | erubi (~> 1.4) 47 | rails-dom-testing (~> 2.0) 48 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 49 | active_link_to (1.0.5) 50 | actionpack 51 | addressable 52 | activejob (6.0.3.7) 53 | activesupport (= 6.0.3.7) 54 | globalid (>= 0.3.6) 55 | activemodel (6.0.3.7) 56 | activesupport (= 6.0.3.7) 57 | activemodel-serializers-xml (1.0.2) 58 | activemodel (> 5.x) 59 | activesupport (> 5.x) 60 | builder (~> 3.1) 61 | activerecord (6.0.3.7) 62 | activemodel (= 6.0.3.7) 63 | activesupport (= 6.0.3.7) 64 | activestorage (6.0.3.7) 65 | actionpack (= 6.0.3.7) 66 | activejob (= 6.0.3.7) 67 | activerecord (= 6.0.3.7) 68 | marcel (~> 1.0.0) 69 | activesupport (6.0.3.7) 70 | concurrent-ruby (~> 1.0, >= 1.0.2) 71 | i18n (>= 0.7, < 2) 72 | minitest (~> 5.1) 73 | tzinfo (~> 1.1) 74 | zeitwerk (~> 2.2, >= 2.2.2) 75 | addressable (2.8.0) 76 | public_suffix (>= 2.0.2, < 5.0) 77 | ast (2.4.2) 78 | autoprefixer-rails (6.7.4) 79 | execjs 80 | awesome_print (1.7.0) 81 | aws-eventstream (1.1.0) 82 | aws-partitions (1.327.0) 83 | aws-sdk-core (3.98.0) 84 | aws-eventstream (~> 1, >= 1.0.2) 85 | aws-partitions (~> 1, >= 1.239.0) 86 | aws-sigv4 (~> 1.1) 87 | jmespath (~> 1.0) 88 | aws-sdk-kms (1.33.0) 89 | aws-sdk-core (~> 3, >= 3.71.0) 90 | aws-sigv4 (~> 1.1) 91 | aws-sdk-s3 (1.67.1) 92 | aws-sdk-core (~> 3, >= 3.96.1) 93 | aws-sdk-kms (~> 1) 94 | aws-sigv4 (~> 1.1) 95 | aws-sigv4 (1.1.4) 96 | aws-eventstream (~> 1.0, >= 1.0.2) 97 | babel-source (5.8.35) 98 | babel-transpiler (0.7.0) 99 | babel-source (>= 4.0, < 6) 100 | execjs (~> 2.0) 101 | bcrypt (3.1.16) 102 | bindex (0.8.1) 103 | bootsnap (1.4.6) 104 | msgpack (~> 1.0) 105 | brakeman (4.8.2) 106 | builder (3.2.4) 107 | bullet (6.1.4) 108 | activesupport (>= 3.0.0) 109 | uniform_notifier (~> 1.11) 110 | bundler-audit (0.8.0) 111 | bundler (>= 1.2.0, < 3) 112 | thor (~> 1.0) 113 | byebug (11.1.3) 114 | capybara (3.35.3) 115 | addressable 116 | mini_mime (>= 0.1.3) 117 | nokogiri (~> 1.8) 118 | rack (>= 1.6.0) 119 | rack-test (>= 0.6.3) 120 | regexp_parser (>= 1.5, < 3.0) 121 | xpath (~> 3.2) 122 | capybara-email (3.0.2) 123 | capybara (>= 2.4, < 4.0) 124 | mail 125 | childprocess (3.0.0) 126 | choice (0.2.0) 127 | coderay (1.1.1) 128 | concurrent-ruby (1.1.8) 129 | content_disposition (1.0.0) 130 | crack (0.4.3) 131 | safe_yaml (~> 1.0.0) 132 | crass (1.0.6) 133 | css_parser (1.7.1) 134 | addressable 135 | database_cleaner (1.6.1) 136 | decent_decoration (0.1.0) 137 | decent_exposure (~> 3.0) 138 | decent_exposure (3.0.4) 139 | activesupport (>= 4.0) 140 | devise (4.7.3) 141 | bcrypt (~> 3.0) 142 | orm_adapter (~> 0.1) 143 | railties (>= 4.1.0) 144 | responders 145 | warden (~> 1.2.3) 146 | diff-lcs (1.4.4) 147 | dotenv (2.7.6) 148 | dotenv-rails (2.7.6) 149 | dotenv (= 2.7.6) 150 | railties (>= 3.2) 151 | down (5.2.0) 152 | addressable (~> 2.5) 153 | draper (4.0.1) 154 | actionpack (>= 5.0) 155 | activemodel (>= 5.0) 156 | activemodel-serializers-xml (>= 1.0) 157 | activesupport (>= 5.0) 158 | request_store (>= 1.0) 159 | erubi (1.10.0) 160 | execjs (2.7.0) 161 | factory_bot (6.1.0) 162 | activesupport (>= 5.0.0) 163 | factory_bot_rails (6.1.0) 164 | factory_bot (~> 6.1.0) 165 | railties (>= 5.0.0) 166 | faker (2.17.0) 167 | i18n (>= 1.6, < 2) 168 | ffi (1.13.1) 169 | flamegraph (0.9.5) 170 | formatador (0.2.5) 171 | formulaic (0.4.1) 172 | activesupport 173 | capybara 174 | i18n 175 | foundation-icons-sass-rails (3.0.0) 176 | railties (>= 3.1.1) 177 | sass-rails (>= 3.1.1) 178 | foundation-rails (6.6.2.0) 179 | railties (>= 3.1.0) 180 | sass (>= 3.3.0) 181 | sprockets-es6 (>= 0.9.0) 182 | foundation_emails (2.2.1.0) 183 | globalid (0.4.2) 184 | activesupport (>= 4.2.0) 185 | google-analytics-rails (1.1.1) 186 | guard (2.14.1) 187 | formatador (>= 0.2.4) 188 | listen (>= 2.7, < 4.0) 189 | lumberjack (~> 1.0) 190 | nenv (~> 0.1) 191 | notiffany (~> 0.0) 192 | pry (>= 0.9.12) 193 | shellany (~> 0.0) 194 | thor (>= 0.18.1) 195 | guard-compat (1.2.1) 196 | guard-rspec (4.7.3) 197 | guard (~> 2.1) 198 | guard-compat (~> 1.1) 199 | rspec (>= 2.99.0, < 4.0) 200 | hashdiff (0.3.2) 201 | health_check (2.6.0) 202 | rails (>= 4.0) 203 | htmlentities (4.3.4) 204 | i18n (1.8.10) 205 | concurrent-ruby (~> 1.0) 206 | image_processing (1.11.0) 207 | mini_magick (>= 4.9.5, < 5) 208 | ruby-vips (>= 2.0.17, < 3) 209 | inky-rb (1.3.7.5) 210 | foundation_emails (~> 2) 211 | nokogiri 212 | interactor (3.1.0) 213 | jmespath (1.4.0) 214 | kaminari (1.2.1) 215 | activesupport (>= 4.1.0) 216 | kaminari-actionview (= 1.2.1) 217 | kaminari-activerecord (= 1.2.1) 218 | kaminari-core (= 1.2.1) 219 | kaminari-actionview (1.2.1) 220 | actionview 221 | kaminari-core (= 1.2.1) 222 | kaminari-activerecord (1.2.1) 223 | activerecord 224 | kaminari-core (= 1.2.1) 225 | kaminari-core (1.2.1) 226 | launchy (2.5.0) 227 | addressable (~> 2.7) 228 | letter_opener (1.7.0) 229 | launchy (~> 2.2) 230 | listen (3.1.5) 231 | rb-fsevent (~> 0.9, >= 0.9.4) 232 | rb-inotify (~> 0.9, >= 0.9.7) 233 | ruby_dep (~> 1.2) 234 | loofah (2.9.1) 235 | crass (~> 1.0.2) 236 | nokogiri (>= 1.5.9) 237 | lumberjack (1.0.11) 238 | mail (2.7.1) 239 | mini_mime (>= 0.1.1) 240 | marcel (1.0.1) 241 | memory_profiler (0.9.7) 242 | meta-tags (2.14.0) 243 | actionpack (>= 3.2.0, < 6.2) 244 | method_source (0.8.2) 245 | mini_magick (4.10.1) 246 | mini_mime (1.1.0) 247 | mini_portile2 (2.5.1) 248 | minitest (5.14.4) 249 | msgpack (1.3.3) 250 | multi_json (1.12.1) 251 | nenv (0.3.0) 252 | newrelic_rpm (6.11.0.365) 253 | nio4r (2.5.7) 254 | nokogiri (1.11.4) 255 | mini_portile2 (~> 2.5.0) 256 | racc (~> 1.4) 257 | notiffany (0.1.1) 258 | nenv (~> 0.1) 259 | shellany (~> 0.0) 260 | orm_adapter (0.5.0) 261 | parallel (1.20.1) 262 | parser (3.0.1.0) 263 | ast (~> 2.4.1) 264 | pg (1.2.3) 265 | premailer (1.11.1) 266 | addressable 267 | css_parser (>= 1.6.0) 268 | htmlentities (>= 4.0.0) 269 | premailer-rails (1.11.1) 270 | actionmailer (>= 3) 271 | premailer (~> 1.7, >= 1.7.9) 272 | pry (0.10.4) 273 | coderay (~> 1.1.0) 274 | method_source (~> 0.8.1) 275 | slop (~> 3.4) 276 | public_suffix (4.0.6) 277 | puma (4.3.8) 278 | nio4r (~> 2.0) 279 | racc (1.5.2) 280 | rack (2.2.3) 281 | rack-canonical-host (0.2.2) 282 | addressable (> 0, < 3) 283 | rack (>= 1.0.0, < 3) 284 | rack-cors (1.1.1) 285 | rack (>= 2.0.0) 286 | rack-proxy (0.6.5) 287 | rack 288 | rack-test (1.1.0) 289 | rack (>= 1.0, < 3) 290 | rails (6.0.3.7) 291 | actioncable (= 6.0.3.7) 292 | actionmailbox (= 6.0.3.7) 293 | actionmailer (= 6.0.3.7) 294 | actionpack (= 6.0.3.7) 295 | actiontext (= 6.0.3.7) 296 | actionview (= 6.0.3.7) 297 | activejob (= 6.0.3.7) 298 | activemodel (= 6.0.3.7) 299 | activerecord (= 6.0.3.7) 300 | activestorage (= 6.0.3.7) 301 | activesupport (= 6.0.3.7) 302 | bundler (>= 1.3.0) 303 | railties (= 6.0.3.7) 304 | sprockets-rails (>= 2.0.0) 305 | rails-dom-testing (2.0.3) 306 | activesupport (>= 4.2.0) 307 | nokogiri (>= 1.6) 308 | rails-erd (1.6.1) 309 | activerecord (>= 4.2) 310 | activesupport (>= 4.2) 311 | choice (~> 0.2.0) 312 | ruby-graphviz (~> 1.2) 313 | rails-html-sanitizer (1.3.0) 314 | loofah (~> 2.3) 315 | railties (6.0.3.7) 316 | actionpack (= 6.0.3.7) 317 | activesupport (= 6.0.3.7) 318 | method_source 319 | rake (>= 0.8.7) 320 | thor (>= 0.20.3, < 2.0) 321 | rainbow (3.0.0) 322 | rake (13.0.3) 323 | rb-fsevent (0.9.8) 324 | rb-inotify (0.10.1) 325 | ffi (~> 1.0) 326 | regexp_parser (2.1.1) 327 | request_store (1.5.0) 328 | rack (>= 1.4) 329 | responders (3.0.1) 330 | actionpack (>= 5.0) 331 | railties (>= 5.0) 332 | rexml (3.2.5) 333 | rollbar (2.14.0) 334 | multi_json 335 | rspec (3.5.0) 336 | rspec-core (~> 3.5.0) 337 | rspec-expectations (~> 3.5.0) 338 | rspec-mocks (~> 3.5.0) 339 | rspec-core (3.5.4) 340 | rspec-support (~> 3.5.0) 341 | rspec-expectations (3.5.0) 342 | diff-lcs (>= 1.2.0, < 2.0) 343 | rspec-support (~> 3.5.0) 344 | rspec-its (1.3.0) 345 | rspec-core (>= 3.0.0) 346 | rspec-expectations (>= 3.0.0) 347 | rspec-mocks (3.5.0) 348 | diff-lcs (>= 1.2.0, < 2.0) 349 | rspec-support (~> 3.5.0) 350 | rspec-rails (3.5.2) 351 | actionpack (>= 3.0) 352 | activesupport (>= 3.0) 353 | railties (>= 3.0) 354 | rspec-core (~> 3.5.0) 355 | rspec-expectations (~> 3.5.0) 356 | rspec-mocks (~> 3.5.0) 357 | rspec-support (~> 3.5.0) 358 | rspec-support (3.5.0) 359 | rubocop (0.85.1) 360 | parallel (~> 1.10) 361 | parser (>= 2.7.0.1) 362 | rainbow (>= 2.2.2, < 4.0) 363 | regexp_parser (>= 1.7) 364 | rexml 365 | rubocop-ast (>= 0.0.3) 366 | ruby-progressbar (~> 1.7) 367 | unicode-display_width (>= 1.4.0, < 2.0) 368 | rubocop-ast (0.0.3) 369 | parser (>= 2.7.0.1) 370 | rubocop-rails (2.6.0) 371 | activesupport (>= 4.2.0) 372 | rack (>= 1.1) 373 | rubocop (>= 0.82.0) 374 | rubocop-rspec (1.40.0) 375 | rubocop (>= 0.68.1) 376 | ruby-graphviz (1.2.5) 377 | rexml 378 | ruby-next-core (0.12.0) 379 | ruby-progressbar (1.11.0) 380 | ruby-vips (2.0.17) 381 | ffi (~> 1.9) 382 | ruby_dep (1.5.0) 383 | rubyzip (2.3.0) 384 | safe_yaml (1.0.4) 385 | sass (3.7.4) 386 | sass-listen (~> 4.0.0) 387 | sass-listen (4.0.0) 388 | rb-fsevent (~> 0.9, >= 0.9.4) 389 | rb-inotify (~> 0.9, >= 0.9.7) 390 | sass-rails (6.0.0) 391 | sassc-rails (~> 2.1, >= 2.1.1) 392 | sassc (2.4.0) 393 | ffi (~> 1.9) 394 | sassc-rails (2.1.2) 395 | railties (>= 4.0.0) 396 | sassc (>= 2.0) 397 | sprockets (> 3.0) 398 | sprockets-rails 399 | tilt 400 | scss_lint (0.59.0) 401 | sass (~> 3.5, >= 3.5.5) 402 | secure_headers (6.3.1) 403 | seedbank (0.5.0) 404 | rake (>= 10.0) 405 | selenium-webdriver (3.142.7) 406 | childprocess (>= 0.5, < 4.0) 407 | rubyzip (>= 1.2.2) 408 | semantic_range (2.3.1) 409 | shellany (0.0.1) 410 | shoulda-matchers (4.1.2) 411 | activesupport (>= 4.2.0) 412 | shrine (3.3.0) 413 | content_disposition (~> 1.0) 414 | down (~> 5.1) 415 | simple_form (5.1.0) 416 | actionpack (>= 5.2) 417 | activemodel (>= 5.2) 418 | slim (4.1.0) 419 | temple (>= 0.7.6, < 0.9) 420 | tilt (>= 2.0.6, < 2.1) 421 | slim-rails (3.2.0) 422 | actionpack (>= 3.1) 423 | railties (>= 3.1) 424 | slim (>= 3.0, < 5.0) 425 | slim_lint (0.20.2) 426 | rubocop (>= 0.78.0) 427 | slim (>= 3.0, < 5.0) 428 | slop (3.6.0) 429 | spring (2.0.1) 430 | activesupport (>= 4.2) 431 | spring-commands-rspec (1.0.4) 432 | spring (>= 0.9.1) 433 | spring-watcher-listen (2.0.1) 434 | listen (>= 2.7, < 4.0) 435 | spring (>= 1.2, < 3.0) 436 | sprockets (3.7.2) 437 | concurrent-ruby (~> 1.0) 438 | rack (> 1, < 3) 439 | sprockets-es6 (0.9.2) 440 | babel-source (>= 5.8.11) 441 | babel-transpiler 442 | sprockets (>= 3.0.0) 443 | sprockets-rails (3.2.2) 444 | actionpack (>= 4.0) 445 | activesupport (>= 4.0) 446 | sprockets (>= 3.0.0) 447 | stackprof (0.2.10) 448 | temple (0.8.2) 449 | terminal-notifier-guard (1.7.0) 450 | thor (1.1.0) 451 | thread_safe (0.3.6) 452 | tilt (2.0.10) 453 | tzinfo (1.2.9) 454 | thread_safe (~> 0.1) 455 | uglifier (3.0.4) 456 | execjs (>= 0.3.0, < 3) 457 | unicode-display_width (1.7.0) 458 | uniform_notifier (1.14.2) 459 | warden (1.2.9) 460 | rack (>= 2.0.9) 461 | web-console (3.7.0) 462 | actionview (>= 5.0) 463 | activemodel (>= 5.0) 464 | bindex (>= 0.4.0) 465 | railties (>= 5.0) 466 | webmock (2.3.2) 467 | addressable (>= 2.3.6) 468 | crack (>= 0.3.2) 469 | hashdiff 470 | webpacker (5.1.1) 471 | activesupport (>= 5.2) 472 | rack-proxy (>= 0.6.1) 473 | railties (>= 5.2) 474 | semantic_range (>= 2.3.0) 475 | websocket-driver (0.7.3) 476 | websocket-extensions (>= 0.1.0) 477 | websocket-extensions (0.1.5) 478 | xpath (3.2.0) 479 | nokogiri (~> 1.8) 480 | zeitwerk (2.4.2) 481 | 482 | PLATFORMS 483 | ruby 484 | 485 | DEPENDENCIES 486 | action_policy 487 | active_link_to 488 | autoprefixer-rails 489 | awesome_print 490 | aws-sdk-s3 491 | bootsnap 492 | brakeman 493 | bullet 494 | bundler-audit 495 | byebug 496 | capybara 497 | capybara-email 498 | database_cleaner 499 | decent_decoration 500 | decent_exposure 501 | devise 502 | dotenv-rails 503 | draper 504 | factory_bot_rails 505 | faker 506 | flamegraph 507 | formulaic 508 | foundation-icons-sass-rails 509 | foundation-rails 510 | google-analytics-rails 511 | guard-rspec 512 | health_check 513 | image_processing 514 | inky-rb 515 | interactor 516 | kaminari 517 | launchy 518 | letter_opener 519 | memory_profiler 520 | meta-tags 521 | newrelic_rpm 522 | pg 523 | premailer-rails 524 | puma 525 | rack-canonical-host 526 | rack-cors 527 | rack-mini-profiler! 528 | rails (~> 6.0.3) 529 | rails-erd 530 | responders 531 | rollbar 532 | rspec-its 533 | rspec-rails 534 | rubocop 535 | rubocop-rails 536 | rubocop-rspec 537 | sass-rails (~> 6.0.0) 538 | scss_lint 539 | secure_headers 540 | seedbank 541 | selenium-webdriver 542 | shoulda-matchers 543 | shrine 544 | simple_form 545 | slim 546 | slim-rails 547 | slim_lint 548 | spring 549 | spring-commands-rspec 550 | spring-watcher-listen 551 | stackprof 552 | terminal-notifier-guard 553 | uglifier (>= 2.7.2) 554 | web-console 555 | webmock 556 | webpacker 557 | 558 | RUBY VERSION 559 | ruby 2.7.1p83 560 | 561 | BUNDLED WITH 562 | 2.1.4 563 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | require "guard/rspec/dsl" 2 | 3 | guard :rspec, cmd: "bin/rspec" do 4 | notification :terminal_notifier if `uname`.match?(/Darwin/) 5 | 6 | dsl = Guard::RSpec::Dsl.new(self) 7 | 8 | # RSpec files 9 | rspec = dsl.rspec 10 | watch(rspec.spec_helper) { rspec.spec_dir } 11 | watch(rspec.spec_support) { rspec.spec_dir } 12 | watch(rspec.spec_files) 13 | 14 | # Ruby files 15 | ruby = dsl.ruby 16 | dsl.watch_spec_files_for(ruby.lib_files) 17 | 18 | # Rails files 19 | rails = dsl.rails(view_extensions: %w[slim]) 20 | dsl.watch_spec_files_for(rails.app_files) 21 | 22 | # Rails config changes 23 | watch(rails.spec_helper) { rspec.spec_dir } 24 | watch(rails.routes) { "#{rspec.spec_dir}/features" } 25 | watch(rails.app_controller) { "#{rspec.spec_dir}/features" } 26 | 27 | # Capybara features specs 28 | watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") } 29 | watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") } 30 | end 31 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rails Base 2 | 3 | [![Build Status](https://flatstack.semaphoreci.com/badges/rails-base.svg?key=1e8f53ac-b0b4-4dc7-bf8b-8027aa5a609e)](https://flatstack.semaphoreci.com/projects/rails-base) 4 | 5 | Rails Base is the base Rails application template used at Flatstack. 6 | It's based on Rails 6 and Ruby 2.7 7 | 8 | ## Application Gems 9 | 10 | * [Sass](https://github.com/rails/sass-rails) for Sass/Scss stylesheets 11 | * [Zurb Foundation](https://github.com/zurb/foundation-rails) as CSS framework. 12 | For more information see [documentation on using Foundation in Rails apps](http://foundation.zurb.com/docs/applications.html) 13 | * [Foundation Icon Font](https://github.com/zaiste/foundation-icons-sass-rails) for icons. Browse [icon set](http://zurb.com/playground/foundation-icon-fonts-3) examples 14 | * [Autoprefixer](https://github.com/ai/autoprefixer-rails) for writing CSS without vendor prefixes 15 | * [Slim](https://github.com/slim-template/slim) for views 16 | * [Simple Form](https://github.com/plataformatec/simple_form) for forms 17 | * [Responders](https://github.com/plataformatec/responders) to DRY controllers 18 | * [Decent Exposure](https://github.com/voxdolo/decent_exposure) to DRY controllers 19 | * [Draper](https://github.com/drapergem/draper) to decorate models for views 20 | * [Decent Decoration](https://github.com/netguru/decent_decoration) to extend Decent Exposure with Draper 21 | * [Interactors](https://github.com/collectiveidea/interactor) encapsulates application's business logic 22 | * [Action Policy](https://github.com/palkan/action_policy) to encapsulate authorization logic 23 | * [Kaminari](https://github.com/amatsuda/kaminari) for pagination 24 | * [Devise](http://github.com/plataformatec/devise) for basic auth 25 | * [Rollbar](https://github.com/rollbar/rollbar-gem) for exception notification 26 | * [Google Analytics Rails](https://github.com/bgarret/google-analytics-rails) for Google Analytics support 27 | 28 | ## Development Gems 29 | 30 | * [Puma](https://github.com/puma/puma) as Rails web server 31 | * [Letter Opener](https://github.com/ryanb/letter_opener) for opening mail in the browser instead of sending it 32 | * [Bullet](https://github.com/flyerhzm/bullet) for detecting N+1 queries and unused eager loading 33 | * [Rubocop](https://github.com/bbatsov/rubocop) and [Rubocop-Rspec](https://github.com/nevir/rubocop-rspec) 34 | for reporting violations of the Ruby style guide 35 | * [Brakeman](https://github.com/presidentbeef/brakeman) for checking application for common security vulnerabilities 36 | * [ByeBug](https://github.com/deivid-rodriguez/byebug) as debugger 37 | * [Bundler Audit](https://github.com/rubysec/bundler-audit) for scanning the Gemfile for 38 | insecure dependencies based on published CVEs 39 | * [Spring](https://github.com/rails/spring) for fast Rails actions via 40 | pre-loading 41 | * [Web Console](https://github.com/rails/web-console) for better debugging via 42 | in-browser IRB consoles 43 | * [SCSS-Lint](https://github.com/brigade/scss-lint) for reporting violations of SCSS coding conventions 44 | * [Slim-Lint](https://github.com/sds/slim-lint) for reporting violations of Ruby style guide in `.slim` templates 45 | * [Rails ERD](https://github.com/voormedia/rails-erd) for generating a diagram based on application's AR models 46 | 47 | ## Testing Gems 48 | 49 | * [Capybara](https://github.com/jnicklas/capybara) for integration testing 50 | * [Factory Bot](https://github.com/thoughtbot/factory_bot_rails) for test data 51 | * [RSpec](https://github.com/rspec/rspec) for unit testing 52 | * [Shoulda Matchers](http://github.com/thoughtbot/shoulda-matchers) for common RSpec matchers 53 | * [Email Spec](https://github.com/bmabey/email-spec) for common matchers for testing emails 54 | 55 | ## Initializers 56 | 57 | * `mailer.rb` - setup default hosts for mailer from configuration 58 | * `requires.rb` - automatically requires everything in lib/ & lib/extensions 59 | 60 | ## Scripts 61 | 62 | * `bin/setup` - setup required gems and migrate db if needed 63 | * `bin/quality` - runs rubocop, brakeman, rails_best_practices and bundle-audit for the app 64 | * `bin/test` - runs all tests and quality checks 65 | * `bin/server` - to run server locally 66 | 67 | ## Getting Started 68 | 69 | ### Prepare dependencies 70 | 71 | Some gems have native extensions. 72 | You should have GCC installed on your development machine. 73 | Dependencies will be automatically installed via setup script. 74 | 75 | * `phantomjs` - to run Javascript unit tests 76 | * `graphviz` - to generate Entity-Relationship Diagram 77 | * `node` - JavaScript runtime 78 | 79 | ### Bootstrap application 80 | 81 | 1. Clone application as new project with original repository named "rails-base". 82 | 83 | ```bash 84 | git clone git://github.com/fs/rails-base.git --origin rails-base [MY-NEW-PROJECT] 85 | ``` 86 | 87 | 2. Create your new repo on GitHub and push master into it. Make sure master branch is tracking origin repo. 88 | 89 | ```bash 90 | git remote add origin git@github.com:[MY-GITHUB-ACCOUNT]/[MY-NEW-PROJECT].git 91 | git push -u origin master 92 | ``` 93 | 94 | 3. Run setup script 95 | 96 | ```bash 97 | bin/setup 98 | ``` 99 | 100 | 4. Run test and quality suits to make sure all dependencies are satisfied and applications works correctly before making changes. 101 | 102 | ```bash 103 | bin/test 104 | ``` 105 | 106 | 5. Run app 107 | 108 | ```bash 109 | bin/server 110 | ``` 111 | 112 | 6. Update README 113 | 114 | Do not forget to update application `README.md` file with detailed information based on the 115 | existing template. 116 | 117 | ```bash 118 | mv doc/README_TEMPLATE.md README.md 119 | # update README.md 120 | git commit -am "Update README.md" 121 | ``` 122 | 123 | ## Deployment 124 | 125 | ### Heroku 126 | 127 | Out of the box Rails Base ready to be deployed to [Heroku.com](http://heroku.com). 128 | 129 | * [Heroku Postgres](https://www.heroku.com/postgres) add-on will be used for database. 130 | * [SendGrid](https://devcenter.heroku.com/articles/sendgrid#ruby-rails) add-on required to be able to send emails. 131 | * [NewRelic](https://devcenter.heroku.com/articles/newrelic#ruby-installation-and-configuration) add-on could be used to monitor application performance. 132 | * [Rollbar](https://elements.heroku.com/addons/rollbar) add-on could be used to application errors. 133 | 134 | ```bash 135 | heroku create --addons=heroku-postgresql,sendgrid,newrelic,rollbar --remote staging rails-base-example 136 | heroku config:add HOST="rails-base-example.herokuapp.com" MAILER_SENDER_ADDRESS="noreply@rails-base-example.herokuapp.com" NEW_RELIC_APP_NAME="Rails Base" 137 | git push staging master 138 | heroku run rake db:schema:load 139 | heroku open 140 | ``` 141 | 142 | ### Custom Server 143 | 144 | To be able to deploy to your custom server [Cloud66.com](http://cloud66.com) recommended as a provisioning and deployment service. 145 | Make sure to review and configure `cloud66/manifest.yml` before deployment. 146 | 147 | ## Credits 148 | 149 | Rails Base is maintained by [Timur Vafin](http://github.com/timurvafin). 150 | It was written by [Flatstack](http://www.flatstack.com) with the help of our 151 | [contributors](http://github.com/fs/rails-base/contributors). 152 | 153 | [](http://www.flatstack.com) 154 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path("config/application", __dir__) 5 | require "rake" 6 | 7 | Rails.application.load_tasks 8 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rails-base", 3 | "scripts": { 4 | "postdeploy": "bundle exec rake db:schema:load db:seed:development" 5 | }, 6 | "env": { 7 | "HOST": { "required": true }, 8 | "LANG": { "required": true }, 9 | "MAILER_SENDER_ADDRESS": { "required": true }, 10 | "RACK_ENV": { "required": true }, 11 | "RAILS_ENV": { "required": true }, 12 | "NODE_ENV": { "required": true }, 13 | "RAILS_SERVE_STATIC_FILES": { "required": true }, 14 | "SECRET_KEY_BASE": { "generator": "secret" } 15 | }, 16 | "addons": [ 17 | "heroku-postgresql", 18 | "sendgrid" 19 | ], 20 | "buildpacks": [ 21 | { 22 | "url": "heroku/nodejs" 23 | }, 24 | { 25 | "url": "heroku/ruby" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /app/assets/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/app/assets/images/.gitkeep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require foundation 2 | //= require_tree . 3 | 4 | $(document).foundation(); 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "core/all"; 2 | @import "components/all"; 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/all.scss: -------------------------------------------------------------------------------- 1 | @import "forms"; 2 | @import "navigation"; 3 | @import "notifications"; 4 | @import "container"; 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/container.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | padding-top: $global-padding; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/forms.scss: -------------------------------------------------------------------------------- 1 | .error { 2 | label { 3 | color: $form-label-color-invalid; 4 | } 5 | 6 | input, 7 | textarea, 8 | select { 9 | @include form-input-error; 10 | } 11 | 12 | .error { 13 | @include form-error; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/navigation.scss: -------------------------------------------------------------------------------- 1 | .top-bar { 2 | @include breakpoint(small only) { 3 | &-menu { 4 | flex-direction: column; 5 | 6 | align-items: flex-start; 7 | } 8 | } 9 | 10 | @include breakpoint(medium) { 11 | &-right { 12 | justify-content: flex-end; 13 | } 14 | } 15 | } 16 | 17 | .no-js { 18 | @include breakpoint(small only) { 19 | .top-bar { 20 | display: none; 21 | } 22 | } 23 | 24 | @include breakpoint(medium) { 25 | .title-bar { 26 | display: none; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/notifications.scss: -------------------------------------------------------------------------------- 1 | .notice { 2 | @include callout($primary-color); 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/core/all.scss: -------------------------------------------------------------------------------- 1 | @import "settings"; 2 | @import "foundation_and_overrides"; 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/core/foundation_and_overrides.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | @import "settings"; 4 | @import "foundation"; 5 | 6 | // If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package. 7 | // 8 | // @import "motion-ui/motion-ui"; 9 | 10 | // We include everything by default. To slim your CSS, remove components you don't use. 11 | 12 | @include foundation-global-styles; 13 | @include foundation-xy-grid-classes; 14 | //@include foundation-grid; 15 | //@include foundation-flex-grid; 16 | @include foundation-flex-classes; 17 | @include foundation-typography; 18 | @include foundation-forms; 19 | @include foundation-button; 20 | @include foundation-accordion; 21 | @include foundation-accordion-menu; 22 | @include foundation-badge; 23 | @include foundation-breadcrumbs; 24 | @include foundation-button-group; 25 | @include foundation-callout; 26 | @include foundation-card; 27 | @include foundation-close-button; 28 | @include foundation-menu; 29 | @include foundation-menu-icon; 30 | @include foundation-drilldown-menu; 31 | @include foundation-dropdown; 32 | @include foundation-dropdown-menu; 33 | @include foundation-responsive-embed; 34 | @include foundation-label; 35 | @include foundation-media-object; 36 | @include foundation-off-canvas; 37 | @include foundation-orbit; 38 | @include foundation-pagination; 39 | @include foundation-progress-bar; 40 | @include foundation-slider; 41 | @include foundation-sticky; 42 | @include foundation-reveal; 43 | @include foundation-switch; 44 | @include foundation-table; 45 | @include foundation-tabs; 46 | @include foundation-thumbnail; 47 | @include foundation-title-bar; 48 | @include foundation-tooltip; 49 | @include foundation-top-bar; 50 | @include foundation-visibility-classes; 51 | @include foundation-float-classes; 52 | 53 | // If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package. 54 | // 55 | // @include motion-ui-transitions; 56 | // @include motion-ui-animations; 57 | -------------------------------------------------------------------------------- /app/assets/stylesheets/core/settings.scss: -------------------------------------------------------------------------------- 1 | // Foundation for Sites Settings 2 | // ----------------------------- 3 | // 4 | // Table of Contents: 5 | // 6 | // 1. Global 7 | // 2. Breakpoints 8 | // 3. The Grid 9 | // 4. Base Typography 10 | // 5. Typography Helpers 11 | // 6. Abide 12 | // 7. Accordion 13 | // 8. Accordion Menu 14 | // 9. Badge 15 | // 10. Breadcrumbs 16 | // 11. Button 17 | // 12. Button Group 18 | // 13. Callout 19 | // 14. Card 20 | // 15. Close Button 21 | // 16. Drilldown 22 | // 17. Dropdown 23 | // 18. Dropdown Menu 24 | // 19. Flexbox Utilities 25 | // 20. Forms 26 | // 21. Label 27 | // 22. Media Object 28 | // 23. Menu 29 | // 24. Meter 30 | // 25. Off-canvas 31 | // 26. Orbit 32 | // 27. Pagination 33 | // 28. Progress Bar 34 | // 29. Prototype Arrow 35 | // 30. Prototype Border-Box 36 | // 31. Prototype Border-None 37 | // 32. Prototype Bordered 38 | // 33. Prototype Display 39 | // 34. Prototype Font-Styling 40 | // 35. Prototype List-Style-Type 41 | // 36. Prototype Overflow 42 | // 37. Prototype Position 43 | // 38. Prototype Rounded 44 | // 39. Prototype Separator 45 | // 40. Prototype Shadow 46 | // 41. Prototype Sizing 47 | // 42. Prototype Spacing 48 | // 43. Prototype Text-Decoration 49 | // 44. Prototype Text-Transformation 50 | // 45. Prototype Text-Utilities 51 | // 46. Responsive Embed 52 | // 47. Reveal 53 | // 48. Slider 54 | // 49. Switch 55 | // 50. Table 56 | // 51. Tabs 57 | // 52. Thumbnail 58 | // 53. Title Bar 59 | // 54. Tooltip 60 | // 55. Top Bar 61 | // 56. Xy Grid 62 | 63 | @import 'util/util'; 64 | 65 | // 1. Global 66 | // --------- 67 | 68 | $global-font-size: 100%; 69 | $global-width: rem-calc(1200); 70 | $global-lineheight: 1.5; 71 | $foundation-palette: ( 72 | primary: #1779ba, 73 | secondary: #767676, 74 | success: #3adb76, 75 | warning: #ffae00, 76 | alert: #cc4b37, 77 | ); 78 | $light-gray: #e6e6e6; 79 | $medium-gray: #cacaca; 80 | $dark-gray: #8a8a8a; 81 | $black: #0a0a0a; 82 | $white: #fefefe; 83 | $body-background: $white; 84 | $body-font-color: $black; 85 | $body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; 86 | $body-antialiased: true; 87 | $global-margin: 1rem; 88 | $global-padding: 1rem; 89 | $global-position: 1rem; 90 | $global-weight-normal: normal; 91 | $global-weight-bold: bold; 92 | $global-radius: 0; 93 | $global-menu-padding: 0.7rem 1rem; 94 | $global-menu-nested-margin: 1rem; 95 | $global-text-direction: ltr; 96 | $global-flexbox: true; 97 | $global-prototype-breakpoints: false; 98 | $global-button-cursor: auto; 99 | $global-color-pick-contrast-tolerance: 0; 100 | $print-transparent-backgrounds: true; 101 | $print-hrefs: true; 102 | 103 | @include add-foundation-colors; 104 | 105 | // 2. Breakpoints 106 | // -------------- 107 | 108 | $breakpoints: ( 109 | small: 0, 110 | medium: 640px, 111 | large: 1024px, 112 | xlarge: 1200px, 113 | xxlarge: 1440px, 114 | ); 115 | $breakpoints-hidpi: ( 116 | hidpi-1: 1, 117 | hidpi-1-5: 1.5, 118 | hidpi-2: 2, 119 | retina: 2, 120 | hidpi-3: 3 121 | ); 122 | $print-breakpoint: large; 123 | $breakpoint-classes: (small medium large); 124 | 125 | // 3. The Grid 126 | // ----------- 127 | 128 | $grid-row-width: $global-width; 129 | $grid-column-count: 12; 130 | $grid-column-gutter: ( 131 | small: 20px, 132 | medium: 30px, 133 | ); 134 | $grid-column-align-edge: true; 135 | $grid-column-alias: 'columns'; 136 | $block-grid-max: 8; 137 | 138 | // 4. Base Typography 139 | // ------------------ 140 | 141 | $header-font-family: $body-font-family; 142 | $header-font-weight: $global-weight-normal; 143 | $header-font-style: normal; 144 | $font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace; 145 | $header-color: inherit; 146 | $header-lineheight: 1.4; 147 | $header-margin-bottom: 0.5rem; 148 | $header-styles: ( 149 | small: ( 150 | 'h1': ('font-size': 24), 151 | 'h2': ('font-size': 20), 152 | 'h3': ('font-size': 19), 153 | 'h4': ('font-size': 18), 154 | 'h5': ('font-size': 17), 155 | 'h6': ('font-size': 16), 156 | ), 157 | medium: ( 158 | 'h1': ('font-size': 48), 159 | 'h2': ('font-size': 40), 160 | 'h3': ('font-size': 31), 161 | 'h4': ('font-size': 25), 162 | 'h5': ('font-size': 20), 163 | 'h6': ('font-size': 16), 164 | ), 165 | ); 166 | $header-text-rendering: optimizeLegibility; 167 | $small-font-size: 80%; 168 | $header-small-font-color: $medium-gray; 169 | $paragraph-lineheight: 1.6; 170 | $paragraph-margin-bottom: 1rem; 171 | $paragraph-text-rendering: optimizeLegibility; 172 | $enable-code-inline: true; 173 | $anchor-color: $primary-color; 174 | $anchor-color-hover: scale-color($anchor-color, $lightness: -14%); 175 | $anchor-text-decoration: none; 176 | $anchor-text-decoration-hover: none; 177 | $hr-width: $global-width; 178 | $hr-border: 1px solid $medium-gray; 179 | $hr-margin: rem-calc(20) auto; 180 | $list-lineheight: $paragraph-lineheight; 181 | $list-margin-bottom: $paragraph-margin-bottom; 182 | $list-style-type: disc; 183 | $list-style-position: outside; 184 | $list-side-margin: 1.25rem; 185 | $list-nested-side-margin: 1.25rem; 186 | $defnlist-margin-bottom: 1rem; 187 | $defnlist-term-weight: $global-weight-bold; 188 | $defnlist-term-margin-bottom: 0.3rem; 189 | $blockquote-color: $dark-gray; 190 | $blockquote-padding: rem-calc(9 20 0 19); 191 | $blockquote-border: 1px solid $medium-gray; 192 | $enable-cite-block: true; 193 | $keystroke-font: $font-family-monospace; 194 | $keystroke-color: $black; 195 | $keystroke-background: $light-gray; 196 | $keystroke-padding: rem-calc(2 4 0); 197 | $keystroke-radius: $global-radius; 198 | $abbr-underline: 1px dotted $black; 199 | 200 | // 5. Typography Helpers 201 | // --------------------- 202 | 203 | $lead-font-size: $global-font-size * 1.25; 204 | $lead-lineheight: 1.6; 205 | $subheader-lineheight: 1.4; 206 | $subheader-color: $dark-gray; 207 | $subheader-font-weight: $global-weight-normal; 208 | $subheader-margin-top: 0.2rem; 209 | $subheader-margin-bottom: 0.5rem; 210 | $stat-font-size: 2.5rem; 211 | $cite-color: $dark-gray; 212 | $cite-font-size: rem-calc(13); 213 | $cite-pseudo-content: '\2014 \0020'; 214 | $code-color: $black; 215 | $code-font-family: $font-family-monospace; 216 | $code-font-weight: $global-weight-normal; 217 | $code-background: $light-gray; 218 | $code-border: 1px solid $medium-gray; 219 | $code-padding: rem-calc(2 5 1); 220 | $code-block-padding: 1rem; 221 | $code-block-margin-bottom: 1.5rem; 222 | 223 | // 6. Abide 224 | // -------- 225 | 226 | $abide-inputs: true; 227 | $abide-labels: true; 228 | $input-background-invalid: get-color(alert); 229 | $form-label-color-invalid: get-color(alert); 230 | $input-error-color: get-color(alert); 231 | $input-error-font-size: rem-calc(12); 232 | $input-error-font-weight: $global-weight-bold; 233 | 234 | // 7. Accordion 235 | // ------------ 236 | 237 | $accordion-background: $white; 238 | $accordion-plusminus: true; 239 | $accordion-plus-content: '\002B'; 240 | $accordion-minus-content: '\2013'; 241 | $accordion-title-font-size: rem-calc(12); 242 | $accordion-item-color: $primary-color; 243 | $accordion-item-background-hover: $light-gray; 244 | $accordion-item-padding: 1.25rem 1rem; 245 | $accordion-content-background: $white; 246 | $accordion-content-border: 1px solid $light-gray; 247 | $accordion-content-color: $body-font-color; 248 | $accordion-content-padding: 1rem; 249 | 250 | // 8. Accordion Menu 251 | // ----------------- 252 | 253 | $accordionmenu-padding: $global-menu-padding; 254 | $accordionmenu-nested-margin: $global-menu-nested-margin; 255 | $accordionmenu-submenu-padding: $accordionmenu-padding; 256 | $accordionmenu-arrows: true; 257 | $accordionmenu-arrow-color: $primary-color; 258 | $accordionmenu-item-background: null; 259 | $accordionmenu-border: null; 260 | $accordionmenu-submenu-toggle-background: null; 261 | $accordion-submenu-toggle-border: $accordionmenu-border; 262 | $accordionmenu-submenu-toggle-width: 40px; 263 | $accordionmenu-submenu-toggle-height: $accordionmenu-submenu-toggle-width; 264 | $accordionmenu-arrow-size: 6px; 265 | 266 | // 9. Badge 267 | // -------- 268 | 269 | $badge-background: $primary-color; 270 | $badge-color: $white; 271 | $badge-color-alt: $black; 272 | $badge-palette: $foundation-palette; 273 | $badge-padding: 0.3em; 274 | $badge-minwidth: 2.1em; 275 | $badge-font-size: 0.6rem; 276 | 277 | // 10. Breadcrumbs 278 | // --------------- 279 | 280 | $breadcrumbs-margin: 0 0 $global-margin 0; 281 | $breadcrumbs-item-font-size: rem-calc(11); 282 | $breadcrumbs-item-color: $primary-color; 283 | $breadcrumbs-item-color-current: $black; 284 | $breadcrumbs-item-color-disabled: $medium-gray; 285 | $breadcrumbs-item-margin: 0.75rem; 286 | $breadcrumbs-item-uppercase: true; 287 | $breadcrumbs-item-separator: true; 288 | $breadcrumbs-item-separator-item: '/'; 289 | $breadcrumbs-item-separator-item-rtl: '\\'; 290 | $breadcrumbs-item-separator-color: $medium-gray; 291 | 292 | // 11. Button 293 | // ---------- 294 | 295 | $button-font-family: inherit; 296 | $button-font-weight: null; 297 | $button-padding: 0.85em 1em; 298 | $button-margin: 0 0 $global-margin 0; 299 | $button-fill: solid; 300 | $button-background: $primary-color; 301 | $button-background-hover: scale-color($button-background, $lightness: -15%); 302 | $button-color: $white; 303 | $button-color-alt: $black; 304 | $button-radius: $global-radius; 305 | $button-border: 1px solid transparent; 306 | $button-hollow-border-width: 1px; 307 | $button-sizes: ( 308 | tiny: 0.6rem, 309 | small: 0.75rem, 310 | default: 0.9rem, 311 | large: 1.25rem, 312 | ); 313 | $button-palette: $foundation-palette; 314 | $button-opacity-disabled: 0.25; 315 | $button-background-hover-lightness: -20%; 316 | $button-hollow-hover-lightness: -50%; 317 | $button-transition: background-color 0.25s ease-out, color 0.25s ease-out; 318 | $button-responsive-expanded: false; 319 | 320 | // 12. Button Group 321 | // ---------------- 322 | 323 | $buttongroup-margin: 1rem; 324 | $buttongroup-spacing: 1px; 325 | $buttongroup-child-selector: '.button'; 326 | $buttongroup-expand-max: 6; 327 | $buttongroup-radius-on-each: true; 328 | 329 | // 13. Callout 330 | // ----------- 331 | 332 | $callout-background: $white; 333 | $callout-background-fade: 85%; 334 | $callout-border: 1px solid rgba($black, 0.25); 335 | $callout-margin: 0 0 1rem 0; 336 | $callout-sizes: ( 337 | small: 0.5rem, 338 | default: 1rem, 339 | large: 3rem, 340 | ); 341 | $callout-font-color: $body-font-color; 342 | $callout-font-color-alt: $body-background; 343 | $callout-radius: $global-radius; 344 | $callout-link-tint: 30%; 345 | 346 | // 14. Card 347 | // -------- 348 | 349 | $card-background: $white; 350 | $card-font-color: $body-font-color; 351 | $card-divider-background: $light-gray; 352 | $card-border: 1px solid $light-gray; 353 | $card-shadow: none; 354 | $card-border-radius: $global-radius; 355 | $card-padding: $global-padding; 356 | $card-margin-bottom: $global-margin; 357 | 358 | // 15. Close Button 359 | // ---------------- 360 | 361 | $closebutton-position: right top; 362 | $closebutton-z-index: 10; 363 | $closebutton-default-size: medium; 364 | $closebutton-offset-horizontal: ( 365 | small: 0.66rem, 366 | medium: 1rem, 367 | ); 368 | $closebutton-offset-vertical: ( 369 | small: 0.33em, 370 | medium: 0.5rem, 371 | ); 372 | $closebutton-size: ( 373 | small: 1.5em, 374 | medium: 2em, 375 | ); 376 | $closebutton-lineheight: 1; 377 | $closebutton-color: $dark-gray; 378 | $closebutton-color-hover: $black; 379 | 380 | // 16. Drilldown 381 | // ------------- 382 | 383 | $drilldown-transition: transform 0.15s linear; 384 | $drilldown-arrows: true; 385 | $drilldown-padding: $global-menu-padding; 386 | $drilldown-nested-margin: 0; 387 | $drilldown-background: $white; 388 | $drilldown-submenu-padding: $drilldown-padding; 389 | $drilldown-submenu-background: $white; 390 | $drilldown-arrow-color: $primary-color; 391 | $drilldown-arrow-size: 6px; 392 | 393 | // 17. Dropdown 394 | // ------------ 395 | 396 | $dropdown-padding: 1rem; 397 | $dropdown-background: $body-background; 398 | $dropdown-border: 1px solid $medium-gray; 399 | $dropdown-font-size: 1rem; 400 | $dropdown-width: 300px; 401 | $dropdown-radius: $global-radius; 402 | $dropdown-sizes: ( 403 | tiny: 100px, 404 | small: 200px, 405 | large: 400px, 406 | ); 407 | 408 | // 18. Dropdown Menu 409 | // ----------------- 410 | 411 | $dropdownmenu-arrows: true; 412 | $dropdownmenu-arrow-color: $anchor-color; 413 | $dropdownmenu-arrow-size: 6px; 414 | $dropdownmenu-arrow-padding: 1.5rem; 415 | $dropdownmenu-min-width: 200px; 416 | $dropdownmenu-background: null; 417 | $dropdownmenu-submenu-background: $white; 418 | $dropdownmenu-padding: $global-menu-padding; 419 | $dropdownmenu-nested-margin: 0; 420 | $dropdownmenu-submenu-padding: $dropdownmenu-padding; 421 | $dropdownmenu-border: 1px solid $medium-gray; 422 | $dropdown-menu-item-color-active: get-color(primary); 423 | $dropdown-menu-item-background-active: transparent; 424 | 425 | // 19. Flexbox Utilities 426 | // --------------------- 427 | 428 | $flex-source-ordering-count: 6; 429 | $flexbox-responsive-breakpoints: true; 430 | 431 | // 20. Forms 432 | // --------- 433 | 434 | $fieldset-border: 1px solid $medium-gray; 435 | $fieldset-padding: rem-calc(20); 436 | $fieldset-margin: rem-calc(18 0); 437 | $legend-padding: rem-calc(0 3); 438 | $form-spacing: rem-calc(16); 439 | $helptext-color: $black; 440 | $helptext-font-size: rem-calc(13); 441 | $helptext-font-style: italic; 442 | $input-prefix-color: $black; 443 | $input-prefix-background: $light-gray; 444 | $input-prefix-border: 1px solid $medium-gray; 445 | $input-prefix-padding: 1rem; 446 | $form-label-color: $black; 447 | $form-label-font-size: rem-calc(14); 448 | $form-label-font-weight: $global-weight-normal; 449 | $form-label-line-height: 1.8; 450 | $select-background: $white; 451 | $select-triangle-color: $dark-gray; 452 | $select-radius: $global-radius; 453 | $input-color: $black; 454 | $input-placeholder-color: $medium-gray; 455 | $input-font-family: inherit; 456 | $input-font-size: rem-calc(16); 457 | $input-font-weight: $global-weight-normal; 458 | $input-line-height: $global-lineheight; 459 | $input-background: $white; 460 | $input-background-focus: $white; 461 | $input-background-disabled: $light-gray; 462 | $input-border: 1px solid $medium-gray; 463 | $input-border-focus: 1px solid $dark-gray; 464 | $input-padding: $form-spacing / 2; 465 | $input-shadow: inset 0 1px 2px rgba($black, 0.1); 466 | $input-shadow-focus: 0 0 5px $medium-gray; 467 | $input-cursor-disabled: not-allowed; 468 | $input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out; 469 | $input-number-spinners: true; 470 | $input-radius: $global-radius; 471 | $form-button-radius: $global-radius; 472 | 473 | // 21. Label 474 | // --------- 475 | 476 | $label-background: $primary-color; 477 | $label-color: $white; 478 | $label-color-alt: $black; 479 | $label-palette: $foundation-palette; 480 | $label-font-size: 0.8rem; 481 | $label-padding: 0.33333rem 0.5rem; 482 | $label-radius: $global-radius; 483 | 484 | // 22. Media Object 485 | // ---------------- 486 | 487 | $mediaobject-margin-bottom: $global-margin; 488 | $mediaobject-section-padding: $global-padding; 489 | $mediaobject-image-width-stacked: 100%; 490 | 491 | // 23. Menu 492 | // -------- 493 | 494 | $menu-margin: 0; 495 | $menu-nested-margin: $global-menu-nested-margin; 496 | $menu-items-padding: $global-menu-padding; 497 | $menu-simple-margin: 1rem; 498 | $menu-item-color-active: $white; 499 | $menu-item-color-alt-active: $black; 500 | $menu-item-background-active: get-color(primary); 501 | $menu-icon-spacing: 0.25rem; 502 | $menu-state-back-compat: true; 503 | $menu-centered-back-compat: true; 504 | $menu-icons-back-compat: true; 505 | 506 | // 24. Meter 507 | // --------- 508 | 509 | $meter-height: 1rem; 510 | $meter-radius: $global-radius; 511 | $meter-background: $medium-gray; 512 | $meter-fill-good: $success-color; 513 | $meter-fill-medium: $warning-color; 514 | $meter-fill-bad: $alert-color; 515 | 516 | // 25. Off-canvas 517 | // -------------- 518 | 519 | $offcanvas-sizes: ( 520 | small: 250px, 521 | ); 522 | $offcanvas-vertical-sizes: ( 523 | small: 250px, 524 | ); 525 | $offcanvas-background: $light-gray; 526 | $offcanvas-shadow: 0 0 10px rgba($black, 0.7); 527 | $offcanvas-inner-shadow-size: 20px; 528 | $offcanvas-inner-shadow-color: rgba($black, 0.25); 529 | $offcanvas-overlay-zindex: 11; 530 | $offcanvas-push-zindex: 12; 531 | $offcanvas-overlap-zindex: 13; 532 | $offcanvas-reveal-zindex: 12; 533 | $offcanvas-transition-length: 0.5s; 534 | $offcanvas-transition-timing: ease; 535 | $offcanvas-fixed-reveal: true; 536 | $offcanvas-exit-background: rgba($white, 0.25); 537 | $maincontent-class: 'off-canvas-content'; 538 | 539 | // 26. Orbit 540 | // --------- 541 | 542 | $orbit-bullet-background: $medium-gray; 543 | $orbit-bullet-background-active: $dark-gray; 544 | $orbit-bullet-diameter: 1.2rem; 545 | $orbit-bullet-margin: 0.1rem; 546 | $orbit-bullet-margin-top: 0.8rem; 547 | $orbit-bullet-margin-bottom: 0.8rem; 548 | $orbit-caption-background: rgba($black, 0.5); 549 | $orbit-caption-padding: 1rem; 550 | $orbit-control-background-hover: rgba($black, 0.5); 551 | $orbit-control-padding: 1rem; 552 | $orbit-control-zindex: 10; 553 | 554 | // 27. Pagination 555 | // -------------- 556 | 557 | $pagination-font-size: rem-calc(14); 558 | $pagination-margin-bottom: $global-margin; 559 | $pagination-item-color: $black; 560 | $pagination-item-padding: rem-calc(3 10); 561 | $pagination-item-spacing: rem-calc(1); 562 | $pagination-radius: $global-radius; 563 | $pagination-item-background-hover: $light-gray; 564 | $pagination-item-background-current: $primary-color; 565 | $pagination-item-color-current: $white; 566 | $pagination-item-color-disabled: $medium-gray; 567 | $pagination-ellipsis-color: $black; 568 | $pagination-mobile-items: false; 569 | $pagination-mobile-current-item: false; 570 | $pagination-arrows: true; 571 | $pagination-arrow-previous: '\00AB'; 572 | $pagination-arrow-next: '\00BB'; 573 | 574 | // 28. Progress Bar 575 | // ---------------- 576 | 577 | $progress-height: 1rem; 578 | $progress-background: $medium-gray; 579 | $progress-margin-bottom: $global-margin; 580 | $progress-meter-background: $primary-color; 581 | $progress-radius: $global-radius; 582 | 583 | // 29. Prototype Arrow 584 | // ------------------- 585 | 586 | $prototype-arrow-directions: ( 587 | down, 588 | up, 589 | right, 590 | left 591 | ); 592 | $prototype-arrow-size: 0.4375rem; 593 | $prototype-arrow-color: $black; 594 | 595 | // 30. Prototype Border-Box 596 | // ------------------------ 597 | 598 | $prototype-border-box-breakpoints: $global-prototype-breakpoints; 599 | 600 | // 31. Prototype Border-None 601 | // ------------------------- 602 | 603 | $prototype-border-none-breakpoints: $global-prototype-breakpoints; 604 | 605 | // 32. Prototype Bordered 606 | // ---------------------- 607 | 608 | $prototype-bordered-breakpoints: $global-prototype-breakpoints; 609 | $prototype-border-width: rem-calc(1); 610 | $prototype-border-type: solid; 611 | $prototype-border-color: $medium-gray; 612 | 613 | // 33. Prototype Display 614 | // --------------------- 615 | 616 | $prototype-display-breakpoints: $global-prototype-breakpoints; 617 | $prototype-display: ( 618 | inline, 619 | inline-block, 620 | block, 621 | table, 622 | table-cell 623 | ); 624 | 625 | // 34. Prototype Font-Styling 626 | // -------------------------- 627 | 628 | $prototype-font-breakpoints: $global-prototype-breakpoints; 629 | $prototype-wide-letter-spacing: rem-calc(4); 630 | $prototype-font-normal: $global-weight-normal; 631 | $prototype-font-bold: $global-weight-bold; 632 | 633 | // 35. Prototype List-Style-Type 634 | // ----------------------------- 635 | 636 | $prototype-list-breakpoints: $global-prototype-breakpoints; 637 | $prototype-style-type-unordered: ( 638 | disc, 639 | circle, 640 | square 641 | ); 642 | $prototype-style-type-ordered: ( 643 | decimal, 644 | lower-alpha, 645 | lower-latin, 646 | lower-roman, 647 | upper-alpha, 648 | upper-latin, 649 | upper-roman 650 | ); 651 | 652 | // 36. Prototype Overflow 653 | // ---------------------- 654 | 655 | $prototype-overflow-breakpoints: $global-prototype-breakpoints; 656 | $prototype-overflow: ( 657 | visible, 658 | hidden, 659 | scroll 660 | ); 661 | 662 | // 37. Prototype Position 663 | // ---------------------- 664 | 665 | $prototype-position-breakpoints: $global-prototype-breakpoints; 666 | $prototype-position: ( 667 | static, 668 | relative, 669 | absolute, 670 | fixed 671 | ); 672 | $prototype-position-z-index: 975; 673 | 674 | // 38. Prototype Rounded 675 | // --------------------- 676 | 677 | $prototype-rounded-breakpoints: $global-prototype-breakpoints; 678 | $prototype-border-radius: rem-calc(3); 679 | 680 | // 39. Prototype Separator 681 | // ----------------------- 682 | 683 | $prototype-separator-breakpoints: $global-prototype-breakpoints; 684 | $prototype-separator-align: center; 685 | $prototype-separator-height: rem-calc(2); 686 | $prototype-separator-width: 3rem; 687 | $prototype-separator-background: $primary-color; 688 | $prototype-separator-margin-top: $global-margin; 689 | 690 | // 40. Prototype Shadow 691 | // -------------------- 692 | 693 | $prototype-shadow-breakpoints: $global-prototype-breakpoints; 694 | $prototype-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16), 695 | 0 2px 10px 0 rgba(0,0,0,.12); 696 | 697 | // 41. Prototype Sizing 698 | // -------------------- 699 | 700 | $prototype-sizing-breakpoints: $global-prototype-breakpoints; 701 | $prototype-sizing: ( 702 | width, 703 | height 704 | ); 705 | $prototype-sizes: ( 706 | 25: 25%, 707 | 50: 50%, 708 | 75: 75%, 709 | 100: 100% 710 | ); 711 | 712 | // 42. Prototype Spacing 713 | // --------------------- 714 | 715 | $prototype-spacing-breakpoints: $global-prototype-breakpoints; 716 | $prototype-spacers-count: 3; 717 | 718 | // 43. Prototype Text-Decoration 719 | // ----------------------------- 720 | 721 | $prototype-decoration-breakpoints: $global-prototype-breakpoints; 722 | $prototype-text-decoration: ( 723 | overline, 724 | underline, 725 | line-through, 726 | ); 727 | 728 | // 44. Prototype Text-Transformation 729 | // --------------------------------- 730 | 731 | $prototype-transformation-breakpoints: $global-prototype-breakpoints; 732 | $prototype-text-transformation: ( 733 | lowercase, 734 | uppercase, 735 | capitalize 736 | ); 737 | 738 | // 45. Prototype Text-Utilities 739 | // ---------------------------- 740 | 741 | $prototype-utilities-breakpoints: $global-prototype-breakpoints; 742 | $prototype-text-overflow: ellipsis; 743 | 744 | // 46. Responsive Embed 745 | // -------------------- 746 | 747 | $responsive-embed-margin-bottom: rem-calc(16); 748 | $responsive-embed-ratios: ( 749 | default: 4 by 3, 750 | widescreen: 16 by 9, 751 | ); 752 | 753 | // 47. Reveal 754 | // ---------- 755 | 756 | $reveal-background: $white; 757 | $reveal-width: 600px; 758 | $reveal-max-width: $global-width; 759 | $reveal-padding: $global-padding; 760 | $reveal-border: 1px solid $medium-gray; 761 | $reveal-radius: $global-radius; 762 | $reveal-zindex: 1005; 763 | $reveal-overlay-background: rgba($black, 0.45); 764 | 765 | // 48. Slider 766 | // ---------- 767 | 768 | $slider-width-vertical: 0.5rem; 769 | $slider-transition: all 0.2s ease-in-out; 770 | $slider-height: 0.5rem; 771 | $slider-background: $light-gray; 772 | $slider-fill-background: $medium-gray; 773 | $slider-handle-height: 1.4rem; 774 | $slider-handle-width: 1.4rem; 775 | $slider-handle-background: $primary-color; 776 | $slider-opacity-disabled: 0.25; 777 | $slider-radius: $global-radius; 778 | 779 | // 49. Switch 780 | // ---------- 781 | 782 | $switch-background: $medium-gray; 783 | $switch-background-active: $primary-color; 784 | $switch-height: 2rem; 785 | $switch-height-tiny: 1.5rem; 786 | $switch-height-small: 1.75rem; 787 | $switch-height-large: 2.5rem; 788 | $switch-radius: $global-radius; 789 | $switch-margin: $global-margin; 790 | $switch-paddle-background: $white; 791 | $switch-paddle-offset: 0.25rem; 792 | $switch-paddle-radius: $global-radius; 793 | $switch-paddle-transition: all 0.25s ease-out; 794 | $switch-opacity-disabled: .5; 795 | $switch-cursor-disabled: not-allowed; 796 | 797 | // 50. Table 798 | // --------- 799 | 800 | $table-background: $white; 801 | $table-color-scale: 5%; 802 | $table-border: 1px solid smart-scale($table-background, $table-color-scale); 803 | $table-padding: rem-calc(8 10 10); 804 | $table-hover-scale: 2%; 805 | $table-row-hover: darken($table-background, $table-hover-scale); 806 | $table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale); 807 | $table-is-striped: true; 808 | $table-striped-background: smart-scale($table-background, $table-color-scale); 809 | $table-stripe: even; 810 | $table-head-background: smart-scale($table-background, $table-color-scale / 2); 811 | $table-head-row-hover: darken($table-head-background, $table-hover-scale); 812 | $table-foot-background: smart-scale($table-background, $table-color-scale); 813 | $table-foot-row-hover: darken($table-foot-background, $table-hover-scale); 814 | $table-head-font-color: $body-font-color; 815 | $table-foot-font-color: $body-font-color; 816 | $show-header-for-stacked: false; 817 | $table-stack-breakpoint: medium; 818 | 819 | // 51. Tabs 820 | // -------- 821 | 822 | $tab-margin: 0; 823 | $tab-background: $white; 824 | $tab-color: $primary-color; 825 | $tab-background-active: $light-gray; 826 | $tab-active-color: $primary-color; 827 | $tab-item-font-size: rem-calc(12); 828 | $tab-item-background-hover: $white; 829 | $tab-item-padding: 1.25rem 1.5rem; 830 | $tab-content-background: $white; 831 | $tab-content-border: $light-gray; 832 | $tab-content-color: $body-font-color; 833 | $tab-content-padding: 1rem; 834 | 835 | // 52. Thumbnail 836 | // ------------- 837 | 838 | $thumbnail-border: 4px solid $white; 839 | $thumbnail-margin-bottom: $global-margin; 840 | $thumbnail-shadow: 0 0 0 1px rgba($black, 0.2); 841 | $thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5); 842 | $thumbnail-transition: box-shadow 200ms ease-out; 843 | $thumbnail-radius: $global-radius; 844 | 845 | // 53. Title Bar 846 | // ------------- 847 | 848 | $titlebar-background: $black; 849 | $titlebar-color: $white; 850 | $titlebar-padding: 0.5rem; 851 | $titlebar-text-font-weight: bold; 852 | $titlebar-icon-color: $white; 853 | $titlebar-icon-color-hover: $medium-gray; 854 | $titlebar-icon-spacing: 0.25rem; 855 | 856 | // 54. Tooltip 857 | // ----------- 858 | 859 | $has-tip-cursor: help; 860 | $has-tip-font-weight: $global-weight-bold; 861 | $has-tip-border-bottom: dotted 1px $dark-gray; 862 | $tooltip-background-color: $black; 863 | $tooltip-color: $white; 864 | $tooltip-padding: 0.75rem; 865 | $tooltip-max-width: 10rem; 866 | $tooltip-font-size: $small-font-size; 867 | $tooltip-pip-width: 0.75rem; 868 | $tooltip-pip-height: $tooltip-pip-width * 0.866; 869 | $tooltip-radius: $global-radius; 870 | 871 | // 55. Top Bar 872 | // ----------- 873 | 874 | $topbar-padding: 0.5rem; 875 | $topbar-background: $light-gray; 876 | $topbar-submenu-background: $topbar-background; 877 | $topbar-title-spacing: 0.5rem 1rem 0.5rem 0; 878 | $topbar-input-width: 200px; 879 | $topbar-unstack-breakpoint: medium; 880 | 881 | // 56. Xy Grid 882 | // ----------- 883 | 884 | $xy-grid: true; 885 | $grid-container: $global-width; 886 | $grid-columns: 12; 887 | $grid-margin-gutters: ( 888 | small: 20px, 889 | medium: 30px 890 | ); 891 | $grid-padding-gutters: $grid-margin-gutters; 892 | $grid-container-padding: $grid-padding-gutters; 893 | $grid-container-max: $global-width; 894 | $xy-block-grid-max: 8; 895 | 896 | -------------------------------------------------------------------------------- /app/assets/stylesheets/emails/core/all.scss: -------------------------------------------------------------------------------- 1 | @import "settings"; 2 | -------------------------------------------------------------------------------- /app/assets/stylesheets/emails/core/settings.scss: -------------------------------------------------------------------------------- 1 | // Foundation for Emails Settings 2 | // ------------------------------ 3 | // 4 | // Table of Contents: 5 | // 6 | // 1. Global 7 | // 2. Grid 8 | // 3. Block Grid 9 | // 4. Typography 10 | // 5. Button 11 | // 6. Callout 12 | // 7. Menu 13 | // 8. Thumbnail 14 | 15 | 16 | // 1. Global 17 | // --------- 18 | 19 | $primary-color: #2199e8; 20 | $secondary-color: #777; 21 | $success-color: #3adb76; 22 | $warning-color: #ffae00; 23 | $alert-color: #ec5840; 24 | $light-gray: #f3f3f3; 25 | $medium-gray: #cacaca; 26 | $dark-gray: #8a8a8a; 27 | $black: #0a0a0a; 28 | $white: #fefefe; 29 | $pre-color: #ff6908; 30 | $global-width: 580px; 31 | $global-width-small: 95%; 32 | $global-gutter: 16px; 33 | $body-background: $light-gray; 34 | $container-background: $white; 35 | $global-padding: 16px; 36 | $global-margin: 16px; 37 | $global-radius: 3px; 38 | $global-rounded: 500px; 39 | $global-breakpoint: $global-width + $global-gutter; 40 | 41 | // 2. Grid 42 | // ------- 43 | 44 | $grid-column-count: 12; 45 | $column-padding-bottom: $global-padding; 46 | $container-radius: 0; 47 | 48 | // 3. Block Grid 49 | // ------------- 50 | 51 | $block-grid-max: 8; 52 | $block-grid-gutter: $global-gutter; 53 | 54 | // 4. Typography 55 | // ------------- 56 | 57 | $global-font-color: $black; 58 | $body-font-family: Helvetica, Arial, sans-serif; 59 | $global-font-weight: normal; 60 | $header-color: inherit; 61 | $global-line-height: 1.3; 62 | $global-font-size: 16px; 63 | $body-line-height: $global-line-height; 64 | $header-font-family: $body-font-family; 65 | $header-font-weight: $global-font-weight; 66 | $h1-font-size: 34px; 67 | $h2-font-size: 30px; 68 | $h3-font-size: 28px; 69 | $h4-font-size: 24px; 70 | $h5-font-size: 20px; 71 | $h6-font-size: 18px; 72 | $header-margin-bottom: 10px; 73 | $paragraph-margin-bottom: 10px; 74 | $small-font-size: 80%; 75 | $small-font-color: $medium-gray; 76 | $lead-font-size: $global-font-size * 1.25; 77 | $lead-line-height: 1.6; 78 | $text-padding: 10px; 79 | $subheader-lineheight: 1.4; 80 | $subheader-color: $dark-gray; 81 | $subheader-font-weight: $global-font-weight; 82 | $subheader-margin-top: 4px; 83 | $subheader-margin-bottom: 8px; 84 | $hr-width: $global-width; 85 | $hr-border: 1px solid $black; 86 | $hr-margin: 20px auto; 87 | $anchor-text-decoration: none; 88 | $anchor-color: $primary-color; 89 | $anchor-color-visited: $anchor-color; 90 | $anchor-color-hover: darken($primary-color, 10%); 91 | $anchor-color-active: $anchor-color-hover; 92 | $stat-font-size: 40px; 93 | 94 | // 5. Button 95 | // --------- 96 | 97 | $button-padding: ( 98 | tiny: 4px 8px 4px 8px, 99 | small: 5px 10px 5px 10px, 100 | default: 8px 16px 8px 16px, 101 | large: 10px 20px 10px 20px, 102 | ); 103 | $button-font-size: ( 104 | tiny: 10px, 105 | small: 12px, 106 | default: 16px, 107 | large: 20px, 108 | ); 109 | $button-color: $white; 110 | $button-color-alt: $medium-gray; 111 | $button-font-weight: bold; 112 | $button-margin: 0 0 $global-margin 0; 113 | $button-background: $primary-color; 114 | $button-border: 2px solid $button-background; 115 | $button-radius: $global-radius; 116 | $button-rounded: $global-rounded; 117 | 118 | // 6. Callout 119 | // ---------- 120 | 121 | $callout-background: $white; 122 | $callout-background-fade: 85%; 123 | $callout-padding: 10px; 124 | $callout-margin-bottom: $global-margin; 125 | $callout-border: 1px solid darken($callout-background, 20%); 126 | $callout-border-secondary: 1px solid darken($secondary-color, 20%); 127 | $callout-border-success: 1px solid darken($success-color, 20%); 128 | $callout-border-warning: 1px solid darken($warning-color, 20%); 129 | $callout-border-alert: 1px solid darken($alert-color, 20%); 130 | 131 | // 7. Menu 132 | // ------- 133 | 134 | $menu-item-padding: 10px; 135 | $menu-item-gutter: 10px; 136 | $menu-item-color: $primary-color; 137 | 138 | // 8. Thumbnail 139 | // ------------ 140 | 141 | $thumbnail-border: solid 4px $white; 142 | $thumbnail-margin-bottom: $global-margin; 143 | $thumbnail-shadow: 0 0 0 1px rgba($black, .2); 144 | $thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, .5); 145 | $thumbnail-transition: box-shadow 200ms ease-out; 146 | $thumbnail-radius: $global-radius; 147 | 148 | @import "foundation-emails"; 149 | -------------------------------------------------------------------------------- /app/assets/stylesheets/mailer.scss: -------------------------------------------------------------------------------- 1 | @import "emails/core/all"; 2 | -------------------------------------------------------------------------------- /app/assets/templates/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/app/assets/templates/.keep -------------------------------------------------------------------------------- /app/constraints/ip_whitelist_constraint.rb: -------------------------------------------------------------------------------- 1 | class IpWhitelistConstraint 2 | attr_reader :ips 3 | private :ips 4 | 5 | def initialize 6 | @ips = ENV.fetch("IP_WHITELIST", "").split(",") 7 | end 8 | 9 | def matches?(request) 10 | ips.include?(ip(request)) 11 | end 12 | 13 | private 14 | 15 | def ip(request) 16 | request.respond_to?(:remote_ip) ? request.remote_ip : request.ip 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include Authentication 3 | include Authorization 4 | include BulletHelper 5 | 6 | protect_from_forgery with: :exception 7 | 8 | responders :flash 9 | respond_to :html 10 | end 11 | -------------------------------------------------------------------------------- /app/controllers/concerns/authentication.rb: -------------------------------------------------------------------------------- 1 | module Authentication 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | helper do 6 | def current_user 7 | super.decorate if user_signed_in? 8 | end 9 | end 10 | end 11 | 12 | private 13 | 14 | def devise_parameter_sanitizer 15 | if resource_class == User 16 | User::ParameterSanitizer.new(User, :user, params) 17 | else 18 | super 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/concerns/authorization.rb: -------------------------------------------------------------------------------- 1 | module Authorization 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | rescue_from ActionPolicy::Unauthorized, with: :user_not_authorized 6 | end 7 | 8 | private 9 | 10 | def user_not_authorized 11 | redirect_to(root_path) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/concerns/bullet_helper.rb: -------------------------------------------------------------------------------- 1 | module BulletHelper 2 | def skip_bullet 3 | return unless defined?(Bullet) 4 | return unless Bullet.enable? 5 | 6 | # Can't use `Bullet.enable=` b/c it's resets settings 7 | # https://github.com/flyerhzm/bullet/blob/master/lib/bullet.rb#L48-L58 8 | Bullet.instance_variable_set :@enable, false 9 | 10 | yield 11 | ensure 12 | Bullet.instance_variable_set :@enable, true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | def home 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | module Users 2 | class RegistrationsController < Devise::RegistrationsController 3 | protected 4 | 5 | def update_resource(resource, params) 6 | resource.update(params) 7 | end 8 | 9 | def account_update_params 10 | params = devise_parameter_sanitizer.sanitize(:account_update) 11 | 12 | if passwords_blank?(params) 13 | params.except(:password, :password_confirmation) 14 | else 15 | params 16 | end 17 | end 18 | 19 | def passwords_blank?(params) 20 | params[:password].blank? && params[:password_confirmation].blank? 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/decorators/application_decorator.rb: -------------------------------------------------------------------------------- 1 | class ApplicationDecorator < Draper::Decorator 2 | delegate :to_model 3 | 4 | def self.collection_decorator_class 5 | PaginatingDecorator 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/decorators/paginating_decorator.rb: -------------------------------------------------------------------------------- 1 | class PaginatingDecorator < Draper::CollectionDecorator 2 | delegate :current_page, :total_pages, :limit_value, :entry_name, :total_count, :offset_value, :last_page? 3 | end 4 | -------------------------------------------------------------------------------- /app/decorators/user_decorator.rb: -------------------------------------------------------------------------------- 1 | class UserDecorator < ApplicationDecorator 2 | delegate :id, :full_name, :email 3 | 4 | def full_name_with_email 5 | "#{object.full_name} (#{object.email})" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/interactors/.keep: -------------------------------------------------------------------------------- 1 | .keep 2 | -------------------------------------------------------------------------------- /app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | require("jquery"); 2 | require("@rails/ujs").start(); 3 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | layout "mailer" 3 | end 4 | -------------------------------------------------------------------------------- /app/mailers/devise_mailer.rb: -------------------------------------------------------------------------------- 1 | class DeviseMailer < Devise::Mailer 2 | layout "mailer" 3 | 4 | protected 5 | 6 | def initialize_from_record(record) 7 | super 8 | instance_variable_set("@#{devise_mapping.name}", record.decorate) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | include AvatarUploader::Attachment(:avatar) 3 | 4 | devise :database_authenticatable, :registerable, :confirmable, 5 | :recoverable, :rememberable, :trackable, :validatable, :lockable 6 | 7 | validates :full_name, presence: true 8 | end 9 | -------------------------------------------------------------------------------- /app/policies/application_policy.rb: -------------------------------------------------------------------------------- 1 | class ApplicationPolicy < ActionPolicy::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/sanitizers/user/parameter_sanitizer.rb: -------------------------------------------------------------------------------- 1 | class User 2 | class ParameterSanitizer < Devise::ParameterSanitizer 3 | SIGN_UP_PARAMS = %i[ 4 | full_name 5 | email 6 | password 7 | password_confirmation 8 | ].freeze 9 | 10 | ACCOUNT_UPDATE_PARAMS = %i[ 11 | full_name 12 | email 13 | avatar 14 | remove_avatar 15 | password 16 | password_confirmation 17 | ].freeze 18 | 19 | def initialize(*) 20 | super 21 | permit(:sign_up, keys: SIGN_UP_PARAMS) 22 | permit(:account_update, keys: ACCOUNT_UPDATE_PARAMS) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/uploaders/avatar_uploader.rb: -------------------------------------------------------------------------------- 1 | class AvatarUploader < Shrine 2 | MAX_AVATAR_FILE_SIZE = 5 * 1024 * 1024 3 | 4 | Attacher.validate do 5 | validate_size 1..MAX_AVATAR_FILE_SIZE 6 | validate_mime_type %w[image/jpeg image/png] 7 | validate_extension %w[jpg jpeg png] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/application/_footer.html.slim: -------------------------------------------------------------------------------- 1 | footer.grid-container 2 | .cell 3 | p © Flatstack #{Time.zone.today.year} 4 | -------------------------------------------------------------------------------- /app/views/application/_messages.html.slim: -------------------------------------------------------------------------------- 1 | - flash.each do |type, content| 2 | div(class="callout #{type}" data-closable) 3 | = content 4 | button.close-button(tabindex="0" aria-label="Dismiss alert" type="button" data-close) 5 | span aria-hidden="true" × 6 | -------------------------------------------------------------------------------- /app/views/application/_navigation.html.slim: -------------------------------------------------------------------------------- 1 | .title-bar data-hide-for="medium" data-responsive-toggle="navigation_menu" 2 | button.menu-icon data-toggle="" type="button" 3 | title-bar-title Menu 4 | 5 | nav.top-bar#navigation_menu 6 | = render "navigation_main" 7 | = render "navigation_user" 8 | -------------------------------------------------------------------------------- /app/views/application/_navigation_main.html.slim: -------------------------------------------------------------------------------- 1 | .top-bar-left 2 | ul.menu 3 | li 4 | = link_to "Rails Base example site", root_path 5 | = active_link_to("Home", root_path, active: :exclusive, wrap_tag: :li) if user_signed_in? 6 | -------------------------------------------------------------------------------- /app/views/application/_navigation_user.html.slim: -------------------------------------------------------------------------------- 1 | ul.top-bar-right.menu.dropdown(data-dropdown-menu) 2 | - if user_signed_in? 3 | li 4 | a = current_user.full_name 5 | ul.menu 6 | li = link_to "Edit profile", edit_user_registration_path 7 | li = link_to "Sign out", destroy_user_session_path 8 | - else 9 | = active_link_to "Sign in", new_user_session_path, active: :exclusive, wrap_tag: :li 10 | = active_link_to "Sign up", new_user_registration_path, active: :exclusive, wrap_tag: :li 11 | -------------------------------------------------------------------------------- /app/views/devise_mailer/confirmation_instructions.html.inky: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

6 | Welcome <%= @user.full_name %>! 7 |

8 |

9 | You can confirm your account email through the link below: 10 |

11 |
12 | 15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /app/views/devise_mailer/confirmation_instructions.text.erb: -------------------------------------------------------------------------------- 1 | Welcome <%= @user.full_name %>! 2 | 3 | You can confirm your account email through the url below: 4 | 5 | <%= confirmation_url(@user, confirmation_token: @token) %> 6 | -------------------------------------------------------------------------------- /app/views/devise_mailer/reset_password_instructions.html.inky: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

6 | Hello <%= @user.full_name %>! 7 |

8 |

9 | Someone has requested a link to change your password, and you can do this through the link below. 10 |

11 |
12 | 15 |
16 |

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

19 |

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

22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /app/views/devise_mailer/reset_password_instructions.text.erb: -------------------------------------------------------------------------------- 1 | Hello <%= @user.full_name %>! 2 | 3 | Someone has requested a link to change your password, and you can do this through the following link: 4 | 5 | <%= edit_password_url(@user, reset_password_token: @token) %> 6 | 7 | If you didn't request this, please ignore this email. 8 | 9 | Your password won't change until you access the link above and create a new one. 10 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless(current_page.first?, 3 | raw(t("views.pagination.first")), 4 | url, 5 | remote: remote) 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.slim: -------------------------------------------------------------------------------- 1 | li.unavailable 2 | = link_to(raw(t("views.pagination.truncate")), "#") 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless(current_page.last?, 3 | raw(t("views.pagination.last")), 4 | url, 5 | remote: remote) 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless(current_page.last?, 3 | raw(t("views.pagination.next")), 4 | url, 5 | rel: "next", 6 | remote: remote) 7 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.slim: -------------------------------------------------------------------------------- 1 | li[class="#{"current" if page.current?}"] 2 | = link_to(page, 3 | page.current? ? "#" : url, 4 | remote: remote, 5 | rel: page.next? ? "next" : (page.prev? ? "prev" : nil)) 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.slim: -------------------------------------------------------------------------------- 1 | == paginator.render do 2 | .pagination-centered 3 | ul.pagination 4 | == first_page_tag unless current_page.first? 5 | == prev_page_tag unless current_page.first? 6 | - each_page do |page| 7 | - if page.left_outer? || page.right_outer? || page.inside_window? 8 | == page_tag(page) 9 | - elsif !page.was_truncated? 10 | == gap_tag 11 | == next_page_tag unless current_page.last? 12 | == last_page_tag unless current_page.last? 13 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.slim: -------------------------------------------------------------------------------- 1 | li 2 | = link_to_unless(current_page.first?, 3 | raw(t("views.pagination.previous")), 4 | url, 5 | rel: "prev", 6 | remote: remote) 7 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | 3 | html class="no-js" lang="en" 4 | 5 | head 6 | meta charset="utf-8" 7 | meta name="viewport" content="width=device-width, initial-scale=1.0" 8 | meta name="robots" content="NOODP,NOYDIR" 9 | 10 | = display_meta_tags site: "RailsBase", keywords: %w[title site] 11 | = csrf_meta_tags 12 | 13 | = stylesheet_link_tag :application 14 | 15 | == analytics_init if GoogleAnalytics.valid_tracker? 16 | 17 | body 18 | = render "navigation" 19 | = render "messages" 20 | 21 | main.grid-container 22 | = yield 23 | 24 | = render "footer" 25 | 26 | = javascript_pack_tag :application 27 | = javascript_include_tag :application 28 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.slim: -------------------------------------------------------------------------------- 1 | doctype Strict 2 | html xmlns="http://www.w3.org/1999/xhtml" 3 | head 4 | meta content="text/html; charset=utf-8" http-equiv="Content-Type" 5 | meta content="width=device-width" name="viewport" 6 | = stylesheet_link_tag :mailer 7 | 8 | body 9 | table.body 10 | tr 11 | td.float-center align="center" valign="top" 12 | center 13 | = yield 14 | -------------------------------------------------------------------------------- /app/views/pages/home.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | | Hello, 4 | =< current_user&.full_name_with_email || "World" 5 | | ! 6 | -------------------------------------------------------------------------------- /app/views/users/confirmations/new.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | h2 = title("Didn't receive confirmation instructions?") 4 | 5 | .grid-x 6 | .cell.medium-5 7 | = simple_form_for resource, 8 | as: resource_name, 9 | url: confirmation_path(resource_name), 10 | html: { method: :post } do |f| 11 | 12 | .form-inputs 13 | = f.input :email, required: true 14 | 15 | .form-actions 16 | = f.button :submit, "Resend confirmation instructions" 17 | 18 | .cell.medium-5.medium-offset-1 19 | = render "users/shared/links" 20 | -------------------------------------------------------------------------------- /app/views/users/passwords/edit.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | h2 = title("Change password") 4 | 5 | .grid-x 6 | .cell.medium-5 7 | = simple_form_for resource, 8 | as: resource_name, 9 | url: password_path(resource_name), 10 | html: { method: :put } do |f| 11 | 12 | = f.input :reset_password_token, as: :hidden 13 | = f.full_error :reset_password_token 14 | 15 | .form-inputs 16 | = f.input :password, required: true 17 | = f.input :password_confirmation, required: true 18 | 19 | .form-actions 20 | = f.button :submit, "Update password" 21 | 22 | .cell.medium-5.medium-offset-1 23 | = render "users/shared/links" 24 | -------------------------------------------------------------------------------- /app/views/users/passwords/new.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | h2 = title("Forgot your password?") 4 | 5 | .grid-x 6 | .cell.medium-5 7 | = simple_form_for resource, 8 | as: resource_name, 9 | url: password_path(resource_name), 10 | html: { method: :post } do |f| 11 | 12 | .form-inputs 13 | = f.input :email, required: true 14 | .form-actions 15 | = f.button :submit, "Send me reset password instructions" 16 | 17 | .cell.medium-5.medium-offset-1 18 | = render "users/shared/links" 19 | -------------------------------------------------------------------------------- /app/views/users/registrations/edit.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | h2 = title("Edit #{current_user.full_name}") 4 | 5 | = simple_form_for resource, 6 | as: resource_name, 7 | url: registration_path(resource_name), 8 | html: { method: :put } do |f| 9 | .grid-x 10 | .cell.medium-5 11 | .form-inputs 12 | = f.input :full_name, required: true, autofocus: true 13 | = f.input :email, required: true 14 | = f.input :password, 15 | autocomplete: "off", 16 | required: false 17 | = f.input :password_confirmation, 18 | required: false 19 | .form-actions 20 | = f.button :submit, "Update" 21 | 22 | .cell.medium-5.medium-offset-1 23 | .form-inputs 24 | - if resource.avatar 25 | = image_tag resource.avatar_url 26 | 27 | = f.input :remove_avatar, as: :boolean 28 | 29 | = f.hidden_field :avatar, value: resource.cached_avatar_data 30 | = f.input :avatar 31 | 32 | h6 33 | b Cancel my account 34 | p 35 | | Unhappy? 36 | ' 37 | = link_to "Cancel my account.", 38 | registration_path(resource_name), 39 | data: { confirm: "Are you sure?" }, 40 | method: :delete 41 | -------------------------------------------------------------------------------- /app/views/users/registrations/new.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | h2 = title("Sign up") 4 | 5 | .grid-x 6 | .cell.medium-5 7 | = simple_form_for resource, 8 | as: resource_name, 9 | url: registration_path(resource_name) do |f| 10 | 11 | legend 12 | | Create your 13 | ' 14 | strong Rails Base example site 15 | ' 16 | | account 17 | 18 | .form-inputs 19 | = f.input :full_name, required: true, autofocus: true 20 | = f.input :email, required: true 21 | = f.input :password, required: true 22 | = f.input :password_confirmation, required: true 23 | 24 | .form-actions 25 | = f.button :submit, "Sign up" 26 | 27 | .cell.medium-5.medium-offset-1 28 | = render "users/shared/links" 29 | -------------------------------------------------------------------------------- /app/views/users/sessions/new.html.slim: -------------------------------------------------------------------------------- 1 | .grid-x 2 | .cell 3 | h2 = title("Sign in") 4 | 5 | .grid-x 6 | .cell.medium-5 7 | = simple_form_for resource, 8 | as: resource_name, 9 | url: session_path(resource_name) do |f| 10 | 11 | legend 12 | | Enter your 13 | ' 14 | strong Rails Base example site 15 | ' 16 | | account 17 | 18 | .form-inputs 19 | = f.input :email, required: false, autofocus: true 20 | = f.input :password, required: false 21 | = f.input :remember_me, as: :boolean 22 | 23 | .form-actions 24 | = f.button :submit, "Sign in" 25 | 26 | .cell.medium-5.medium-offset-1 27 | = render "users/shared/links" 28 | -------------------------------------------------------------------------------- /app/views/users/shared/_links.html.slim: -------------------------------------------------------------------------------- 1 | h6 Anything else? 2 | ul.no-bullet 3 | - if controller_name != "sessions" 4 | li = link_to "Sign in", new_session_path(resource_name) 5 | 6 | - if devise_mapping.registerable? && controller_name != "registrations" 7 | li = link_to "Create a new account", new_registration_path(resource_name) 8 | 9 | - if devise_mapping.recoverable? && controller_name != "passwords" 10 | li = link_to "Forgot your password?", new_password_path(resource_name) 11 | 12 | - if devise_mapping.confirmable? && controller_name != "confirmations" 13 | li = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) 14 | 15 | - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != "unlocks" 16 | li = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) 17 | 18 | - if devise_mapping.omniauthable? 19 | - resource_class.omniauth_providers.each do |provider| 20 | li = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) 21 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | var validEnv = ['development', 'test', 'production'] 3 | var currentEnv = api.env() 4 | var isDevelopmentEnv = api.env('development') 5 | var isProductionEnv = api.env('production') 6 | var isTestEnv = api.env('test') 7 | 8 | if (!validEnv.includes(currentEnv)) { 9 | throw new Error( 10 | 'Please specify a valid `NODE_ENV` or ' + 11 | '`BABEL_ENV` environment variables. Valid values are "development", ' + 12 | '"test", and "production". Instead, received: ' + 13 | JSON.stringify(currentEnv) + 14 | '.' 15 | ) 16 | } 17 | 18 | return { 19 | presets: [ 20 | isTestEnv && [ 21 | '@babel/preset-env', 22 | { 23 | targets: { 24 | node: 'current' 25 | }, 26 | modules: 'commonjs' 27 | }, 28 | ], 29 | (isProductionEnv || isDevelopmentEnv) && [ 30 | '@babel/preset-env', 31 | { 32 | forceAllTransforms: true, 33 | useBuiltIns: 'entry', 34 | corejs: 3, 35 | modules: false, 36 | exclude: ['transform-typeof-symbol'] 37 | } 38 | ] 39 | ].filter(Boolean), 40 | plugins: [ 41 | 'babel-plugin-macros', 42 | '@babel/plugin-syntax-dynamic-import', 43 | isTestEnv && 'babel-plugin-dynamic-import-node', 44 | '@babel/plugin-transform-destructuring', 45 | [ 46 | '@babel/plugin-proposal-class-properties', 47 | { 48 | loose: true 49 | } 50 | ], 51 | [ 52 | '@babel/plugin-proposal-object-rest-spread', 53 | { 54 | useBuiltIns: true 55 | } 56 | ], 57 | [ 58 | '@babel/plugin-transform-runtime', 59 | { 60 | helpers: false, 61 | regenerator: true, 62 | corejs: false 63 | } 64 | ], 65 | [ 66 | '@babel/plugin-transform-regenerator', 67 | { 68 | async: false 69 | } 70 | ], 71 | isProductionEnv && [ 72 | 'remove-import-export', 73 | { 74 | removeImport: true 75 | } 76 | ] 77 | ].filter(Boolean) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /bin/autospec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'autospec' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rspec-core', 'autospec') 17 | -------------------------------------------------------------------------------- /bin/brakeman: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'brakeman' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('brakeman', 'brakeman') 17 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 3 | load Gem.bin_path("bundler", "bundle") 4 | -------------------------------------------------------------------------------- /bin/bundle-audit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'bundle-audit' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('bundler-audit', 'bundle-audit') 17 | -------------------------------------------------------------------------------- /bin/guard: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | # 4 | # This file was generated by Bundler. 5 | # 6 | # The application 'guard' is installed as part of a gem, and 7 | # this file is here to facilitate running it. 8 | # 9 | 10 | require "pathname" 11 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 12 | Pathname.new(__FILE__).realpath) 13 | 14 | require "rubygems" 15 | require "bundler/setup" 16 | 17 | load Gem.bin_path("guard", "guard") 18 | -------------------------------------------------------------------------------- /bin/quality: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | bin/rubocop --display-style-guide 6 | bin/brakeman --quiet --skip-libs --no-pager --exit-on-warn 7 | 8 | bin/scss-lint 9 | bin/slim-lint app/views 10 | 11 | bin/bundle-audit update 12 | bin/bundle-audit 13 | 14 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?("spring") 6 | end 7 | APP_PATH = File.expand_path("../config/application", __dir__) 8 | require_relative "../config/boot" 9 | require "rails/commands" 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?("spring") 6 | end 7 | require_relative "../config/boot" 8 | require "rake" 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?("spring") 6 | end 7 | 8 | # frozen_string_literal: true 9 | # 10 | # This file was generated by Bundler. 11 | # 12 | # The application "rspec" is installed as part of a gem, and 13 | # this file is here to facilitate running it. 14 | # 15 | 16 | require "pathname" 17 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 18 | Pathname.new(__FILE__).realpath) 19 | 20 | require "rubygems" 21 | require "bundler/setup" 22 | 23 | load Gem.bin_path("rspec-core", "rspec") 24 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rubocop' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rubocop', 'rubocop') 17 | -------------------------------------------------------------------------------- /bin/scss-lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'scss-lint' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('scss_lint', 'scss-lint') 17 | -------------------------------------------------------------------------------- /bin/server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | bin/rails server --port 5000 --binding lvh.me 4 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | # Setup dependencies if running macOS and brew 6 | if [ `uname -s` = "Darwin" ] && [ -x "$(command -v brew)" ]; then 7 | brew bundle check --no-upgrade || brew bundle --no-upgrade 8 | fi 9 | 10 | # Setup specific Bundler options if this is CI 11 | if [ "$CI" ]; then 12 | BUNDLER_ARGS="--without development staging production" 13 | fi 14 | 15 | # Make sure we have Bundler installed 16 | gem install bundler --conservative 17 | 18 | # Set up Ruby dependencies via Bundler into .bundle folder 19 | rm -f .bundle/config 20 | 21 | bundle check --path .bundle > /dev/null 2>&1 || 22 | bundle install --path=.bundle $BUNDLER_ARGS 23 | 24 | # Install yarn globally in current Nodejs installation 25 | npm install -g yarn 26 | 27 | # Set up node.js modules 28 | bin/yarn install 29 | 30 | # Set up configurable environment variables 31 | if [ ! -f .env ]; then 32 | cp .env.example .env 33 | fi 34 | 35 | # Set up database and add any development seed data 36 | bin/rails db:setup 37 | 38 | # Clean log files and tmp directory 39 | bin/rails log:clear tmp:clear 40 | -------------------------------------------------------------------------------- /bin/slim-lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'slim-lint' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('slim_lint', 'slim-lint') 17 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require "rubygems" 8 | require "bundler" 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem "spring", spring.version 15 | require "spring/binstub" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | bin/quality 6 | 7 | bin/rspec spec 8 | yarn test --passWithNoTests 9 | -------------------------------------------------------------------------------- /bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/webpack_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::WebpackRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/dev_server_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::DevServerRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path("../config/environment", __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "active_record/railtie" 4 | require "action_controller/railtie" 5 | require "action_view/railtie" 6 | require "action_mailer/railtie" 7 | require "active_job/railtie" 8 | require "sprockets/railtie" 9 | 10 | # Require the gems listed in Gemfile, including any gems 11 | # you've limited to :test, :development, or :production. 12 | Bundler.require(*Rails.groups) 13 | 14 | module RailsBase 15 | class Application < Rails::Application 16 | # Initialize configuration defaults for originally generated Rails version. 17 | config.load_defaults "6.0" 18 | 19 | # Settings in config/environments/* take precedence over those specified here. 20 | # Application configuration should go into files in config/initializers 21 | # -- all .rb files in that directory are automatically loaded. 22 | 23 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 24 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 25 | # config.time_zone = 'Central Time (US & Canada)' 26 | 27 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 28 | config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")] 29 | # config.i18n.default_locale = :de 30 | 31 | # Enable deflate / gzip compression of controller-generated responses 32 | config.middleware.use Rack::Deflater 33 | 34 | # Set default From address for all Mailers 35 | config.action_mailer.default_options = { from: ENV.fetch("MAILER_SENDER_ADDRESS") } 36 | 37 | # Set URL options to be able to use url_for helpers 38 | config.action_mailer.default_url_options = { host: ENV.fetch("HOST"), port: ENV.fetch("PORT") } 39 | 40 | # Add HOST to allowed list 41 | config.hosts << ENV.fetch("HOST") 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /config/autoprefixer.yml: -------------------------------------------------------------------------------- 1 | browsers: 2 | - "last 2 versions" 3 | - "> 2%" 4 | -------------------------------------------------------------------------------- /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 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: rails_base_production 11 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | adapter: postgresql 3 | encoding: unicode 4 | min_messages: warning 5 | timeout: 5000 6 | pool: <%= [ENV.fetch("MAX_THREADS", 5).to_i, ENV.fetch("DB_POOL", 5).to_i].max %> 7 | 8 | development: 9 | database: <%= ENV.fetch("DATABASE_NAME", File.basename(Rails.root)) %>_dev 10 | <<: *defaults 11 | 12 | test: 13 | database: <%= ENV.fetch("DATABASE_NAME", File.basename(Rails.root)) %>_test 14 | <<: *defaults 15 | 16 | production: &deploy 17 | <<: *defaults 18 | 19 | staging: *deploy 20 | -------------------------------------------------------------------------------- /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 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 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/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join("tmp", "caching-dev.txt").exist? 18 | config.action_controller.perform_caching = true 19 | config.action_controller.enable_fragment_cache_logging = true 20 | config.cache_store = :memory_store 21 | config.public_file_server.headers = { 22 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 23 | } 24 | else 25 | config.action_controller.perform_caching = false 26 | 27 | config.cache_store = :null_store 28 | end 29 | 30 | # Don't care if the mailer can't send. 31 | config.action_mailer.raise_delivery_errors = false 32 | 33 | config.action_mailer.perform_caching = false 34 | 35 | # Print deprecation notices to the Rails logger. 36 | config.active_support.deprecation = :log 37 | 38 | # Raise an error on page load if there are pending migrations. 39 | config.active_record.migration_error = :page_load 40 | 41 | # Highlight code that triggered database queries in logs. 42 | config.active_record.verbose_query_logs = true 43 | 44 | # Debug mode disables concatenation and preprocessing of assets. 45 | # This option may cause significant delays in view rendering with a large 46 | # number of complex assets. 47 | config.assets.debug = true 48 | 49 | # Suppress logger output for asset requests. 50 | config.assets.quiet = true 51 | 52 | # Raises error for missing translations 53 | # config.action_view.raise_on_missing_translations = true 54 | 55 | # Use an evented file watcher to asynchronously detect changes in source code, 56 | # routes, locales, etc. This feature depends on the listen gem. 57 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 58 | 59 | # Preview email in the browser instead of sending it. 60 | config.action_mailer.delivery_method = :letter_opener 61 | end 62 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? 24 | 25 | # Compress CSS using a preprocessor. 26 | # config.assets.css_compressor = :sass 27 | 28 | # Do not fallback to assets pipeline if a precompiled asset is missed. 29 | config.assets.compile = false 30 | 31 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 32 | # config.action_controller.asset_host = 'http://assets.example.com' 33 | 34 | # Specifies the header that your server uses for sending files. 35 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 37 | 38 | # Use the lowest log level to ensure availability of diagnostic information 39 | # when problems arise. 40 | config.log_level = :debug 41 | 42 | # Prepend all log lines with the following tags. 43 | config.log_tags = [:request_id] 44 | 45 | # Use a different cache store in production. 46 | # config.cache_store = :mem_cache_store 47 | 48 | # Use a real queuing backend for Active Job (and separate queues per environment). 49 | # config.active_job.queue_adapter = :resque 50 | # config.active_job.queue_name_prefix = "rails_base_production" 51 | 52 | config.action_mailer.perform_caching = false 53 | 54 | # Ignore bad email addresses and do not raise email delivery errors. 55 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 56 | # config.action_mailer.raise_delivery_errors = false 57 | 58 | # Enable Email delivery via custom SMTP server or via SendGrid by default 59 | if ENV["SMTP_USERNAME"] || ENV["SENDGRID_USERNAME"] 60 | config.action_mailer.delivery_method = :smtp 61 | 62 | config.action_mailer.smtp_settings = { 63 | authentication: :plain, 64 | enable_starttls_auto: true, 65 | openssl_verify_mode: ENV.fetch("SMTP_OPENSSL_VERIFY_MODE", nil), 66 | address: ENV.fetch("SMTP_ADDRESS", "smtp.sendgrid.net"), 67 | port: ENV.fetch("SMTP_PORT", 587), 68 | domain: ENV.fetch("SMTP_DOMAIN", "heroku.com"), 69 | user_name: ENV.fetch("SMTP_USERNAME") { ENV.fetch("SENDGRID_USERNAME") }, 70 | password: ENV.fetch("SMTP_PASSWORD") { ENV.fetch("SENDGRID_PASSWORD") } 71 | } 72 | end 73 | 74 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 75 | # the I18n.default_locale when a translation cannot be found). 76 | config.i18n.fallbacks = true 77 | 78 | # Send deprecation notices to registered listeners. 79 | config.active_support.deprecation = :notify 80 | 81 | # Use default logging formatter so that PID and timestamp are not suppressed. 82 | config.log_formatter = ::Logger::Formatter.new 83 | 84 | # Use a different logger for distributed setups. 85 | # require 'syslog/logger' 86 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 87 | 88 | if ENV["RAILS_LOG_TO_STDOUT"].present? 89 | logger = ActiveSupport::Logger.new(STDOUT) 90 | logger.formatter = config.log_formatter 91 | config.logger = ActiveSupport::TaggedLogging.new(logger) 92 | end 93 | 94 | # Do not dump schema after migrations. 95 | config.active_record.dump_schema_after_migration = false 96 | 97 | # Inserts middleware to perform automatic connection switching. 98 | # The `database_selector` hash is used to pass options to the DatabaseSelector 99 | # middleware. The `delay` is used to determine how long to wait after a write 100 | # to send a subsequent read to the primary. 101 | # 102 | # The `database_resolver` class is used by the middleware to determine which 103 | # database is appropriate to use based on the time delay. 104 | # 105 | # The `database_resolver_context` class is used by the middleware to set 106 | # timestamps for the last write to the primary. The resolver uses the context 107 | # class timestamps to determine how long to wait before reading from the 108 | # replica. 109 | # 110 | # By default Rails will store a last write timestamp in the session. The 111 | # DatabaseSelector middleware is designed as such you can define your own 112 | # strategy for connection switching and pass that into the middleware through 113 | # these configuration options. 114 | # config.active_record.database_selector = { delay: 2.seconds } 115 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver 116 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session 117 | end 118 | -------------------------------------------------------------------------------- /config/environments/staging.rb: -------------------------------------------------------------------------------- 1 | require_relative "production" 2 | 3 | Rails.application.configure do 4 | end 5 | -------------------------------------------------------------------------------- /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 | config.cache_classes = false 10 | config.action_view.cache_template_loading = true 11 | 12 | # Do not eager load code on boot. This avoids loading your whole application 13 | # just for the purpose of running a single test. If you are using a tool that 14 | # preloads Rails for running tests, you may have to set it to true. 15 | config.eager_load = false 16 | 17 | # Configure public file server for tests with Cache-Control for performance. 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = { 20 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 21 | } 22 | 23 | # Show full error reports and disable caching. 24 | config.consider_all_requests_local = true 25 | config.action_controller.perform_caching = false 26 | config.cache_store = :null_store 27 | 28 | # Raise exceptions instead of rendering exception templates. 29 | config.action_dispatch.show_exceptions = false 30 | 31 | # Disable request forgery protection in test environment. 32 | config.action_controller.allow_forgery_protection = false 33 | 34 | config.action_mailer.perform_caching = false 35 | 36 | # Tell Action Mailer not to deliver emails to the real world. 37 | # The :test delivery method accumulates sent emails in the 38 | # ActionMailer::Base.deliveries array. 39 | config.action_mailer.delivery_method = :test 40 | 41 | # Print deprecation notices to the stderr. 42 | config.active_support.deprecation = :stderr 43 | 44 | # Raises error for missing translations. 45 | # config.action_view.raise_on_missing_translations = true 46 | 47 | config.cache_classes = false 48 | end 49 | -------------------------------------------------------------------------------- /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 = ENV.fetch("ASSETS_VERSION", "1.0") 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join("node_modules") 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | Rails.application.config.assets.precompile += %w[mailer jasmine-jquery.js] 16 | 17 | # Unknown asset fallback will return the path passed in when the given 18 | # asset is not present in the asset pipeline. 19 | # Rails.application.config.assets.unknown_asset_fallback = false 20 | -------------------------------------------------------------------------------- /config/initializers/auth_basic.rb: -------------------------------------------------------------------------------- 1 | # Require authentication if password is set 2 | if ENV["AUTH_BASIC_PASS"] 3 | realm = ENV.fetch("AUTH_BASIC_REALM", Rails.application.class.to_s.deconstantize.titleize) 4 | Rails.application.config.middleware.use Rack::Auth::Basic, realm do |_, password| 5 | ENV["AUTH_BASIC_PASS"] == password 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/bullet.rb: -------------------------------------------------------------------------------- 1 | if defined?(Bullet) 2 | Rails.application.config.after_initialize do 3 | Bullet.enable = true 4 | Bullet.unused_eager_loading_enable = false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /config/initializers/canonical_host.rb: -------------------------------------------------------------------------------- 1 | # Ensure requests are only served from one, canonical host name 2 | Rails.application.config.middleware.use Rack::CanonicalHost, ENV["CANONICAL_HOST"] if ENV["CANONICAL_HOST"] 3 | -------------------------------------------------------------------------------- /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 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https 14 | # # If you are using webpack-dev-server then specify webpack-dev-server host 15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? 16 | 17 | # # Specify URI for violation reports 18 | # # policy.report_uri "/csp-violation-report-endpoint" 19 | # end 20 | 21 | # If you are using UJS then enable automatic nonce generation 22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 23 | 24 | # Set the nonce only to specific directives 25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 26 | 27 | # Report CSP violations to a specified URI 28 | # For further information see the following documentation: 29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 30 | # Rails.application.config.content_security_policy_report_only = true 31 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | if ENV["CORS_ORIGINS"].present? 9 | Rails.application.config.middleware.insert_before 0, Rack::Cors, debug: true, logger: (-> { Rails.logger }) do 10 | allow do 11 | origins(*ENV.fetch("CORS_ORIGINS").split(",")) 12 | 13 | resource "/assets/*", headers: :any, methods: :get 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # Use this hook to configure devise mailer, warden hooks and so forth. 2 | # Many of these configuration options can be set straight in your model. 3 | Devise.setup do |config| 4 | # The secret key used by Devise. Devise uses this key to generate 5 | # random tokens. Changing this key will render invalid all existing 6 | # confirmation, reset password and unlock tokens in the database. 7 | # config.secret_key = '' 8 | 9 | # ==> Mailer Configuration 10 | # Configure the e-mail address which will be shown in Devise::Mailer, 11 | # note that it will be overwritten if you use your own mailer class 12 | # with default "from" parameter. 13 | config.mailer_sender = ENV.fetch("MAILER_SENDER_ADDRESS") 14 | 15 | # Configure the class responsible to send e-mails. 16 | config.mailer = "DeviseMailer" 17 | 18 | # ==> ORM configuration 19 | # Load and configure the ORM. Supports :active_record (default) and 20 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 21 | # available as additional gems. 22 | require "devise/orm/active_record" 23 | 24 | # ==> Configuration for any authentication mechanism 25 | # Configure which keys are used when authenticating a user. The default is 26 | # just :email. You can configure it to use [:username, :subdomain], so for 27 | # authenticating a user, both parameters are required. Remember that those 28 | # parameters are used only when authenticating and not when retrieving from 29 | # session. If you need permissions, you should implement that in a before filter. 30 | # You can also supply a hash where the value is a boolean determining whether 31 | # or not authentication should be aborted when the value is not present. 32 | # config.authentication_keys = [ :email ] 33 | 34 | # Configure parameters from the request object used for authentication. Each entry 35 | # given should be a request method and it will automatically be passed to the 36 | # find_for_authentication method and considered in your model lookup. For instance, 37 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. 38 | # The same considerations mentioned for authentication_keys also apply to request_keys. 39 | # config.request_keys = [] 40 | 41 | # Configure which authentication keys should be case-insensitive. 42 | # These keys will be downcased upon creating or modifying a user and when used 43 | # to authenticate or find a user. Default is :email. 44 | config.case_insensitive_keys = [:email] 45 | 46 | # Configure which authentication keys should have whitespace stripped. 47 | # These keys will have whitespace before and after removed upon creating or 48 | # modifying a user and when used to authenticate or find a user. Default is :email. 49 | config.strip_whitespace_keys = [:email] 50 | 51 | # Tell if authentication through request.params is enabled. True by default. 52 | # It can be set to an array that will enable params authentication only for the 53 | # given strategies, for example, `config.params_authenticatable = [:database]` will 54 | # enable it only for database (email + password) authentication. 55 | # config.params_authenticatable = true 56 | 57 | # Tell if authentication through HTTP Auth is enabled. False by default. 58 | # It can be set to an array that will enable http authentication only for the 59 | # given strategies, for example, `config.http_authenticatable = [:database]` will 60 | # enable it only for database authentication. The supported strategies are: 61 | # :database = Support basic authentication with authentication key + password 62 | # config.http_authenticatable = false 63 | 64 | # If http headers should be returned for AJAX requests. True by default. 65 | # config.http_authenticatable_on_xhr = true 66 | 67 | # The realm used in Http Basic Authentication. 'Application' by default. 68 | # config.http_authentication_realm = 'Application' 69 | 70 | # It will change confirmation, password recovery and other workflows 71 | # to behave the same regardless if the e-mail provided was right or wrong. 72 | # Does not affect registerable. 73 | # config.paranoid = true 74 | 75 | # By default Devise will store the user in session. You can skip storage for 76 | # particular strategies by setting this option. 77 | # Notice that if you are skipping storage for all authentication paths, you 78 | # may want to disable generating routes to Devise's sessions controller by 79 | # passing skip: :sessions to `devise_for` in your config/routes.rb 80 | config.skip_session_storage = [:http_auth] 81 | 82 | # By default, Devise cleans up the CSRF token on authentication to 83 | # avoid CSRF token fixation attacks. This means that, when using AJAX 84 | # requests for sign in and sign up, you need to get a new CSRF token 85 | # from the server. You can disable this option at your own risk. 86 | # config.clean_up_csrf_token_on_authentication = true 87 | 88 | # ==> Configuration for :database_authenticatable 89 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If 90 | # using other encryptors, it sets how many times you want the password re-encrypted. 91 | # 92 | # Limiting the stretches to just one in testing will increase the performance of 93 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use 94 | # a value less than 10 in other environments. Note that, for bcrypt (the default 95 | # encryptor), the cost increases exponentially with the number of stretches (e.g. 96 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). 97 | config.stretches = Rails.env.test? ? 1 : 10 98 | 99 | # Setup a pepper to generate the encrypted password. 100 | # config.pepper = '' 101 | 102 | # ==> Configuration for :confirmable 103 | # A period that the user is allowed to access the website even without 104 | # confirming their account. For instance, if set to 2.days, the user will be 105 | # able to access the website for two days without confirming their account, 106 | # access will be blocked just in the third day. Default is 0.days, meaning 107 | # the user cannot access the website without confirming their account. 108 | config.allow_unconfirmed_access_for = 2.days 109 | 110 | # A period that the user is allowed to confirm their account before their 111 | # token becomes invalid. For example, if set to 3.days, the user can confirm 112 | # their account within 3 days after the mail was sent, but on the fourth day 113 | # their account can't be confirmed with the token any more. 114 | # Default is nil, meaning there is no restriction on how long a user can take 115 | # before confirming their account. 116 | # config.confirm_within = 3.days 117 | 118 | # If true, requires any email changes to be confirmed (exactly the same way as 119 | # initial account confirmation) to be applied. Requires additional unconfirmed_email 120 | # db field (see migrations). Until confirmed, new email is stored in 121 | # unconfirmed_email column, and copied to email column on successful confirmation. 122 | config.reconfirmable = true 123 | 124 | # Defines which key will be used when confirming an account 125 | # config.confirmation_keys = [ :email ] 126 | 127 | # ==> Configuration for :rememberable 128 | # The time the user will be remembered without asking for credentials again. 129 | # config.remember_for = 2.weeks 130 | 131 | # If true, extends the user's remember period when remembered via cookie. 132 | # config.extend_remember_period = false 133 | 134 | # Options to be passed to the created cookie. For instance, you can set 135 | # secure: true in order to force SSL only cookies. 136 | # config.rememberable_options = {} 137 | 138 | # ==> Configuration for :validatable 139 | # Range for password length. 140 | config.password_length = 6..128 141 | 142 | # Email regex used to validate email formats. It simply asserts that 143 | # one (and only one) @ exists in the given string. This is mainly 144 | # to give user feedback and not to assert the e-mail validity. 145 | # config.email_regexp = /\A[^@]+@[^@]+\z/ 146 | 147 | # ==> Configuration for :timeoutable 148 | # The time you want to timeout the user session without activity. After this 149 | # time the user will be asked for credentials again. Default is 30 minutes. 150 | # config.timeout_in = 30.minutes 151 | 152 | # If true, expires auth token on session timeout. 153 | # config.expire_auth_token_on_timeout = false 154 | 155 | # ==> Configuration for :lockable 156 | # Defines which strategy will be used to lock an account. 157 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 158 | # :none = No lock strategy. You should handle locking by yourself. 159 | config.lock_strategy = :failed_attempts 160 | 161 | # Defines which key will be used when locking and unlocking an account 162 | config.unlock_keys = [:email] 163 | 164 | # Defines which strategy will be used to unlock an account. 165 | # :email = Sends an unlock link to the user email 166 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 167 | # :both = Enables both strategies 168 | # :none = No unlock strategy. You should handle unlocking by yourself. 169 | config.unlock_strategy = :both 170 | 171 | # Number of authentication tries before locking an account if lock_strategy 172 | # is failed attempts. 173 | config.maximum_attempts = ENV.fetch("MAX_SIGNIN_ATTEMPTS", "5").to_i 174 | 175 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 176 | config.unlock_in = 1.hour 177 | 178 | # Warn on the last attempt before the account is locked. 179 | config.last_attempt_warning = true 180 | 181 | # ==> Configuration for :recoverable 182 | # 183 | # Defines which key will be used when recovering the password for an account 184 | # config.reset_password_keys = [ :email ] 185 | 186 | # Time interval you can reset your password with a reset password key. 187 | # Don't put a too small interval or your users won't have the time to 188 | # change their passwords. 189 | config.reset_password_within = 6.hours 190 | 191 | # ==> Configuration for :encryptable 192 | # Allow you to use another encryption algorithm besides bcrypt (default). You can use 193 | # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, 194 | # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) 195 | # and :restful_authentication_sha1 (then you should set stretches to 10, and copy 196 | # REST_AUTH_SITE_KEY to pepper). 197 | # 198 | # Require the `devise-encryptable` gem when using anything other than bcrypt 199 | # config.encryptor = :sha512 200 | 201 | # ==> Scopes configuration 202 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 203 | # "users/sessions/new". It's turned off by default because it's slower if you 204 | # are using only default views. 205 | config.scoped_views = true 206 | 207 | # Configure the default scope given to Warden. By default it's the first 208 | # devise role declared in your routes (usually :user). 209 | # config.default_scope = :user 210 | 211 | # Set this configuration to false if you want /users/sign_out to sign out 212 | # only the current scope. By default, Devise signs out all scopes. 213 | # config.sign_out_all_scopes = true 214 | 215 | # ==> Navigation configuration 216 | # Lists the formats that should be treated as navigational. Formats like 217 | # :html, should redirect to the sign in page when the user does not have 218 | # access, but formats like :xml or :json, should return 401. 219 | # 220 | # If you have any extra navigational formats, like :iphone or :mobile, you 221 | # should add them to the navigational formats lists. 222 | # 223 | # The "*/*" below is required to match Internet Explorer requests. 224 | # config.navigational_formats = ['*/*', :html] 225 | 226 | # The default HTTP method used to sign out a resource. Default is :delete. 227 | config.sign_out_via = :get 228 | 229 | # ==> OmniAuth 230 | # Add a new OmniAuth provider. Check the wiki for more information on setting 231 | # up on your models and hooks. 232 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' 233 | 234 | # ==> Warden configuration 235 | # If you want to use other strategies, that are not supported by Devise, or 236 | # change the failure app, you can configure them inside the config.warden block. 237 | # 238 | # config.warden do |manager| 239 | # manager.intercept_401 = false 240 | # manager.default_strategies(scope: :user).unshift :some_external_strategy 241 | # end 242 | 243 | # ==> Mountable engine configurations 244 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine 245 | # is mountable, there are some extra configurations to be taken into account. 246 | # The following options are available, assuming the engine is mounted as: 247 | # 248 | # mount MyEngine, at: '/my_engine' 249 | # 250 | # The router that invoked `devise_for`, in the example above, would be: 251 | # config.router_name = :my_engine 252 | # 253 | # When using omniauth, Devise cannot automatically set Omniauth path, 254 | # so you need to do it manually. For the users scope, it would be: 255 | # config.omniauth_path_prefix = '/my_engine/users/auth' 256 | end 257 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | 6 | # Filter out from log files some sensitive locations your application is redirecting to. 7 | Rails.application.config.filter_redirect << "s3.amazonaws.com" 8 | -------------------------------------------------------------------------------- /config/initializers/generators.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.app_generators do |g| 2 | g.stylesheets false 3 | g.helper false 4 | g.integration_tool false 5 | g.fixture_replacement(:factory_bot, dir: "spec/factories") 6 | g.test_framework( 7 | :rspec, 8 | controller_specs: false, 9 | helper_specs: false, 10 | routing_specs: false, 11 | view_specs: false 12 | ) 13 | end 14 | 15 | # Make `form_with` generate non-remote forms. 16 | Rails.application.config.action_view.form_with_generates_remote_forms = false 17 | 18 | # Make `form_with` generate id attributes for any generated HTML tags. 19 | Rails.application.config.action_view.form_with_generates_ids = true 20 | -------------------------------------------------------------------------------- /config/initializers/google_analytics.rb: -------------------------------------------------------------------------------- 1 | GA.tracker = ENV["GA_TRACKER"] 2 | -------------------------------------------------------------------------------- /config/initializers/health_check.rb: -------------------------------------------------------------------------------- 1 | HealthCheck.setup do |config| 2 | # Text output upon success 3 | config.success = "success" 4 | 5 | # Timeout in seconds used when checking smtp server 6 | config.smtp_timeout = 30.0 7 | 8 | # http status code used when plain text error message is output 9 | # Set to 200 if you want your want to distinguish between partial (text does not include success) and 10 | # total failure of rails application (http status of 500 etc) 11 | 12 | config.http_status_for_error_text = 500 13 | 14 | # http status code used when an error object is output (json or xml) 15 | # Set to 200 if you want your want to distinguish between partial (healthy property == false) and 16 | # total failure of rails application (http status of 500 etc) 17 | 18 | config.http_status_for_error_object = 500 19 | 20 | # You can customize which checks happen on a standard health check 21 | config.standard_checks = %w[database migrations site] 22 | 23 | # You can set what tests are run with the 'full' or 'all' parameter 24 | config.full_checks = %w[database migrations site email cache] 25 | end 26 | -------------------------------------------------------------------------------- /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/meta_tags.rb: -------------------------------------------------------------------------------- 1 | MetaTags.configure do |c| 2 | c.title_limit = 70 3 | c.description_limit = 160 4 | c.keywords_limit = 255 5 | c.keywords_separator = ", " 6 | end 7 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/mini_profiler.rb: -------------------------------------------------------------------------------- 1 | require "rack-mini-profiler" 2 | Rack::MiniProfilerRails.initialize!(Rails.application) 3 | 4 | # We prefer MemoryStore 5 | Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore 6 | 7 | # Disable MiniProfiler by default, it could be enabled by query string "pp=enable" 8 | Rack::MiniProfiler.config.enabled = Rails.env.development? || Rails.env.staging? 9 | 10 | # Configure authorization hook based on IP whitelist 11 | Rack::MiniProfiler.config.authorization_mode = :allow_all 12 | Rack::MiniProfiler.config.pre_authorize_cb = lambda { |env| 13 | IpWhitelistConstraint.new.matches?(Rack::Request.new(env)) 14 | } 15 | -------------------------------------------------------------------------------- /config/initializers/premailer_rails.rb: -------------------------------------------------------------------------------- 1 | Premailer::Rails.config[:remove_ids] = true 2 | -------------------------------------------------------------------------------- /config/initializers/rollbar.rb: -------------------------------------------------------------------------------- 1 | require "rollbar/rails" 2 | 3 | Rollbar.configure do |config| 4 | config.access_token = ENV["ROLLBAR_ACCESS_TOKEN"] 5 | 6 | # Without configuration, Rollbar is enabled by in all environments. 7 | # To disable in specific environments, set config.enabled=false. 8 | # Here we'll disable in 'test': 9 | config.enabled = !(Rails.env.test? || Rails.env.development?) 10 | 11 | # By default, Rollbar will try to call the `current_user` controller method 12 | # to fetch the logged-in user object, and then call that object's `id`, 13 | # `username`, and `email` methods to fetch those properties. To customize: 14 | # config.person_method = "my_current_user" 15 | # config.person_id_method = "my_id" 16 | # config.person_username_method = "my_username" 17 | # config.person_email_method = "my_email" 18 | 19 | # If you want to attach custom data to all exception and message reports, 20 | # provide a lambda like the following. It should return a hash. 21 | # config.custom_data_method = lambda { {:some_key => "some_value" } } 22 | 23 | # Add exception class names to the exception_level_filters hash to 24 | # change the level that exception is reported at. Note that if an exception 25 | # has already been reported and logged the level will need to be changed 26 | # via the rollbar interface. 27 | # Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore' 28 | # 'ignore' will cause the exception to not be reported at all. 29 | # config.exception_level_filters.merge!('MyCriticalException' => 'critical') 30 | 31 | # Enable asynchronous reporting (uses girl_friday or Threading if girl_friday 32 | # is not installed) 33 | # config.use_async = true 34 | # Supply your own async handler: 35 | # config.async_handler = Proc.new { |payload| 36 | # Thread.new { Rollbar.process_payload(payload) } 37 | # } 38 | end 39 | -------------------------------------------------------------------------------- /config/initializers/secure_headers.rb: -------------------------------------------------------------------------------- 1 | # Default settings 2 | # For more details see: https://github.com/github/secure_headers#configuration 3 | 4 | SecureHeaders::Configuration.default do |config| 5 | config.csp = { 6 | default_src: %w('self'), 7 | script_src: %w('self' 'unsafe-inline'), 8 | style_src: %w('self') 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: "_rails_base_session" 4 | -------------------------------------------------------------------------------- /config/initializers/shrine.rb: -------------------------------------------------------------------------------- 1 | require "shrine" 2 | require "shrine/storage/file_system" 3 | require "shrine/storage/s3" 4 | require "shrine/storage/memory" 5 | 6 | Shrine.plugin :activerecord 7 | Shrine.plugin :cached_attachment_data 8 | Shrine.plugin :determine_mime_type 9 | Shrine.plugin :remove_attachment 10 | Shrine.plugin :restore_cached_data 11 | Shrine.plugin :validation 12 | Shrine.plugin :validation_helpers 13 | 14 | def s3_options 15 | { 16 | access_key_id: ENV["S3_ACCESS_KEY_ID"], 17 | secret_access_key: ENV["S3_SECRET_ACCESS_KEY"], 18 | region: ENV["S3_BUCKET_REGION"], 19 | bucket: ENV["S3_BUCKET_NAME"] 20 | } 21 | end 22 | 23 | def amazon_s3_storages 24 | { 25 | cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options), 26 | store: Shrine::Storage::S3.new(prefix: "store", upload_options: { acl: "public-read" }, **s3_options) 27 | } 28 | end 29 | 30 | def local_storages 31 | if Rails.env.test? 32 | { 33 | cache: Shrine::Storage::Memory.new, 34 | store: Shrine::Storage::Memory.new 35 | } 36 | else 37 | { 38 | cache: Shrine::Storage::FileSystem.new("public", prefix: "/uploads/cache"), 39 | store: Shrine::Storage::FileSystem.new("public", prefix: "/uploads") 40 | } 41 | end 42 | end 43 | 44 | Shrine.storages = s3_options.values.all? ? amazon_s3_storages : local_storages 45 | -------------------------------------------------------------------------------- /config/initializers/simple_form.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in SimpleForm. 2 | SimpleForm.setup do |config| 3 | # Wrappers are used by the form builder to generate a 4 | # complete input. You can remove any component from the 5 | # wrapper, change the order or even add your own to the 6 | # stack. The options given below are used to wrap the 7 | # whole input. 8 | config.wrappers( 9 | :default, 10 | class: :input, 11 | hint_class: :field_with_hint, 12 | error_class: :field_with_errors 13 | ) do |b| 14 | ## Extensions enabled by default 15 | # Any of these extensions can be disabled for a 16 | # given input by passing: `f.input EXTENSION_NAME => false`. 17 | # You can make any of these extensions optional by 18 | # renaming `b.use` to `b.optional`. 19 | 20 | # Determines whether to use HTML5 (:email, :url, ...) 21 | # and required attributes 22 | b.use :html5 23 | 24 | # Calculates placeholders automatically from I18n 25 | # You can also pass a string as f.input placeholder: "Placeholder" 26 | b.use :placeholder 27 | 28 | ## Optional extensions 29 | # They are disabled unless you pass `f.input EXTENSION_NAME => :lookup` 30 | # to the input. If so, they will retrieve the values from the model 31 | # if any exists. If you want to enable the lookup for any of those 32 | # extensions by default, you can change `b.optional` to `b.use`. 33 | 34 | # Calculates maxlength from length validations for string inputs 35 | b.optional :maxlength 36 | 37 | # Calculates pattern from format validations for string inputs 38 | b.optional :pattern 39 | 40 | # Calculates min and max from length validations for numeric inputs 41 | b.optional :min_max 42 | 43 | # Calculates readonly automatically from readonly attributes 44 | b.optional :readonly 45 | 46 | ## Inputs 47 | b.use :label_input 48 | b.use :hint, wrap_with: { tag: :span, class: :hint } 49 | b.use :error, wrap_with: { tag: :span, class: :error } 50 | end 51 | 52 | config.wrappers :foundation, class: :input, hint_class: "has-hint", error_class: :error do |b| 53 | b.use :html5 54 | b.use :placeholder 55 | b.use :label 56 | 57 | b.optional :maxlength 58 | b.optional :pattern 59 | b.optional :min_max 60 | b.optional :readonly 61 | 62 | b.use :input 63 | b.use :error, wrap_with: { tag: :small, class: "error is-visible" } 64 | 65 | # Uncomment the following line to enable hints. The line is commented out by default since Foundation 66 | # does't provide styles for hints. You will need to provide your own CSS styles for hints. 67 | b.use :hint, wrap_with: { tag: :p, class: "help-text" } 68 | end 69 | 70 | config.wrappers :with_labels, class: :row, hint_class: "has-hint", error_class: :error do |b| 71 | b.use :html5 72 | b.use :placeholder 73 | 74 | b.optional :maxlength 75 | b.optional :pattern 76 | b.optional :min_max 77 | b.optional :readonly 78 | 79 | b.use :label, wrap_with: { class: "large-4 medium-4 column" } 80 | b.use :input, wrap_with: { class: "large-8 medium-8 column" } 81 | 82 | b.wrapper tag: "div", class: "large-8 medium-8 column" do |error| 83 | error.use :error, wrap_with: { tag: :small, class: "error" } 84 | end 85 | end 86 | 87 | config.wrappers :right_label do |b| 88 | b.use :input 89 | b.use :label 90 | end 91 | 92 | # Wrappers for forms and inputs using the Twitter Bootstrap toolkit. 93 | # Check the Bootstrap docs (http://twitter.github.com/bootstrap) 94 | # to learn about the different styles for forms and inputs, 95 | # buttons and other elements. 96 | config.default_wrapper = :foundation 97 | 98 | # Define the way to render check boxes / radio buttons with labels. 99 | # Defaults to :nested for bootstrap config. 100 | # inline: input + label 101 | # nested: label > input 102 | config.boolean_style = :inline 103 | 104 | # Default class for buttons 105 | config.button_class = "button" 106 | 107 | # Method used to tidy up errors. Specify any Rails Array method. 108 | # :first lists the first message for each field. 109 | # Use :to_sentence to list all errors for each field. 110 | # config.error_method = :first 111 | 112 | # Default tag used for error notification helper. 113 | config.error_notification_tag = :div 114 | 115 | # CSS class to add for error notification helper. 116 | config.error_notification_class = "alert-box alert" 117 | 118 | # ID to add for error notification helper. 119 | # config.error_notification_id = nil 120 | 121 | # Series of attempts to detect a default label method for collection. 122 | # config.collection_label_methods = [ :to_label, :name, :title, :to_s ] 123 | 124 | # Series of attempts to detect a default value method for collection. 125 | # config.collection_value_methods = [ :id, :to_s ] 126 | 127 | # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none. 128 | # config.collection_wrapper_tag = nil 129 | 130 | # You can define the class to use on all collection wrappers. Defaulting to none. 131 | # config.collection_wrapper_class = nil 132 | 133 | # You can wrap each item in a collection of radio/check boxes with a tag, 134 | # defaulting to :span. Please note that when using :boolean_style = :nested, 135 | # SimpleForm will force this option to be a label. 136 | # config.item_wrapper_tag = :div 137 | 138 | # You can define a class to use in all item wrappers. Defaulting to none. 139 | # config.item_wrapper_class = 'row' 140 | 141 | # How the label text should be generated altogether with the required text. 142 | # config.label_text = lambda { |label, required| "#{required} #{label}" } 143 | 144 | # You can define the class to use on all labels. Default is nil. 145 | # config.label_class = 'column large-4' 146 | 147 | # You can define the class to use on all forms. Default is simple_form. 148 | config.default_form_class = "simple-form" 149 | 150 | # You can define which elements should obtain additional classes 151 | # config.generate_additional_classes_for = [:wrapper, :label, :input] 152 | 153 | # Whether attributes are required by default (or not). Default is true. 154 | # config.required_by_default = true 155 | 156 | # Tell browsers whether to use default HTML5 validations (novalidate option). 157 | # Default is enabled. 158 | config.browser_validations = false 159 | 160 | # Collection of methods to detect if a file type was given. 161 | # config.file_methods = [ :mounted_as, :file?, :public_filename ] 162 | 163 | # Custom mappings for input types. This should be a hash containing a regexp 164 | # to match as key, and the input type that will be used when the field name 165 | # matches the regexp as value. 166 | # config.input_mappings = { /count/ => :integer } 167 | config.wrapper_mappings = { boolean: :right_label } 168 | 169 | # Default priority for time_zone inputs. 170 | # config.time_zone_priority = nil 171 | 172 | # Default priority for country inputs. 173 | # config.country_priority = nil 174 | 175 | # Default size for text inputs. 176 | # config.default_input_size = 50 177 | 178 | # When false, do not use translations for labels. 179 | # config.translate_labels = true 180 | 181 | # Automatically discover new inputs in Rails' autoload path. 182 | # config.inputs_discovery = true 183 | 184 | # Cache SimpleForm inputs discovery 185 | # config.cache_discovery = !Rails.env.development? 186 | end 187 | -------------------------------------------------------------------------------- /config/initializers/slim.rb: -------------------------------------------------------------------------------- 1 | Slim::Engine.set_options(pretty: ENV.fetch("SLIM_PRETTY", false)) 2 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /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/locales/flash.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | flash: 3 | devise: 4 | passwords: 5 | create: 6 | alert: "Password could not be sent." 7 | update: 8 | alert: "Password could not be updated." 9 | confirmations: 10 | create: 11 | alert: "Confirmations could not be sent" 12 | actions: 13 | create: 14 | notice: '%{resource_name} was successfully created.' 15 | alert: '%{resource_name} could not be created.' 16 | update: 17 | notice: '%{resource_name} was successfully updated.' 18 | alert: '%{resource_name} could not be updated.' 19 | destroy: 20 | notice: '%{resource_name} was successfully destroyed.' 21 | alert: '%{resource_name} could not be destroyed.' 22 | -------------------------------------------------------------------------------- /config/locales/models/user.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | active_record: 3 | models: 4 | user: 5 | attributes: 6 | user: 7 | full_name: 8 | simple_form: 9 | labels: 10 | user: 11 | new: 12 | email: Enter your email address 13 | edit: 14 | password: Enter new password 15 | password_confirmation: Confirm your new password 16 | hints: 17 | user: 18 | edit: 19 | password: Leave it blank if you dont want to change it 20 | current_password: We need your current password to confirm your changes 21 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | 'yes': Yes 4 | 'no': No 5 | required: 6 | text: required 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: 'Please review the problems below:' 13 | -------------------------------------------------------------------------------- /config/locales/time.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | time: 3 | formats: 4 | short_date: '%x' 5 | long_date: '%a, %b %d, %Y' 6 | us: '%m/%d/%Y %I:%M %p' 7 | us_date: '%m/%d/%Y' 8 | us_time: '%I:%M %p' 9 | -------------------------------------------------------------------------------- /config/newrelic.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This file configures the New Relic Agent. New Relic monitors 3 | # Ruby, Java, .NET, PHP, and Python applications with deep visibility and low overhead. 4 | # For more information, visit www.newrelic.com. 5 | # 6 | # Generated June 03, 2013 7 | # 8 | # This configuration file is custom generated for Barsoom 9 | 10 | 11 | # Here are the settings that are common to all environments 12 | common: &default_settings 13 | # ============================== LICENSE KEY =============================== 14 | 15 | # You must specify the license key associated with your New Relic 16 | # account. This key binds your Agent's data to your account in the 17 | # New Relic service. 18 | license_key: <%= ENV["NEW_RELIC_LICENSE_KEY"] %> 19 | 20 | # Agent Enabled (Ruby/Rails Only) 21 | # Use this setting to force the agent to run or not run. 22 | # Default is 'auto' which means the agent will install and run only 23 | # if a valid dispatcher such as Mongrel is running. This prevents 24 | # it from running with Rake or the console. Set to false to 25 | # completely turn the agent off regardless of the other settings. 26 | # Valid values are true, false and auto. 27 | # 28 | # agent_enabled: auto 29 | 30 | # Application Name Set this to be the name of your application as 31 | # you'd like it show up in New Relic. The service will then auto-map 32 | # instances of your application into an "application" on your 33 | # dashboard page. If you want to map this instance into multiple 34 | # apps, like "AJAX Requests" and "All UI" then specify a semicolon 35 | # separated list of up to three distinct names, or a yaml list. 36 | # Defaults to the capitalized RAILS_ENV or RACK_ENV (i.e., 37 | # Production, Staging, etc) 38 | # 39 | # Example: 40 | # 41 | # app_name: 42 | # - Ajax Service 43 | # - All Services 44 | # 45 | app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> 46 | 47 | # When "true", the agent collects performance data about your 48 | # application and reports this data to the New Relic service at 49 | # newrelic.com. This global switch is normally overridden for each 50 | # environment below. (formerly called 'enabled') 51 | monitor_mode: true 52 | 53 | # Developer mode should be off in every environment but 54 | # development as it has very high overhead in memory. 55 | developer_mode: false 56 | 57 | # The newrelic agent generates its own log file to keep its logging 58 | # information separate from that of your application. Specify its 59 | # log level here. 60 | log_level: info 61 | 62 | # Optionally set the path to the log file This is expanded from the 63 | # root directory (may be relative or absolute, e.g. 'log/' or 64 | # '/var/log/') The agent will attempt to create this directory if it 65 | # does not exist. 66 | # log_file_path: 'log' 67 | 68 | # Optionally set the name of the log file, defaults to 'newrelic_agent.log' 69 | # log_file_name: 'newrelic_agent.log' 70 | 71 | # The newrelic agent communicates with the service via https by default. This 72 | # prevents eavesdropping on the performance metrics transmitted by the agent. 73 | # The encryption required by SSL introduces a nominal amount of CPU overhead, 74 | # which is performed asynchronously in a background thread. If you'd prefer 75 | # to send your metrics over http uncomment the following line. 76 | # ssl: false 77 | 78 | #============================== Browser Monitoring =============================== 79 | # New Relic Real User Monitoring gives you insight into the performance real users are 80 | # experiencing with your website. This is accomplished by measuring the time it takes for 81 | # your users' browsers to download and render your web pages by injecting a small amount 82 | # of JavaScript code into the header and footer of each page. 83 | browser_monitoring: 84 | # By default the agent automatically injects the monitoring JavaScript 85 | # into web pages. Set this attribute to false to turn off this behavior. 86 | auto_instrument: true 87 | 88 | # Proxy settings for connecting to the New Relic server. 89 | # 90 | # If a proxy is used, the host setting is required. Other settings 91 | # are optional. Default port is 8080. 92 | # 93 | # proxy_host: hostname 94 | # proxy_port: 8080 95 | # proxy_user: 96 | # proxy_pass: 97 | 98 | # The agent can optionally log all data it sends to New Relic servers to a 99 | # separate log file for human inspection and auditing purposes. To enable this 100 | # feature, change 'enabled' below to true. 101 | # See: https://newrelic.com/docs/ruby/audit-log 102 | audit_log: 103 | enabled: false 104 | 105 | # Tells transaction tracer and error collector (when enabled) 106 | # whether or not to capture HTTP params. When true, frameworks can 107 | # exclude HTTP parameters from being captured. 108 | # Rails: the RoR filter_parameter_logging excludes parameters 109 | # Java: create a config setting called "ignored_params" and set it to 110 | # a comma separated list of HTTP parameter names. 111 | # ex: ignored_params: credit_card, ssn, password 112 | capture_params: false 113 | 114 | # Transaction tracer captures deep information about slow 115 | # transactions and sends this to the New Relic service once a 116 | # minute. Included in the transaction is the exact call sequence of 117 | # the transactions including any SQL statements issued. 118 | transaction_tracer: 119 | 120 | # Transaction tracer is enabled by default. Set this to false to 121 | # turn it off. This feature is only available at the Professional 122 | # and above product levels. 123 | enabled: true 124 | 125 | # Threshold in seconds for when to collect a transaction 126 | # trace. When the response time of a controller action exceeds 127 | # this threshold, a transaction trace will be recorded and sent to 128 | # New Relic. Valid values are any float value, or (default) "apdex_f", 129 | # which will use the threshold for an dissatisfying Apdex 130 | # controller action - four times the Apdex T value. 131 | transaction_threshold: apdex_f 132 | 133 | # When transaction tracer is on, SQL statements can optionally be 134 | # recorded. The recorder has three modes, "off" which sends no 135 | # SQL, "raw" which sends the SQL statement in its original form, 136 | # and "obfuscated", which strips out numeric and string literals. 137 | record_sql: obfuscated 138 | 139 | # Threshold in seconds for when to collect stack trace for a SQL 140 | # call. In other words, when SQL statements exceed this threshold, 141 | # then capture and send to New Relic the current stack trace. This is 142 | # helpful for pinpointing where long SQL calls originate from. 143 | stack_trace_threshold: 0.500 144 | 145 | # Determines whether the agent will capture query plans for slow 146 | # SQL queries. Only supported in mysql and postgres. Should be 147 | # set to false when using other adapters. 148 | # explain_enabled: true 149 | 150 | # Threshold for query execution time below which query plans will 151 | # not be captured. Relevant only when `explain_enabled` is true. 152 | # explain_threshold: 0.5 153 | 154 | # Error collector captures information about uncaught exceptions and 155 | # sends them to New Relic for viewing 156 | error_collector: 157 | 158 | # Error collector is enabled by default. Set this to false to turn 159 | # it off. This feature is only available at the Professional and above 160 | # product levels. 161 | enabled: true 162 | 163 | # Rails Only - tells error collector whether or not to capture a 164 | # source snippet around the place of the error when errors are View 165 | # related. 166 | capture_source: true 167 | 168 | # To stop specific errors from reporting to New Relic, set this property 169 | # to comma-separated values. Default is to ignore routing errors, 170 | # which are how 404's get triggered. 171 | ignore_errors: "ActionController::RoutingError,Sinatra::NotFound" 172 | 173 | # If you're interested in capturing memcache keys as though they 174 | # were SQL uncomment this flag. Note that this does increase 175 | # overhead slightly on every memcached call, and can have security 176 | # implications if your memcached keys are sensitive 177 | # capture_memcache_keys: true 178 | 179 | # Application Environments 180 | # ------------------------------------------ 181 | # Environment-specific settings are in this section. 182 | # For Rails applications, RAILS_ENV is used to determine the environment. 183 | # For Java applications, pass -Dnewrelic.environment to set 184 | # the environment. 185 | 186 | # NOTE if your application has other named environments, you should 187 | # provide newrelic configuration settings for these environments here. 188 | 189 | development: 190 | <<: *default_settings 191 | # Turn off communication to New Relic service in development mode (also 192 | # 'enabled'). 193 | # NOTE: for initial evaluation purposes, you may want to temporarily 194 | # turn the agent on in development mode. 195 | monitor_mode: false 196 | 197 | # Rails Only - when running in Developer Mode, the New Relic Agent will 198 | # present performance information on the last 100 transactions you have 199 | # executed since starting the mongrel. 200 | # NOTE: There is substantial overhead when running in developer mode. 201 | # Do not use for production or load testing. 202 | developer_mode: true 203 | 204 | # Enable textmate links 205 | # textmate: true 206 | 207 | test: 208 | <<: *default_settings 209 | # It almost never makes sense to turn on the agent when running 210 | # unit, functional or integration tests or the like. 211 | monitor_mode: false 212 | 213 | # Turn on the agent in production for 24x7 monitoring. NewRelic 214 | # testing shows an average performance impact of < 5 ms per 215 | # transaction, you can leave this on all the time without 216 | # incurring any user-visible performance degradation. 217 | production: 218 | <<: *default_settings 219 | monitor_mode: true 220 | 221 | # Many applications have a staging environment which behaves 222 | # identically to production. Support for that environment is provided 223 | # here. By default, the staging environment has the agent turned on. 224 | staging: 225 | <<: *default_settings 226 | monitor_mode: true 227 | app_name: <%= ENV["NEW_RELIC_APP_NAME"] %> (Staging) 228 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers ENV.fetch("WEB_CONCURRENCY", 2).to_i 2 | threads_count = ENV.fetch("MAX_THREADS", 5).to_i 3 | 4 | threads threads_count, threads_count 5 | 6 | rackup DefaultRackup 7 | port ENV.fetch("PORT", 5000) 8 | environment ENV.fetch("RACK_ENV", "development") 9 | 10 | preload_app! 11 | 12 | on_worker_boot do 13 | ActiveRecord::Base.establish_connection 14 | end 15 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | devise_for :users, controllers: { registrations: "users/registrations" } 3 | root to: "pages#home" 4 | end 5 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | secret_key_base: <%= ENV['SECRET_KEY_BASE'] %> 3 | 4 | development: 5 | <<: *default 6 | 7 | test: 8 | <<: *default 9 | 10 | staging: 11 | <<: *default 12 | 13 | production: 14 | <<: *default 15 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /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 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 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 23 | 24 | # Use 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 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /config/webpack/development.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker') 2 | 3 | const webpack = require('webpack') 4 | 5 | environment.plugins.prepend('Provide', 6 | new webpack.ProvidePlugin({ 7 | $: 'jquery/src/jquery', 8 | jQuery: 'jquery/src/jquery' 9 | }) 10 | ) 11 | 12 | module.exports = environment 13 | -------------------------------------------------------------------------------- /config/webpack/production.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpack/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development' 2 | 3 | const environment = require('./environment') 4 | 5 | module.exports = environment.toWebpackConfig() 6 | -------------------------------------------------------------------------------- /config/webpacker.yml: -------------------------------------------------------------------------------- 1 | # Note: You must restart bin/webpack-dev-server for changes to take effect 2 | 3 | default: &default 4 | source_path: app/javascript 5 | source_entry_path: packs 6 | public_root_path: public 7 | public_output_path: packs 8 | cache_path: tmp/cache/webpacker 9 | webpack_compile_output: true 10 | 11 | # Additional paths webpack should lookup modules 12 | # ['app/assets', 'engine/foo/app/assets'] 13 | resolved_paths: [] 14 | 15 | # Reload manifest.json on all requests so we reload latest compiled packs 16 | cache_manifest: false 17 | 18 | # Extract and emit a css file 19 | extract_css: false 20 | 21 | static_assets_extensions: 22 | - .jpg 23 | - .jpeg 24 | - .png 25 | - .gif 26 | - .tiff 27 | - .ico 28 | - .svg 29 | - .eot 30 | - .otf 31 | - .ttf 32 | - .woff 33 | - .woff2 34 | 35 | extensions: 36 | - .jsx 37 | - .mjs 38 | - .js 39 | - .sass 40 | - .scss 41 | - .css 42 | - .module.sass 43 | - .module.scss 44 | - .module.css 45 | - .png 46 | - .svg 47 | - .gif 48 | - .jpeg 49 | - .jpg 50 | 51 | development: 52 | <<: *default 53 | compile: true 54 | 55 | # Reference: https://webpack.js.org/configuration/dev-server/ 56 | dev_server: 57 | https: false 58 | host: localhost 59 | port: 3035 60 | public: localhost:3035 61 | hmr: false 62 | # Inline should be set to true if using HMR 63 | inline: true 64 | overlay: true 65 | compress: true 66 | disable_host_check: true 67 | use_local_ip: false 68 | quiet: false 69 | pretty: false 70 | headers: 71 | 'Access-Control-Allow-Origin': '*' 72 | watch_options: 73 | ignored: '**/node_modules/**' 74 | 75 | 76 | test: 77 | <<: *default 78 | compile: true 79 | 80 | # Compile test packs to a separate directory 81 | public_output_path: packs-test 82 | 83 | production: 84 | <<: *default 85 | 86 | # Production depends on precompilation of packs prior to booting for performance. 87 | compile: false 88 | 89 | # Extract and emit a css file 90 | extract_css: true 91 | 92 | # Cache manifest.json for performance 93 | cache_manifest: true 94 | -------------------------------------------------------------------------------- /db/migrate/20100713113845_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.1] 2 | def self.up 3 | create_table(:users) do |t| 4 | # Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | # Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | # Rememberable 13 | t.datetime :remember_created_at 14 | 15 | # Confirmable 16 | t.string :confirmation_token 17 | t.datetime :confirmed_at 18 | t.datetime :confirmation_sent_at 19 | t.string :unconfirmed_email # Only if using reconfirmable 20 | 21 | # Trackable 22 | t.integer :sign_in_count, default: 0 23 | t.datetime :current_sign_in_at 24 | t.datetime :last_sign_in_at 25 | t.string :current_sign_in_ip 26 | t.string :last_sign_in_ip 27 | 28 | # Lockable 29 | t.integer :failed_attempts, default: 0, null: false 30 | t.string :users, :unlock_token 31 | t.datetime :locked_at 32 | 33 | t.timestamps 34 | t.string :full_name 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | add_index :users, :unlock_token, unique: true 40 | end 41 | 42 | def self.down 43 | drop_table :users 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /db/migrate/20200610083210_add_avatar_data_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAvatarDataToUsers < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :users, :avatar_data, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 2020_06_10_083210) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "users", force: :cascade do |t| 19 | t.string "email", default: "", null: false 20 | t.string "encrypted_password", default: "", null: false 21 | t.string "reset_password_token" 22 | t.datetime "reset_password_sent_at" 23 | t.datetime "remember_created_at" 24 | t.string "confirmation_token" 25 | t.datetime "confirmed_at" 26 | t.datetime "confirmation_sent_at" 27 | t.string "unconfirmed_email" 28 | t.integer "sign_in_count", default: 0 29 | t.datetime "current_sign_in_at" 30 | t.datetime "last_sign_in_at" 31 | t.string "current_sign_in_ip" 32 | t.string "last_sign_in_ip" 33 | t.integer "failed_attempts", default: 0, null: false 34 | t.string "users" 35 | t.string "unlock_token" 36 | t.datetime "locked_at" 37 | t.datetime "created_at", null: false 38 | t.datetime "updated_at", null: false 39 | t.string "full_name" 40 | t.text "avatar_data" 41 | t.index ["email"], name: "index_users_on_email", unique: true 42 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 43 | t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/db/seeds.rb -------------------------------------------------------------------------------- /db/seeds/development/all.seeds.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.create(:user, email: "user@example.com") 2 | -------------------------------------------------------------------------------- /doc/README_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Project name 2 | 3 | Third-party service badges (if available) 4 | 5 | [![Build Status](https://flatstack.semaphoreci.com/badges/rails-base.svg?key=1e8f53ac-b0b4-4dc7-bf8b-8027aa5a609e)](https://flatstack.semaphoreci.com/projects/rails-base) 6 | 7 | ## Project description 8 | 9 | Some short project description. Link to Basecamp project will be fine too. 10 | 11 | ## Dependencies 12 | 13 | * PostgreSQL 14 | * Ruby >= 2.7.1 15 | * Rails >= 6.0.3.1 16 | 17 | Setup required dependencies from `Brewfile`: 18 | ```bash 19 | brew tap Homebrew/bundle 20 | brew bundle 21 | ``` 22 | 23 | ## Quick Start 24 | 25 | ```bash 26 | # clone repo 27 | git clone git@github.com:account/repo.git 28 | cd repo 29 | 30 | # run setup script 31 | bin/setup 32 | 33 | # configure ENV variables in .env 34 | vim .env 35 | 36 | # run server on 5000 port 37 | bin/server 38 | ``` 39 | 40 | ## Scripts 41 | 42 | * `bin/setup` - setup required gems and migrate db if needed 43 | * `bin/quality` - run brakeman and rails_best_practices for the app 44 | * `bin/ci` - should be used in the CI to run specs 45 | 46 | ## Staging 47 | 48 | * http://fs-rails-base.herokuapp.com 49 | 50 | ## Production 51 | 52 | * http://fs-rails-base.herokuapp.com 53 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/assets.rake: -------------------------------------------------------------------------------- 1 | # Extend the default assets:precompile by gzipping all assets once the default 2 | # Sprockets behavior has completed. This is needed because Sprockets 2.x does 3 | # not gzip static assets like SVGs, and Sprockets 3 no longer does gzipping at 4 | # all! 5 | 6 | namespace :assets do 7 | desc "Create .gz versions of assets" 8 | task gzip: :environment do 9 | Dir[Rails.root.join("public", "assets", "**", "*.{css,html,js,otf,svg,txt,xml}")].each do |file| 10 | mtime = File.mtime(file) 11 | gz_file = "#{file}.gz" 12 | 13 | next if File.exist?(gz_file) && File.mtime(gz_file) >= mtime 14 | 15 | Zlib::GzipWriter.open(gz_file, Zlib::BEST_COMPRESSION) do |gz| 16 | IO.copy_stream(File.open(file), gz) 17 | end 18 | 19 | File.utime(mtime, mtime, gz_file) 20 | end 21 | end 22 | 23 | # Hook into existing assets:precompile task 24 | Rake::Task["assets:precompile"].enhance do 25 | Rake::Task["assets:gzip"].invoke 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 4 | "@rails/ujs": "^6.0.3-1", 5 | "@rails/webpacker": "5.1.1", 6 | "babel-plugin-remove-import-export": "^1.1.1", 7 | "jquery": "^3.5.1" 8 | }, 9 | "devDependencies": { 10 | "babel-eslint": "^10.1.0", 11 | "babel-jest": "^26.1.0", 12 | "babel-preset-es2015": "^6.24.1", 13 | "eslint": "^7.4.0", 14 | "eslint-config-airbnb": "^18.2.0", 15 | "eslint-config-import": "^0.13.0", 16 | "eslint-config-prettier": "^6.11.0", 17 | "eslint-plugin-import": "^2.22.0", 18 | "jest": "^26.1.0", 19 | "webpack-dev-server": "^3.11.0" 20 | }, 21 | "scripts": { 22 | "test": "jest", 23 | "lint": "eslint --ext .js,.jsx app/javascript" 24 | }, 25 | "jest": { 26 | "roots": [ 27 | "spec/javascript" 28 | ], 29 | "moduleDirectories": [ 30 | "node_modules", 31 | "app/javascript" 32 | ], 33 | "setupFilesAfterEnv": [ 34 | "./spec/javascript/setupTests.js" 35 | ] 36 | }, 37 | "license": "MIT" 38 | } 39 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-import'), 4 | require('postcss-flexbugs-fixes'), 5 | require('postcss-preset-env')({ 6 | autoprefixer: { 7 | flexbox: 'no-2009' 8 | }, 9 | stage: 3 10 | }) 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

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

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

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

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |

We've been notified about this issue and we'll take a look at it shortly.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/factories/sequences.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | sequence(:email) { Faker::Internet.email } 3 | sequence(:title) { |n| "#{Faker::Lorem.words} #{n}" } 4 | end 5 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | email 4 | full_name { Faker::Name.name } 5 | password { "123456" } 6 | password_confirmation { password } 7 | confirmed_at { 1.hour.ago } 8 | end 9 | 10 | trait :not_confirmed do 11 | confirmed_at { nil } 12 | 13 | after(:create) do |user| 14 | user.update(confirmation_sent_at: 3.days.ago) 15 | end 16 | end 17 | 18 | trait :with_avatar do 19 | avatar { File.open(Rails.root.join("spec", "support", "fixtures", "avatar.jpg")) } 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/features/user/account/cancel_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Cancel Account" do 4 | include_context "when current user signed in" 5 | 6 | background do 7 | visit edit_user_registration_path(current_user) 8 | end 9 | 10 | scenario "User cancels account" do 11 | click_link "Cancel my account" 12 | 13 | expect(page).to have_content("Sign in") 14 | expect(page).to have_content("Bye! Your account has been successfully cancelled. We hope to see you again soon.") 15 | 16 | click_link "Sign in" 17 | fill_form(:user, current_user.attributes.slice(:email, :password)) 18 | click_button "Sign in" 19 | 20 | expect(page).to have_content("Invalid Email or password.") 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/features/user/account/update_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Update Account" do 4 | include_context "when current user signed in" 5 | 6 | background do 7 | visit edit_user_registration_path(current_user) 8 | end 9 | 10 | scenario "User updates account with valid data" do 11 | fill_form(:user, :edit, full_name: "New Name") 12 | click_on "Update" 13 | 14 | expect(page).to have_content("New Name") 15 | end 16 | 17 | scenario "User enters not matched passwords" do 18 | fill_form(:user, :edit, password: "qwerty", password_confirmation: "123123") 19 | click_on "Update" 20 | 21 | expect(page).to have_content("doesn't match Password") 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/features/user/avatar/update_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Update Avatar" do 4 | include_context "when current user signed in" 5 | 6 | scenario "User updates it's avatar" do 7 | visit edit_user_registration_path(current_user) 8 | 9 | attach_file :user_avatar, Rails.root.join("spec", "support", "fixtures", "avatar.jpg") 10 | click_on "Update" 11 | 12 | expect(current_user.reload.avatar).to be_an_instance_of(AvatarUploader::UploadedFile) 13 | end 14 | 15 | scenario "User removes it's avatar" do 16 | current_user.avatar = File.open(Rails.root.join("spec", "support", "fixtures", "avatar.jpg")) 17 | current_user.save 18 | 19 | visit edit_user_registration_path(current_user) 20 | page.check "Remove avatar" 21 | click_on "Update" 22 | 23 | expect(current_user.reload.avatar).to be_nil 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/features/user/sign_out_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Sign Out" do 4 | include_context "when current user signed in" 5 | 6 | scenario "User signs out" do 7 | visit "/" 8 | 9 | find("a", text: current_user.full_name).click 10 | click_link "Sign out" 11 | 12 | expect(page).to have_content("Sign in") 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/features/visitor/password_reset_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Password Reset" do 4 | let(:new_password) { "qwe123" } 5 | let(:user) { create :user } 6 | 7 | def update_password 8 | fill_in "Enter new password", with: new_password 9 | fill_in "Confirm your new password", with: new_password 10 | click_button "Update password" 11 | end 12 | 13 | scenario "Visitor resets his password" do 14 | visit new_user_password_path 15 | 16 | fill_in "Enter your email address", with: user.email 17 | click_button "Send me reset password instructions" 18 | 19 | open_email(user.email) 20 | 21 | expect(current_email.subject).to eq("Reset password instructions") 22 | expect(current_email).to have_content(user.full_name) 23 | 24 | current_email.click_link("Change my password") 25 | update_password 26 | 27 | expect(page).to have_content("Your password has been changed successfully") 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/features/visitor/resend_confirmation_email_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Resend Confirmation Email" do 4 | let(:user) { create :user, :not_confirmed } 5 | 6 | scenario "Visitor resends email confirmation instructions" do 7 | visit new_user_confirmation_path 8 | 9 | fill_in "Enter your email address", with: user.email 10 | click_button "Resend confirmation instructions" 11 | 12 | open_email(user.email) 13 | 14 | expect(current_email.subject).to eq("Confirmation instructions") 15 | expect(current_email).to have_content(user.full_name) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/features/visitor/sign_in_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Sign In" do 4 | let(:user) { create :user } 5 | let(:unconfirmed_user) { create :user, :not_confirmed } 6 | 7 | def sign_in(email, password) 8 | visit new_user_session_path 9 | 10 | fill_form(:user, email: email, password: password) 11 | 12 | click_button "Sign in" 13 | end 14 | 15 | scenario "Visitor signs in with valid credentials" do 16 | sign_in(user.email, user.password) 17 | 18 | expect(page).to have_content(user.full_name) 19 | end 20 | 21 | scenario "Visitor signs in with invalid credentials" do 22 | sign_in(user.email, "wrong password") 23 | 24 | expect(page).to have_content("Sign in") 25 | expect(page).to have_content("Invalid Email or password") 26 | end 27 | 28 | scenario "Visitor signs in with unconfirmed email address" do 29 | sign_in(unconfirmed_user.email, user.password) 30 | 31 | expect(page).to have_content("You have to confirm your email address before continuing.") 32 | end 33 | 34 | scenario "Visitor signs in with invalid credentials many times" do 35 | Devise.maximum_attempts.times do 36 | sign_in(user.email, "wrong password") 37 | end 38 | 39 | expect(page).to have_content("Your account is locked.") 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/features/visitor/sign_up_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | feature "Sign Up" do 4 | let(:user_attributes) { attributes_for(:user).slice(:full_name, :email, :password, :password_confirmation) } 5 | let(:registered_user) { User.find_by(email: user_attributes[:email]) } 6 | 7 | scenario "Visitor signs up" do 8 | visit new_user_registration_path 9 | 10 | fill_form(:user, user_attributes) 11 | click_button "Sign up" 12 | 13 | open_email(registered_user.email) 14 | 15 | expect(current_email.subject).to eq("Confirmation instructions") 16 | expect(current_email).to have_content(registered_user.full_name) 17 | 18 | current_email.click_link("Confirm my account") 19 | 20 | expect(page).to have_content("Your email address has been successfully confirmed") 21 | expect(page).to have_text(registered_user.full_name) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/javascript/setupTests.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/spec/javascript/setupTests.js -------------------------------------------------------------------------------- /spec/mailers/previews/devise_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | class DeviseMailerPreview < ActionMailer::Preview 2 | def confirmation_instructions 3 | DeviseMailer.confirmation_instructions(User.first, "token", {}) 4 | end 5 | 6 | def reset_password_instructions 7 | DeviseMailer.reset_password_instructions(User.first, "token", {}) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe User do 4 | it { is_expected.to validate_presence_of :full_name } 5 | end 6 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= "test" 2 | 3 | require "spec_helper" 4 | require File.expand_path("../config/environment", __dir__) 5 | require "rspec/rails" 6 | require "shoulda/matchers" 7 | require "capybara/email/rspec" 8 | 9 | Dir[Rails.root.join("spec", "support", "**", "*.rb")].sort.each { |f| require f } 10 | 11 | RSpec.configure do |config| 12 | config.use_transactional_fixtures = false 13 | config.infer_spec_type_from_file_location! 14 | end 15 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.backtrace_exclusion_patterns << /\.bundle/ 3 | 4 | config.expect_with :rspec do |c| 5 | c.syntax = :expect 6 | end 7 | 8 | config.mock_with :rspec do |mocks| 9 | mocks.syntax = :expect 10 | mocks.verify_partial_doubles = true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/support/bullet.rb: -------------------------------------------------------------------------------- 1 | Bullet.bullet_logger = true 2 | Bullet.raise = true 3 | 4 | RSpec.configure do |config| 5 | config.before(:each) do 6 | Bullet.start_request 7 | end 8 | 9 | config.after(:each) do 10 | Bullet.perform_out_of_channel_notifications if Bullet.notification? 11 | Bullet.end_request 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/support/capybara.rb: -------------------------------------------------------------------------------- 1 | Capybara.configure do |config| 2 | config.match = :prefer_exact 3 | config.app_host = "http://#{ENV.fetch('HOST')}:#{ENV.fetch('PORT')}" 4 | config.asset_host = "http://#{ENV.fetch('HOST')}:#{ENV.fetch('PORT')}" 5 | end 6 | 7 | Capybara.javascript_driver = :selenium_chrome_headless 8 | 9 | Capybara.disable_animation = true 10 | -------------------------------------------------------------------------------- /spec/support/database_cleaner.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.before(:suite) do 3 | DatabaseCleaner.clean_with(:deletion) 4 | end 5 | 6 | config.before(:each) do 7 | DatabaseCleaner.strategy = :transaction 8 | end 9 | 10 | config.before(:each, js: true) do 11 | DatabaseCleaner.strategy = :deletion 12 | end 13 | 14 | config.before(:each) do 15 | DatabaseCleaner.start 16 | end 17 | 18 | config.after(:each) do 19 | DatabaseCleaner.clean 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include FactoryBot::Syntax::Methods 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/feature_helpers.rb: -------------------------------------------------------------------------------- 1 | # Extend this module in spec/support/features/*.rb 2 | module FeatureHelpers 3 | end 4 | 5 | RSpec.configure do |config| 6 | config.include FeatureHelpers, type: :feature 7 | end 8 | -------------------------------------------------------------------------------- /spec/support/features/shared_contexts/user_signed_in.rb: -------------------------------------------------------------------------------- 1 | shared_context "when current user signed in" do 2 | let(:current_user) { create :user } 3 | 4 | background do 5 | login_as current_user 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/support/fixtures/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/spec/support/fixtures/avatar.jpg -------------------------------------------------------------------------------- /spec/support/formulaic.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include Formulaic::Dsl, type: :feature 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/maintain_test_schema.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Migration.maintain_test_schema! if defined?(ActiveRecord::Migration) 2 | -------------------------------------------------------------------------------- /spec/support/matchers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/spec/support/matchers/.keep -------------------------------------------------------------------------------- /spec/support/shared_examples/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fs/rails-base/53437ea23fd026d524bef35939366f3417fbb126/spec/support/shared_examples/.keep -------------------------------------------------------------------------------- /spec/support/shoulda_matchers.rb: -------------------------------------------------------------------------------- 1 | Shoulda::Matchers.configure do |config| 2 | config.integrate do |with| 3 | with.test_framework :rspec 4 | with.library :rails 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/support/warden.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include Warden::Test::Helpers, type: :feature 3 | 4 | config.before :suite do 5 | Warden.test_mode! 6 | end 7 | 8 | config.after :each do 9 | Warden.test_reset! 10 | end 11 | end 12 | --------------------------------------------------------------------------------