├── .browserslistrc ├── .buildpacks ├── .env.example ├── .eslintignore ├── .eslintrc.yml ├── .flowconfig ├── .gitignore ├── .hound.yml ├── .nvmrc ├── .rspec ├── .rubocop.yml ├── .ruby-gemset ├── .ruby-version ├── .tool-versions ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── cable.js │ │ └── channels │ │ │ └── .keep │ └── stylesheets │ │ ├── application.scss │ │ ├── client.scss │ │ └── partials │ │ ├── _environment.scss │ │ └── _print.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── api │ │ └── jwts_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ ├── jwt_token.rb │ │ └── paging.rb │ ├── home_controller.rb │ ├── omniauth_callbacks_controller.rb │ ├── registrations_controller.rb │ └── sessions_controller.rb ├── helpers │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── lib │ ├── auth_token.rb │ └── url_helper.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── ability.rb │ ├── application_record.rb │ ├── authentication.rb │ ├── permission.rb │ ├── role.rb │ └── user.rb ├── services │ └── create_admin_service.rb └── views │ ├── devise │ └── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── home │ └── index.html.erb │ ├── layouts │ ├── _head.html.erb │ ├── _messages.html.erb │ ├── _navigation.html.erb │ ├── _show_environment.erb │ ├── application.html.erb │ └── client.html.erb │ ├── shared │ ├── _default_client_settings.html.erb │ ├── _links.html.erb │ └── _omniauth_error.html.erb │ └── users │ ├── _user.html.erb │ ├── index.html.erb │ └── show.html.erb ├── babel.config.js ├── bin ├── bootstrap ├── bundle ├── ci ├── deploy_heroku ├── rails ├── rake ├── setup ├── setup-linux ├── spring ├── update ├── webpack ├── webpack-dev-server └── yarn ├── client ├── apps │ ├── hello_world │ │ ├── app.jsx │ │ ├── assets │ │ │ └── images │ │ │ │ ├── atomicjolt.jpg │ │ │ │ ├── atomicjolt.png │ │ │ │ └── atomicjolt.svg │ │ ├── components │ │ │ ├── common │ │ │ │ └── not_found.jsx │ │ │ ├── home.jsx │ │ │ ├── home.spec.jsx │ │ │ └── layout │ │ │ │ ├── errors.jsx │ │ │ │ ├── index.jsx │ │ │ │ └── index.spec.jsx │ │ ├── html │ │ │ ├── 404.html │ │ │ ├── about.md │ │ │ ├── crossdomain.xml │ │ │ ├── humans.txt │ │ │ ├── index.html │ │ │ ├── layouts │ │ │ │ ├── application.html │ │ │ │ └── partials │ │ │ │ │ ├── _google_analytics.html │ │ │ │ │ ├── _head.html │ │ │ │ │ └── _header.html │ │ │ └── robots.txt │ │ ├── libs │ │ │ ├── __mocks__ │ │ │ │ └── assets.js │ │ │ ├── assets.js │ │ │ └── assets.spec.js │ │ ├── middleware │ │ │ └── api.js │ │ ├── reducers │ │ │ ├── application.js │ │ │ ├── application.spec.js │ │ │ └── index.js │ │ ├── static │ │ │ ├── .keep │ │ │ ├── android-icon-144x144.png │ │ │ ├── android-icon-192x192.png │ │ │ ├── android-icon-36x36.png │ │ │ ├── android-icon-48x48.png │ │ │ ├── android-icon-72x72.png │ │ │ ├── android-icon-96x96.png │ │ │ ├── apple-icon-114x114.png │ │ │ ├── apple-icon-120x120.png │ │ │ ├── apple-icon-144x144.png │ │ │ ├── apple-icon-152x152.png │ │ │ ├── apple-icon-180x180.png │ │ │ ├── apple-icon-57x57.png │ │ │ ├── apple-icon-60x60.png │ │ │ ├── apple-icon-72x72.png │ │ │ ├── apple-icon-76x76.png │ │ │ ├── apple-icon-precomposed.png │ │ │ ├── apple-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon-96x96.png │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ ├── ms-icon-144x144.png │ │ │ ├── ms-icon-150x150.png │ │ │ ├── ms-icon-310x310.png │ │ │ └── ms-icon-70x70.png │ │ ├── store │ │ │ └── configure_store.js │ │ └── styles │ │ │ ├── .csscomb.json │ │ │ ├── .csslintrc │ │ │ ├── _defines.scss │ │ │ ├── fonts │ │ │ ├── roboto-bold-webfont.woff │ │ │ ├── roboto-bold-webfont.woff2 │ │ │ ├── roboto-regular-webfont.woff │ │ │ └── roboto-regular-webfont.woff2 │ │ │ ├── modules │ │ │ └── _mixins.scss │ │ │ ├── styles.js │ │ │ ├── styles.less │ │ │ └── styles.scss │ └── hello_world_graphql │ │ ├── app.jsx │ │ ├── assets │ │ └── images │ │ │ ├── atomicjolt.jpg │ │ │ ├── atomicjolt.png │ │ │ └── atomicjolt.svg │ │ ├── components │ │ ├── common │ │ │ └── not_found.jsx │ │ ├── home.jsx │ │ ├── home.spec.jsx │ │ └── layout │ │ │ ├── index.jsx │ │ │ └── index.spec.jsx │ │ ├── history.js │ │ ├── html │ │ ├── 404.html │ │ ├── about.md │ │ ├── crossdomain.xml │ │ ├── humans.txt │ │ ├── index.html │ │ ├── layouts │ │ │ ├── application.html │ │ │ └── partials │ │ │ │ ├── _google_analytics.html │ │ │ │ ├── _head.html │ │ │ │ └── _header.html │ │ └── robots.txt │ │ ├── libs │ │ ├── __mocks__ │ │ │ └── assets.js │ │ ├── assets.js │ │ └── assets.spec.js │ │ ├── settings.js │ │ ├── static │ │ ├── .keep │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── manifest.json │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png │ │ └── styles │ │ ├── .csscomb.json │ │ ├── .csslintrc │ │ ├── _defines.scss │ │ ├── fonts │ │ ├── roboto-bold-webfont.woff │ │ ├── roboto-bold-webfont.woff2 │ │ ├── roboto-regular-webfont.woff │ │ └── roboto-regular-webfont.woff2 │ │ ├── modules │ │ └── _mixins.scss │ │ ├── styles.js │ │ ├── styles.less │ │ └── styles.scss ├── packs │ ├── hello_world.js │ └── hello_world_graphql.js └── testing │ ├── __mocks__ │ ├── file_mock.js │ └── style_mock.js │ ├── setup_tests.js │ └── shim.js ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── ci.database.yml ├── database.example.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── new_framework_defaults_5_1.rb │ ├── new_framework_defaults_5_2.rb │ ├── session_store.rb │ ├── webpack.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.example.yml ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db ├── migrate │ ├── 20120209004849_initial.rb │ ├── 20170612172246_add_context_id_to_permission.rb │ ├── 20170613231518_add_create_type_to_user.rb │ ├── 20170921202325_add_authentication_index.rb │ ├── 20171003155408_change_rr_integer_to_bigint.rb │ ├── 20181127204956_remove_unused_fields.rb │ └── 20190219201006_add_unique_index_to_permissions.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package.json ├── postcss.config.js ├── public ├── 401.html ├── 404.html ├── 422.html ├── 500.html ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── apple-icon-precomposed.png ├── apple-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── logo.png ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png ├── ms-icon-70x70.png ├── oauth_icon.png └── robots.txt ├── spec ├── controllers │ ├── api │ │ └── jwts_controller_spec.rb │ ├── concerns │ │ ├── jwt_token_spec.rb │ │ └── paging_spec.rb │ └── home_controller_spec.rb ├── factories │ ├── _common.rb │ ├── authentication.rb │ ├── permission.rb │ ├── role.rb │ └── users.rb ├── fixtures │ └── avatar.jpg ├── helpers │ └── application_helper_spec.rb ├── lib │ ├── auth_token_spec.rb │ └── url_helper_spec.rb ├── models │ ├── authentication_spec.rb │ ├── permission_spec.rb │ ├── role_spec.rb │ └── user_spec.rb ├── rails_helper.rb ├── spec_helper.rb └── support │ ├── capybara.rb │ ├── controller_macros.rb │ ├── http_party.rb │ ├── omniauth.rb │ └── webmock_requests.rb ├── template.rb ├── vendor └── assets │ ├── javascripts │ └── .keep │ └── stylesheets │ └── .keep └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/heroku/heroku-buildpack-nodejs.git#v86 2 | https://github.com/heroku/heroku-buildpack-ruby.git#v140 3 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_SUBDOMAIN=reactrailsstarterapp 2 | APP_ROOT_DOMAIN=reactrailsstarterapp.dev 3 | APP_PORT=3000 4 | ASSETS_SUBDOMAIN=assets 5 | ASSETS_PORT=3035 # This is the default port used by webpacker 6 | 7 | 8 | # ASSETS_URL is used for the webpack server's public path and will set an absolute url 9 | # when loading assets. Not setting a value means the assets will be loaded from the local 10 | # server. This works fine in development and will also work if you attempt to access your 11 | # development server from a remote device (such as a mobile phone) for testing. You can set 12 | # a localhost value if desired however note that doing so will prevent the assets from loading 13 | # properly when attempting to access the server from a remote device. 14 | # ASSETS_URL is mainly in place for situations where the react client starter app is incorporated 15 | # into another project such as Ruby on Rails where there will be multiple servers. In this situation 16 | # the client application will likely be loaded via a link in a page served from the Rail (or other similar) 17 | # server. Since the webpack server will be serving assets on a different port (or domain) we can't use 18 | # the default root path since doing so will result in the scripts requesting assets from the Rails 19 | # server instead of the webpack server. In this instance setting ASSETS_URL will tell webpack to 20 | # build and serve assets from a specific url - the webpack server's url. 21 | 22 | # Use localhost 23 | ASSETS_URL=http://0.0.0.0 24 | 25 | # Use a local domain. 26 | # Change /etc/hosts (on a mac) and add sites.dev and rcsassets.sites.dev. Each entry will need to point to 127.0.0.1 27 | # ASSETS_URL=http://rcsassets.sites.dev 28 | 29 | ASSETS_URL=https://assets.reactrailsstarterapp.dev -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # /node_modules/* and /bower_components/* ignored by default 2 | 3 | # Ignore built files except build/index.js 4 | build/* 5 | !build/index.js 6 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | env: 3 | browser: true 4 | es6: true 5 | extends: airbnb 6 | parser: babel-eslint 7 | plugins: 8 | - react 9 | - jsx-a11y 10 | - import 11 | globals: 12 | describe: false 13 | beforeAll: false 14 | beforeEach: false 15 | afterEach: false 16 | it: false 17 | expect: false 18 | xit: false 19 | spyOn: false 20 | jasmine: false 21 | module: false 22 | jest: false 23 | require: false 24 | rules: 25 | linebreak-style: 26 | - error 27 | - unix 28 | import/no-unresolved: 0 29 | no-multi-spaces: 30 | - 1 31 | - exceptions: 32 | VariableDeclarator: true 33 | ImportDeclaration: true 34 | AssignmentExpression: true 35 | comma-dangle: 36 | - error 37 | - only-multiline 38 | import/no-extraneous-dependencies: 39 | - error 40 | - devDependencies: true 41 | padded-blocks: 0 42 | key-spacing: 0 43 | react/require-default-props: 0 44 | react/no-danger: 0 45 | jsx-a11y/no-static-element-interactions: 0 46 | no-underscore-dangle: 0 47 | space-before-function-paren: 48 | - 2 49 | - never 50 | func-names: 51 | - 2 52 | - as-needed 53 | react/prefer-stateless-function: 0 54 | react/forbid-prop-types: 55 | - 2 56 | - forbid: 57 | - any 58 | import/extensions: 0 59 | react/prop-types: 0 60 | import/no-named-as-default: 0 61 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | .*/apps/.* 4 | [include] 5 | 6 | [libs] 7 | 8 | [lints] 9 | 10 | [options] 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | /vendor/bundle 10 | 11 | # Ignore the default SQLite database. 12 | /db/*.sqlite3 13 | /db/*.sqlite3-journal 14 | 15 | # Ignore all logfiles and tempfiles. 16 | /log/* 17 | /tmp/* 18 | !/log/.keep 19 | !/tmp/.keep 20 | 21 | # Ignore Byebug command history file. 22 | .byebug_history 23 | 24 | # Ignore mac files 25 | .DS_Store 26 | 27 | # TODO Comment out these rules if you are OK with secrets being uploaded to the repo 28 | .env 29 | config/secrets.yml 30 | config/database.yml 31 | config/assets/ 32 | bin/deploy 33 | s3_website.yml 34 | secrets.js 35 | 36 | # these should all be checked in to normalise the environment: 37 | # Gemfile.lock, .ruby-version, .ruby-gemset 38 | 39 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 40 | .rvmrc 41 | 42 | # if using bower-rails ignore default bower_components path bower.json files 43 | /vendor/assets/bower_components 44 | *.bowerrc 45 | bower.json 46 | 47 | # node artifacts 48 | client/node_modules 49 | node_modules 50 | npm-debug.log 51 | yarn-error.log 52 | 53 | # avoid adding built files 54 | /public/system 55 | public/assets 56 | /build 57 | 58 | # git artifacts 59 | **.orig 60 | 61 | # Test artifacts 62 | rerun.txt 63 | /coverage/ 64 | /spec/tmp 65 | 66 | /.idea/ 67 | /public/packs 68 | /public/packs-test 69 | /node_modules 70 | /yarn-error.log 71 | yarn-debug.log* 72 | .yarn-integrity 73 | .vscode -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | # reference: https://houndci.com/configuration 2 | 3 | fail_on_violations: true 4 | 5 | ruby: 6 | enabled: true 7 | config_file: .rubocop.yml 8 | 9 | eslint: 10 | enabled: true 11 | config_file: .eslintrc.yml 12 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.16.0 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | ltistarterapp 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.5.7 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | ruby 2.5.7 2 | nodejs 10.22.0 3 | postgres 9.5.19 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: required 3 | dist: trusty 4 | 5 | env: 6 | - CXX=g++-4.8 7 | 8 | addons: 9 | postgresql: "9.5" 10 | apt: 11 | sources: 12 | - ubuntu-toolchain-r-test 13 | packages: 14 | - g++-4.8 15 | 16 | cache: 17 | bundler: true 18 | directories: 19 | - node_modules 20 | - client/node_modules 21 | - client/apps/hello_world/node_modules 22 | 23 | branches: 24 | only: 25 | - master 26 | - staging 27 | - stable 28 | 29 | services: 30 | - postgresql 31 | 32 | before_install: 33 | - cp config/secrets.example.yml config/secrets.yml 34 | - cp config/ci.database.yml config/database.yml 35 | - cp .env.example .env 36 | - gem install bundler:1.17.3 37 | - nvm install 8.16.0 38 | - nvm use 8.16.0 39 | - npm install -g yarn 40 | 41 | install: 42 | - bundle install --path vendor/bundle 43 | - yarn 44 | 45 | before_script: 46 | - export CHROME_BIN=chromium-browser 47 | - "export DISPLAY=:99.0" 48 | - "sh -e /etc/init.d/xvfb start" 49 | - bundle exec rails db:create 50 | - bundle exec rails db:schema:load 51 | 52 | script: 53 | - ./bin/ci 54 | - yarn test 55 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # If bundler starts to act up run these commands to start over and clean up: 2 | # rm -rf ~/.bundle/ ~/.gem/; rm -rf $GEM_HOME/bundler/ $GEM_HOME/cache/bundler/; rm -rf .bundle/; rm -rf vendor/cache/; rm -rf Gemfile.lock 3 | # rvm gemset empty reactrailsstarterapp 4 | # bundle install 5 | 6 | source "https://rubygems.org" 7 | 8 | git_source(:github) do |repo_name| 9 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 10 | "https://github.com/#{repo_name}.git" 11 | end 12 | 13 | # Bundle edge Rails instead: gem "rails", github: "rails/rails" 14 | gem "rails", "~> 5.2.4" 15 | 16 | # Improve boot time 17 | gem "bootsnap", require: false 18 | 19 | # Database 20 | gem "pg" 21 | 22 | # authentication, authorization, integrations 23 | gem "attr_encrypted" 24 | gem "cancancan" 25 | gem "devise" 26 | gem "jwt", "~> 1.5.0" # json web token 27 | gem "oauth", "~> 0.5.0" 28 | gem "omniauth" 29 | 30 | # Email 31 | gem "sendgrid" 32 | 33 | # JSON parser 34 | gem "yajl-ruby", require: "yajl" 35 | 36 | # server 37 | gem "puma" 38 | 39 | # Used for deploying to Heroku. Can be removed if not deploying to Heroku. 40 | gem "heroku_secrets", git: "https://github.com/alexpeattie/heroku_secrets.git" 41 | 42 | # API Related 43 | gem "httparty" 44 | gem "rack-cors", require: "rack/cors" 45 | 46 | # Paging 47 | gem "will_paginate" 48 | 49 | # Javascript 50 | gem "webpacker" 51 | 52 | # Assets 53 | gem "sassc-rails" 54 | 55 | # This is only here because we are on ruby 2.4. When we upgrade ruby we can remove this 56 | gem "sprockets", "~>3.7.2" 57 | 58 | group :development do 59 | # UI 60 | gem "autoprefixer-rails" 61 | gem "non-stupid-digest-assets" # also compile assets without digest (fixes font problem) 62 | gem "uglifier" 63 | 64 | gem "better_errors" 65 | gem "binding_of_caller", platforms: [:mri_21] 66 | gem "hub", require: nil 67 | gem "mail_view" 68 | gem "rails_apps_pages" 69 | gem "rails_apps_testing" 70 | gem "rails_layout" 71 | gem "rb-fchange", require: false 72 | gem "rb-fsevent", require: false 73 | gem "rb-inotify", require: false 74 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 75 | gem "listen" 76 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 77 | gem "spring" 78 | gem "spring-commands-rspec" 79 | gem "spring-watcher-listen" 80 | gem "web-console" 81 | end 82 | 83 | group :linter do 84 | gem "pronto" 85 | gem "pronto-eslint_npm", require: false 86 | gem "pronto-rubocop", require: false 87 | gem "rubocop" 88 | end 89 | 90 | group :development, :test do 91 | gem "byebug", platform: :mri 92 | gem "dotenv-rails" 93 | gem "factory_bot_rails" 94 | gem "faker" 95 | gem "guard-rspec", require: false 96 | gem "rspec-rails" 97 | end 98 | 99 | group :test do 100 | gem "capybara" 101 | gem "database_cleaner" 102 | gem "launchy" 103 | gem "selenium-webdriver" 104 | gem "shoulda-matchers" 105 | gem "webmock" 106 | end 107 | 108 | group :production do 109 | gem "rails_12factor" 110 | end 111 | 112 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 113 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 114 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | ## Uncomment and set this to only include directories you want to watch 5 | # directories %w(app lib config test spec features) \ 6 | # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} 7 | 8 | ## Note: if you are using the `directories` clause above and you are not 9 | ## watching the project directory ('.'), then you will want to move 10 | ## the Guardfile to a watched dir and symlink it back, e.g. 11 | # 12 | # $ mkdir config 13 | # $ mv Guardfile config/ 14 | # $ ln -s config/Guardfile . 15 | # 16 | # and, you'll have to watch "config/Guardfile" instead of "Guardfile" 17 | 18 | # Note: The cmd option is now required due to the increasing number of ways 19 | # rspec may be run, below are examples of the most common uses. 20 | # * bundler: 'bundle exec rspec' 21 | # * bundler binstubs: 'bin/rspec' 22 | # * spring: 'bin/rspec' (This will use spring if running and you have 23 | # installed the spring binstubs per the docs) 24 | # * zeus: 'zeus rspec' (requires the server to be started separately) 25 | # * 'just' rspec: 'rspec' 26 | 27 | guard :rspec, cmd: "spring rspec" do 28 | require "guard/rspec/dsl" 29 | dsl = Guard::RSpec::Dsl.new(self) 30 | 31 | # Feel free to open issues for suggestions and improvements 32 | 33 | # RSpec files 34 | rspec = dsl.rspec 35 | watch(rspec.spec_helper) { rspec.spec_dir } 36 | watch(rspec.spec_support) { rspec.spec_dir } 37 | watch(rspec.spec_files) 38 | 39 | # Ruby files 40 | ruby = dsl.ruby 41 | dsl.watch_spec_files_for(ruby.lib_files) 42 | 43 | # Rails files 44 | rails = dsl.rails(view_extensions: %w(erb haml slim)) 45 | dsl.watch_spec_files_for(rails.app_files) 46 | dsl.watch_spec_files_for(rails.views) 47 | 48 | watch(rails.controllers) do |m| 49 | [ 50 | rspec.spec.call("routing/#{m[1]}_routing"), 51 | rspec.spec.call("controllers/#{m[1]}_controller"), 52 | rspec.spec.call("acceptance/#{m[1]}"), 53 | ] 54 | end 55 | 56 | # Rails config changes 57 | watch(rails.spec_helper) { rspec.spec_dir } 58 | watch(rails.routes) { "#{rspec.spec_dir}/routing" } 59 | watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" } 60 | 61 | # Capybara features specs 62 | watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") } 63 | watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") } 64 | 65 | # Turnip features and steps 66 | watch(%r{^spec/acceptance/(.+)\.feature$}) 67 | watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m| 68 | Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance" 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2016 Atomic Jolt (http://www.atomicjolt.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb -------------------------------------------------------------------------------- /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", __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the rails generate channel command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | // Partials 2 | @import "partials/print"; 3 | @import "partials/environment"; 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/client.scss: -------------------------------------------------------------------------------- 1 | // Environment 2 | @import "partials/print"; 3 | @import "partials/environment"; 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/_environment.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Roboto:700'); 2 | 3 | .aj-env-banner { 4 | height: 26px; 5 | width: 200px; 6 | background: #272121; 7 | font-family: ‘Roboto’, sans-serif; 8 | font-size: 12px; 9 | font-weight: 700; 10 | color: #FCCC0F; 11 | text-align: center; 12 | text-transform: uppercase; 13 | line-height: 24px; 14 | letter-spacing: 1px; 15 | 16 | position: fixed; 17 | top: 35px; 18 | right: -55px; 19 | border: 1px solid white; 20 | z-index: 999; 21 | pointer-events: none; 22 | 23 | transform: rotate(45deg); 24 | -moz-transform: rotate(45deg); 25 | -webkit-transform: rotate(45deg); 26 | -ms-transform: rotate(45deg); 27 | 28 | -webkit-box-sizing: border-box; 29 | -moz-box-sizing: border-box; 30 | box-sizing: border-box; 31 | vertical-align: top; 32 | -webkit-font-smoothing: antialiased; 33 | -moz-osx-font-smoothing: grayscale; 34 | text-shadow: 1px 1px 1px rgba(0,0,0,0.004); 35 | } 36 | -------------------------------------------------------------------------------- /app/assets/stylesheets/partials/_print.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | 3 | .no-print { 4 | display: none !important; 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/api/jwts_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::JwtsController < ApplicationController 2 | 3 | include Concerns::JwtToken 4 | 5 | before_action :validate_token 6 | 7 | respond_to :json 8 | 9 | def show 10 | token = AuthToken.issue_token({ user_id: current_user.id }) 11 | respond_to do |format| 12 | format.json { render json: { jwt: token } } 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | 3 | # Prevent CSRF attacks by raising an exception. 4 | # For APIs, you may want to use :null_session instead. 5 | protect_from_forgery with: :exception 6 | before_action :configure_permitted_parameters, if: :devise_controller? 7 | 8 | protected 9 | 10 | rescue_from CanCan::AccessDenied do |exception| 11 | respond_to do |format| 12 | format.html { redirect_to root_url, alert: exception.message } 13 | format.json { render json: { error: exception.message }, status: :unauthorized } 14 | end 15 | end 16 | 17 | def configure_permitted_parameters 18 | devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) 19 | devise_parameter_sanitizer.permit(:account_update, keys: [:name]) 20 | end 21 | 22 | def current_ability 23 | @current_ability ||= Ability.new(current_user) 24 | end 25 | 26 | def user_not_authorized 27 | respond_to do |format| 28 | format.html { render file: "public/401.html", status: :unauthorized } 29 | format.json { render json: {}, status: :unauthorized } 30 | end 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/concerns/jwt_token.rb: -------------------------------------------------------------------------------- 1 | module Concerns 2 | module JwtToken 3 | extend ActiveSupport::Concern 4 | 5 | class InvalidTokenError < StandardError; end 6 | 7 | def validate_token 8 | authorization = request.headers["Authorization"] 9 | raise InvalidTokenError if authorization.nil? 10 | 11 | token = request.headers["Authorization"].split(" ").last 12 | decoded_token = AuthToken.valid?(token) 13 | 14 | raise InvalidTokenError if Rails.application.secrets.auth0_client_id != decoded_token[0]["aud"] 15 | 16 | @user = User.find(decoded_token[0]["user_id"]) 17 | sign_in(@user, event: :authentication) 18 | rescue JWT::DecodeError, InvalidTokenError 19 | render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/concerns/paging.rb: -------------------------------------------------------------------------------- 1 | module Concerns 2 | module Paging 3 | extend ActiveSupport::Concern 4 | 5 | included do 6 | before_action :setup_will_paginate 7 | end 8 | 9 | def setup_paging 10 | @page = (params[:page] || 1).to_i 11 | @page = 1 if @page < 1 12 | @per_page = (params[:per_page] || (::Rails.env == "test" ? 1 : 40)).to_i 13 | end 14 | 15 | def set_will_paginate_string 16 | # Because I18n.locale are dynamically determined in ApplicationController, 17 | # it should not put in config/initializers/will_paginate.rb 18 | WillPaginate::ViewHelpers.pagination_options[:previous_label] = "previous" 19 | WillPaginate::ViewHelpers.pagination_options[:next_label] = "next" 20 | end 21 | 22 | def setup_will_paginate 23 | setup_paging 24 | set_will_paginate_string 25 | end 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | 3 | layout "client" 4 | 5 | def index; end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | before_filter :update_sanitized_params, if: :devise_controller? 3 | 4 | def update_sanitized_params 5 | devise_parameter_sanitizer.for(:sign_up) do |u| 6 | u.permit(:name, :email, :password, :password_confirmation) 7 | end 8 | devise_parameter_sanitizer.for(:account_update) do |u| 9 | u.permit(:name, :email, :password, :password_confirmation, :current_password) 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < Devise::SessionsController 2 | # Require our abstraction for encoding/deconding JWT. 3 | require "auth_token" 4 | 5 | respond_to :json 6 | 7 | def create 8 | # This is the default behavior from devise - view the sessions controller source: 9 | # https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb 10 | self.resource = warden.authenticate!(auth_options) 11 | set_flash_message(:notice, :signed_in) if is_flashing_format? 12 | sign_in(resource_name, resource) 13 | yield resource if block_given? 14 | 15 | # Here we're deviating from the standard behavior by issuing our JWT 16 | # to any JS based client. 17 | token = AuthToken.issue_token({ user_id: resource.id }) 18 | respond_with resource, location: after_sign_in_path_for(resource) do |format| 19 | format.json do 20 | render json: { 21 | userId: resource.id, 22 | email: resource.email, 23 | displayName: resource.name, 24 | jwt_token: token, 25 | } 26 | end 27 | end 28 | end 29 | 30 | def destroy 31 | # Destroy all authentications when the user logs out: 32 | # current_user.authentications.where(provider: 'facebook').destroy_all 33 | super 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | def application_base_url 4 | File.join(request.base_url, "/") 5 | end 6 | 7 | def jwt_token 8 | return unless signed_in? 9 | AuthToken.issue_token({ user_id: current_user.id }) 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/lib/auth_token.rb: -------------------------------------------------------------------------------- 1 | require "jwt" 2 | 3 | module AuthToken 4 | 5 | # More information on jwt available at 6 | # http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#rfc.section.4.1.6 7 | def self.issue_token(payload, exp = 24.hours.from_now) 8 | payload["iat"] = DateTime.now.to_i # issued at claim 9 | payload["exp"] = exp.to_i # Default expiration set to 24 hours. 10 | payload["aud"] = Rails.application.secrets.auth0_client_id 11 | JWT.encode(payload, Rails.application.secrets.auth0_client_secret, "HS512") 12 | end 13 | 14 | def self.valid?(token) 15 | JWT.decode(token, Rails.application.secrets.auth0_client_secret) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/lib/url_helper.rb: -------------------------------------------------------------------------------- 1 | class UrlHelper 2 | def self.safe_host(url) 3 | return nil if url.blank? 4 | UrlHelper.host(url) 5 | end 6 | 7 | def self.ensure_scheme(url) 8 | return nil unless url.present? 9 | url = "http://#{url}" unless url.start_with?("http") 10 | url 11 | end 12 | 13 | def self.host(url) 14 | return nil unless url.present? 15 | URI.parse(ensure_scheme(url)).host 16 | end 17 | 18 | def self.scheme_host(url) 19 | return nil unless url.present? 20 | parsed = URI.parse(ensure_scheme(url)) 21 | "#{parsed.scheme}://#{parsed.host}" 22 | end 23 | 24 | def self.scheme_host_port(url) 25 | return nil unless url.present? 26 | parsed = URI.parse(ensure_scheme(url)) 27 | port = parsed.port != 80 && parsed.port != 443 ? ":#{parsed.port}" : "" 28 | "#{parsed.scheme}://#{parsed.host}#{port}" 29 | end 30 | 31 | def self.host_from_instance_guid(url) 32 | url = URI.parse(ensure_scheme(url)) 33 | parts = url.host.split(".") 34 | if parts.length > 2 35 | parts[1, parts.length].join(".") 36 | else 37 | parts.join(".") 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | # Define abilities for the passed in user here. For example: 6 | # 7 | # user ||= User.new # guest user (not logged in) 8 | # if user.admin? 9 | # can :manage, :all 10 | # else 11 | # can :read, :all 12 | # end 13 | # 14 | # The first argument to `can` is the action you are giving the user 15 | # permission to do. 16 | # If you pass :manage it will apply to every action. Other common actions 17 | # here are :read, :create, :update and :destroy. 18 | # 19 | # The second argument is the resource the user can perform the action on. 20 | # If you pass :all it will apply to every resource. Otherwise pass a Ruby 21 | # class of the resource. 22 | # 23 | # The third argument is an optional hash of conditions to further filter the 24 | # objects. 25 | # For example, here the user can only update published articles. 26 | # 27 | # can :update, Article, :published => true 28 | # 29 | # See the wiki for details: 30 | # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities 31 | 32 | user ||= User.new # guest user (not logged in) 33 | 34 | basic(user) 35 | admin(user) if user.admin? 36 | end 37 | 38 | def basic(user) 39 | can :manage, User, id: user.id 40 | end 41 | 42 | def admin(_user) 43 | can :manage, :all 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/authentication.rb: -------------------------------------------------------------------------------- 1 | class Authentication < ApplicationRecord 2 | 3 | attr_encrypted :token, key: Rails.application.secrets.encryption_key, mode: :per_attribute_iv_and_salt 4 | attr_encrypted :secret, key: Rails.application.secrets.encryption_key, mode: :per_attribute_iv_and_salt 5 | attr_encrypted :refresh_token, key: Rails.application.secrets.encryption_key, mode: :per_attribute_iv_and_salt 6 | 7 | belongs_to :user, inverse_of: :authentications 8 | 9 | validates :provider, presence: true, uniqueness: { scope: [:uid, :user_id, :provider_url] } 10 | 11 | # Find an authentication using an auth object provided by omniauth 12 | def self.for_auth(auth) 13 | provider_url = UrlHelper.scheme_host_port(auth["info"]["url"]) 14 | Authentication.find_by( 15 | uid: auth["uid"].to_s, 16 | provider: auth["provider"], 17 | provider_url: provider_url, 18 | ) 19 | end 20 | 21 | # Build an authentication using an auth object provided by omniauth 22 | def self.authentication_attrs_from_auth(auth) 23 | info = auth["info"] || {} 24 | provider_url = UrlHelper.scheme_host_port(info["url"]) 25 | attributes = { 26 | uid: auth["uid"].to_s, 27 | username: info["nickname"], 28 | provider: auth["provider"], 29 | provider_url: provider_url, 30 | } 31 | if credentials = auth["credentials"] 32 | attributes[:token] = credentials["token"] 33 | attributes[:secret] = credentials["secret"] 34 | attributes[:refresh_token] = credentials["refresh_token"] if credentials["refresh_token"] 35 | end 36 | attributes 37 | end 38 | 39 | end 40 | -------------------------------------------------------------------------------- /app/models/permission.rb: -------------------------------------------------------------------------------- 1 | class Permission < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :role 4 | 5 | def self.by_nil_or_context(context_id) 6 | where(context_id: [context_id, nil]) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | 3 | has_many :permissions, dependent: :destroy 4 | has_many :users, through: :permissions 5 | 6 | validates :name, presence: true, uniqueness: true 7 | 8 | def self.by_alpha 9 | order("roles.name ASC") 10 | end 11 | 12 | # roles can be defined as symbols. We want to store them as strings in the database 13 | def name=(val) 14 | write_attribute(:name, val.to_s) 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | 3 | # Include default devise modules. Others available are: 4 | # :confirmable, :lockable, :timeoutable and :omniauthable 5 | devise :database_authenticatable, :registerable, :confirmable, 6 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable 7 | 8 | has_many :authentications, dependent: :destroy, inverse_of: :user 9 | has_many :permissions 10 | has_many :roles, through: :permissions 11 | 12 | validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create } 13 | 14 | enum create_method: %i{sign_up oauth} 15 | 16 | def display_name 17 | name || email 18 | end 19 | 20 | #################################################### 21 | # 22 | # Omniauth related methods 23 | # 24 | def self.for_auth(auth) 25 | Authentication.for_auth(auth)&.user 26 | end 27 | 28 | def apply_oauth(auth) 29 | self.attributes = User.params_for_create(auth) 30 | setup_authentication(auth) 31 | end 32 | 33 | def update_oauth(auth) 34 | setup_authentication(auth) 35 | end 36 | 37 | def self.oauth_info(auth) 38 | info = auth["info"] || {} 39 | raw_info = auth["extra"]["raw_info"] if auth["extra"].present? 40 | raw_info ||= {} 41 | [info, raw_info] 42 | end 43 | 44 | def self.oauth_name(auth) 45 | info, raw_info = oauth_info(auth) 46 | info["name"] || 47 | "#{info['first_name']} #{info['last_name']}" || 48 | info["nickname"] || 49 | raw_info["name"] || 50 | raw_info["short_name"] || 51 | raw_info["login_id"] 52 | end 53 | 54 | def self.oauth_email(auth) 55 | info, raw_info = oauth_info(auth) 56 | email = info["email"] || 57 | raw_info["primary_email"] || 58 | raw_info["login_id"] 59 | 60 | # Try a basic validation on the email 61 | if email =~ /\A[^@]+@[^@]+\Z/ 62 | email 63 | else 64 | # we have to make one up 65 | domain = UrlHelper.safe_host(info["url"]) 66 | name = auth["uid"] 67 | "#{name}@#{domain}" 68 | end 69 | end 70 | 71 | def self.params_for_create(auth) 72 | { 73 | email: oauth_email(auth), 74 | name: oauth_name(auth), 75 | } 76 | end 77 | 78 | def setup_authentication(auth) 79 | attributes = Authentication.authentication_attrs_from_auth(auth) 80 | if persisted? && 81 | authentication = authentications.find_by( 82 | provider: attributes[:provider], 83 | provider_url: attributes[:provider_url], 84 | ) 85 | authentication.update_attributes!(attributes) 86 | else 87 | authentications.build(attributes) 88 | end 89 | end 90 | 91 | def associate_account(auth) 92 | self.name ||= User.oauth_name(auth) 93 | save! 94 | setup_authentication(auth) 95 | end 96 | 97 | #################################################### 98 | # 99 | # Role related methods 100 | # 101 | def set_default_role 102 | self.role ||= :user 103 | end 104 | 105 | def context_roles(context_id = nil) 106 | roles.where(permissions: { context_id: context_id }).distinct 107 | end 108 | 109 | def nil_or_context_roles(context_id = nil) 110 | roles.where(permissions: { context_id: [context_id, nil] }).distinct 111 | end 112 | 113 | def role?(name, context_id = nil) 114 | has_role?(context_id, name) 115 | end 116 | 117 | def has_role?(context_id, *test_names) 118 | test_names = [test_names] unless test_names.is_a?(Array) 119 | test_names = test_names.map(&:downcase).flatten 120 | @role_names = nil_or_context_roles(context_id).map(&:name).map(&:downcase) if @role_names.blank? 121 | return false if @role_names.blank? 122 | !(@role_names & test_names).empty? 123 | end 124 | 125 | def any_role?(*test_names) 126 | has_role?(nil, *test_names) 127 | end 128 | 129 | # Add the user to a new role 130 | def add_to_role(name, context_id = nil) 131 | role = Role.where(name: name).first_or_create 132 | # Make sure that the user can only be put into a role once 133 | if context_roles(context_id).exclude?(role) 134 | Permission.create(user: self, role: role, context_id: context_id) 135 | end 136 | end 137 | 138 | def admin? 139 | role?("administrator") 140 | end 141 | 142 | def can_edit?(user) 143 | return false if user.nil? 144 | id == user.id || user.admin? 145 | end 146 | 147 | def self.convert_name_to_initials(sortable_name) 148 | parts = sortable_name.split(",") 149 | "#{parts[1].strip[0]}#{parts[0].strip[0]}".upcase 150 | rescue 151 | return "?" unless sortable_name && !sortable_name.empty? 152 | return sortable_name[0..1].upcase if sortable_name.length > 1 153 | sortable_name[0] 154 | end 155 | 156 | end 157 | -------------------------------------------------------------------------------- /app/services/create_admin_service.rb: -------------------------------------------------------------------------------- 1 | class CreateAdminService 2 | def call 3 | user = User.find_or_initialize_by(email: Rails.application.secrets.admin_email) 4 | user.password = Rails.application.secrets.admin_password 5 | user.password_confirmation = Rails.application.secrets.admin_password 6 | user.confirm unless user.confirmed? 7 | user.add_to_role("administrator") 8 | user.save! 9 | user 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

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

