├── .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 |
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.
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 ;
238 | }
239 | }
240 |
241 | export default DangerButton;
242 | ```
243 |
244 | Be aware of the [difference between default and named exports](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281). It is a common source of mistakes.
245 |
246 | We suggest that you stick to using default imports and exports when a module only exports a single thing (for example, a component). That’s what you get when you use `export default Button` and `import Button from './Button'`.
247 |
248 | Named exports are useful for utility modules that export several functions. A module may have at most one default export and as many named exports as you like.
249 |
250 | Learn more about ES6 modules:
251 |
252 | * [When to use the curly braces?](http://stackoverflow.com/questions/36795819/react-native-es-6-when-should-i-use-curly-braces-for-import/36796281#36796281)
253 | * [Exploring ES6: Modules](http://exploringjs.com/es6/ch_modules.html)
254 | * [Understanding ES6: Modules](https://leanpub.com/understandinges6/read#leanpub-auto-encapsulating-code-with-modules)
255 |
256 | ## Adding a Stylesheet
257 |
258 | This project setup uses [Webpack](https://webpack.github.io/) for handling all assets. Webpack offers a custom way of “extending” the concept of `import` beyond JavaScript. To express that a JavaScript file depends on a CSS file, you need to **import the CSS from the JavaScript file**:
259 |
260 | ### `Button.css`
261 |
262 | ```css
263 | .Button {
264 | padding: 20px;
265 | }
266 | ```
267 |
268 | ### `Button.js`
269 |
270 | ```js
271 | import React, { Component } from 'react';
272 | import './Button.css'; // Tell Webpack that Button.js uses these styles
273 |
274 | class Button extends Component {
275 | render() {
276 | // You can use them as regular CSS styles
277 | return ;
278 | }
279 | }
280 | ```
281 |
282 | **This is not required for React** but many people find this feature convenient. You can read about the benefits of this approach [here](https://medium.com/seek-ui-engineering/block-element-modifying-your-javascript-components-d7f99fcab52b). However you should be aware that this makes your code less portable to other build tools and environments than Webpack.
283 |
284 | In development, expressing dependencies this way allows your styles to be reloaded on the fly as you edit them. In production, all CSS files will be concatenated into a single minified `.css` file in the build output.
285 |
286 | If you are concerned about using Webpack-specific semantics, you can put all your CSS right into `src/index.css`. It would still be imported from `src/index.js`, but you could always remove that import if you later migrate to a different build tool.
287 |
288 | ## Post-Processing CSS
289 |
290 | This project setup minifies your CSS and adds vendor prefixes to it automatically through [Autoprefixer](https://github.com/postcss/autoprefixer) so you don’t need to worry about it.
291 |
292 | For example, this:
293 |
294 | ```css
295 | .App {
296 | display: flex;
297 | flex-direction: row;
298 | align-items: center;
299 | }
300 | ```
301 |
302 | becomes this:
303 |
304 | ```css
305 | .App {
306 | display: -webkit-box;
307 | display: -ms-flexbox;
308 | display: flex;
309 | -webkit-box-orient: horizontal;
310 | -webkit-box-direction: normal;
311 | -ms-flex-direction: row;
312 | flex-direction: row;
313 | -webkit-box-align: center;
314 | -ms-flex-align: center;
315 | align-items: center;
316 | }
317 | ```
318 |
319 | There is currently no support for preprocessors such as Less, or for sharing variables across CSS files.
320 |
321 | ## Adding Images and Fonts
322 |
323 | With Webpack, using static assets like images and fonts works similarly to CSS.
324 |
325 | You can **`import` an image right in a JavaScript module**. This tells Webpack to include that image in the bundle. Unlike CSS imports, importing an image or a font gives you a string value. This value is the final image path you can reference in your code.
326 |
327 | Here is an example:
328 |
329 | ```js
330 | import React from 'react';
331 | import logo from './logo.png'; // Tell Webpack this JS file uses this image
332 |
333 | console.log(logo); // /logo.84287d09.png
334 |
335 | function Header() {
336 | // Import result is the URL of your image
337 | return ;
338 | }
339 |
340 | export default Header;
341 | ```
342 |
343 | This ensures that when the project is built, Webpack will correctly move the images into the build folder, and provide us with correct paths.
344 |
345 | This works in CSS too:
346 |
347 | ```css
348 | .Logo {
349 | background-image: url(./logo.png);
350 | }
351 | ```
352 |
353 | Webpack finds all relative module references in CSS (they start with `./`) and replaces them with the final paths from the compiled bundle. If you make a typo or accidentally delete an important file, you will see a compilation error, just like when you import a non-existent JavaScript module. The final filenames in the compiled bundle are generated by Webpack from content hashes. If the file content changes in the future, Webpack will give it a different name in production so you don’t need to worry about long-term caching of assets.
354 |
355 | Please be advised that this is also a custom feature of Webpack.
356 |
357 | **It is not required for React** but many people enjoy it (and React Native uses a similar mechanism for images).
358 | An alternative way of handling static assets is described in the next section.
359 |
360 | ## Using the `public` Folder
361 |
362 | >Note: this feature is available with `react-scripts@0.5.0` and higher.
363 |
364 | Normally we encourage you to `import` assets in JavaScript files as described above. This mechanism provides a number of benefits:
365 |
366 | * Scripts and stylesheets get minified and bundled together to avoid extra network requests.
367 | * Missing files cause compilation errors instead of 404 errors for your users.
368 | * Result filenames include content hashes so you don’t need to worry about browsers caching their old versions.
369 |
370 | However there is an **escape hatch** that you can use to add an asset outside of the module system.
371 |
372 | If you put a file into the `public` folder, it will **not** be processed by Webpack. Instead it will be copied into the build folder untouched. To reference assets in the `public` folder, you need to use a special variable called `PUBLIC_URL`.
373 |
374 | Inside `index.html`, you can use it like this:
375 |
376 | ```html
377 |
378 | ```
379 |
380 | Only files inside the `public` folder will be accessible by `%PUBLIC_URL%` prefix. If you need to use a file from `src` or `node_modules`, you’ll have to copy it there to explicitly specify your intention to make this file a part of the build.
381 |
382 | When you run `npm run build`, Create React App will substitute `%PUBLIC_URL%` with a correct absolute path so your project works even if you use client-side routing or host it at a non-root URL.
383 |
384 | In JavaScript code, you can use `process.env.PUBLIC_URL` for similar purposes:
385 |
386 | ```js
387 | render() {
388 | // Note: this is an escape hatch and should be used sparingly!
389 | // Normally we recommend using `import` for getting asset URLs
390 | // as described in “Adding Images and Fonts” above this section.
391 | return ;
392 | }
393 | ```
394 |
395 | Keep in mind the downsides of this approach:
396 |
397 | * None of the files in `public` folder get post-processed or minified.
398 | * Missing files will not be called at compilation time, and will cause 404 errors for your users.
399 | * Result filenames won’t include content hashes so you’ll need to add query arguments or rename them every time they change.
400 |
401 | However, it can be handy for referencing assets like [`manifest.webmanifest`](https://developer.mozilla.org/en-US/docs/Web/Manifest) from HTML, or including small scripts like [`pace.js`](http://github.hubspot.com/pace/docs/welcome/) outside of the bundled code.
402 |
403 | Note that if you add a `