├── .travis.yml ├── README.md ├── backend ├── .env.example ├── .gitignore ├── .rspec ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── README.md ├── Rakefile ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ └── .keep │ │ └── stylesheets │ │ │ └── application.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── concepts │ │ └── user │ │ │ └── representers │ │ │ └── user_representer.rb │ ├── controllers │ │ ├── application_controller.rb │ │ ├── auth_controller.rb │ │ └── concerns │ │ │ └── .keep │ ├── helpers │ │ └── application_helper.rb │ ├── jobs │ │ └── application_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── user.rb │ ├── serializers │ │ └── hash_serializer.rb │ └── views │ │ └── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb ├── bin │ ├── bundle │ ├── rails │ ├── rake │ ├── setup │ ├── spring │ └── update ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── database.travis.yml │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── application_controller_renderer.rb │ │ ├── backtrace_silencers.rb │ │ ├── cookies_serializer.rb │ │ ├── cors.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── new_framework_defaults.rb │ │ ├── reset_db.rb │ │ ├── session_store.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ ├── secrets.yml │ └── spring.rb ├── db │ ├── migrate │ │ ├── 20170113150159_enable_uuid_extension.rb │ │ └── 20170113202548_create_users.rb │ ├── schema.rb │ └── seeds.rb ├── lib │ ├── assets │ │ └── .keep │ ├── facebook_api.rb │ ├── json_web_token.rb │ └── tasks │ │ └── .keep ├── log │ └── .keep ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── favicon.ico │ └── robots.txt ├── spec │ ├── lib │ │ └── json_web_token_spec.rb │ ├── models │ │ └── user_spec.rb │ ├── rails_helper.rb │ ├── request │ │ └── auth_spec.rb │ └── spec_helper.rb ├── tmp │ └── .keep └── vendor │ └── assets │ └── stylesheets │ └── .keep ├── deploy └── frontend ├── .env.example ├── .eslintrc.js ├── .gitignore ├── .nvmrc ├── .tern-project ├── README.md ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.js ├── App.test.js ├── Navbar │ ├── LogoutButton │ │ ├── LogoutButton.js │ │ ├── actions.js │ │ └── index.js │ ├── Navbar.js │ └── index.js ├── Router.js ├── TodoList │ ├── Item.js │ ├── Item.test.js │ ├── NewItem.js │ ├── TodoList.css │ ├── TodoList.js │ ├── TodoList.test.js │ ├── __snapshots__ │ │ ├── Item.test.js.snap │ │ └── TodoList.test.js.snap │ ├── actions.js │ ├── api.js │ ├── api.mock.js │ ├── helpers.js │ ├── helpers.test.js │ ├── index.js │ ├── reducer.js │ ├── reducer.test.js │ └── sagas.js ├── Welcome │ └── index.js ├── apiMockAdapter.js ├── auth │ ├── actions.js │ ├── api.js │ ├── api.mock.js │ ├── reducer.js │ └── sagas.js ├── config.js ├── fbAuth │ ├── FacebookLoginButton │ │ ├── FacebookLoginButton.js │ │ └── index.js │ ├── actions.js │ └── reducer.js ├── index.css ├── index.js ├── logo.svg └── store.js └── yarn.lock /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 7.2.1 4 | addons: 5 | postgresql: "9.4" 6 | before_install: 7 | - pwd 8 | - rvm install 2.2.4 9 | - gem install bundler 10 | - nvm install 7.2.1 11 | install: 12 | - cd frontend && npm install && cd .. 13 | - cd backend && bundle && cd .. 14 | before_script: 15 | - psql -c 'create database test_db;' -U postgres 16 | - cp backend/config/database.travis.yml backend/config/database.yml 17 | script: 18 | - cd frontend && npm test && cd .. 19 | - cd backend && rspec && cd .. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-redux-rails-oauth-boilerplate 2 | 3 | **Warning: this code is not maintained, use at your own risk** 4 | 5 | [Live demo](http://holap.herokuapp.com/) 6 | 7 | Note: Both frontend and backend in the demo run on free Heroku dynos, they might need some time to boot. 8 | 9 | build status 10 | 11 | An experimental boilerplate with decoupled frontend/backend apps, featuring React, Redux, Redux sagas, Rails 5, Trailblazer,... with token authentication and using Facebook as an auth provider. 12 | 13 | The spirit of this boilerplate is to provide the cleanest, most scalable possible base for developing a web app with its frontend and backend completely decoupled. 14 | 15 | It aims to achieve maximal decoupling and componentization, both in frontend and in backend. Therefore, files are grouped by feature, not by type. 16 | 17 | The backend aims to minimize its dependecy to Rails, by capturing the business logic into services (simple, stateless ruby classes, and Trailblazer-like operations), an by abstracting dependencies to specific libraries (ActiveRecord, Koala gem, ...) 18 | 19 | Resources: 20 | 21 | * Article explaining the Feature First pattern for code organization: [Organizing Large React Applications](http://engineering.kapost.com/2016/01/organizing-large-react-applications/) 22 | 23 | # Basic directions 24 | 25 | ## Environment variables (mandatory step) 26 | 27 | Important configuration options are set through environment variables. 28 | 29 | The easiest way to define them is to use .env ([dotenv](https://www.npmjs.com/package/dotenv)) files. 30 | 31 | The frontend and the backend both need their separate .env files 32 | 33 | You need to create your own .env files, you can start by copying the example ones: 34 | 35 | ```sh 36 | cp frontend/.env.example frontend/.env 37 | cp backend/.env.example backend/.env 38 | ``` 39 | 40 | Remember to edit these files with your own credentials and preferences. 41 | 42 | ## Starting the frontend 43 | 44 | ```sh 45 | $ cd frontend; npm run start 46 | ``` 47 | 48 | ## Starting the backend 49 | 50 | ```sh 51 | $ cd backend; rails s -p 3001 52 | ``` 53 | 54 | You can now navigate to http://localhost:3000, try the Facebook authentication and the provided example features like the todolist. 55 | 56 | 57 | -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | FACEBOOK_APP_ID=2306XXXXXXXXXXX 2 | FACEBOOK_APP_SECRET=b583XXXXXXXXXXXXXXXXXXXXXXXXXXXX 3 | RESET_DB=true 4 | CORS_ORIGINS='localhost:3000' 5 | GEMNASIUM_TOKEN=1574cXXXXXXXXXXXXXXXXXXXXXXXXXXX 6 | GEMNASIUM_PROJECT_SLUG=github.com/danielres/react-redux-rails-oauth-boilerplate 7 | GEMNASIUM_TESTSUITE="rspec" 8 | -------------------------------------------------------------------------------- /backend/.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 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore Byebug command history file. 17 | .byebug_history 18 | 19 | .env 20 | -------------------------------------------------------------------------------- /backend/.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | --format doc 4 | -------------------------------------------------------------------------------- /backend/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | ruby '2.2.4' 3 | 4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 5 | gem 'rails', '~> 5.0.0', '>= 5.0.0.1' 6 | # Use postgresql as the database for Active Record 7 | gem 'pg', '~> 0.18' 8 | # Use Puma as the app server 9 | gem 'puma', '~> 3.0' 10 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 11 | gem 'jbuilder', '~> 2.5' 12 | # Use Redis adapter to run Action Cable in production 13 | # gem 'redis', '~> 3.0' 14 | # Use ActiveModel has_secure_password 15 | # gem 'bcrypt', '~> 3.1.7' 16 | 17 | # Use Capistrano for deployment 18 | # gem 'capistrano-rails', group: :development 19 | gem 'thor', '0.19.1' 20 | gem 'trailblazer-rails' 21 | gem "koala", "~> 2.2" 22 | gem "jwt" 23 | gem 'rack-cors' 24 | 25 | group :development, :test do 26 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 27 | gem 'byebug', platform: :mri 28 | gem 'dotenv-rails' 29 | gem 'database_cleaner' 30 | gem "rspec-rails" 31 | gem "guard" 32 | gem "guard-rspec" 33 | gem 'webmock' 34 | end 35 | 36 | group :development do 37 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 38 | gem 'web-console' 39 | gem 'listen', '~> 3.0.5' 40 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 41 | gem 'spring' 42 | gem 'spring-watcher-listen', '~> 2.0.0' 43 | gem 'guard' 44 | gem 'guard-minitest' 45 | end 46 | 47 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 48 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 49 | -------------------------------------------------------------------------------- /backend/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.0.1) 5 | actionpack (= 5.0.1) 6 | nio4r (~> 1.2) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.0.1) 9 | actionpack (= 5.0.1) 10 | actionview (= 5.0.1) 11 | activejob (= 5.0.1) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.0.1) 15 | actionview (= 5.0.1) 16 | activesupport (= 5.0.1) 17 | rack (~> 2.0) 18 | rack-test (~> 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.0.1) 22 | activesupport (= 5.0.1) 23 | builder (~> 3.1) 24 | erubis (~> 2.7.0) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 27 | activejob (5.0.1) 28 | activesupport (= 5.0.1) 29 | globalid (>= 0.3.6) 30 | activemodel (5.0.1) 31 | activesupport (= 5.0.1) 32 | activerecord (5.0.1) 33 | activemodel (= 5.0.1) 34 | activesupport (= 5.0.1) 35 | arel (~> 7.0) 36 | activesupport (5.0.1) 37 | concurrent-ruby (~> 1.0, >= 1.0.2) 38 | i18n (~> 0.7) 39 | minitest (~> 5.1) 40 | tzinfo (~> 1.1) 41 | addressable (2.5.0) 42 | public_suffix (~> 2.0, >= 2.0.2) 43 | arel (7.1.4) 44 | builder (3.2.3) 45 | byebug (9.0.6) 46 | coderay (1.1.1) 47 | concurrent-ruby (1.0.4) 48 | crack (0.4.3) 49 | safe_yaml (~> 1.0.0) 50 | database_cleaner (1.5.3) 51 | debug_inspector (0.0.2) 52 | declarative (0.0.8) 53 | uber (>= 0.0.15) 54 | diff-lcs (1.3) 55 | disposable (0.3.2) 56 | declarative (>= 0.0.8, < 1.0.0) 57 | representable (>= 2.4.0, <= 3.1.0) 58 | uber 59 | dotenv (2.1.2) 60 | dotenv-rails (2.1.2) 61 | dotenv (= 2.1.2) 62 | railties (>= 3.2, < 5.1) 63 | erubis (2.7.0) 64 | faraday (0.11.0) 65 | multipart-post (>= 1.2, < 3) 66 | ffi (1.9.17) 67 | formatador (0.2.5) 68 | globalid (0.3.7) 69 | activesupport (>= 4.1.0) 70 | guard (2.14.0) 71 | formatador (>= 0.2.4) 72 | listen (>= 2.7, < 4.0) 73 | lumberjack (~> 1.0) 74 | nenv (~> 0.1) 75 | notiffany (~> 0.0) 76 | pry (>= 0.9.12) 77 | shellany (~> 0.0) 78 | thor (>= 0.18.1) 79 | guard-compat (1.2.1) 80 | guard-minitest (2.4.6) 81 | guard-compat (~> 1.2) 82 | minitest (>= 3.0) 83 | guard-rspec (4.7.3) 84 | guard (~> 2.1) 85 | guard-compat (~> 1.1) 86 | rspec (>= 2.99.0, < 4.0) 87 | hashdiff (0.3.2) 88 | i18n (0.7.0) 89 | jbuilder (2.6.1) 90 | activesupport (>= 3.0.0, < 5.1) 91 | multi_json (~> 1.2) 92 | jwt (1.5.6) 93 | koala (2.4.0) 94 | addressable 95 | faraday 96 | multi_json (>= 1.3.0) 97 | listen (3.0.8) 98 | rb-fsevent (~> 0.9, >= 0.9.4) 99 | rb-inotify (~> 0.9, >= 0.9.7) 100 | loofah (2.0.3) 101 | nokogiri (>= 1.5.9) 102 | lumberjack (1.0.11) 103 | mail (2.6.4) 104 | mime-types (>= 1.16, < 4) 105 | method_source (0.8.2) 106 | mime-types (3.1) 107 | mime-types-data (~> 3.2015) 108 | mime-types-data (3.2016.0521) 109 | mini_portile2 (2.1.0) 110 | minitest (5.10.1) 111 | multi_json (1.12.1) 112 | multipart-post (2.0.0) 113 | nenv (0.3.0) 114 | nio4r (1.2.1) 115 | nokogiri (1.7.0.1) 116 | mini_portile2 (~> 2.1.0) 117 | notiffany (0.1.1) 118 | nenv (~> 0.1) 119 | shellany (~> 0.0) 120 | pg (0.19.0) 121 | pipetree (0.1.0) 122 | uber 123 | pry (0.10.4) 124 | coderay (~> 1.1.0) 125 | method_source (~> 0.8.1) 126 | slop (~> 3.4) 127 | public_suffix (2.0.5) 128 | puma (3.6.2) 129 | rack (2.0.1) 130 | rack-cors (0.4.0) 131 | rack-test (0.6.3) 132 | rack (>= 1.0) 133 | rails (5.0.1) 134 | actioncable (= 5.0.1) 135 | actionmailer (= 5.0.1) 136 | actionpack (= 5.0.1) 137 | actionview (= 5.0.1) 138 | activejob (= 5.0.1) 139 | activemodel (= 5.0.1) 140 | activerecord (= 5.0.1) 141 | activesupport (= 5.0.1) 142 | bundler (>= 1.3.0, < 2.0) 143 | railties (= 5.0.1) 144 | sprockets-rails (>= 2.0.0) 145 | rails-dom-testing (2.0.2) 146 | activesupport (>= 4.2.0, < 6.0) 147 | nokogiri (~> 1.6) 148 | rails-html-sanitizer (1.0.3) 149 | loofah (~> 2.0) 150 | railties (5.0.1) 151 | actionpack (= 5.0.1) 152 | activesupport (= 5.0.1) 153 | method_source 154 | rake (>= 0.8.7) 155 | thor (>= 0.18.1, < 2.0) 156 | rake (12.0.0) 157 | rb-fsevent (0.9.8) 158 | rb-inotify (0.9.7) 159 | ffi (>= 0.5.0) 160 | reform (2.2.3) 161 | disposable (>= 0.3.0, < 0.4.0) 162 | representable (>= 2.4.0, < 3.1.0) 163 | uber (>= 0.0.15, < 0.2.0) 164 | reform-rails (0.1.7) 165 | activemodel (>= 3.2) 166 | reform (>= 2.2.0) 167 | representable (3.0.2) 168 | declarative (~> 0.0.5) 169 | uber (>= 0.0.15, < 0.2.0) 170 | rspec (3.5.0) 171 | rspec-core (~> 3.5.0) 172 | rspec-expectations (~> 3.5.0) 173 | rspec-mocks (~> 3.5.0) 174 | rspec-core (3.5.4) 175 | rspec-support (~> 3.5.0) 176 | rspec-expectations (3.5.0) 177 | diff-lcs (>= 1.2.0, < 2.0) 178 | rspec-support (~> 3.5.0) 179 | rspec-mocks (3.5.0) 180 | diff-lcs (>= 1.2.0, < 2.0) 181 | rspec-support (~> 3.5.0) 182 | rspec-rails (3.5.2) 183 | actionpack (>= 3.0) 184 | activesupport (>= 3.0) 185 | railties (>= 3.0) 186 | rspec-core (~> 3.5.0) 187 | rspec-expectations (~> 3.5.0) 188 | rspec-mocks (~> 3.5.0) 189 | rspec-support (~> 3.5.0) 190 | rspec-support (3.5.0) 191 | safe_yaml (1.0.4) 192 | shellany (0.0.1) 193 | slop (3.6.0) 194 | spring (2.0.0) 195 | activesupport (>= 4.2) 196 | spring-watcher-listen (2.0.1) 197 | listen (>= 2.7, < 4.0) 198 | spring (>= 1.2, < 3.0) 199 | sprockets (3.7.1) 200 | concurrent-ruby (~> 1.0) 201 | rack (> 1, < 3) 202 | sprockets-rails (3.2.0) 203 | actionpack (>= 4.0) 204 | activesupport (>= 4.0) 205 | sprockets (>= 3.0.0) 206 | thor (0.19.1) 207 | thread_safe (0.3.5) 208 | trailblazer (2.0.1) 209 | declarative 210 | reform (>= 2.2.0, < 3.0.0) 211 | trailblazer-operation (>= 0.0.10, < 0.1.0) 212 | uber (>= 0.1.0, < 0.2.0) 213 | trailblazer-loader (0.1.0) 214 | trailblazer-operation (0.0.10) 215 | declarative 216 | pipetree (>= 0.1.0, < 0.2.0) 217 | uber (>= 0.1.0, < 0.2.0) 218 | trailblazer-rails (1.0.2) 219 | reform-rails (>= 0.1.4, < 0.2.0) 220 | trailblazer (>= 2.0.0, < 2.1.0) 221 | trailblazer-loader (>= 0.1.0) 222 | tzinfo (1.2.2) 223 | thread_safe (~> 0.1) 224 | uber (0.1.0) 225 | web-console (3.4.0) 226 | actionview (>= 5.0) 227 | activemodel (>= 5.0) 228 | debug_inspector 229 | railties (>= 5.0) 230 | webmock (2.3.2) 231 | addressable (>= 2.3.6) 232 | crack (>= 0.3.2) 233 | hashdiff 234 | websocket-driver (0.6.4) 235 | websocket-extensions (>= 0.1.0) 236 | websocket-extensions (0.1.2) 237 | 238 | PLATFORMS 239 | ruby 240 | 241 | DEPENDENCIES 242 | byebug 243 | database_cleaner 244 | dotenv-rails 245 | guard 246 | guard-minitest 247 | guard-rspec 248 | jbuilder (~> 2.5) 249 | jwt 250 | koala (~> 2.2) 251 | listen (~> 3.0.5) 252 | pg (~> 0.18) 253 | puma (~> 3.0) 254 | rack-cors 255 | rails (~> 5.0.0, >= 5.0.0.1) 256 | rspec-rails 257 | spring 258 | spring-watcher-listen (~> 2.0.0) 259 | thor (= 0.19.1) 260 | trailblazer-rails 261 | tzinfo-data 262 | web-console 263 | webmock 264 | 265 | RUBY VERSION 266 | ruby 2.2.4p230 267 | 268 | BUNDLED WITH 269 | 1.13.7 270 | -------------------------------------------------------------------------------- /backend/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: "bundle exec 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 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /backend/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_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /backend/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /backend/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/app/assets/images/.keep -------------------------------------------------------------------------------- /backend/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /backend/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /backend/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /backend/app/concepts/user/representers/user_representer.rb: -------------------------------------------------------------------------------- 1 | class UserRepresenter < Representable::Decorator 2 | include Representable::JSON 3 | property :id 4 | property :profile 5 | property :inviter_id 6 | property :created_at 7 | property :updated_at 8 | property :access_token, as: :accessToken 9 | end 10 | -------------------------------------------------------------------------------- /backend/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /backend/app/controllers/auth_controller.rb: -------------------------------------------------------------------------------- 1 | class AuthController < ApplicationController 2 | def auth_with_access_token 3 | user = User.find_by_access_token(params[:accessToken]) 4 | if user 5 | render json: UserRepresenter.new(user).to_json 6 | else 7 | render json: {error: "not-found"}, status: :not_found 8 | end 9 | end 10 | 11 | def auth_with_provider 12 | user = User.find_or_create_by_provider(params[:provider], params['oauthAccessToken']) 13 | if user 14 | render json: UserRepresenter.new(user).to_json 15 | else 16 | render json: {error: "not-found"}, status: :not_found 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /backend/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /backend/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /backend/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /backend/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /backend/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /backend/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/app/models/concerns/.keep -------------------------------------------------------------------------------- /backend/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | def self.find_by_access_token(access_token) 3 | decoded = JsonWebToken.decode(access_token) 4 | return nil unless decoded 5 | find(decoded['user_id']) rescue nil 6 | end 7 | 8 | def self.find_or_create_by_provider(provider, oauth_access_token) 9 | case provider 10 | when 'facebook' 11 | fb_user = FacebookApi.find_user(oauth_access_token) 12 | else 13 | return nil 14 | end 15 | user = where("auths -> 'facebook' ->> 'id' = ?", fb_user['id']).first 16 | user || User.create(auths: {facebook: fb_user}) 17 | end 18 | 19 | def access_token 20 | JsonWebToken.encode({user_id: id}) 21 | end 22 | 23 | def to_h 24 | JSON.parse(to_json) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /backend/app/serializers/hash_serializer.rb: -------------------------------------------------------------------------------- 1 | class HashSerializer 2 | def self.dump(hash) 3 | hash.to_json 4 | end 5 | 6 | def self.load(hash) 7 | (hash || {}).with_indifferent_access 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /backend/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Todolist1 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all' %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /backend/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /backend/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /backend/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /backend/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /backend/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 | if spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 13 | gem 'spring', spring.version 14 | require 'spring/binstub' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /backend/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /backend/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /backend/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "action_controller/railtie" 9 | require "action_mailer/railtie" 10 | require "action_view/railtie" 11 | require "action_cable/engine" 12 | # require "sprockets/railtie" 13 | # require "rails/test_unit/railtie" 14 | 15 | # Require the gems listed in Gemfile, including any gems 16 | # you've limited to :test, :development, or :production. 17 | Bundler.require(*Rails.groups) 18 | 19 | module Todolist1 20 | class Application < Rails::Application 21 | # Settings in config/environments/* take precedence over those specified here. 22 | # Application configuration should go into files in config/initializers 23 | # -- all .rb files in that directory are automatically loaded. 24 | config.generators do |g| 25 | g.orm :active_record, primary_key_type: :uuid 26 | end 27 | config.autoload_paths << "#{Rails.root}/lib" 28 | config.eager_load_paths << Rails.root.join('lib') 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /backend/config/database.travis.yml: -------------------------------------------------------------------------------- 1 | test: 2 | adapter: postgresql 3 | encoding: unicode 4 | username: postgres 5 | password: postgres 6 | # For details on connection pooling, see rails configuration guide 7 | # http://guides.rubyonrails.org/configuring.html#database-pooling 8 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 9 | host: localhost 10 | database: test_db 11 | -------------------------------------------------------------------------------- /backend/config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | username: todolist1 21 | password: todolist1 22 | # For details on connection pooling, see rails configuration guide 23 | # http://guides.rubyonrails.org/configuring.html#database-pooling 24 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 25 | host: localhost 26 | 27 | development: 28 | <<: *default 29 | database: todolist1_development 30 | 31 | # The specified database role being used to connect to postgres. 32 | # To create additional roles in postgres see `$ createuser --help`. 33 | # When left blank, postgres will use the default role. This is 34 | # the same name as the operating system user that initialized the database. 35 | 36 | # The password associated with the postgres role (username). 37 | 38 | # Connect on a TCP socket. Omitted by default since the client uses a 39 | # domain socket that doesn't need configuration. Windows does not have 40 | # domain sockets, so uncomment these lines. 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: todolist1_test 61 | 62 | # As with config/secrets.yml, you never want to store sensitive information, 63 | # like your database password, in your source code. If your source code is 64 | # ever seen by anyone, they now have access to your database. 65 | # 66 | # Instead, provide the password as a unix environment variable when you boot 67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 68 | # for a full rundown on how to provide these environment variables in a 69 | # production deployment. 70 | # 71 | # On Heroku and other platform providers, you may have a full connection URL 72 | # available as an environment variable. For example: 73 | # 74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 75 | # 76 | # You can use this database configuration with: 77 | # 78 | # production: 79 | # url: <%= ENV['DATABASE_URL'] %> 80 | # 81 | production: 82 | <<: *default 83 | database: todolist1_production 84 | username: todolist1 85 | password: <%= ENV['TODOLIST1_DATABASE_PASSWORD'] %> 86 | -------------------------------------------------------------------------------- /backend/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /backend/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 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | 44 | # Use an evented file watcher to asynchronously detect changes in source code, 45 | # routes, locales, etc. This feature depends on the listen gem. 46 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 47 | end 48 | -------------------------------------------------------------------------------- /backend/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | 21 | 22 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 23 | # config.action_controller.asset_host = 'http://assets.example.com' 24 | 25 | # Specifies the header that your server uses for sending files. 26 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 27 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 28 | 29 | # Mount Action Cable outside main process or domain 30 | # config.action_cable.mount_path = nil 31 | # config.action_cable.url = 'wss://example.com/cable' 32 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 33 | 34 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 35 | # config.force_ssl = true 36 | 37 | # Use the lowest log level to ensure availability of diagnostic information 38 | # when problems arise. 39 | config.log_level = :debug 40 | 41 | # Prepend all log lines with the following tags. 42 | config.log_tags = [ :request_id ] 43 | 44 | # Use a different cache store in production. 45 | # config.cache_store = :mem_cache_store 46 | 47 | # Use a real queuing backend for Active Job (and separate queues per environment) 48 | # config.active_job.queue_adapter = :resque 49 | # config.active_job.queue_name_prefix = "todolist1_#{Rails.env}" 50 | config.action_mailer.perform_caching = false 51 | 52 | # Ignore bad email addresses and do not raise email delivery errors. 53 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 54 | # config.action_mailer.raise_delivery_errors = false 55 | 56 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 57 | # the I18n.default_locale when a translation cannot be found). 58 | config.i18n.fallbacks = true 59 | 60 | # Send deprecation notices to registered listeners. 61 | config.active_support.deprecation = :notify 62 | 63 | # Use default logging formatter so that PID and timestamp are not suppressed. 64 | config.log_formatter = ::Logger::Formatter.new 65 | 66 | # Use a different logger for distributed setups. 67 | # require 'syslog/logger' 68 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 69 | 70 | if ENV["RAILS_LOG_TO_STDOUT"].present? 71 | logger = ActiveSupport::Logger.new(STDOUT) 72 | logger.formatter = config.log_formatter 73 | config.logger = ActiveSupport::TaggedLogging.new(logger) 74 | end 75 | 76 | # Do not dump schema after migrations. 77 | config.active_record.dump_schema_after_migration = false 78 | end 79 | -------------------------------------------------------------------------------- /backend/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=3600' 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 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /backend/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 2 | allow do 3 | origins ENV['CORS_ORIGINS'] || '' 4 | resource '*', 5 | headers: :any, 6 | methods: %i(get post put patch delete options head) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 Rails 5.0 release notes 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 | # Do not halt callback chains when a callback returns false. Previous versions had true. 21 | ActiveSupport.halt_callback_chains_on_return_false = false 22 | 23 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 24 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 25 | -------------------------------------------------------------------------------- /backend/config/initializers/reset_db.rb: -------------------------------------------------------------------------------- 1 | if(defined? DatabaseCleaner) 2 | DatabaseCleaner.strategy = :truncation 3 | DatabaseCleaner.clean if(ENV['RESET_DB'] == 'true') 4 | end 5 | -------------------------------------------------------------------------------- /backend/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: '_todolist1_session' 4 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 3 | match 'auth', to: 'auth#auth_with_access_token', via: [:post] 4 | match 'auth/:provider', to: 'auth#auth_with_provider', via: [:post] 5 | end 6 | -------------------------------------------------------------------------------- /backend/config/secrets.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 `rails 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 | 13 | development: 14 | secret_key_base: 42a6e17d9b59c3b06d146bda2dc19b4c728269cfbec5ea09cd601f1c36a1d411979a03629139c24ff7ea8bbd6eea75af9834caa06a92d24da712bf89220107ea 15 | 16 | test: 17 | secret_key_base: 7f2021fa05c0cfc5cd5c72885cc274aadaf1e3edc6c09eed3440a1947e53efbe60c71d443d1bbd3ca9f6b74ef54b05fefd028634a1ab3cc2f45eee52610f70d9 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/db/migrate/20170113150159_enable_uuid_extension.rb: -------------------------------------------------------------------------------- 1 | class EnableUuidExtension < ActiveRecord::Migration[5.0] 2 | def change 3 | enable_extension 'uuid-ossp' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /backend/db/migrate/20170113202548_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :users, id: :uuid do |t| 4 | t.jsonb :auths, index: true, default: {facebook: {}} 5 | t.jsonb :profile, index: true, default: { 6 | display_name: nil, 7 | first_name: nil, 8 | last_name: nil, 9 | email: nil, 10 | phone: nil, 11 | membership: { 12 | start_date: nil, 13 | end_date: nil, 14 | }, 15 | facebook:{ 16 | profile_url: nil, 17 | member_intro_url: nil, 18 | invitation_url: nil, 19 | } 20 | } 21 | t.uuid :inviter_id, index: true 22 | t.timestamps 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /backend/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: 20170113202548) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | enable_extension "uuid-ossp" 18 | 19 | create_table "users", id: :uuid, default: -> { "uuid_generate_v4()" }, force: :cascade do |t| 20 | t.jsonb "auths", default: {"facebook"=>{}} 21 | t.jsonb "profile", default: {"email"=>nil, "phone"=>nil, "facebook"=>{"profile_url"=>nil, "invitation_url"=>nil, "member_intro_url"=>nil}, "last_name"=>nil, "first_name"=>nil, "membership"=>{"end_date"=>nil, "start_date"=>nil}, "display_name"=>nil} 22 | t.uuid "inviter_id" 23 | t.datetime "created_at", null: false 24 | t.datetime "updated_at", null: false 25 | t.index ["auths"], name: "index_users_on_auths", using: :btree 26 | t.index ["inviter_id"], name: "index_users_on_inviter_id", using: :btree 27 | t.index ["profile"], name: "index_users_on_profile", using: :btree 28 | end 29 | 30 | end 31 | -------------------------------------------------------------------------------- /backend/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /backend/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/lib/assets/.keep -------------------------------------------------------------------------------- /backend/lib/facebook_api.rb: -------------------------------------------------------------------------------- 1 | class FacebookApi 2 | def self.find_user(oauth_access_token) 3 | Koala::Facebook::API.new(oauth_access_token, ENV['FACEBOOK_APP_SECRET']).get_object('me') 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /backend/lib/json_web_token.rb: -------------------------------------------------------------------------------- 1 | class JsonWebToken 2 | def self.encode(payload) 3 | JWT.encode(payload, Rails.application.secrets.secret_key_base) 4 | end 5 | 6 | def self.decode(token) 7 | return HashWithIndifferentAccess.new(JWT.decode(token, Rails.application.secrets.secret_key_base)[0]) 8 | rescue 9 | nil 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /backend/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/lib/tasks/.keep -------------------------------------------------------------------------------- /backend/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/log/.keep -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /backend/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/public/apple-touch-icon.png -------------------------------------------------------------------------------- /backend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/public/favicon.ico -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/spec/lib/json_web_token_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | describe JsonWebToken do 4 | let(:payload) { {'foo' => 'foo'} } 5 | 6 | it "can encode and decode a hash" do 7 | encoded = JsonWebToken.encode(payload) 8 | result = JsonWebToken.decode(encoded) 9 | expect(result).to eq(payload) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /backend/spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe User, type: :model do 4 | let(:user) { User.create } 5 | 6 | it do 7 | expect(user).to be_valid 8 | end 9 | 10 | it "implements #access_token" do 11 | expect(user.access_token.length > 100).to eq true 12 | end 13 | 14 | it "implements #find_by_access_token" do 15 | access_token = user.access_token 16 | expect( User.find_by_access_token(access_token) ).to eq(user) 17 | end 18 | 19 | describe '#find_or_create_by_provider' do 20 | it 'finds user if returning' do 21 | User.create(profile: {display_name: 'Tom'}, auths: {facebook: {id: '123'}}) 22 | stub_request(:get, /.*graph.facebook.com\/me\?.*/). 23 | to_return(status: 200, body: {id: '123'}.to_json, headers: {}) 24 | 25 | expect( User.find_or_create_by_provider('facebook', '_').profile['display_name'] ).to eq('Tom') 26 | end 27 | 28 | it 'creates user if first-timer' do 29 | stub_request(:get, /.*graph.facebook.com\/me\?.*/). 30 | to_return(status: 200, body: {id: '123'}.to_json, headers: {}) 31 | 32 | expect{ User.find_or_create_by_provider('facebook', '_') } 33 | .to change{User.count} 34 | .from(0) 35 | .to(1) 36 | expect(User.last.auths['facebook']['id']).to eq('123') 37 | end 38 | end 39 | end 40 | 41 | 42 | -------------------------------------------------------------------------------- /backend/spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require File.expand_path('../../config/environment', __FILE__) 4 | # Prevent database truncation if the environment is production 5 | abort("The Rails environment is running in production mode!") if Rails.env.production? 6 | require 'spec_helper' 7 | require 'rspec/rails' 8 | # Add additional requires below this line. Rails is not loaded until this point! 9 | 10 | # Requires supporting ruby files with custom matchers and macros, etc, in 11 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 12 | # run as spec files by default. This means that files in spec/support that end 13 | # in _spec.rb will both be required and run as specs, causing the specs to be 14 | # run twice. It is recommended that you do not name files matching this glob to 15 | # end with _spec.rb. You can configure this pattern with the --pattern 16 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 17 | # 18 | # The following line is provided for convenience purposes. It has the downside 19 | # of increasing the boot-up time by auto-requiring all files in the support 20 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 21 | # require only the support files necessary. 22 | # 23 | # Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 24 | 25 | # Checks for pending migration and applies them before tests are run. 26 | # If you are not using ActiveRecord, you can remove this line. 27 | ActiveRecord::Migration.maintain_test_schema! 28 | 29 | RSpec.configure do |config| 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 | # Filter lines from Rails gems in backtraces. 54 | config.filter_rails_from_backtrace! 55 | # arbitrary gems may also be filtered via: 56 | # config.filter_gems_from_backtrace("gem name") 57 | end 58 | -------------------------------------------------------------------------------- /backend/spec/request/auth_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | RSpec.describe "find user by access_token", type: :request do 4 | describe 'access token invalid' do 5 | it 'returns json with error + code "not_found"' do 6 | post "/auth", params: {accessToken: 'non-valid'} 7 | expect(response.content_type).to eq("application/json") 8 | expect(response).to have_http_status(:not_found) 9 | expect(response.body).to eq({error: "not-found"}.to_json) 10 | end 11 | end 12 | 13 | describe 'access token valid' do 14 | it 'returns json with found user + accessToken + code "ok"' do 15 | user = User.create 16 | allow(User).to receive(:find_by_access_token){ user } 17 | 18 | post "/auth", params: {accessToken: user.access_token} 19 | expect(response.content_type).to eq("application/json") 20 | expect(response).to have_http_status(:ok) 21 | expect(JSON.parse(response.body)['id']).to eq(user.id) 22 | expect(JSON.parse(response.body)['accessToken']) 23 | .to eq(JsonWebToken.encode({user_id: user.id})) 24 | end 25 | end 26 | end 27 | 28 | RSpec.describe "find or create user by provider + oauthAccessToken", type: :request do 29 | describe 'provider not supported' do 30 | it 'returns json with error + code "not_found"' do 31 | post '/auth/whateverbook', params: {oauthAccessToken: '_'} 32 | expect(response.content_type).to eq('application/json') 33 | expect(response).to have_http_status(:not_found) 34 | expect(response.body).to eq({error: 'not-found'}.to_json) 35 | end 36 | end 37 | 38 | describe 'oauth failed' do 39 | it 'returns json with error + code "not_found"' do 40 | allow(User).to receive(:find_or_create_by_provider){ nil } 41 | 42 | post '/auth/facebook' 43 | expect(response.content_type).to eq('application/json') 44 | expect(response).to have_http_status(:not_found) 45 | expect(response.body).to eq({error: 'not-found'}.to_json) 46 | end 47 | end 48 | 49 | describe 'oauth succeeded' do 50 | it 'returns json with found user + accessToken + code "ok"' do 51 | user = User.create 52 | allow(User).to receive(:find_or_create_by_provider){user} 53 | 54 | post "/auth/facebook" 55 | expect(response.content_type).to eq('application/json') 56 | expect(response).to have_http_status(:ok) 57 | expect(JSON.parse(response.body)['accessToken']) 58 | .to eq(JsonWebToken.encode({user_id: user.id})) 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /backend/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | require 'webmock/rspec' 20 | WebMock.disable_net_connect!(allow_localhost: true) 21 | 22 | RSpec.configure do |config| 23 | # rspec-expectations config goes here. You can use an alternate 24 | # assertion/expectation library such as wrong or the stdlib/minitest 25 | # assertions if you prefer. 26 | config.expect_with :rspec do |expectations| 27 | # This option will default to `true` in RSpec 4. It makes the `description` 28 | # and `failure_message` of custom matchers include text for helper methods 29 | # defined using `chain`, e.g.: 30 | # be_bigger_than(2).and_smaller_than(4).description 31 | # # => "be bigger than 2 and smaller than 4" 32 | # ...rather than: 33 | # # => "be bigger than 2" 34 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 35 | end 36 | 37 | # rspec-mocks config goes here. You can use an alternate test double 38 | # library (such as bogus or mocha) by changing the `mock_with` option here. 39 | config.mock_with :rspec do |mocks| 40 | # Prevents you from mocking or stubbing a method that does not exist on 41 | # a real object. This is generally recommended, and will default to 42 | # `true` in RSpec 4. 43 | mocks.verify_partial_doubles = true 44 | end 45 | 46 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 47 | # have no way to turn it off -- the option exists only for backwards 48 | # compatibility in RSpec 3). It causes shared context metadata to be 49 | # inherited by the metadata hash of host groups and examples, rather than 50 | # triggering implicit auto-inclusion in groups with matching metadata. 51 | config.shared_context_metadata_behavior = :apply_to_host_groups 52 | 53 | config.filter_run_when_matching :focus 54 | 55 | # The settings below are suggested to provide a good initial experience 56 | # with RSpec, but feel free to customize to your heart's content. 57 | =begin 58 | # This allows you to limit a spec run to individual examples or groups 59 | # you care about by tagging them with `:focus` metadata. When nothing 60 | # is tagged with `:focus`, all examples get run. RSpec also provides 61 | # aliases for `it`, `describe`, and `context` that include `:focus` 62 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 63 | config.filter_run_when_matching :focus 64 | 65 | # Allows RSpec to persist some state between runs in order to support 66 | # the `--only-failures` and `--next-failure` CLI options. We recommend 67 | # you configure your source control system to ignore this file. 68 | config.example_status_persistence_file_path = "spec/examples.txt" 69 | 70 | # Limits the available syntax to the non-monkey patched syntax that is 71 | # recommended. For more details, see: 72 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 73 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 74 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 75 | config.disable_monkey_patching! 76 | 77 | # Many RSpec users commonly either run the entire suite or an individual 78 | # file, and it's useful to allow more verbose output when running an 79 | # individual spec file. 80 | if config.files_to_run.one? 81 | # Use the documentation formatter for detailed output, 82 | # unless a formatter has already been configured 83 | # (e.g. via a command-line flag). 84 | config.default_formatter = 'doc' 85 | end 86 | 87 | # Print the 10 slowest examples and example groups at the 88 | # end of the spec run, to help surface which specs are running 89 | # particularly slow. 90 | config.profile_examples = 10 91 | 92 | # Run specs in random order to surface order dependencies. If you find an 93 | # order dependency and want to debug it, you can fix the order by providing 94 | # the seed, which is printed after each run. 95 | # --seed 1234 96 | config.order = :random 97 | 98 | # Seed global randomization in this process using the `--seed` CLI option. 99 | # Setting this allows you to use `--seed` to deterministically reproduce 100 | # test failures related to randomization by passing the same `--seed` value 101 | # as the one that triggered the failure. 102 | Kernel.srand config.seed 103 | =end 104 | end 105 | -------------------------------------------------------------------------------- /backend/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/tmp/.keep -------------------------------------------------------------------------------- /backend/vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielres/react-redux-rails-oauth-boilerplate/44d7137c25810244ed0f22f5da6445c48b720d82/backend/vendor/assets/stylesheets/.keep -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")" 4 | 5 | function deploy_backend { 6 | echo "============ deploying backend" 7 | git subtree push --prefix backend heroku_backend master 8 | } 9 | 10 | function deploy_frontend { 11 | echo "============ deploying frontend" 12 | git subtree push --prefix frontend heroku_frontend master 13 | } 14 | 15 | function deploy_all { 16 | deploy_backend 17 | deploy_frontend 18 | } 19 | 20 | if [[ "$1" == "backend" ]] 21 | then deploy_backend 22 | elif [[ "$1" == "frontend" ]] 23 | then deploy_frontend 24 | elif [[ "$1" == "all" ]] 25 | then deploy_all 26 | else 27 | echo "usage: deploy frontend|backend|all" 28 | fi 29 | 30 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_MOCK_APIS=false 2 | REACT_APP_API_URL=http://585847f5b66f521200e755f7.mockapi.io/api 3 | REACT_APP_AUTH_API_URL=http://localhost:3001 4 | 5 | REACT_APP_FACEBOOK_APP_ID=230XXXXXXXXXXXX 6 | REACT_APP_FACEBOOK_FIELDS=name,picture,friends,events,groups 7 | REACT_APP_FACEBOOK_SCOPE=user_friends,user_events 8 | -------------------------------------------------------------------------------- /frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "parserOptions": { 3 | "ecmaVersion": 8, 4 | "ecmaFeatures": { 5 | "experimentalObjectRestSpread": true, 6 | "jsx": true 7 | }, 8 | "sourceType": "module" 9 | }, 10 | 11 | "env": { 12 | "es6": true, 13 | "node": true, 14 | "jest": true 15 | }, 16 | 17 | "plugins": [ 18 | "standard", 19 | "react", 20 | "promise" 21 | ], 22 | 23 | "globals": { 24 | "localStorage": true, 25 | "document": false, 26 | "navigator": false, 27 | "window": false 28 | }, 29 | 30 | "rules": { 31 | "react/jsx-uses-react": 1, 32 | "react/jsx-uses-vars": 1, 33 | "accessor-pairs": 2, 34 | "arrow-spacing": [2, { "before": true, "after": true }], 35 | "block-spacing": [2, "always"], 36 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 37 | "camelcase": [2, { "properties": "never" }], 38 | "comma-dangle": [2, "always-multiline"], 39 | "comma-spacing": [2, { "before": false, "after": true }], 40 | "comma-style": [2, "last"], 41 | "constructor-super": 2, 42 | "curly": [2, "multi-line"], 43 | "dot-location": [2, "property"], 44 | "eol-last": 2, 45 | "eqeqeq": [2, "always", {"null": "ignore"}], 46 | "func-call-spacing": [2, "never"], 47 | "handle-callback-err": [2, "^(err|error)$" ], 48 | "indent": [2, 2, { "SwitchCase": 1 }], 49 | "key-spacing": ["error", { 50 | "beforeColon": false, 51 | "afterColon": true, 52 | "mode": "minimum", 53 | }], 54 | "keyword-spacing": [2, { "before": true, "after": true }], 55 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 56 | "new-parens": 2, 57 | "no-array-constructor": 2, 58 | "no-caller": 2, 59 | "no-class-assign": 2, 60 | "no-cond-assign": 2, 61 | "no-const-assign": 2, 62 | "no-constant-condition": [2, { "checkLoops": false }], 63 | "no-control-regex": 2, 64 | "no-debugger": 2, 65 | "no-delete-var": 2, 66 | "no-dupe-args": 2, 67 | "no-dupe-class-members": 2, 68 | "no-dupe-keys": 2, 69 | "no-duplicate-case": 2, 70 | "no-duplicate-imports": 2, 71 | "no-empty-character-class": 2, 72 | "no-empty-pattern": 2, 73 | "no-eval": 2, 74 | "no-ex-assign": 2, 75 | "no-extend-native": 2, 76 | "no-extra-bind": 2, 77 | "no-extra-boolean-cast": 2, 78 | "no-extra-parens": [2, "functions"], 79 | "no-fallthrough": 2, 80 | "no-floating-decimal": 2, 81 | "no-func-assign": 2, 82 | "no-global-assign": 2, 83 | "no-implied-eval": 2, 84 | "no-inner-declarations": [2, "functions"], 85 | "no-invalid-regexp": 2, 86 | "no-irregular-whitespace": 2, 87 | "no-iterator": 2, 88 | "no-label-var": 2, 89 | "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], 90 | "no-lone-blocks": 2, 91 | "no-mixed-spaces-and-tabs": 2, 92 | "no-multi-spaces": [1, { 93 | "exceptions": { 94 | "VariableDeclarator": true 95 | } 96 | }], 97 | "no-multi-str": 2, 98 | "no-multiple-empty-lines": [2, { "max": 1 }], 99 | "no-native-reassign": 2, 100 | "no-negated-in-lhs": 2, 101 | "no-new": 2, 102 | "no-new-func": 2, 103 | "no-new-object": 2, 104 | "no-new-require": 2, 105 | "no-new-symbol": 2, 106 | "no-new-wrappers": 2, 107 | "no-obj-calls": 2, 108 | "no-octal": 2, 109 | "no-octal-escape": 2, 110 | "no-path-concat": 2, 111 | "no-proto": 2, 112 | "no-redeclare": 2, 113 | "no-regex-spaces": 2, 114 | "no-return-assign": [2, "except-parens"], 115 | "no-self-assign": 2, 116 | "no-self-compare": 2, 117 | "no-sequences": 2, 118 | "no-shadow-restricted-names": 2, 119 | "no-sparse-arrays": 2, 120 | "no-tabs": 2, 121 | "no-template-curly-in-string": 2, 122 | "no-this-before-super": 2, 123 | "no-throw-literal": 2, 124 | "no-trailing-spaces": 2, 125 | "no-undef": 2, 126 | "no-undef-init": 2, 127 | "no-unexpected-multiline": 2, 128 | "no-unmodified-loop-condition": 2, 129 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 130 | "no-unreachable": 2, 131 | "no-unsafe-finally": 2, 132 | "no-unsafe-negation": 2, 133 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 134 | "no-useless-call": 2, 135 | "no-useless-computed-key": 2, 136 | "no-useless-constructor": 2, 137 | "no-useless-escape": 2, 138 | "no-useless-rename": 2, 139 | "no-whitespace-before-property": 2, 140 | "no-with": 2, 141 | "object-property-newline": [2, { "allowMultiplePropertiesPerLine": true }], 142 | "one-var": [2, { "initialized": "never" }], 143 | "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], 144 | "padded-blocks": [2, "never"], 145 | "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }], 146 | "rest-spread-spacing": [2, "never"], 147 | "semi": [2, "never"], 148 | "semi-spacing": [2, { "before": false, "after": true }], 149 | "space-before-blocks": [2, "always"], 150 | "space-before-function-paren": [2, "always"], 151 | "space-in-parens": [2, "never"], 152 | "space-infix-ops": 2, 153 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 154 | "spaced-comment": [2, "always", { "line": { "markers": ["*package", "!", ","] }, "block": { "balanced": true, "markers": ["*package", "!", ","], "exceptions": ["*"] } }], 155 | "template-curly-spacing": [2, "never"], 156 | "unicode-bom": [2, "never"], 157 | "use-isnan": 2, 158 | "valid-typeof": 2, 159 | "wrap-iife": [2, "any", { "functionPrototypeMethods": true }], 160 | "yield-star-spacing": [2, "both"], 161 | "yoda": [2, "never"], 162 | 163 | "object-curly-spacing": [2, "always", { 164 | "arraysInObjects": false, 165 | "objectsInObjects": false 166 | }], 167 | // "standard/object-curly-even-spacing": [2, "either"], 168 | "standard/array-bracket-even-spacing": [2, "either"], 169 | "standard/computed-property-even-spacing": [2, "even"], 170 | 171 | "promise/param-names": 2 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /frontend/.nvmrc: -------------------------------------------------------------------------------- 1 | 7.2.1 2 | -------------------------------------------------------------------------------- /frontend/.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "node": {}, 4 | "node-express": {}, 5 | "es_modules": {}, 6 | "doc_comment": { 7 | "fullDocs": true, 8 | "strong": true 9 | } 10 | }, 11 | "libs": [ 12 | "browser", 13 | "ecma5", 14 | "ecma6" 15 | ], 16 | "ecmaVersion": 6 17 | } 18 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 2 | 3 | Below you will find some information on how to perform common tasks.
4 | You can find the most recent version of this guide [here](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md). 5 | 6 | ## Table of Contents 7 | 8 | - [Updating to New Releases](#updating-to-new-releases) 9 | - [Sending Feedback](#sending-feedback) 10 | - [Folder Structure](#folder-structure) 11 | - [Available Scripts](#available-scripts) 12 | - [npm start](#npm-start) 13 | - [npm test](#npm-test) 14 | - [npm run build](#npm-run-build) 15 | - [npm run eject](#npm-run-eject) 16 | - [Syntax Highlighting in the Editor](#syntax-highlighting-in-the-editor) 17 | - [Displaying Lint Output in the Editor](#displaying-lint-output-in-the-editor) 18 | - [Installing a Dependency](#installing-a-dependency) 19 | - [Importing a Component](#importing-a-component) 20 | - [Adding a Stylesheet](#adding-a-stylesheet) 21 | - [Post-Processing CSS](#post-processing-css) 22 | - [Adding Images and Fonts](#adding-images-and-fonts) 23 | - [Using the `public` Folder](#using-the-public-folder) 24 | - [Using Global Variables](#using-global-variables) 25 | - [Adding Bootstrap](#adding-bootstrap) 26 | - [Adding Flow](#adding-flow) 27 | - [Adding Custom Environment Variables](#adding-custom-environment-variables) 28 | - [Can I Use Decorators?](#can-i-use-decorators) 29 | - [Integrating with a Node Backend](#integrating-with-a-node-backend) 30 | - [Proxying API Requests in Development](#proxying-api-requests-in-development) 31 | - [Using HTTPS in Development](#using-https-in-development) 32 | - [Generating Dynamic `` Tags on the Server](#generating-dynamic-meta-tags-on-the-server) 33 | - [Running Tests](#running-tests) 34 | - [Filename Conventions](#filename-conventions) 35 | - [Command Line Interface](#command-line-interface) 36 | - [Version Control Integration](#version-control-integration) 37 | - [Writing Tests](#writing-tests) 38 | - [Testing Components](#testing-components) 39 | - [Using Third Party Assertion Libraries](#using-third-party-assertion-libraries) 40 | - [Initializing Test Environment](#initializing-test-environment) 41 | - [Focusing and Excluding Tests](#focusing-and-excluding-tests) 42 | - [Coverage Reporting](#coverage-reporting) 43 | - [Continuous Integration](#continuous-integration) 44 | - [Disabling jsdom](#disabling-jsdom) 45 | - [Experimental Snapshot Testing](#experimental-snapshot-testing) 46 | - [Editor Integration](#editor-integration) 47 | - [Developing Components in Isolation](#developing-components-in-isolation) 48 | - [Making a Progressive Web App](#making-a-progressive-web-app) 49 | - [Deployment](#deployment) 50 | - [Serving Apps with Client-Side Routing](#serving-apps-with-client-side-routing) 51 | - [Building for Relative Paths](#building-for-relative-paths) 52 | - [Firebase](#firebase) 53 | - [GitHub Pages](#github-pages) 54 | - [Heroku](#heroku) 55 | - [Modulus](#modulus) 56 | - [Netlify](#netlify) 57 | - [Now](#now) 58 | - [S3 and CloudFront](#s3-and-cloudfront) 59 | - [Surge](#surge) 60 | - [Troubleshooting](#troubleshooting) 61 | - [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra) 62 | - [`npm run build` silently fails](#npm-run-build-silently-fails) 63 | - [Something Missing?](#something-missing) 64 | 65 | ## Updating to New Releases 66 | 67 | Create React App is divided into two packages: 68 | 69 | * `create-react-app` is a global command-line utility that you use to create new projects. 70 | * `react-scripts` is a development dependency in the generated projects (including this one). 71 | 72 | You almost never need to update `create-react-app` itself: it delegates all the setup to `react-scripts`. 73 | 74 | When you run `create-react-app`, it always creates the project with the latest version of `react-scripts` so you’ll get all the new features and improvements in newly created apps automatically. 75 | 76 | To update an existing project to a new version of `react-scripts`, [open the changelog](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md), find the version you’re currently on (check `package.json` in this folder if you’re not sure), and apply the migration instructions for the newer versions. 77 | 78 | In most cases bumping the `react-scripts` version in `package.json` and running `npm install` in this folder should be enough, but it’s good to consult the [changelog](https://github.com/facebookincubator/create-react-app/blob/master/CHANGELOG.md) for potential breaking changes. 79 | 80 | We commit to keeping the breaking changes minimal so you can upgrade `react-scripts` painlessly. 81 | 82 | ## Sending Feedback 83 | 84 | We are always open to [your feedback](https://github.com/facebookincubator/create-react-app/issues). 85 | 86 | ## Folder Structure 87 | 88 | After creation, your project should look like this: 89 | 90 | ``` 91 | my-app/ 92 | README.md 93 | node_modules/ 94 | package.json 95 | public/ 96 | index.html 97 | favicon.ico 98 | src/ 99 | App.css 100 | App.js 101 | App.test.js 102 | index.css 103 | index.js 104 | logo.svg 105 | ``` 106 | 107 | For the project to build, **these files must exist with exact filenames**: 108 | 109 | * `public/index.html` is the page template; 110 | * `src/index.js` is the JavaScript entry point. 111 | 112 | You can delete or rename the other files. 113 | 114 | You may create subdirectories inside `src`. For faster rebuilds, only files inside `src` are processed by Webpack.
115 | You need to **put any JS and CSS files inside `src`**, or Webpack won’t see them. 116 | 117 | Only files inside `public` can be used from `public/index.html`.
118 | Read instructions below for using assets from JavaScript and HTML. 119 | 120 | You can, however, create more top-level directories.
121 | They will not be included in the production build so you can use them for things like documentation. 122 | 123 | ## Available Scripts 124 | 125 | In the project directory, you can run: 126 | 127 | ### `npm start` 128 | 129 | Runs the app in the development mode.
130 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 131 | 132 | The page will reload if you make edits.
133 | You will also see any lint errors in the console. 134 | 135 | ### `npm test` 136 | 137 | Launches the test runner in the interactive watch mode.
138 | See the section about [running tests](#running-tests) for more information. 139 | 140 | ### `npm run build` 141 | 142 | Builds the app for production to the `build` folder.
143 | It correctly bundles React in production mode and optimizes the build for the best performance. 144 | 145 | The build is minified and the filenames include the hashes.
146 | Your app is ready to be deployed! 147 | 148 | See the section about [deployment](#deployment) for more information. 149 | 150 | ### `npm run eject` 151 | 152 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 153 | 154 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 155 | 156 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 157 | 158 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 159 | 160 | ## Syntax Highlighting in the Editor 161 | 162 | To configure the syntax highlighting in your favorite text editor, head to the [Babel's docs](https://babeljs.io/docs/editors) and follow the instructions. Some of the most popular editors are covered. 163 | 164 | ## Displaying Lint Output in the Editor 165 | 166 | >Note: this feature is available with `react-scripts@0.2.0` and higher. 167 | 168 | Some editors, including Sublime Text, Atom, and Visual Studio Code, provide plugins for ESLint. 169 | 170 | They are not required for linting. You should see the linter output right in your terminal as well as the browser console. However, if you prefer the lint results to appear right in your editor, there are some extra steps you can do. 171 | 172 | You would need to install an ESLint plugin for your editor first. 173 | 174 | >**A note for Atom `linter-eslint` users** 175 | 176 | >If you are using the Atom `linter-eslint` plugin, make sure that **Use global ESLint installation** option is checked: 177 | 178 | > 179 | 180 | Then add this block to the `package.json` file of your project: 181 | 182 | ```js 183 | { 184 | // ... 185 | "eslintConfig": { 186 | "extends": "react-app" 187 | } 188 | } 189 | ``` 190 | 191 | Finally, you will need to install some packages *globally*: 192 | 193 | ```sh 194 | npm install -g eslint-config-react-app@0.3.0 eslint@3.8.1 babel-eslint@7.0.0 eslint-plugin-react@6.4.1 eslint-plugin-import@2.0.1 eslint-plugin-jsx-a11y@2.2.3 eslint-plugin-flowtype@2.21.0 195 | ``` 196 | 197 | We recognize that this is suboptimal, but it is currently required due to the way we hide the ESLint dependency. The ESLint team is already [working on a solution to this](https://github.com/eslint/eslint/issues/3458) so this may become unnecessary in a couple of months. 198 | 199 | ## Installing a Dependency 200 | 201 | The generated project includes React and ReactDOM as dependencies. It also includes a set of scripts used by Create React App as a development dependency. You may install other dependencies (for example, React Router) with `npm`: 202 | 203 | ``` 204 | npm install --save 205 | ``` 206 | 207 | ## Importing a Component 208 | 209 | This project setup supports ES6 modules thanks to Babel.
210 | While you can still use `require()` and `module.exports`, we encourage you to use [`import` and `export`](http://exploringjs.com/es6/ch_modules.html) instead. 211 | 212 | For example: 213 | 214 | ### `Button.js` 215 | 216 | ```js 217 | import React, { Component } from 'react'; 218 | 219 | class Button extends Component { 220 | render() { 221 | // ... 222 | } 223 | } 224 | 225 | export default Button; // Don’t forget to use export default! 226 | ``` 227 | 228 | ### `DangerButton.js` 229 | 230 | 231 | ```js 232 | import React, { Component } from 'react'; 233 | import Button from './Button'; // Import a component from another file 234 | 235 | class DangerButton extends Component { 236 | render() { 237 | return