Cancel my account

41 | 42 |

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

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

Sign up

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.label :name %>
13 | <%= f.text_field :name, autofocus: true %> 14 |
15 | 16 |
17 | <%= f.label :password %> 18 | <% if @minimum_password_length %> 19 | (<%= @minimum_password_length %> characters minimum) 20 | <% end %>
21 | <%= f.password_field :password, autocomplete: "off" %> 22 |
23 | 24 |
25 | <%= f.label :password_confirmation %>
26 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 27 |
28 | 29 |
30 | <%= f.submit "Sign up" %> 31 |
32 | <% end %> 33 | 34 | <%= render "devise/shared/links" %> 35 | -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :head do -%> 2 | <%= stylesheet_pack_tag "hello_world" %> 3 | <% end -%> 4 | 5 | <%= render "shared/default_client_settings" %> 6 |
7 | <%= javascript_packs_with_chunks_tag "hello_world", "data-turbolinks-track": "reload" %> 8 | -------------------------------------------------------------------------------- /app/views/layouts/_head.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= content_for?(:title) ? yield(:title) : Rails.application.secrets.application_name %> 5 | 6 | 7 | 8 | 9 | <%= csrf_meta_tags %> 10 | <%= yield(:head) %> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/views/layouts/_messages.html.erb: -------------------------------------------------------------------------------- 1 | <%# Rails flash messages styled for Bootstrap 3.0 %> 2 | <% flash.each do |name, msg| %> 3 | <% if msg.is_a?(String) %> 4 |
5 | 6 | <%= content_tag :div, msg, :id => "flash_#{name}" %> 7 |
8 | <% end %> 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/layouts/_navigation.html.erb: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /app/views/layouts/_show_environment.erb: -------------------------------------------------------------------------------- 1 | <% if !Rails.env.production? || Rails.application.secrets.deploy_env != "production" -%> 2 |
<%= Rails.application.secrets.deploy_env.present? ? Rails.application.secrets.deploy_env.upcase : Rails.env.upcase %>
3 | <% end -%> 4 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= stylesheet_link_tag 'application', media: 'all' %> 5 | <%= render 'layouts/head' %> 6 | 7 | 8 | <%= render "layouts/show_environment" %> 9 | <%= render 'layouts/navigation' %> 10 |
11 | <%= render 'layouts/messages' %> 12 | <%= yield %> 13 |
14 | <%= javascript_include_tag 'application'%> 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/views/layouts/client.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= stylesheet_link_tag 'client', media: 'all' %> 5 | <%= render 'layouts/head' %> 6 | 7 | 8 | <%= render "layouts/show_environment" %> 9 | <%= yield %> 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/views/shared/_default_client_settings.html.erb: -------------------------------------------------------------------------------- 1 | <%- 2 | settings = { 3 | api_url: application_base_url, 4 | csrf_token: form_authenticity_token 5 | } 6 | if signed_in? 7 | settings[:user_id] = current_user.id 8 | settings[:email] = current_user.email 9 | settings[:display_name] = current_user.display_name 10 | end 11 | %> 12 | -------------------------------------------------------------------------------- /app/views/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Sign in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), class: "btn btn-primary" %> 24 | <% end -%> 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /app/views/shared/_omniauth_error.html.erb: -------------------------------------------------------------------------------- 1 |

An OauthError has occured 2 | -------------------------------------------------------------------------------- /app/views/users/_user.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= link_to user.email, user %> 3 | 4 | 5 | <%= form_for(user) do |f| %> 6 | <%= f.select(:role, User.roles.keys.map {|role| [role.titleize,role]}) %> 7 | <%= f.submit 'Change Role', :class => 'button-xs' %> 8 | <% end %> 9 | 10 | 11 | <%= link_to("Delete user", user_path(user), :data => { :confirm => "Are you sure?" }, :method => :delete, :class => 'button-xs') unless user == current_user %> 12 | 13 | -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Users

4 |
5 | 6 | 7 | <% @users.each do |user| %> 8 | 9 | <%= render user %> 10 | 11 | <% end %> 12 | 13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 |

User

2 |

Name: <%= @user.name if @user.name %>

3 |

Email: <%= @user.email if @user.email %>

4 | -------------------------------------------------------------------------------- /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 | '@babel/preset-react' 29 | ], 30 | (isProductionEnv || isDevelopmentEnv) && [ 31 | '@babel/preset-env', 32 | { 33 | forceAllTransforms: true, 34 | useBuiltIns: 'entry', 35 | corejs: 3, 36 | modules: false, 37 | exclude: ['transform-typeof-symbol'] 38 | } 39 | ], 40 | [ 41 | '@babel/preset-react', 42 | { 43 | development: isDevelopmentEnv || isTestEnv, 44 | useBuiltIns: true 45 | } 46 | ] 47 | ].filter(Boolean), 48 | plugins: [ 49 | 'babel-plugin-macros', 50 | '@babel/plugin-syntax-dynamic-import', 51 | isTestEnv && 'babel-plugin-dynamic-import-node', 52 | '@babel/plugin-transform-destructuring', 53 | [ 54 | '@babel/plugin-proposal-class-properties', 55 | { 56 | loose: true 57 | } 58 | ], 59 | [ 60 | '@babel/plugin-proposal-object-rest-spread', 61 | { 62 | useBuiltIns: true 63 | } 64 | ], 65 | [ 66 | '@babel/plugin-transform-runtime', 67 | { 68 | helpers: false, 69 | regenerator: true, 70 | corejs: false 71 | } 72 | ], 73 | [ 74 | '@babel/plugin-transform-regenerator', 75 | { 76 | async: false 77 | } 78 | ], 79 | isProductionEnv && [ 80 | 'babel-plugin-transform-react-remove-prop-types', 81 | { 82 | removeImport: true 83 | } 84 | ] 85 | ].filter(Boolean) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /bin/bootstrap: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Meant to be run from root directory of project 4 | # ./bin/bootstrap 5 | DROPBOX_FOLDER=aj-dev/react_rails_starter_app 6 | 7 | # All config files for project 8 | files=("config/secrets.yml" "config/database.yml") 9 | 10 | for file in "${files[@]}" 11 | do 12 | if [ -e $file ] 13 | then 14 | rm $file 15 | fi 16 | mkdir -p $(dirname "${file}") 17 | ln -s "$HOME/Dropbox/$DROPBOX_FOLDER/$file" $file 18 | done 19 | 20 | # project specific code or symlinks 21 | # cd config 22 | # ln -s database.development.yml database.yml 23 | -------------------------------------------------------------------------------- /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/ci: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit if any subcommand fails 4 | set -e 5 | 6 | bundle exec rspec 7 | -------------------------------------------------------------------------------- /bin/deploy_heroku: -------------------------------------------------------------------------------- 1 | !/usr/bin/env bash 2 | 3 | set -o nounset 4 | set -o errexit 5 | set -o pipefail 6 | 7 | SHA=$(git rev-parse --short HEAD) 8 | 9 | function finish { 10 | git reset --hard $SHA 11 | bin/bootstrap 12 | } 13 | trap finish EXIT 14 | 15 | RAILS_ENV=production bundle exec rake assets:precompile 16 | 17 | git add -f public/assets 18 | git add -f config/assets 19 | 20 | rsync `readlink config/secrets.yml` config/secrets.yml 21 | git add -f config/secrets.yml 22 | 23 | git commit -m "parent sha: [$SHA] add precompile files" 24 | 25 | git push -f heroku master 26 | 27 | heroku run rails db:migrate 28 | heroku run rails db:seed -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/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/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! 'bin/rails db:migrate' 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! 'bin/rails log:clear tmp:clear' 28 | 29 | puts "\n== Restarting application server ==" 30 | system! 'bin/rails restart' 31 | end 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /client/apps/hello_world/app.jsx: -------------------------------------------------------------------------------- 1 | import 'core-js'; 2 | import 'regenerator-runtime/runtime'; 3 | import es6Promise from 'es6-promise'; 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import PropTypes from 'prop-types'; 7 | import { Provider } from 'react-redux'; 8 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 9 | import { getInitialSettings } from 'atomic-fuel/libs/reducers/settings'; 10 | import jwt from 'atomic-fuel/libs/loaders/jwt'; 11 | import configureStore from './store/configure_store'; 12 | import Index from './components/layout/index'; 13 | 14 | import './styles/styles'; 15 | 16 | // Polyfill es6 promises for IE 17 | es6Promise.polyfill(); 18 | 19 | class Root extends React.PureComponent { 20 | static propTypes = { 21 | store: PropTypes.object.isRequired, 22 | }; 23 | 24 | render() { 25 | const { store } = this.props; 26 | return ( 27 | 28 | 29 |
30 | 31 |
32 |
33 |
34 | ); 35 | } 36 | } 37 | 38 | const settings = getInitialSettings(window.DEFAULT_SETTINGS); 39 | const store = configureStore({ settings, jwt: window.DEFAULT_JWT }); 40 | if (window.DEFAULT_JWT) { // Setup JWT refresh 41 | jwt(store.dispatch, settings.userId); 42 | } 43 | 44 | ReactDOM.render( 45 | , 46 | document.getElementById('main-app'), 47 | ); 48 | -------------------------------------------------------------------------------- /client/apps/hello_world/assets/images/atomicjolt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/assets/images/atomicjolt.jpg -------------------------------------------------------------------------------- /client/apps/hello_world/assets/images/atomicjolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/assets/images/atomicjolt.png -------------------------------------------------------------------------------- /client/apps/hello_world/assets/images/atomicjolt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 12 | 16 | 18 | 22 | 25 | 30 | 33 | 36 | 38 | 39 | 42 | 43 | 44 | 52 | 53 | -------------------------------------------------------------------------------- /client/apps/hello_world/components/common/not_found.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 |
5 |

Page Not Found

6 |

Sorry, but the page you were trying to view does not exist.

7 |
8 | ); 9 | -------------------------------------------------------------------------------- /client/apps/hello_world/components/home.jsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import assets from '../libs/assets'; 3 | 4 | export default class Home extends React.Component { 5 | 6 | render() { 7 | const img = assets('./images/atomicjolt.jpg'); 8 | return ( 9 |
10 | Atomic Jolt Logo 11 |

Welcome to the React Client starter app by Atomic Jolt

12 |
13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /client/apps/hello_world/components/home.spec.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Home from './home'; 4 | 5 | jest.mock('../libs/assets.js'); 6 | 7 | describe('home', () => { 8 | let result; 9 | let props; 10 | 11 | beforeEach(() => { 12 | props = {}; 13 | result = shallow(); 14 | }); 15 | 16 | it('renders the home component', () => { 17 | expect(result).toBeDefined(); 18 | }); 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /client/apps/hello_world/components/layout/errors.jsx: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import { connect } from 'react-redux'; 5 | 6 | import { clearErrors } from 'atomic-fuel/libs/actions/errors'; 7 | 8 | const select = state => ({ errors: state.errors }); 9 | 10 | export class Errors extends React.Component { 11 | 12 | static propTypes = { 13 | clearErrors: PropTypes.func, 14 | } 15 | 16 | componentDidMount() { 17 | setTimeout(() => { 18 | this.props.clearErrors(); 19 | }, 5000); 20 | } 21 | 22 | render() { 23 | const errors = _.map(this.props.errors, error =>
  • {error}
  • ); 24 | return ; 25 | } 26 | } 27 | 28 | export default connect(select, { clearErrors })(Errors); 29 | -------------------------------------------------------------------------------- /client/apps/hello_world/components/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route } from 'react-router'; 3 | import Errors from './errors'; 4 | import Home from '../home'; 5 | 6 | 7 | export default class Index extends React.Component { 8 | 9 | render() { 10 | return ( 11 |
    12 | 13 | 14 |
    15 | ); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /client/apps/hello_world/components/layout/index.spec.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Index from './index'; 4 | 5 | jest.mock('../../libs/assets.js'); 6 | 7 | describe('index', () => { 8 | let result; 9 | let props; 10 | 11 | beforeEach(() => { 12 | props = {}; 13 | result = shallow(); 14 | }); 15 | 16 | it('renders the index', () => { 17 | expect(result).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/404.html: -------------------------------------------------------------------------------- 1 |

    Page Not Found

    2 |

    Sorry, but the page you were trying to view does not exist.

    -------------------------------------------------------------------------------- /client/apps/hello_world/html/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | author: Atomic Jolt 4 | layout: application 5 | permalink: /about-us/ 6 | categories: 7 | - About Us 8 | tags: 9 | - About Us 10 | - Atomic Jolt 11 | --- 12 | About Us 13 | --------------- 14 | <%= metadata.tags %> -------------------------------------------------------------------------------- /client/apps/hello_world/html/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3, JavaScript 15 | React, jQuery, Bootstrap 16 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/index.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/layouts/application.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include('./partials/_head.html') %> 5 | 6 | 7 | <%- include('./partials/_header.html') %> 8 | <%- content %> 9 | <%- include('./partials/_google_analytics.html') %> 10 | 11 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/layouts/partials/_google_analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/layouts/partials/_head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | React Client Starter 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /client/apps/hello_world/html/layouts/partials/_header.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/html/layouts/partials/_header.html -------------------------------------------------------------------------------- /client/apps/hello_world/html/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /client/apps/hello_world/libs/assets.js: -------------------------------------------------------------------------------- 1 | // See http://webpack.github.io/docs/context.html#dynamic-requires 2 | 3 | export default require.context('../assets', true, /^\.\//); 4 | -------------------------------------------------------------------------------- /client/apps/hello_world/middleware/api.js: -------------------------------------------------------------------------------- 1 | import Api from 'atomic-fuel/libs/middleware/api'; 2 | 3 | // This file just exports the default global api. If modifications are needed 4 | // make the modifications in this file by extending the Api or copy pasting the 5 | // code into this file. 6 | export default Api; 7 | -------------------------------------------------------------------------------- /client/apps/hello_world/reducers/application.js: -------------------------------------------------------------------------------- 1 | const initialState = {}; 2 | 3 | export default (state = initialState, action) => { 4 | switch (action.type) { 5 | 6 | default: 7 | return state; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /client/apps/hello_world/reducers/application.spec.js: -------------------------------------------------------------------------------- 1 | import application from './application'; 2 | 3 | describe('application reducer', () => { 4 | describe('initial state', () => { 5 | it('returns empty state', () => { 6 | const initialState = {}; 7 | const state = application(initialState, {}); 8 | expect(state).toEqual({}); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /client/apps/hello_world/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import settings from 'atomic-fuel/libs/reducers/settings'; 3 | import jwt from 'atomic-fuel/libs/reducers/jwt'; 4 | import errors from 'atomic-fuel/libs/reducers/errors'; 5 | import application from './application'; 6 | 7 | const rootReducer = combineReducers({ 8 | settings, 9 | jwt, 10 | application, 11 | errors, 12 | }); 13 | 14 | export default rootReducer; 15 | -------------------------------------------------------------------------------- /client/apps/hello_world/static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/.keep -------------------------------------------------------------------------------- /client/apps/hello_world/static/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/android-icon-144x144.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/android-icon-192x192.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/android-icon-36x36.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/android-icon-48x48.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/android-icon-72x72.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/android-icon-96x96.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-114x114.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-120x120.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-144x144.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-152x152.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-180x180.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-57x57.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-60x60.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-72x72.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-76x76.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon-precomposed.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/apple-icon.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /client/apps/hello_world/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/favicon-16x16.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/favicon-32x32.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/favicon-96x96.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/favicon.ico -------------------------------------------------------------------------------- /client/apps/hello_world/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /client/apps/hello_world/static/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/ms-icon-144x144.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/ms-icon-150x150.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/ms-icon-310x310.png -------------------------------------------------------------------------------- /client/apps/hello_world/static/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/static/ms-icon-70x70.png -------------------------------------------------------------------------------- /client/apps/hello_world/store/configure_store.js: -------------------------------------------------------------------------------- 1 | import configureStore from 'atomic-fuel/libs/store/configure_store'; 2 | import rootReducer from '../reducers/index'; 3 | import API from '../middleware/api'; 4 | 5 | const middleware = [API]; 6 | 7 | // This file just exports the default configure store. If modifications are needed 8 | // make the modifications in this file by extending the configureStore 9 | // or copy pasting the code into this file. 10 | export default function(initialState) { 11 | return configureStore(initialState, rootReducer, middleware); 12 | } 13 | -------------------------------------------------------------------------------- /client/apps/hello_world/styles/.csslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "adjoining-classes": false, 3 | "box-sizing": false, 4 | "box-model": false, 5 | "compatible-vendor-prefixes": false, 6 | "floats": false, 7 | "font-sizes": false, 8 | "gradients": false, 9 | "important": false, 10 | "known-properties": false, 11 | "outline-none": false, 12 | "qualified-headings": false, 13 | "regex-selectors": false, 14 | "shorthand": false, 15 | "text-indent": false, 16 | "unique-headings": false, 17 | "universal-selector": false, 18 | "unqualified-attributes": false 19 | } 20 | -------------------------------------------------------------------------------- /client/apps/hello_world/styles/_defines.scss: -------------------------------------------------------------------------------- 1 | $white: #fff; 2 | $black: #000; 3 | -------------------------------------------------------------------------------- /client/apps/hello_world/styles/fonts/roboto-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/styles/fonts/roboto-bold-webfont.woff -------------------------------------------------------------------------------- /client/apps/hello_world/styles/fonts/roboto-bold-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/styles/fonts/roboto-bold-webfont.woff2 -------------------------------------------------------------------------------- /client/apps/hello_world/styles/fonts/roboto-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/styles/fonts/roboto-regular-webfont.woff -------------------------------------------------------------------------------- /client/apps/hello_world/styles/fonts/roboto-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/styles/fonts/roboto-regular-webfont.woff2 -------------------------------------------------------------------------------- /client/apps/hello_world/styles/modules/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Fonts must be relative to the main scss file (i.e. the file that includes this mixin) 2 | // That's why the urls includes/ fonts/ even though that is up one directory 3 | @font-face { 4 | font-family: "robotobold"; 5 | src: url("/fonts/roboto-bold-webfont.woff2") format("woff2"), 6 | url("/fonts/roboto-bold-webfont.woff") format("woff"); 7 | font-weight: normal; 8 | font-style: normal; 9 | 10 | } 11 | 12 | @font-face { 13 | font-family: "robotoregular"; 14 | src: url("/fonts/roboto-regular-webfont.woff2") format("woff2"), 15 | url("/fonts/roboto-regular-webfont.woff") format("woff"); 16 | font-weight: normal; 17 | font-style: normal; 18 | } 19 | -------------------------------------------------------------------------------- /client/apps/hello_world/styles/styles.js: -------------------------------------------------------------------------------- 1 | // This file exists to compile all css - less and scss - into one file 2 | require('./styles.less'); 3 | require('./styles.scss'); 4 | -------------------------------------------------------------------------------- /client/apps/hello_world/styles/styles.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world/styles/styles.less -------------------------------------------------------------------------------- /client/apps/hello_world/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import "defines"; 2 | @import "modules/mixins"; 3 | 4 | body { 5 | text-align: center; 6 | font-family: 'robotoregular'; 7 | } 8 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/app.jsx: -------------------------------------------------------------------------------- 1 | import 'core-js'; 2 | import 'regenerator-runtime/runtime'; 3 | import es6Promise from 'es6-promise'; 4 | import React from 'react'; 5 | import ReactDOM from 'react-dom'; 6 | import PropTypes from 'prop-types'; 7 | import _ from 'lodash'; 8 | import { ApolloProvider } from '@apollo/react-hooks'; 9 | import { ApolloClient } from 'apollo-client'; 10 | import { HttpLink } from 'apollo-link-http'; 11 | import { ApolloLink } from 'apollo-link'; 12 | import { withClientState } from 'apollo-link-state'; 13 | import { InMemoryCache } from 'apollo-cache-inmemory'; 14 | import { Router } from 'react-router'; 15 | import { Route } from 'react-router-dom'; 16 | import { Jwt } from 'atomic-fuel/libs/loaders/jwt'; 17 | import settings from './settings'; 18 | 19 | import appHistory from './history'; 20 | import Index from './components/layout/index'; 21 | 22 | import './styles/styles'; 23 | 24 | // Polyfill es6 promises for IE 25 | es6Promise.polyfill(); 26 | 27 | const jwt = new Jwt(window.DEFAULT_JWT, window.DEFAULT_SETTINGS.api_url); 28 | jwt.enableRefresh(); 29 | 30 | class Root extends React.PureComponent { 31 | static propTypes = { 32 | client: PropTypes.object, 33 | }; 34 | 35 | render() { 36 | const { client } = this.props; 37 | return ( 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | } 46 | 47 | export default Root; 48 | 49 | const inCacheMemory = new InMemoryCache(); 50 | 51 | const stateLink = withClientState({ 52 | cache: inCacheMemory, 53 | resolvers: { 54 | Mutation: {}, 55 | }, 56 | defaults: { 57 | welcomeMessage: 'Welcome to the GraphQL starter app' 58 | } 59 | }); 60 | 61 | const links = [ 62 | stateLink 63 | ]; 64 | 65 | if (!_.isEmpty(settings.api_url)) { 66 | const authenticationLink = new ApolloLink((operation, forward) => { 67 | operation.setContext({ 68 | headers: { 69 | authorization: `Bearer ${jwt.currentJwt}` 70 | } 71 | }); 72 | return forward(operation); 73 | }); 74 | links.push(authenticationLink); 75 | 76 | const httpLink = new HttpLink({ 77 | uri: `${settings.api_url}api/graphql`, 78 | }); 79 | links.push(httpLink); 80 | } 81 | 82 | const client = new ApolloClient({ 83 | link: ApolloLink.from(links), 84 | cache: inCacheMemory, 85 | }); 86 | 87 | ReactDOM.render( 88 | , 89 | document.getElementById('main-app'), 90 | ); 91 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/assets/images/atomicjolt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/assets/images/atomicjolt.jpg -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/assets/images/atomicjolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/assets/images/atomicjolt.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/assets/images/atomicjolt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 12 | 16 | 18 | 22 | 25 | 30 | 33 | 36 | 38 | 39 | 42 | 43 | 44 | 52 | 53 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/components/common/not_found.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 |
    5 |

    Page Not Found

    6 |

    Sorry, but the page you were trying to view does not exist.

    7 |
    8 | ); 9 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/components/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gql from 'graphql-tag'; 3 | import { useQuery } from '@apollo/react-hooks'; 4 | 5 | import assets from '../libs/assets'; 6 | 7 | export const GET_WELCOME = gql` 8 | query getWelcome($name: String!) { 9 | welcomeMessage(name: $name) 10 | } 11 | `; 12 | 13 | export default function home() { 14 | const { loading, error, data } = useQuery(GET_WELCOME, { 15 | variables: { 16 | name: 'World', 17 | }, 18 | }); 19 | 20 | if (loading) return 'Loading...'; 21 | if (error) return `Error! ${error.message}`; 22 | 23 | const img = assets('./images/atomicjolt.jpg'); 24 | 25 | return ( 26 |
    27 | Atomic Jolt Logo 28 |

    29 | {data.welcomeMessage} 30 |

    31 |

    32 | by 33 | Atomic Jolt 34 |

    35 |
    36 | ); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/components/home.spec.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestRenderer, { act } from 'react-test-renderer'; 3 | import { MockedProvider } from '@apollo/react-testing'; 4 | import waitForExpect from 'wait-for-expect'; 5 | import Home, { GET_WELCOME } from './home'; 6 | 7 | jest.mock('../libs/assets.js'); 8 | 9 | const mocks = [ 10 | { 11 | request: { 12 | query: GET_WELCOME, 13 | variables: { name: 'World' }, 14 | }, 15 | result: { 16 | data: { 17 | welcomeMessage: 'Hello World!', 18 | }, 19 | }, 20 | }, 21 | ]; 22 | 23 | describe('home', () => { 24 | it('should render loading state initially', () => { 25 | const testRenderer = TestRenderer.create( 26 | 27 | 28 | , 29 | ); 30 | 31 | const tree = testRenderer.toJSON(); 32 | expect(JSON.stringify(tree).indexOf('Hello World!') === -1).toBe(true); 33 | }); 34 | 35 | it('renders the home component', async() => { 36 | let testRenderer; 37 | await act(async() => { 38 | testRenderer = TestRenderer.create( 39 | 40 | 41 | , 42 | ); 43 | }); 44 | 45 | await waitForExpect(() => { 46 | expect(JSON.stringify(testRenderer.toJSON()).indexOf('Hello World!') >= 0).toBe(true); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/components/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Switch } from 'react-router'; 3 | import Home from '../home'; 4 | import NotFound from '../common/not_found'; 5 | 6 | export default class Index extends React.Component { 7 | 8 | render() { 9 | return ( 10 |
    11 | 12 | 13 | 14 | 15 |
    16 | ); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/components/layout/index.spec.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Index from './index'; 4 | 5 | jest.mock('../../libs/assets.js'); 6 | 7 | describe('index', () => { 8 | let result; 9 | let props; 10 | 11 | beforeEach(() => { 12 | props = {}; 13 | result = shallow(); 14 | }); 15 | 16 | it('renders the index', () => { 17 | expect(result).toBeDefined(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/history.js: -------------------------------------------------------------------------------- 1 | import createHashHistory from 'history/createHashHistory'; 2 | 3 | export default createHashHistory(); 4 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/404.html: -------------------------------------------------------------------------------- 1 |

    Page Not Found

    2 |

    Sorry, but the page you were trying to view does not exist.

    -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/about.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | author: Atomic Jolt 4 | layout: application 5 | permalink: /about-us/ 6 | categories: 7 | - About Us 8 | tags: 9 | - About Us 10 | - Atomic Jolt 11 | --- 12 | About Us 13 | --------------- 14 | <%= metadata.tags %> -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3, JavaScript 15 | React, jQuery, Bootstrap 16 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/index.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/layouts/application.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include('./partials/_head.html') %> 5 | 6 | 7 | <%- include('./partials/_header.html') %> 8 | <%- content %> 9 | <%- include('./partials/_google_analytics.html') %> 10 | 11 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/layouts/partials/_google_analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/layouts/partials/_head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | React Client Starter GraphQL 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/layouts/partials/_header.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/html/layouts/partials/_header.html -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/html/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/libs/assets.js: -------------------------------------------------------------------------------- 1 | // See http://webpack.github.io/docs/context.html#dynamic-requires 2 | 3 | export default require.context('../assets', true, /^\.\//); 4 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/settings.js: -------------------------------------------------------------------------------- 1 | export default window.DEFAULT_SETTINGS; 2 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/.keep -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/android-icon-144x144.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/android-icon-192x192.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/android-icon-36x36.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/android-icon-48x48.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/android-icon-72x72.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/android-icon-96x96.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-114x114.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-120x120.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-144x144.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-152x152.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-180x180.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-57x57.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-60x60.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-72x72.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-76x76.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon-precomposed.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/apple-icon.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/favicon-16x16.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/favicon-32x32.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/favicon-96x96.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/favicon.ico -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/ms-icon-144x144.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/ms-icon-150x150.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/ms-icon-310x310.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/static/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/static/ms-icon-70x70.png -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/.csslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "adjoining-classes": false, 3 | "box-sizing": false, 4 | "box-model": false, 5 | "compatible-vendor-prefixes": false, 6 | "floats": false, 7 | "font-sizes": false, 8 | "gradients": false, 9 | "important": false, 10 | "known-properties": false, 11 | "outline-none": false, 12 | "qualified-headings": false, 13 | "regex-selectors": false, 14 | "shorthand": false, 15 | "text-indent": false, 16 | "unique-headings": false, 17 | "universal-selector": false, 18 | "unqualified-attributes": false 19 | } 20 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/_defines.scss: -------------------------------------------------------------------------------- 1 | $white: #fff; 2 | $black: #000; 3 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/fonts/roboto-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/styles/fonts/roboto-bold-webfont.woff -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/fonts/roboto-bold-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/styles/fonts/roboto-bold-webfont.woff2 -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/fonts/roboto-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/styles/fonts/roboto-regular-webfont.woff -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/fonts/roboto-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/styles/fonts/roboto-regular-webfont.woff2 -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/modules/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Fonts must be relative to the main scss file (i.e. the file that includes this mixin) 2 | // That's why the urls includes /fonts/ even though that is up one directory 3 | @font-face { 4 | font-family: "robotobold"; 5 | src: url("/fonts/roboto-bold-webfont.woff2") format("woff2"), 6 | url("/fonts/roboto-bold-webfont.woff") format("woff"); 7 | font-weight: normal; 8 | font-style: normal; 9 | 10 | } 11 | 12 | @font-face { 13 | font-family: "robotoregular"; 14 | src: url("/fonts/roboto-regular-webfont.woff2") format("woff2"), 15 | url("/fonts/roboto-regular-webfont.woff") format("woff"); 16 | font-weight: normal; 17 | font-style: normal; 18 | } 19 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/styles.js: -------------------------------------------------------------------------------- 1 | // This file exists to compile all css - less and scss - into one file 2 | require('./styles.less'); 3 | require('./styles.scss'); 4 | -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/styles.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/client/apps/hello_world_graphql/styles/styles.less -------------------------------------------------------------------------------- /client/apps/hello_world_graphql/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import "defines"; 2 | @import "modules/mixins"; 3 | 4 | body { 5 | text-align: center; 6 | font-family: 'robotoregular'; 7 | } 8 | -------------------------------------------------------------------------------- /client/packs/hello_world.js: -------------------------------------------------------------------------------- 1 | import '../apps/hello_world/app'; 2 | -------------------------------------------------------------------------------- /client/packs/hello_world_graphql.js: -------------------------------------------------------------------------------- 1 | import '../apps/hello_world_graphql/app'; 2 | -------------------------------------------------------------------------------- /client/testing/__mocks__/file_mock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /client/testing/__mocks__/style_mock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /client/testing/setup_tests.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /client/testing/shim.js: -------------------------------------------------------------------------------- 1 | global.requestAnimationFrame = (callback) => { 2 | setTimeout(callback, 0); 3 | }; 4 | -------------------------------------------------------------------------------- /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 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module ReactRailsStarterApp 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time se(US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | 23 | config.autoload_paths += Dir["#{config.root}/lib/**/"] 24 | 25 | config.action_dispatch.default_headers = { 26 | "X-Frame-Options" => "ALLOWALL", 27 | } 28 | 29 | config.middleware.insert_before 0, Rack::Cors do 30 | allow do 31 | origins "*" 32 | resource "*", headers: :any, methods: [:get, :post, :options] 33 | end 34 | end 35 | 36 | config.webpack = { 37 | use_manifest: false, 38 | asset_manifest: {}, 39 | common_manifest: {}, 40 | } 41 | 42 | # Initialize configuration defaults for originally generated Rails version. 43 | config.load_defaults 5.0 44 | 45 | # Settings in config/environments/* take precedence over those specified here. 46 | # Application configuration can go into files in config/initializers 47 | # -- all .rb files in that directory are automatically loaded after loading 48 | # the framework and any gems in your application. 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /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: react_rails_starter_app_production 11 | -------------------------------------------------------------------------------- /config/ci.database.yml: -------------------------------------------------------------------------------- 1 | staging: 2 | adapter: postgresql 3 | host: localhost 4 | encoding: unicode 5 | pool: 10 6 | username: <%= ENV['PG_USER'] %> 7 | template: template1 8 | password: <%= ENV['PG_PASSWORD'] %> 9 | database: development<%= ENV['TEST_ENV_NUMBER'] %> 10 | sslmode: disable 11 | development: 12 | adapter: postgresql 13 | host: localhost 14 | encoding: unicode 15 | pool: 10 16 | username: <%= ENV['PG_USER'] %> 17 | template: template1 18 | password: <%= ENV['PG_PASSWORD'] %> 19 | database: development<%= ENV['TEST_ENV_NUMBER'] %> 20 | sslmode: disable 21 | test: 22 | adapter: postgresql 23 | database: travis_ci_test 24 | -------------------------------------------------------------------------------- /config/database.example.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 8.2 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On Mac OS X with macports: 6 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 7 | # On Windows: 8 | # gem install pg 9 | # Choose the win32 build. 10 | # Install PostgreSQL and put its /bin directory on your path. 11 | # 12 | # Configure Using Gemfile 13 | # gem 'pg' 14 | # 15 | development: 16 | adapter: postgresql 17 | host: localhost 18 | encoding: unicode 19 | database: lti_starter_app_development 20 | pool: 5 21 | username: 22 | password: 23 | template: template0 24 | 25 | # Connect on a TCP socket. Omitted by default since the client uses a 26 | # domain socket that doesn't need configuration. Windows does not have 27 | # domain sockets, so uncomment these lines. 28 | #host: localhost 29 | #port: 5432 30 | 31 | # Schema search path. The server defaults to $user,public 32 | #schema_search_path: myapp,sharedapp,public 33 | 34 | # Minimum log levels, in increasing order: 35 | # debug5, debug4, debug3, debug2, debug1, 36 | # log, notice, warning, error, fatal, and panic 37 | # The server defaults to notice. 38 | #min_messages: warning 39 | 40 | # Warning: The database defined as "test" will be erased and 41 | # re-generated from your development database when you run "rake". 42 | # Do not set this db to the same as development or production. 43 | test: 44 | adapter: postgresql 45 | host: localhost 46 | encoding: unicode 47 | database: lti_starter_app_test 48 | pool: 5 49 | username: 50 | password: 51 | template: template0 52 | 53 | production: 54 | adapter: postgresql 55 | host: localhost 56 | encoding: unicode 57 | database: lti_starter_app_production 58 | pool: 5 59 | username: 60 | password: 61 | template: template0 62 | -------------------------------------------------------------------------------- /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 | 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 | # Store uploaded files on the local file system (see config/storage.yml for options) 31 | config.active_storage.service = :local 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 | # 60 | # Custom configuration 61 | # 62 | config.generators do |g| 63 | g.test_framework :rspec, 64 | fixtures: true, 65 | view_specs: false, 66 | helper_specs: false, 67 | routing_specs: false, 68 | controller_specs: false, 69 | request_specs: false 70 | g.fixture_replacement :factory_bot, dir: "spec/factories" 71 | end 72 | 73 | # ActionMailer Config 74 | config.action_mailer.default_url_options = { host: "localhost:#{ENV['APP_PORT']}" } 75 | config.action_mailer.delivery_method = :smtp 76 | config.action_mailer.raise_delivery_errors = true 77 | # Send email in development mode? 78 | config.action_mailer.perform_deliveries = false 79 | 80 | end 81 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | "Cache-Control" => "public, max-age=#{1.hour.to_i}", 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Store uploaded files on the local file system in a temporary directory 32 | config.active_storage.service = :test 33 | 34 | 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 | # 48 | # Custom configuration 49 | # 50 | config.action_mailer.default_url_options = { host: "localhost" } 51 | end 52 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # 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 app/assets folder are already added. 13 | Rails.application.config.assets.precompile += %w(client.css) 14 | -------------------------------------------------------------------------------- /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/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 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # If you are using UJS then enable automatic nonce generation 20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 21 | 22 | # Report CSP violations to a specified URI 23 | # For further information see the following documentation: 24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # Rails.application.config.content_security_policy_report_only = true 26 | Rails.application.config.content_security_policy do |policy| 27 | if Rails.env.development? 28 | policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" 29 | 30 | host = "#{ENV['ASSETS_SUBDOMAIN']}.#{ENV['APP_ROOT_DOMAIN']}" 31 | policy.connect_src :self, :https, "http://#{host}", "ws://#{host}" 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 6 | 7 | # Enable per-form CSRF tokens. Previous versions had false. 8 | Rails.application.config.action_controller.per_form_csrf_tokens = true 9 | 10 | # Enable origin-checking CSRF mitigation. Previous versions had false. 11 | Rails.application.config.action_controller.forgery_protection_origin_check = true 12 | 13 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 14 | # Previous versions had false. 15 | ActiveSupport.to_time_preserves_timezone = true 16 | 17 | # Require `belongs_to` associations by default. Previous versions had false. 18 | Rails.application.config.active_record.belongs_to_required_by_default = true 19 | 20 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 21 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 22 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults_5_1.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.1 upgrade. 4 | # 5 | # Once upgraded flip defaults one by one to migrate to the new default. 6 | # 7 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 8 | 9 | # Make `form_with` generate non-remote forms. 10 | Rails.application.config.action_view.form_with_generates_remote_forms = false 11 | 12 | # Unknown asset fallback will return the path passed in when the given 13 | # asset is not present in the asset pipeline. 14 | # Rails.application.config.assets.unknown_asset_fallback = false 15 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults_5_2.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.2 upgrade. 4 | # 5 | # Once upgraded flip defaults one by one to migrate to the new default. 6 | # 7 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 8 | 9 | # Make Active Record use stable #cache_key alongside new #cache_version method. 10 | # This is needed for recyclable cache keys. 11 | # Rails.application.config.active_record.cache_versioning = true 12 | 13 | # Use AES-256-GCM authenticated encryption for encrypted cookies. 14 | # Also, embed cookie expiry in signed or encrypted cookies for increased security. 15 | # 16 | # This option is not backwards compatible with earlier Rails versions. 17 | # It's best enabled when your entire app is migrated and stable on 5.2. 18 | # 19 | # Existing cookies will be converted on read then written with the new scheme. 20 | # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true 21 | 22 | # Use AES-256-GCM authenticated encryption as default cipher for encrypting messages 23 | # instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. 24 | # Rails.application.config.active_support.use_authenticated_message_encryption = true 25 | 26 | # Add default protection from forgery to ActionController::Base instead of in 27 | # ApplicationController. 28 | # Rails.application.config.action_controller.default_protect_from_forgery = true 29 | 30 | # Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and 31 | # 'f' after migrating old data. 32 | # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true 33 | 34 | # Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. 35 | # Rails.application.config.active_support.use_sha1_digests = true 36 | 37 | # Make `form_with` generate id attributes for any generated HTML tags. 38 | # Rails.application.config.action_view.form_with_generates_ids = true 39 | -------------------------------------------------------------------------------- /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: "_react_rails_starter_app_session" 4 | -------------------------------------------------------------------------------- /config/initializers/webpack.rb: -------------------------------------------------------------------------------- 1 | 2 | def build_manifest(manifest_name, kind) 3 | configs_dir = Rails.root.join("config", "assets") 4 | Rails.configuration.webpack[manifest_name] = {} 5 | Dir.glob("#{configs_dir}/*#{kind}") do |file| 6 | app_name = File.basename(file).gsub("-#{kind}", "") 7 | Rails.configuration.webpack[manifest_name][app_name] = JSON.parse( 8 | File.read(file), 9 | ).with_indifferent_access 10 | end 11 | end 12 | 13 | if Rails.configuration.webpack[:use_manifest] 14 | build_manifest(:asset_manifest, "webpack-assets.json") 15 | build_manifest(:common_manifest, "webpack-common-manifest.json") 16 | end 17 | -------------------------------------------------------------------------------- /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 http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | on_worker_boot do 43 | ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root to: "home#index" 3 | 4 | devise_for :users, controllers: { 5 | sessions: "sessions", 6 | registrations: "registrations", 7 | omniauth_callbacks: "omniauth_callbacks", 8 | } 9 | 10 | as :user do 11 | get "/auth/failure" => "sessions#new" 12 | get "users/auth/:provider" => "users/omniauth_callbacks#passthru" 13 | get "sign_in" => "sessions#new" 14 | post "sign_in" => "sessions#create" 15 | get "sign_up" => "devise/registrations#new" 16 | delete "sign_out" => "sessions#destroy" 17 | get "sign_out" => "sessions#destroy" 18 | end 19 | 20 | resources :users 21 | 22 | namespace :api do 23 | resources :jwts 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /config/secrets.example.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | Defaults: &defaults 13 | 14 | admin_name: Administrator 15 | admin_email: admin@example.com 16 | admin_password: changeme 17 | 18 | encryption_key: '' 19 | 20 | # Main application settings 21 | application_name: "React Starter App" 22 | application_main_domain: <%= %Q{#{ENV["APP_SUBDOMAIN"]}.#{ENV["APP_ROOT_DOMAIN"] || "lvh.me"}} %> 23 | 24 | # Email Settings 25 | application_root_domain: <%= ENV["APP_ROOT_DOMAIN"] || "reactrailsstarterapp.com" %> 26 | email_provider_username: admin@example.com 27 | email_provider_password: changeme 28 | 29 | # Assets (webpack server in dev mode) 30 | assets_url: <%= ENV["ASSETS_URL"] || "http://localhost:8080" %> 31 | 32 | auth0_client_id: react_rails_starter_app 33 | auth0_client_secret: '' 34 | 35 | development: 36 | <<: *defaults 37 | 38 | secret_key_base: '' 39 | 40 | test: 41 | <<: *defaults 42 | 43 | secret_key_base: '' 44 | 45 | # Do not keep production secrets in the repository, 46 | # instead read values from the environment. 47 | production: 48 | <<: *defaults 49 | 50 | application_main_domain: <%= "#{ENV['APP_SUBDOMAIN']}.herokuapp.com" %> 51 | 52 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 53 | 54 | encryption_key: '' 55 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 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 | /* eslint-env node */ 2 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'; 3 | 4 | const environment = require('./environment'); 5 | 6 | module.exports = environment.toWebpackConfig(); 7 | -------------------------------------------------------------------------------- /config/webpack/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker'); 2 | 3 | const graphqlLoader = { 4 | test: /\.(graphql|gql)$/, 5 | exclude: /node_modules/, 6 | loader: 'graphql-tag/loader' 7 | }; 8 | 9 | // Insert json loader at the end of list 10 | environment.loaders.append('graphql', graphqlLoader); 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: client 5 | source_entry_path: packs 6 | public_root_path: public 7 | public_output_path: packs 8 | cache_path: tmp/cache/webpacker 9 | check_yarn_integrity: false 10 | webpack_compile_output: false 11 | 12 | # Additional paths webpack should lookup modules 13 | # ['app/assets', 'engine/foo/app/assets'] 14 | resolved_paths: [] 15 | 16 | # Reload manifest.json on all requests so we reload latest compiled packs 17 | cache_manifest: false 18 | 19 | # Extract and emit a css file 20 | extract_css: false 21 | 22 | static_assets_extensions: 23 | - .jpg 24 | - .jpeg 25 | - .png 26 | - .gif 27 | - .tiff 28 | - .ico 29 | - .svg 30 | - .eot 31 | - .otf 32 | - .ttf 33 | - .woff 34 | - .woff2 35 | 36 | extensions: 37 | - .jsx 38 | - .mjs 39 | - .js 40 | - .sass 41 | - .scss 42 | - .css 43 | - .module.sass 44 | - .module.scss 45 | - .module.css 46 | - .png 47 | - .svg 48 | - .gif 49 | - .jpeg 50 | - .jpg 51 | - .gql 52 | - .graphql 53 | 54 | development: 55 | <<: *default 56 | compile: true 57 | 58 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules 59 | check_yarn_integrity: true 60 | 61 | # Reference: https://webpack.js.org/configuration/dev-server/ 62 | dev_server: 63 | https: false 64 | # This path is setup in .env and requires that an entry be added to nginx 65 | # usually via bin/setup. NOTE be sure to update this value in each project 66 | public: rsa-assets.atomicjolt.xyz 67 | # hot module reload requires additional setup in each client app 68 | hmr: false 69 | # 'inline' causes the brower to refresh when changes occur. This does not 70 | # work in an lti app, since it will refresh the iframe and fail to load the 71 | # app again. It should be set to true if using HMR 72 | inline: false 73 | overlay: true 74 | compress: true 75 | disable_host_check: true 76 | use_local_ip: false 77 | quiet: false 78 | headers: 79 | 'Access-Control-Allow-Origin': '*' 80 | watch_options: 81 | ignored: '**/node_modules/**' 82 | 83 | 84 | test: 85 | <<: *default 86 | compile: false 87 | 88 | # Compile test packs to a separate directory 89 | public_output_path: packs-test 90 | 91 | production: 92 | <<: *default 93 | 94 | # Production depends on precompilation of packs prior to booting for performance. 95 | compile: false 96 | 97 | # Extract and emit a css file 98 | extract_css: true 99 | 100 | # Cache manifest.json for performance 101 | cache_manifest: true 102 | -------------------------------------------------------------------------------- /db/migrate/20120209004849_initial.rb: -------------------------------------------------------------------------------- 1 | class Initial < ActiveRecord::Migration[4.2] 2 | create_table "authentications", force: :cascade do |t| 3 | t.integer "user_id" 4 | t.string "provider" 5 | t.datetime "created_at", null: false 6 | t.datetime "updated_at", null: false 7 | t.text "json_response" 8 | t.string "uid" 9 | t.string "provider_avatar" 10 | t.string "username" 11 | t.string "provider_url", limit: 2048 12 | t.string "encrypted_token" 13 | t.string "encrypted_token_salt" 14 | t.string "encrypted_token_iv" 15 | t.string "encrypted_secret" 16 | t.string "encrypted_secret_salt" 17 | t.string "encrypted_secret_iv" 18 | t.string "encrypted_refresh_token" 19 | t.string "encrypted_refresh_token_salt" 20 | t.string "encrypted_refresh_token_iv" 21 | end 22 | 23 | add_index "authentications", ["provider", "uid"], name: "index_authentications_on_provider_and_uid", using: :btree 24 | add_index "authentications", ["user_id"], name: "index_authentications_on_user_id", using: :btree 25 | 26 | create_table "permissions", force: :cascade do |t| 27 | t.integer "role_id" 28 | t.integer "user_id" 29 | t.datetime "created_at" 30 | t.datetime "updated_at" 31 | end 32 | 33 | add_index "permissions", ["role_id", "user_id"], name: "index_permissions_on_role_id_and_user_id", using: :btree 34 | 35 | create_table "roles", force: :cascade do |t| 36 | t.string "name" 37 | t.datetime "created_at" 38 | t.datetime "updated_at" 39 | end 40 | 41 | create_table "users", force: :cascade do |t| 42 | t.string "email", default: "", null: false 43 | t.string "encrypted_password", default: "", null: false 44 | t.string "reset_password_token" 45 | t.datetime "reset_password_sent_at" 46 | t.datetime "remember_created_at" 47 | t.integer "sign_in_count", default: 0, null: false 48 | t.datetime "current_sign_in_at" 49 | t.datetime "last_sign_in_at" 50 | t.string "current_sign_in_ip" 51 | t.string "last_sign_in_ip" 52 | t.datetime "created_at" 53 | t.datetime "updated_at" 54 | t.string "name" 55 | t.string "confirmation_token" 56 | t.datetime "confirmed_at" 57 | t.datetime "confirmation_sent_at" 58 | t.string "unconfirmed_email" 59 | t.string "time_zone", default: "UTC" 60 | t.string "password_salt" 61 | end 62 | 63 | add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree 64 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree 65 | 66 | end -------------------------------------------------------------------------------- /db/migrate/20170612172246_add_context_id_to_permission.rb: -------------------------------------------------------------------------------- 1 | class AddContextIdToPermission < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :permissions, :context_id, :string 4 | add_index :permissions, :context_id 5 | add_index :permissions, [:role_id, :user_id, :context_id] 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20170613231518_add_create_type_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddCreateTypeToUser < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :create_method, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170921202325_add_authentication_index.rb: -------------------------------------------------------------------------------- 1 | class AddAuthenticationIndex < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :authentications, [ :uid, :provider, :provider_url ] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171003155408_change_rr_integer_to_bigint.rb: -------------------------------------------------------------------------------- 1 | class ChangeRrIntegerToBigint < ActiveRecord::Migration[5.0] 2 | def up 3 | change_column :authentications, :id, :bigint 4 | change_column :permissions, :id, :bigint 5 | change_column :roles, :id, :bigint 6 | change_column :users, :id, :bigint 7 | 8 | change_column :authentications, :user_id, :bigint 9 | change_column :permissions, :role_id, :bigint 10 | change_column :permissions, :user_id, :bigint 11 | change_column :users, :sign_in_count, :bigint 12 | change_column :users, :create_method, :bigint 13 | end 14 | 15 | def down 16 | change_column :authentications, :id, :integer 17 | change_column :permissions, :id, :integer 18 | change_column :roles, :id, :integer 19 | change_column :users, :id, :integer 20 | 21 | change_column :authentications, :user_id, :integer 22 | change_column :permissions, :role_id, :integer 23 | change_column :permissions, :user_id, :integer 24 | change_column :users, :sign_in_count, :integer 25 | change_column :users, :create_method, :integer 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /db/migrate/20181127204956_remove_unused_fields.rb: -------------------------------------------------------------------------------- 1 | class RemoveUnusedFields < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :authentications, :json_response, :text 4 | remove_column :users, :time_zone, :string, default: "UTC" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20190219201006_add_unique_index_to_permissions.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexToPermissions < ActiveRecord::Migration[5.1] 2 | def up 3 | remove_index :permissions, [:role_id, :user_id] 4 | remove_index :permissions, [:role_id, :user_id, :context_id] 5 | add_index :permissions, [:role_id, :user_id], unique: true, where: "(context_id IS NULL)" 6 | add_index :permissions, [:role_id, :user_id, :context_id], unique: true 7 | end 8 | 9 | def down 10 | remove_index :permissions, [:role_id, :user_id, :context_id] 11 | remove_index :permissions, [:role_id, :user_id] 12 | add_index :permissions, [:role_id, :user_id, :context_id] 13 | add_index :permissions, [:role_id, :user_id] 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /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: 20190219201006) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "authentications", force: :cascade do |t| 19 | t.bigint "user_id" 20 | t.string "provider" 21 | t.datetime "created_at", null: false 22 | t.datetime "updated_at", null: false 23 | t.string "uid" 24 | t.string "provider_avatar" 25 | t.string "username" 26 | t.string "provider_url", limit: 2048 27 | t.string "encrypted_token" 28 | t.string "encrypted_token_salt" 29 | t.string "encrypted_token_iv" 30 | t.string "encrypted_secret" 31 | t.string "encrypted_secret_salt" 32 | t.string "encrypted_secret_iv" 33 | t.string "encrypted_refresh_token" 34 | t.string "encrypted_refresh_token_salt" 35 | t.string "encrypted_refresh_token_iv" 36 | t.index ["provider", "uid"], name: "index_authentications_on_provider_and_uid" 37 | t.index ["uid", "provider", "provider_url"], name: "index_authentications_on_uid_and_provider_and_provider_url" 38 | t.index ["user_id"], name: "index_authentications_on_user_id" 39 | end 40 | 41 | create_table "permissions", force: :cascade do |t| 42 | t.bigint "role_id" 43 | t.bigint "user_id" 44 | t.datetime "created_at" 45 | t.datetime "updated_at" 46 | t.string "context_id" 47 | t.index ["context_id"], name: "index_permissions_on_context_id" 48 | t.index ["role_id", "user_id", "context_id"], name: "index_permissions_on_role_id_and_user_id_and_context_id", unique: true 49 | t.index ["role_id", "user_id"], name: "index_permissions_on_role_id_and_user_id", unique: true, where: "(context_id IS NULL)" 50 | end 51 | 52 | create_table "roles", force: :cascade do |t| 53 | t.string "name" 54 | t.datetime "created_at" 55 | t.datetime "updated_at" 56 | end 57 | 58 | create_table "users", force: :cascade do |t| 59 | t.string "email", default: "", null: false 60 | t.string "encrypted_password", default: "", null: false 61 | t.string "reset_password_token" 62 | t.datetime "reset_password_sent_at" 63 | t.datetime "remember_created_at" 64 | t.bigint "sign_in_count", default: 0, null: false 65 | t.datetime "current_sign_in_at" 66 | t.datetime "last_sign_in_at" 67 | t.string "current_sign_in_ip" 68 | t.string "last_sign_in_ip" 69 | t.datetime "created_at" 70 | t.datetime "updated_at" 71 | t.string "name" 72 | t.string "confirmation_token" 73 | t.datetime "confirmed_at" 74 | t.datetime "confirmation_sent_at" 75 | t.string "unconfirmed_email" 76 | t.string "password_salt" 77 | t.bigint "create_method", default: 0 78 | t.index ["email"], name: "index_users_on_email", unique: true 79 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # add an admin to the default account 2 | admin = CreateAdminService.new.call 3 | puts "CREATED ADMIN USER: " << admin.email 4 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react_rails_starter_app", 3 | "version": "1.0.0", 4 | "description": "React Starter App with Rails. Uses webpacker.", 5 | "author": "atomicjolt", 6 | "license": "MIT", 7 | "homepage": "https://github.com/atomicjolt/react_rails_starter_app", 8 | "scripts": { 9 | "test": "jest --config package.json", 10 | "test_update": "jest -u --config package.json", 11 | "test_debug": "node inspect jest --runInBand --config package.json || SUCCESS=false", 12 | "hot": "./bin/webpack-dev-server", 13 | "lint": "eslint client", 14 | "lint_fix": "eslint client --fix", 15 | "nuke": "rm -rf node_modules", 16 | "inspect_fuel": "\"${EDITOR:-vi}\" ./node_modules/atomic-fuel" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/atomicjolt/react_rails_starter_app" 21 | }, 22 | "keywords": [ 23 | "React", 24 | "Rails", 25 | "Atomic Jolt" 26 | ], 27 | "jest": { 28 | "roots": [ 29 | "/client" 30 | ], 31 | "verbose": true, 32 | "moduleDirectories": [ 33 | "node_modules", 34 | "/client" 35 | ], 36 | "transform": { 37 | "\\.(gql|graphql)$": "jest-transform-graphql", 38 | ".*": "babel-jest" 39 | }, 40 | "moduleNameMapper": { 41 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "./client/testing/__mocks__/file_mock.js", 42 | "\\.(sass|scss|css|less)$": "./client/testing/__mocks__/style_mock.js" 43 | }, 44 | "setupFilesAfterEnv": [ 45 | "./client/testing/shim.js", 46 | "./client/testing/setup_tests.js" 47 | ] 48 | }, 49 | "dependencies": { 50 | "@apollo/react-hooks": "^4.0.0", 51 | "@apollo/react-testing": "^4.0.0", 52 | "@babel/core": "^7.11.6", 53 | "@babel/plugin-proposal-class-properties": "^7.10.4", 54 | "@babel/plugin-proposal-object-rest-spread": "^7.11.0", 55 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 56 | "@babel/plugin-transform-destructuring": "^7.10.4", 57 | "@babel/plugin-transform-regenerator": "^7.10.4", 58 | "@babel/plugin-transform-runtime": "^7.11.5", 59 | "@babel/preset-env": "^7.11.5", 60 | "@babel/preset-react": "^7.10.4", 61 | "@rails/webpacker": "^4.2.2", 62 | "apollo-cache-inmemory": "^1.6.6", 63 | "apollo-client": "^2.6.10", 64 | "apollo-link": "^1.2.14", 65 | "apollo-link-http": "^1.5.17", 66 | "apollo-link-state": "^0.4.0", 67 | "atomic-fuel": "^4.8.1", 68 | "babel-loader": "^8.1.0", 69 | "babel-plugin-dynamic-import-node": "^2.3.3", 70 | "babel-plugin-macros": "^2.8.0", 71 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 72 | "babel-preset-es2015": "^6.24.1", 73 | "core-js": "^3.6.5", 74 | "css-loader": "^2.1.1", 75 | "es6-promise": "^4.2.4", 76 | "file-loader": "^4.3.0", 77 | "graphql": "^15.3.0", 78 | "graphql-tag": "^2.11.0", 79 | "history": "^4.10.1", 80 | "lodash": "4.17.20", 81 | "mime": "^2.4.6", 82 | "prop-types": "^15.7.2", 83 | "react": "^16.13.1", 84 | "react-apollo": "^3.1.5", 85 | "react-dom": "^16.13.1", 86 | "react-proxy-loader": "^0.3.5", 87 | "react-redux": "^7.1.3", 88 | "react-router": "^5.1.2", 89 | "react-router-dom": "^5.1.2", 90 | "react-router3": "npm:react-router@3.2.3", 91 | "redux": "4.0.5", 92 | "regenerator-runtime": "^0.13.7", 93 | "sass-loader": "^8.0.0", 94 | "style-loader": "^1.2.1", 95 | "superagent": "^5.2.1", 96 | "uuid": "^3.3.3", 97 | "webpack": "^4.44.1", 98 | "webpack-cli": "^3.3.12" 99 | }, 100 | "devDependencies": { 101 | "babel-eslint": "^10.1.0", 102 | "babel-jest": "^24.9.0", 103 | "enzyme": "^3.11.0", 104 | "enzyme-adapter-react-16": "^1.15.4", 105 | "enzyme-to-json": "^3.5.0", 106 | "eslint": "^6.8.0", 107 | "eslint-config-airbnb": "^18.2.0", 108 | "eslint-plugin-babel": "^5.3.1", 109 | "eslint-plugin-import": "^2.22.0", 110 | "eslint-plugin-jsx-a11y": "^6.3.1", 111 | "eslint-plugin-react": "^7.20.6", 112 | "eslint-plugin-react-hooks": "^2.3.0", 113 | "eventsource-polyfill": "^0.9.6", 114 | "jest": "^24.9.0", 115 | "jest-transform-graphql": "^2.1.0", 116 | "nock": "^11.7.2", 117 | "react-test-renderer": "^16.13.1", 118 | "wait-for-expect": "^3.0.2", 119 | "webpack-dev-server": "^3.10.1" 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require */ 2 | module.exports = { 3 | plugins: [ 4 | require('postcss-import'), 5 | require('postcss-flexbugs-fixes'), 6 | require('postcss-preset-env')({ 7 | autoprefixer: { 8 | flexbox: 'no-2009' 9 | }, 10 | stage: 3 11 | }) 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /public/401.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Access denied (401) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    You don't have permission to access the requested page.

    62 |
    63 |
    64 | 65 | 66 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

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

    63 |
    64 |

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

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

    The change you wanted was rejected.

    62 |

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

    63 |
    64 |

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

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

    We're sorry, but something went wrong.

    62 |
    63 |

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

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/android-icon-144x144.png -------------------------------------------------------------------------------- /public/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/android-icon-192x192.png -------------------------------------------------------------------------------- /public/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/android-icon-36x36.png -------------------------------------------------------------------------------- /public/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/android-icon-48x48.png -------------------------------------------------------------------------------- /public/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/android-icon-72x72.png -------------------------------------------------------------------------------- /public/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/android-icon-96x96.png -------------------------------------------------------------------------------- /public/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-114x114.png -------------------------------------------------------------------------------- /public/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-144x144.png -------------------------------------------------------------------------------- /public/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-57x57.png -------------------------------------------------------------------------------- /public/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-60x60.png -------------------------------------------------------------------------------- /public/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-72x72.png -------------------------------------------------------------------------------- /public/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-76x76.png -------------------------------------------------------------------------------- /public/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/apple-icon.png -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/favicon.ico -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/logo.png -------------------------------------------------------------------------------- /public/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/ms-icon-70x70.png -------------------------------------------------------------------------------- /public/oauth_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/public/oauth_icon.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.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/controllers/api/jwts_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe Api::JwtsController, type: :controller do 4 | before do 5 | @user = FactoryBot.create(:user) 6 | @user.confirm 7 | @user_token = AuthToken.issue_token({ user_id: @user.id }) 8 | end 9 | 10 | context "as user" do 11 | describe "GET show" do 12 | it "should not be authorized" do 13 | get :show, params: { id: @user.id }, format: :json 14 | expect(response).to have_http_status(:unauthorized) 15 | end 16 | it "should get a new jwt" do 17 | request.headers["Authorization"] = @user_token 18 | get :show, params: { id: @user.id }, format: :json 19 | expect(response).to have_http_status(:success) 20 | result = JSON.parse(response.body) 21 | expect(result["jwt"]).to be_present 22 | end 23 | end 24 | end 25 | 26 | describe "includes JwtToken" do 27 | it { expect(Api::JwtsController.ancestors.include?(Concerns::JwtToken)).to eq(true) } 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/controllers/concerns/jwt_token_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe ApplicationController, type: :controller do 4 | controller do 5 | include Concerns::JwtToken 6 | 7 | before_action :validate_token 8 | respond_to :json 9 | 10 | def index 11 | render plain: "User: #{@user.display_name}" 12 | end 13 | end 14 | 15 | context "no authorization header" do 16 | it "should not be authorized" do 17 | get :index, format: :json 18 | expect(response).to have_http_status(:unauthorized) 19 | end 20 | end 21 | 22 | context "invalid authorization header" do 23 | it "should not be authorized" do 24 | request.headers["Authorization"] = "A fake header" 25 | get :index, format: :json 26 | expect(response).to have_http_status(:unauthorized) 27 | end 28 | end 29 | 30 | context "valid authorization header" do 31 | it "should be authorized" do 32 | user = FactoryBot.create(:user) 33 | user.confirm 34 | user_token = AuthToken.issue_token({ user_id: user.id }) 35 | request.headers["Authorization"] = user_token 36 | get :index, format: :json 37 | expect(response).to have_http_status(:success) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/controllers/concerns/paging_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe ApplicationController, type: :controller do 4 | controller do 5 | include Concerns::Paging 6 | 7 | respond_to :json 8 | 9 | def index 10 | render plain: "Page: #{@page} Per Page: #{@per_page}" 11 | end 12 | end 13 | 14 | describe "paging" do 15 | it "should add paging to controller" do 16 | page = 1 17 | per_page = 1 18 | get :index, format: :json 19 | expect(response).to have_http_status(:success) 20 | expect(response.body).to eq("Page: #{page} Per Page: #{per_page}") 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/controllers/home_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe HomeController, type: :controller do 4 | describe "GET index" do 5 | it "loads the home page" do 6 | get :index 7 | expect(response).to have_http_status(200) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/_common.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | sequence :domain do |n| 3 | "www.example#{n}.com" 4 | end 5 | 6 | sequence :code do |n| 7 | "code#{n}" 8 | end 9 | 10 | sequence :name do |n| 11 | "user_#{n}" 12 | end 13 | 14 | sequence :email do |n| 15 | "user_#{n}@example.com" 16 | end 17 | 18 | sequence :password do |n| 19 | "password_#{n}" 20 | end 21 | 22 | sequence :title do |n| 23 | "a_title#{n}" 24 | end 25 | 26 | sequence :abbr do |n| 27 | "a#{n}" 28 | end 29 | 30 | sequence :uri do |n| 31 | "n#{n}.example.com" 32 | end 33 | 34 | sequence :description do |n| 35 | "This is the description: #{n}" 36 | end 37 | 38 | sequence :locale do |n| 39 | "a#{n}" 40 | end 41 | 42 | sequence :address do |n| 43 | "#{n} West #{n} South" 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/factories/authentication.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :authentication do 3 | user 4 | provider { FactoryBot.generate(:name) } 5 | token { FactoryBot.generate(:name) } 6 | secret { FactoryBot.generate(:password) } 7 | provider_url { FactoryBot.generate(:uri) } 8 | 9 | factory :authentication_facebook do 10 | provider { "facebook" } 11 | uid { "12345" } 12 | username { "myusername" } 13 | provider_avatar { "http://graph.facebook.com/12345/picture?type=large" } 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/factories/permission.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :permission do 3 | user 4 | role 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/role.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :role do 3 | name { FactoryBot.generate(:name) } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | name { FactoryBot.generate(:name) } 4 | email { FactoryBot.generate(:email) } 5 | password { FactoryBot.generate(:password) } 6 | after(:build, &:confirm) 7 | 8 | factory :user_facebook do 9 | active_avatar { "facebook" } 10 | provider_avatar { "http://graph.facebook.com/12345/picture?type=large" } 11 | after(:build) do |user| 12 | FakeWeb.register_uri(:get, user.provider_avatar, body: File.join(Rails.root, "spec", "fixtures", "avatar.jpg")) 13 | end 14 | end 15 | 16 | factory :user_with_avatar do 17 | avatar { File.open File.join(Rails.root, "spec", "fixtures", "avatar.jpg") } 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/fixtures/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/spec/fixtures/avatar.jpg -------------------------------------------------------------------------------- /spec/helpers/application_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe ApplicationHelper do 4 | describe "application_base_url" do 5 | it "adds a trailing / onto the request's base url" do 6 | expect(helper.application_base_url).to be 7 | end 8 | end 9 | 10 | describe "jwt_token" do 11 | it "generates a new jwt token" do 12 | expect(helper).to receive("signed_in?").and_return(true) 13 | expect(helper).to receive(:current_user).and_return(double(id: 1)) 14 | result = helper.jwt_token 15 | expect(result).to be 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/lib/auth_token_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe AuthToken do 4 | before do 5 | @user = FactoryBot.create(:user) 6 | end 7 | 8 | it "issues a valid jwt for the user id" do 9 | token = AuthToken.issue_token({ user_id: @user.id }) 10 | decoded = AuthToken.valid?(token) 11 | decoded_token = decoded[0] 12 | expect(decoded_token["user_id"]).to eq @user.id 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/lib/url_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "url_helper" 3 | 4 | describe UrlHelper do 5 | describe "#ensure_scheme" do 6 | it "should add http onto url if it doesn't exist" do 7 | expect(UrlHelper.ensure_scheme("www.example.com")).to eq("http://www.example.com") 8 | end 9 | it "should not add http onto url if it does exist" do 10 | expect(UrlHelper.ensure_scheme("https://www.example.com")).to eq("https://www.example.com") 11 | end 12 | end 13 | 14 | describe "#host" do 15 | it "should get the host name from the url" do 16 | expect(UrlHelper.host("http://www.example.com")).to eq("www.example.com") 17 | expect(UrlHelper.host("http://www.example.com/some/path")).to eq("www.example.com") 18 | expect(UrlHelper.host("http://www.example.com?some=thing")).to eq("www.example.com") 19 | expect(UrlHelper.host("www.example.com")).to eq("www.example.com") 20 | expect(UrlHelper.host("www.example.com/some/path")).to eq("www.example.com") 21 | expect(UrlHelper.host("www.example.com?some=thing")).to eq("www.example.com") 22 | end 23 | it "should get the host with subdomain from the url" do 24 | expect(UrlHelper.host("http://foo.example.com")).to eq("foo.example.com") 25 | expect(UrlHelper.host("http://foo.example.com/some/path")).to eq("foo.example.com") 26 | expect(UrlHelper.host("http://foo.example.com?some=thing")).to eq("foo.example.com") 27 | expect(UrlHelper.host("foo.example.com")).to eq("foo.example.com") 28 | expect(UrlHelper.host("foo.example.com/some/path")).to eq("foo.example.com") 29 | expect(UrlHelper.host("foo.example.com?some=thing")).to eq("foo.example.com") 30 | end 31 | end 32 | 33 | describe "#scheme_host" do 34 | it "should return the scheme and host" do 35 | expect(UrlHelper.scheme_host("https://www.example.com")).to eq("https://www.example.com") 36 | expect(UrlHelper.scheme_host("https://www.example.com/some/path")).to eq("https://www.example.com") 37 | expect(UrlHelper.scheme_host("https://www.example.com?some=thing")).to eq("https://www.example.com") 38 | expect(UrlHelper.scheme_host("http://www.example.com")).to eq("http://www.example.com") 39 | expect(UrlHelper.scheme_host("http://www.example.com/some/path")).to eq("http://www.example.com") 40 | expect(UrlHelper.scheme_host("http://www.example.com?some=thing")).to eq("http://www.example.com") 41 | expect(UrlHelper.scheme_host("www.example.com")).to eq("http://www.example.com") 42 | expect(UrlHelper.scheme_host("www.example.com/some/path")).to eq("http://www.example.com") 43 | expect(UrlHelper.scheme_host("www.example.com?some=thing")).to eq("http://www.example.com") 44 | end 45 | it "should return the scheme and host with subdomain" do 46 | expect(UrlHelper.scheme_host("https://foo.example.com")).to eq("https://foo.example.com") 47 | expect(UrlHelper.scheme_host("https://foo.example.com/some/path")).to eq("https://foo.example.com") 48 | expect(UrlHelper.scheme_host("https://foo.example.com?some=thing")).to eq("https://foo.example.com") 49 | expect(UrlHelper.scheme_host("http://foo.example.com")).to eq("http://foo.example.com") 50 | expect(UrlHelper.scheme_host("http://foo.example.com/some/path")).to eq("http://foo.example.com") 51 | expect(UrlHelper.scheme_host("http://foo.example.com?some=thing")).to eq("http://foo.example.com") 52 | expect(UrlHelper.scheme_host("foo.example.com")).to eq("http://foo.example.com") 53 | expect(UrlHelper.scheme_host("foo.example.com/some/path")).to eq("http://foo.example.com") 54 | expect(UrlHelper.scheme_host("foo.example.com?some=thing")).to eq("http://foo.example.com") 55 | end 56 | end 57 | 58 | describe "#host_from_instance_guid" do 59 | it "should get the base domain" do 60 | expect(UrlHelper.host_from_instance_guid("http://www.example.com")).to eq("example.com") 61 | expect(UrlHelper.host_from_instance_guid("http://foo.example.com")).to eq("example.com") 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/models/authentication_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Authentication, type: :model do 4 | before do 5 | @user = FactoryBot.create(:user) 6 | end 7 | 8 | it { should belong_to :user } 9 | 10 | it "Requires the provider" do 11 | authentication = FactoryBot.build(:authentication, provider: nil) 12 | expect(authentication.save).to eq(false) 13 | end 14 | 15 | describe "valid?" do 16 | it "is true when provider is unique" do 17 | provider = "asdf1234" 18 | authentication = create(:authentication, provider: provider) 19 | expect(authentication.valid?).to be true 20 | end 21 | 22 | it "is false for duplicate provider" do 23 | provider = "asdf1234" 24 | uid = "1234aoeu" 25 | provider_url = "example.com" 26 | create( 27 | :authentication, 28 | provider: provider, 29 | uid: uid, 30 | user: @user, 31 | provider_url: provider_url, 32 | ) 33 | authentication = build( 34 | :authentication, 35 | provider: provider, 36 | uid: uid, 37 | user: @user, 38 | provider_url: provider_url, 39 | ) 40 | expect(authentication.valid?(provider)).to be false 41 | end 42 | 43 | it "is false when provider is missing" do 44 | provider = nil 45 | authentication = build(:authentication, provider: provider) 46 | expect(authentication.valid?).to be false 47 | end 48 | end 49 | 50 | describe "for_auth" do 51 | it "finds an authentication given an auth object from omniauth" do 52 | auth = get_canvas_auth 53 | attributes = Authentication.authentication_attrs_from_auth(auth).merge(user_id: @user.id) 54 | Authentication.create!(attributes) 55 | authentication = Authentication.for_auth(auth) 56 | expect(authentication.provider_url).to eq auth["info"]["url"] 57 | end 58 | it "finds the correct authentication object when UIDs are duplicated" do 59 | canvas1 = { 60 | "uid" => "123", 61 | "info" => { 62 | "url" => "https://atomicjolt.instructure.com", 63 | }, 64 | } 65 | canvas2 = { 66 | "uid" => "123", 67 | "info" => { 68 | "url" => "https://canvas.instructure.com", 69 | }, 70 | } 71 | auth1 = get_canvas_auth(canvas1) 72 | auth2 = get_canvas_auth(canvas2) 73 | 74 | attributes1 = Authentication.authentication_attrs_from_auth(auth1) 75 | @user.authentications.create!(attributes1) 76 | 77 | attributes2 = Authentication.authentication_attrs_from_auth(auth2) 78 | @user.authentications.create!(attributes2) 79 | 80 | authentication1 = Authentication.for_auth(auth1) 81 | authentication2 = Authentication.for_auth(auth2) 82 | 83 | expect(authentication1.id).to_not eq(authentication2.id) 84 | expect(authentication1.uid).to eq(canvas1["uid"]) 85 | expect(authentication2.uid).to eq(canvas1["uid"]) 86 | 87 | expect(authentication1.provider_url).to eq(canvas1["info"]["url"]) 88 | expect(authentication2.provider_url).to eq(canvas2["info"]["url"]) 89 | end 90 | end 91 | 92 | describe "authentication_attrs_from_auth" do 93 | it "generates authentication attributes from an omniauth auth object" do 94 | auth = get_canvas_auth 95 | attributes = Authentication.authentication_attrs_from_auth(auth) 96 | expect(attributes[:uid]).to eq auth["uid"].to_s 97 | expect(attributes[:username]).to eq auth["info"]["nickname"] 98 | expect(attributes[:provider]).to eq auth["provider"] 99 | expect(attributes[:provider_url]).to eq auth["info"]["url"] 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /spec/models/permission_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Permission, type: :model do 4 | it "should find matching permissions with or without context" do 5 | context_id = "1234" 6 | permission = create(:permission) 7 | permission1 = create(:permission, context_id: context_id) 8 | permission2 = create(:permission, context_id: "asdf1234") 9 | permissions = Permission.by_nil_or_context(context_id) 10 | expect(permissions.include?(permission)).to be true 11 | expect(permissions.include?(permission1)).to be true 12 | expect(permissions.include?(permission2)).to be false 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/models/role_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe Role, type: :model do 4 | it "should order by name" do 5 | role1 = create(:role, name: "student") 6 | role2 = create(:role, name: "admin") 7 | role3 = create(:role, name: "teacher") 8 | roles = Role.by_alpha 9 | expect(roles.first).to eq(role2) 10 | expect(roles.second).to eq(role1) 11 | expect(roles.third).to eq(role3) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= "test" 3 | 4 | require "spec_helper" 5 | require File.expand_path("../../config/environment", __FILE__) 6 | require "rspec/rails" 7 | require "capybara/rails" 8 | require "database_cleaner" 9 | require "shoulda/matchers" 10 | require "webmock/rspec" 11 | 12 | # Requires supporting ruby files with custom matchers and macros, etc, in 13 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 14 | # run as spec files by default. This means that files in spec/support that end 15 | # in _spec.rb will both be required and run as specs, causing the specs to be 16 | # run twice. It is recommended that you do not name files matching this glob to 17 | # end with _spec.rb. You can configure this pattern with with the --pattern 18 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 19 | Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } 20 | 21 | # Checks for pending migrations before tests are run. 22 | # If you are not using ActiveRecord, you can remove this line. 23 | ActiveRecord::Migration.maintain_test_schema! 24 | 25 | RSpec.configure do |config| 26 | config.include FactoryBot::Syntax::Methods 27 | config.include Devise::Test::ControllerHelpers, type: :controller 28 | config.extend ControllerMacros, type: :controller 29 | 30 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 31 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 32 | 33 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 34 | # examples within a transaction, remove the following line or assign false 35 | # instead of true. 36 | config.use_transactional_fixtures = true 37 | 38 | # RSpec Rails can automatically mix in different behaviours to your tests 39 | # based on their file location, for example enabling you to call `get` and 40 | # `post` in specs under `spec/controllers`. 41 | # 42 | # You can disable this behaviour by removing the line below, and instead 43 | # explicitly tag your specs with their type, e.g.: 44 | # 45 | # RSpec.describe UsersController, :type => :controller do 46 | # # ... 47 | # end 48 | # 49 | # The different available types are documented in the features, such as in 50 | # https://relishapp.com/rspec/rspec-rails/docs 51 | config.infer_spec_type_from_file_location! 52 | 53 | config.before(:suite) do 54 | begin 55 | DatabaseCleaner.strategy = :transaction 56 | DatabaseCleaner.clean_with(:truncation) 57 | DatabaseCleaner.start 58 | # FactoryBot.lint 59 | ensure 60 | DatabaseCleaner.clean 61 | end 62 | 63 | # compile js once before tests run 64 | Webpacker.compile 65 | end 66 | 67 | config.append_after(:each) do 68 | DatabaseCleaner.clean 69 | end 70 | 71 | config.before(:each) do 72 | DatabaseCleaner.start 73 | end 74 | end 75 | 76 | Shoulda::Matchers.configure do |config| 77 | config.integrate do |with| 78 | with.test_framework :rspec 79 | with.library :rails 80 | end 81 | end 82 | 83 | Devise.setup do |config| 84 | config.omniauth :facebook, "1234", "1234", scope: "user" 85 | end 86 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause this 4 | # file to always be loaded, without a need to explicitly require it in any files. 5 | # 6 | # Given that it is always loaded, you are encouraged to keep this file as 7 | # light-weight as possible. Requiring heavyweight dependencies from this file 8 | # will add to the boot time of your test suite on EVERY test run, even for an 9 | # individual file that may not need all of that loaded. Instead, make a 10 | # separate helper file that requires this one and then use it only in the specs 11 | # that actually need it. 12 | # 13 | # The `.rspec` file also contains a few flags that are not defaults but that 14 | # users commonly want. 15 | # 16 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 17 | 18 | RSpec.configure do |config| 19 | # The settings below are suggested to provide a good initial experience 20 | # with RSpec, but feel free to customize to your heart's content. 21 | begin 22 | # These two settings work together to allow you to limit a spec run 23 | # to individual examples or groups you care about by tagging them with 24 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 25 | # get run. 26 | config.filter_run :focus 27 | config.run_all_when_everything_filtered = true 28 | 29 | # Many RSpec users commonly either run the entire suite or an individual 30 | # file, and it's useful to allow more verbose output when running an 31 | # individual spec file. 32 | if config.files_to_run.one? 33 | # Use the documentation formatter for detailed output, 34 | # unless a formatter has already been configured 35 | # (e.g. via a command-line flag). 36 | config.default_formatter = 'doc' 37 | end 38 | 39 | # Print the 10 slowest examples and example groups at the 40 | # end of the spec run, to help surface which specs are running 41 | # particularly slow. 42 | config.profile_examples = 10 43 | 44 | # Run specs in random order to surface order dependencies. If you find an 45 | # order dependency and want to debug it, you can fix the order by providing 46 | # the seed, which is printed after each run. 47 | # --seed 1234 48 | config.order = :random 49 | 50 | # Seed global randomization in this process using the `--seed` CLI option. 51 | # Setting this allows you to use `--seed` to deterministically reproduce 52 | # test failures related to randomization by passing the same `--seed` value 53 | # as the one that triggered the failure. 54 | Kernel.srand config.seed 55 | 56 | # rspec-expectations config goes here. You can use an alternate 57 | # assertion/expectation library such as wrong or the stdlib/minitest 58 | # assertions if you prefer. 59 | config.expect_with :rspec do |expectations| 60 | # Enable only the newer, non-monkey-patching expect syntax. 61 | # For more details, see: 62 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 63 | expectations.syntax = :expect 64 | end 65 | 66 | # rspec-mocks config goes here. You can use an alternate test double 67 | # library (such as bogus or mocha) by changing the `mock_with` option here. 68 | config.mock_with :rspec do |mocks| 69 | # Enable only the newer, non-monkey-patching expect syntax. 70 | # For more details, see: 71 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 72 | mocks.syntax = :expect 73 | 74 | # Prevents you from mocking or stubbing a method that does not exist on 75 | # a real object. This is generally recommended. 76 | mocks.verify_partial_doubles = true 77 | end 78 | 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/support/capybara.rb: -------------------------------------------------------------------------------- 1 | Capybara.asset_host = "http://localhost:3000" 2 | -------------------------------------------------------------------------------- /spec/support/controller_macros.rb: -------------------------------------------------------------------------------- 1 | module ControllerMacros 2 | def login_admin 3 | before(:each) do 4 | @request.env["devise.mapping"] = Devise.mappings[:admin] 5 | @admin ||= CreateAdminService.create_admin 6 | sign_in @admin 7 | end 8 | end 9 | 10 | def login_user 11 | before(:each) do 12 | @request.env["devise.mapping"] = Devise.mappings[:user] 13 | @user ||= FactoryBot.create(:user) 14 | @user.confirm # or set a confirmed_at inside the factory. Only necessary if you are using the "confirmable" module 15 | sign_in @user 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/support/http_party.rb: -------------------------------------------------------------------------------- 1 | def http_party_get_response(code = 200, code_response = "OK", body = "{foo:'bar'}") 2 | request_object = HTTParty::Request.new Net::HTTP::Get, "/" 3 | http_party_response(code, code_response, body, request_object) 4 | end 5 | 6 | def http_party_post_response(code = 200, code_response = "OK", body = "{foo:'bar'}") 7 | request_object = HTTParty::Request.new Net::HTTP::Post, "/" 8 | http_party_response(code, code_response, body, request_object) 9 | end 10 | 11 | def http_party_put_response(code = 200, code_response = "OK", body = "{foo:'bar'}") 12 | request_object = HTTParty::Request.new Net::HTTP::Put, "/" 13 | http_party_response(code, code_response, body, request_object) 14 | end 15 | 16 | def http_party_response(code, code_response, body, request_object) 17 | last_modified = Date.new(2010, 1, 15).to_s 18 | content_length = "1024" 19 | response_object = Net::HTTPOK.new("1.1", code.to_s, code_response) 20 | allow(response_object).to receive_messages(body: body) 21 | response_object["last-modified"] = last_modified 22 | response_object["content-length"] = content_length 23 | parsed_response = lambda { { "foo" => "bar" } } 24 | HTTParty::Response.new(request_object, response_object, parsed_response) 25 | end 26 | -------------------------------------------------------------------------------- /spec/support/webmock_requests.rb: -------------------------------------------------------------------------------- 1 | require "webmock/rspec" 2 | 3 | # Don't allow real connections when testing 4 | WebMock.disable_net_connect!(allow_localhost: true) 5 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomicjolt/react_rails_starter_app/3420640523b9d503054196f649fbe93461273b35/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------