├── .browserslistrc
├── .gitignore
├── .ruby-version
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── images
│ │ └── .keep
│ ├── javascripts
│ │ ├── application.js
│ │ ├── cable.js
│ │ └── channels
│ │ │ └── .keep
│ └── stylesheets
│ │ └── application.css
├── channels
│ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
├── controllers
│ ├── api
│ │ └── v1
│ │ │ └── posts_controller.rb
│ ├── application_controller.rb
│ ├── concerns
│ │ └── .keep
│ └── welcome_controller.rb
├── helpers
│ └── application_helper.rb
├── javascript
│ └── packs
│ │ └── application.js
├── jobs
│ └── application_job.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── application_record.rb
│ ├── concerns
│ │ └── .keep
│ ├── post.rb
│ └── user.rb
└── views
│ ├── layouts
│ ├── application.html.erb
│ ├── mailer.html.erb
│ └── mailer.text.erb
│ └── welcome
│ ├── app.html.erb
│ └── home.html.erb
├── babel.config.js
├── bin
├── bundle
├── rails
├── rake
├── setup
├── spring
├── update
├── webpack
├── webpack-dev-server
└── yarn
├── client
├── .gitignore
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ │ ├── NewPost.jsx
│ │ └── PostList.jsx
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
└── yarn.lock
├── config.ru
├── config
├── application.rb
├── boot.rb
├── cable.yml
├── credentials.yml.enc
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── devise.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ └── wrap_parameters.rb
├── locales
│ ├── devise.en.yml
│ └── en.yml
├── puma.rb
├── routes.rb
├── spring.rb
├── storage.yml
├── webpack
│ ├── development.js
│ ├── environment.js
│ ├── production.js
│ └── test.js
└── webpacker.yml
├── db
├── migrate
│ ├── 20190503170520_devise_create_users.rb
│ └── 20190507162309_create_posts.rb
├── schema.rb
└── seeds.rb
├── lib
├── assets
│ └── .keep
└── tasks
│ └── .keep
├── log
└── .keep
├── media
├── home-to-app-link.gif
├── login-flow.gif
├── new-post-component-submission.gif
├── post-list-component.gif
└── react-app-behind-login.gif
├── package.json
├── postcss.config.js
├── public
├── 404.html
├── 422.html
├── 500.html
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── favicon.ico
└── robots.txt
├── storage
└── .keep
├── tmp
└── .keep
├── vendor
└── .keep
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.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 the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 |
14 | # Ignore all logfiles and tempfiles.
15 | /log/*
16 | /tmp/*
17 | !/log/.keep
18 | !/tmp/.keep
19 |
20 | # Ignore uploaded files in development
21 | /storage/*
22 | !/storage/.keep
23 |
24 | /node_modules
25 | /client/node_modules
26 | /yarn-error.log
27 |
28 | /public/assets
29 | .byebug_history
30 |
31 | # Ignore master key for decrypting credentials and more.
32 | /config/master.key
33 |
34 | /public/packs
35 | /public/packs-test
36 | /node_modules
37 | /yarn-error.log
38 | yarn-debug.log*
39 | .yarn-integrity
40 | .DS_Store
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-2.6.1
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby '2.6.1'
5 |
6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7 | gem 'rails', '~> 5.2.3'
8 | # Use sqlite3 as the database for Active Record
9 | gem 'sqlite3'
10 | # Use Puma as the app server
11 | gem 'puma', '~> 3.11'
12 | # Use SCSS for stylesheets
13 | gem 'sass-rails', '~> 5.0'
14 | # Use Uglifier as compressor for JavaScript assets
15 | gem 'uglifier', '>= 1.3.0'
16 | # See https://github.com/rails/execjs#readme for more supported runtimes
17 | # gem 'mini_racer', platforms: :ruby
18 |
19 | # Use CoffeeScript for .coffee assets and views
20 | gem 'coffee-rails', '~> 4.2'
21 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
22 | gem 'turbolinks', '~> 5'
23 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
24 | gem 'jbuilder', '~> 2.5'
25 | # Use Redis adapter to run Action Cable in production
26 | # gem 'redis', '~> 4.0'
27 | # Use ActiveModel has_secure_password
28 | # gem 'bcrypt', '~> 3.1.7'
29 | gem 'devise'
30 | gem 'webpacker'
31 |
32 | # Use ActiveStorage variant
33 | # gem 'mini_magick', '~> 4.8'
34 |
35 | # Use Capistrano for deployment
36 | # gem 'capistrano-rails', group: :development
37 |
38 | # Reduces boot times through caching; required in config/boot.rb
39 | gem 'bootsnap', '>= 1.1.0', require: false
40 |
41 | group :development, :test do
42 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
43 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
44 | end
45 |
46 | group :development do
47 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
48 | gem 'web-console', '>= 3.3.0'
49 | gem 'listen', '>= 3.0.5', '< 3.2'
50 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
51 | gem 'spring'
52 | gem 'spring-watcher-listen', '~> 2.0.0'
53 | end
54 |
55 |
56 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
57 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
58 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | actioncable (5.2.3)
5 | actionpack (= 5.2.3)
6 | nio4r (~> 2.0)
7 | websocket-driver (>= 0.6.1)
8 | actionmailer (5.2.3)
9 | actionpack (= 5.2.3)
10 | actionview (= 5.2.3)
11 | activejob (= 5.2.3)
12 | mail (~> 2.5, >= 2.5.4)
13 | rails-dom-testing (~> 2.0)
14 | actionpack (5.2.3)
15 | actionview (= 5.2.3)
16 | activesupport (= 5.2.3)
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.2.3)
22 | activesupport (= 5.2.3)
23 | builder (~> 3.1)
24 | erubi (~> 1.4)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
27 | activejob (5.2.3)
28 | activesupport (= 5.2.3)
29 | globalid (>= 0.3.6)
30 | activemodel (5.2.3)
31 | activesupport (= 5.2.3)
32 | activerecord (5.2.3)
33 | activemodel (= 5.2.3)
34 | activesupport (= 5.2.3)
35 | arel (>= 9.0)
36 | activestorage (5.2.3)
37 | actionpack (= 5.2.3)
38 | activerecord (= 5.2.3)
39 | marcel (~> 0.3.1)
40 | activesupport (5.2.3)
41 | concurrent-ruby (~> 1.0, >= 1.0.2)
42 | i18n (>= 0.7, < 2)
43 | minitest (~> 5.1)
44 | tzinfo (~> 1.1)
45 | arel (9.0.0)
46 | bcrypt (3.1.12)
47 | bindex (0.7.0)
48 | bootsnap (1.4.4)
49 | msgpack (~> 1.0)
50 | builder (3.2.3)
51 | byebug (11.0.1)
52 | coffee-rails (4.2.2)
53 | coffee-script (>= 2.2.0)
54 | railties (>= 4.0.0)
55 | coffee-script (2.4.1)
56 | coffee-script-source
57 | execjs
58 | coffee-script-source (1.12.2)
59 | concurrent-ruby (1.1.5)
60 | crass (1.0.4)
61 | devise (4.6.2)
62 | bcrypt (~> 3.0)
63 | orm_adapter (~> 0.1)
64 | railties (>= 4.1.0, < 6.0)
65 | responders
66 | warden (~> 1.2.3)
67 | erubi (1.8.0)
68 | execjs (2.7.0)
69 | ffi (1.10.0)
70 | globalid (0.4.2)
71 | activesupport (>= 4.2.0)
72 | i18n (1.6.0)
73 | concurrent-ruby (~> 1.0)
74 | jbuilder (2.8.0)
75 | activesupport (>= 4.2.0)
76 | multi_json (>= 1.2)
77 | listen (3.1.5)
78 | rb-fsevent (~> 0.9, >= 0.9.4)
79 | rb-inotify (~> 0.9, >= 0.9.7)
80 | ruby_dep (~> 1.2)
81 | loofah (2.2.3)
82 | crass (~> 1.0.2)
83 | nokogiri (>= 1.5.9)
84 | mail (2.7.1)
85 | mini_mime (>= 0.1.1)
86 | marcel (0.3.3)
87 | mimemagic (~> 0.3.2)
88 | method_source (0.9.2)
89 | mimemagic (0.3.3)
90 | mini_mime (1.0.1)
91 | mini_portile2 (2.4.0)
92 | minitest (5.11.3)
93 | msgpack (1.2.10)
94 | multi_json (1.13.1)
95 | nio4r (2.3.1)
96 | nokogiri (1.10.3)
97 | mini_portile2 (~> 2.4.0)
98 | orm_adapter (0.5.0)
99 | puma (3.12.1)
100 | rack (2.0.7)
101 | rack-proxy (0.6.5)
102 | rack
103 | rack-test (1.1.0)
104 | rack (>= 1.0, < 3)
105 | rails (5.2.3)
106 | actioncable (= 5.2.3)
107 | actionmailer (= 5.2.3)
108 | actionpack (= 5.2.3)
109 | actionview (= 5.2.3)
110 | activejob (= 5.2.3)
111 | activemodel (= 5.2.3)
112 | activerecord (= 5.2.3)
113 | activestorage (= 5.2.3)
114 | activesupport (= 5.2.3)
115 | bundler (>= 1.3.0)
116 | railties (= 5.2.3)
117 | sprockets-rails (>= 2.0.0)
118 | rails-dom-testing (2.0.3)
119 | activesupport (>= 4.2.0)
120 | nokogiri (>= 1.6)
121 | rails-html-sanitizer (1.0.4)
122 | loofah (~> 2.2, >= 2.2.2)
123 | railties (5.2.3)
124 | actionpack (= 5.2.3)
125 | activesupport (= 5.2.3)
126 | method_source
127 | rake (>= 0.8.7)
128 | thor (>= 0.19.0, < 2.0)
129 | rake (12.3.2)
130 | rb-fsevent (0.10.3)
131 | rb-inotify (0.10.0)
132 | ffi (~> 1.0)
133 | responders (2.4.1)
134 | actionpack (>= 4.2.0, < 6.0)
135 | railties (>= 4.2.0, < 6.0)
136 | ruby_dep (1.5.0)
137 | sass (3.7.4)
138 | sass-listen (~> 4.0.0)
139 | sass-listen (4.0.0)
140 | rb-fsevent (~> 0.9, >= 0.9.4)
141 | rb-inotify (~> 0.9, >= 0.9.7)
142 | sass-rails (5.0.7)
143 | railties (>= 4.0.0, < 6)
144 | sass (~> 3.1)
145 | sprockets (>= 2.8, < 4.0)
146 | sprockets-rails (>= 2.0, < 4.0)
147 | tilt (>= 1.1, < 3)
148 | spring (2.0.2)
149 | activesupport (>= 4.2)
150 | spring-watcher-listen (2.0.1)
151 | listen (>= 2.7, < 4.0)
152 | spring (>= 1.2, < 3.0)
153 | sprockets (3.7.2)
154 | concurrent-ruby (~> 1.0)
155 | rack (> 1, < 3)
156 | sprockets-rails (3.2.1)
157 | actionpack (>= 4.0)
158 | activesupport (>= 4.0)
159 | sprockets (>= 3.0.0)
160 | sqlite3 (1.4.1)
161 | thor (0.20.3)
162 | thread_safe (0.3.6)
163 | tilt (2.0.9)
164 | turbolinks (5.2.0)
165 | turbolinks-source (~> 5.2)
166 | turbolinks-source (5.2.0)
167 | tzinfo (1.2.5)
168 | thread_safe (~> 0.1)
169 | uglifier (4.1.20)
170 | execjs (>= 0.3.0, < 3)
171 | warden (1.2.8)
172 | rack (>= 2.0.6)
173 | web-console (3.7.0)
174 | actionview (>= 5.0)
175 | activemodel (>= 5.0)
176 | bindex (>= 0.4.0)
177 | railties (>= 5.0)
178 | webpacker (4.0.2)
179 | activesupport (>= 4.2)
180 | rack-proxy (>= 0.6.1)
181 | railties (>= 4.2)
182 | websocket-driver (0.7.0)
183 | websocket-extensions (>= 0.1.0)
184 | websocket-extensions (0.1.3)
185 |
186 | PLATFORMS
187 | ruby
188 |
189 | DEPENDENCIES
190 | bootsnap (>= 1.1.0)
191 | byebug
192 | coffee-rails (~> 4.2)
193 | devise
194 | jbuilder (~> 2.5)
195 | listen (>= 3.0.5, < 3.2)
196 | puma (~> 3.11)
197 | rails (~> 5.2.3)
198 | sass-rails (~> 5.0)
199 | spring
200 | spring-watcher-listen (~> 2.0.0)
201 | sqlite3
202 | turbolinks (~> 5)
203 | tzinfo-data
204 | uglifier (>= 1.3.0)
205 | web-console (>= 3.3.0)
206 | webpacker
207 |
208 | RUBY VERSION
209 | ruby 2.6.1p33
210 |
211 | BUNDLED WITH
212 | 1.17.2
213 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rails-React-Devise Tutorial
2 |
3 | This tutorial covers how to set up a Rails/React app for User authentication using Devise. It will cover the following tasks:
4 |
5 | - [Set up Webpacker](#set-up-webpacker)
6 | - [Generate the client app with create-react-app](#generate-the-client-app-with-create-react-app)
7 | - [Configure webpacker to read from create-react-app](#configure-webpacker-to-read-from-create-react-app)
8 | - [Add a home route and a route to mount the react app](#add-a-home-route-and-a-route-to-mount-the-react-app)
9 | - [Install devise and set up user logins](#install-devise-and-set-up-user-logins)
10 | - [Check for authentication before rendering the react view](#check-for-authentication-before-rendering-the-react-view)
11 | - [Create a posts resource and some seeds](#create-a-posts-resource-and-some-seeds)
12 | - [Create a namespace for our API](#create-a-namespace-for-our-api)
13 | - [Add an index action to display the current user's posts if authenticated](#add-an-index-action-to-display-the-current-user's-posts-if-authenticated)
14 | - [Add react router and a posts index that pulls the current user's posts](#add-react-router-and-a-posts-index-that-pulls-the-current-user's-posts)
15 | - [Add the new post component and the create action in the rails controller](#add-the-new-post-component-and-the-create-action-in-the-rails-controller)
16 | - [Configure the new post component to pull csrf token from view and add to headers on fetch request](#configure-the-new-post-component-to-pullcsrf-token-from-view-and-add-to-headers-on-fetch-request)
17 |
18 | ### 2 ways to use this repository
19 |
20 | 1. Follow along with the tutorial starting with a new rails app.
21 | 2. Clone it and play around with and extend it.
22 |
23 | If you choose the second route, here's what you'll need to do to get started.
24 |
25 | #### My Environment
26 |
27 | Ruby Version: 2.6.1
28 |
29 | Rails Version: 5.2.3
30 |
31 | Node Version: 12.1.0
32 |
33 | Yarn Version: 1.15.2
34 |
35 | OS: Mac OS 10.12.6
36 |
37 | Text Editor: Visual Studio Code
38 |
39 | #### Local set up instructions
40 |
41 | - `git clone git@github.com:DakotaLMartinez/rails-react-devise-tutorial.git`
42 | - `cd rails-react-devise-tutorial`
43 | - `yarn install`
44 | - `rails db:migrate`
45 | - (optional) `rails db:seed`
46 | - `cd client`
47 | - `yarn install`
48 | - `cd ..`
49 | - `rails s`
50 | - (in new terminal) `./bin/webpack-dev-server`
51 |
52 | You should now be able to navigate the site at localhost:3000, log in/log out/sign up and view the react app with the working Post index and Post New routes protected behind a login.
53 |
54 | ### Tips before we start
55 |
56 | If you're following along with this tutorial in your own repo, then when you finish one of the steps, it's a good idea to make a commit, that way you can see in your version control which files are being added/modified in each step. (In VS Code, I see this in the source control view within the left hand menu bar.)
57 |
58 | ## Set up Webpacker
59 |
60 | We'll want to add webpacker to the gemfile
61 |
62 | ```
63 | gem 'webpacker'
64 | ```
65 | and run `bundle install`. Then we'll do
66 | ```
67 | rails webpacker:install
68 | ```
69 | Finally, we'll want some of the configuration we get from installing webpacker for react, so let's do
70 | ```
71 | rails webpacker:install:react
72 | ```
73 | We'll keep all of the changes except for the hello_react component.
74 |
75 |
76 | ## Generate the client app with create-react-app
77 |
78 | Now we'll want to generate the client app
79 |
80 | ```
81 | create-react-app client
82 | ```
83 | After this, make sure to add this to your .gitignore file at the root so the repo doesn't have all your node_modules in client:
84 | ```
85 | # .gitignore
86 | /client/node_modules
87 | ```
88 | We'll also want to remove all references to serviceworker in index.js so it looks like this:
89 | ```
90 | import React from 'react';
91 | import ReactDOM from 'react-dom';
92 | import './index.css';
93 | import App from './App';
94 |
95 |
96 | ReactDOM.render(, document.getElementById('root'));
97 |
98 |
99 | ```
100 |
101 | ## Configure Webpacker to Read From create-react-app
102 |
103 | To get webpacker working with create-react-app, you'll want to change the first couple of lines of the default configuration in `config/webpacker.yml` from:
104 | ```
105 | default: &default
106 | source_path: app/javascript
107 | source_entry_path: packs
108 | ```
109 |
110 | to:
111 | ```
112 | default: &default
113 | source_path: client
114 | source_entry_path: src
115 | ```
116 |
117 | ## Add a Home Route and a Route to Mount The React App
118 |
119 | We'll generate a welcome controller with a couple of actions to handle a home page and also our react app.
120 |
121 | ```
122 | rails g controller welcome home app --no-assets --no-helper
123 | ```
124 |
125 | We'll set up the routes like this:
126 | ```
127 | Rails.application.routes.draw do
128 | get 'welcome/home'
129 | get '/app', to: 'welcome#app', as: 'app'
130 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
131 | root 'welcome#home'
132 | end
133 | ```
134 | Then we'll add a link from the home view to the app view.
135 | ```
136 | # app/views/welcome/home.html.erb
137 |
Welcome#home
138 |
If you're logged in, check out <%= link_to "the app", app_path %>!
139 | ```
140 | Finally, we'll add the code we need to mount the react app in the app view:
141 | ```
142 | # app/views/welcome/app.html.erb
143 |
144 | <%= javascript_pack_tag 'index' %>
145 | ```
146 | Now, let's check it out in the browser, if you click the link you should see create-react-app's landing page. **Note**: We'll get much better performance here if we're running the webpack dev server, which you can do by running
147 | ```
148 | ./bin/webpack-dev-server
149 | ```
150 | in a spare terminal. This is what you should see at that point:
151 |
152 | 
153 |
154 | ## Install Devise and Set Up User Logins
155 |
156 | First, let's add devise to the gemfile
157 |
158 | ```
159 | gem 'devise'
160 | ```
161 |
162 | and the run `bundle install`
163 |
164 | Next we'll run the devise installer:
165 | ```
166 | rails g devise:install
167 | ```
168 | Then, following along with the 4 printed instructions we get after it runs, we'll add default_url_options for action mailer to the development environment file.
169 |
170 | ```
171 | # config/environments/development.rb
172 | config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
173 | ```
174 |
175 | I usually puts this at around line 38 (right below the rest of the action_mailer config at the time of this writing).
176 |
177 | Next, let's add some flash messages above the yield tag in our application layout:
178 |
179 | ```
180 | # app/views/layouts/application.html.erb
181 |
<%= notice %>
182 |
<%= alert %>
183 | ```
184 |
185 | Note, I skipped steps 2 and 4 because in our case we've already got a root route, and we're not going to do any customization of the default devise views. If you want to customize the login and signup forms, feel free to run
186 |
187 | ```
188 | rails g devise:views
189 | ```
190 |
191 | At this point, we'll want to actually create our users:
192 |
193 | ```
194 | rails g devise User
195 | ```
196 |
197 | [Devise](https://github.com/plataformatec/devise) has a bunch of features we can turn on and off, for now I'm going to stick with the defaults. To add our table we'll run
198 |
199 | ```
200 | rails db:migrate
201 | ```
202 |
203 | Before we hop back into the browser, let's add some simple navigation to our home view so we log in and signup/logout of our little app. (Note, we're adding this in the home view and not the layout because our react app will have its own navigation)
204 |
205 | ```
206 | # app/views/welcome/home.html.erb
207 |
215 |
Welcome#home
216 |
If you're logged in, check out <%= link_to "the app", app_path %>!
217 | ```
218 |
219 | Now we can head back to the browser and try creating a new account.
220 |
221 | **NOTE**: you'll need to restart your rails server if you had it running while setting up devise
222 |
223 | 
224 |
225 | Now that we have users, we can work on restricting the app to only logged in users.
226 |
227 | ## Check for Authentication Before Rendering the React View
228 |
229 | Now that we've got authentication up and running, let's protect our app so only logged in users can view it. The quickest way to do this is to add a redirect to the controller action that will render our react app unless a user is signed in.
230 |
231 | ```
232 | class WelcomeController < ApplicationController
233 | before_action :authenticate_user!, only: [:app]
234 | def home
235 | end
236 |
237 | def app
238 |
239 | end
240 | end
241 | ```
242 |
243 | Now if we try to click on the link to the app page in the browser, we get redirected to the login screen. After logging in, it sends us to the app.
244 |
245 | 
246 |
247 | ## Create a Posts Resource and Some Seeds
248 |
249 | Next, let's create a posts resource so we can set up a couple of AJAX requests from our react front end to our rails API.
250 |
251 | ```
252 | rails g resource post title content:text user:references --no-assets --no-helper
253 | ```
254 |
255 | then we can run
256 | ```
257 | rails db:migrate
258 | ```
259 | to update our database with the posts table.
260 |
261 | We'll also want to make sure that we update the user model so it has many posts:
262 |
263 | ```
264 | # app/models/user.rb
265 | class User < ApplicationRecord
266 | # Include default devise modules. Others available are:
267 | # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
268 | devise :database_authenticatable, :registerable,
269 | :recoverable, :rememberable, :validatable
270 | has_many :posts
271 | end
272 |
273 | ```
274 |
275 | Finally, let's add some seeds so we've got some posts to work with.
276 |
277 | ```
278 | # db/seeds.rb
279 | user = User.first || User.create(email: 'test@test.com', password: 'password', password_confirmation: 'password')
280 | posts = [
281 | {
282 | title: 'My first post',
283 | content: 'The start of something special'
284 | },
285 | {
286 | title: 'My second post',
287 | content: 'This is really getting good'
288 | },
289 | {
290 | title: 'Oh my god, Yeah!!!',
291 | content: 'Enough said.'
292 | }
293 | ]
294 | posts.each do |post_hash|
295 | user.posts.create(post_hash)
296 | end
297 | ```
298 |
299 | Then we can run those with `rails db:seed`
300 |
301 | ## Create a namespace for our API
302 |
303 | One thing we probably want to do at this point as well is to create a namespace for our api and make sure that the PostsController falls within it. This is a little extra work now and has the potential to save us large headaches down the road. For more info about why this is a good idea to do now, check out this [article on API versioning](https://paweljw.github.io/2017/07/rails-5.1-api-app-part-3-api-versioning/).
304 |
305 | Practically, a few things will need to change, first the routes will be namespaced, next the controller directory structure will change, and finally the way we define the controller class will be slightly different as well.
306 |
307 | First, let's look at the routes:
308 | ```
309 | # config/routes.rb
310 | # ..
311 | namespace :api do
312 | namespace :v1 do
313 | resources :posts
314 | end
315 | end
316 | ```
317 |
318 | Now the directory structure for the controllers:
319 |
320 | ```
321 | app/controllers
322 | ├── api
323 | │ └── v1
324 | │ └── posts_controller.rb
325 | ├── concerns
326 | ├── application_controller.rb
327 | └── welcome_controller.rb
328 | ```
329 | And then update the controller to be defined under the namespaced modules.
330 |
331 | ```
332 | module Api
333 | module V1
334 | class PostsController < ApplicationController
335 | end
336 | end
337 | end
338 |
339 | ```
340 |
341 | Now if we run `rails routes` we can see what that does for us
342 |
343 | ```
344 | Prefix Verb URI Pattern Controller#Action
345 | api_v1_posts GET /api/v1/posts(.:format) api/v1/posts#index
346 | POST /api/v1/posts(.:format) api/v1/posts#create
347 | new_api_v1_post GET /api/v1/posts/new(.:format) api/v1/posts#new
348 | edit_api_v1_post GET /api/v1/posts/:id/edit(.:format) api/v1/posts#edit
349 | api_v1_post GET /api/v1/posts/:id(.:format) api/v1/posts#show
350 | PATCH /api/v1/posts/:id(.:format) api/v1/posts#update
351 | PUT /api/v1/posts/:id(.:format) api/v1/posts#update
352 | DELETE /api/v1/posts/:id(.:format) api/v1/posts#destroy
353 | ```
354 |
355 | ## Add an Index Action to Display the Current User's Posts if Authenticated
356 |
357 | Now, let's create an index PostsController that returns the current user's posts if we're authenticated and renders an empty response with a 401 status code if not.
358 |
359 | ```
360 | # app/controllers/api/v1/posts_controller.rb
361 | module Api
362 | module V1
363 | class PostsController < ApplicationController
364 | def index
365 | if user_signed_in?
366 | render json: current_user.posts
367 | else
368 | render json: {}, status: 401
369 | end
370 | end
371 | end
372 | end
373 | end
374 | ```
375 |
376 | After we've got this endpoint stubbed out, we'll be able to use it from our react front-end.
377 |
378 | ## Add React Router and a Posts Index that Pulls the Current User's Posts
379 |
380 | To start, we'll want to cd into the client directory and add in the dependencies we'll need here. (We're not using redux here yet, but we will eventually so might as well pull it in now).
381 |
382 | ```
383 | cd client
384 | yarn add react-router-dom react-redux redux
385 | ```
386 |
387 | After that we'll want to create the `PostList` component `client/src/components/PostList.jsx`. This component will make a fetch request within `componentDidMount` and update the state of the component when the promise is fulfilled.
388 |
389 | ```
390 | componentDidMount() {
391 | fetch('/api/v1/posts')
392 | .then(posts => posts.json())
393 | .then(posts => {
394 | this.setState({
395 | posts: posts
396 | })
397 | })
398 | }
399 | ```
400 |
401 | Next, we'll use a renderPosts method to return the html for the posts that we'll render to the DOM.
402 |
403 | ```
404 | renderPosts = () => {
405 | return this.state.posts.map(post => {
406 | return (
407 |
408 | {post.title} - {post.content}
409 |
410 | )
411 | })
412 | }
413 | ```
414 |
415 | Finally, we'll render the list as well as a link to the new posts route which we'll build out in a minute. When we're done it should look something like this:
416 |
417 | ```
418 | // client/src/components/PostList.jsx
419 | import React, { Component } from 'react'
420 | import { Link } from 'react-router-dom'
421 |
422 | class PostList extends Component {
423 |
424 | state = {
425 | posts: []
426 | }
427 |
428 | componentDidMount() {
429 | fetch('/api/v1/posts')
430 | .then(posts => posts.json())
431 | .then(posts => {
432 | this.setState({
433 | posts: posts
434 | })
435 | })
436 | }
437 |
438 | renderPosts = () => {
439 | return this.state.posts.map(post => {
440 | return (
441 |
451 | PostList Component
452 | {this.renderPosts()}
453 | Add a New Post
454 |
455 | )
456 | }
457 | }
458 |
459 | export default PostList
460 | ```
461 |
462 | In order to test this out, we'll need to add react router to the `client/src/App.js` and configure the root route to point to our `PostList` component. In this case we'll also want to use the `HashRouter` instead of the `BrowserRouter` so that we'll be able to easily integrate our rails routes with our client side routing in a way that still allows for page refreshes and inbound links to work properly.
463 |
464 | ```
465 | // client/src/App.js
466 | import React from 'react';
467 | import {
468 | HashRouter as Router,
469 | Route
470 | } from 'react-router-dom';
471 | import PostList from './components/PostList';
472 | import './App.css';
473 |
474 | function App() {
475 | return (
476 |
477 |
478 |
479 |
480 |
481 | );
482 | }
483 |
484 | export default App;
485 | ```
486 |
487 | Now that we've got these changes into our react code, let's make sure that we've still got `./bin/webpack-dev-server` running and check out our app in the browser!
488 |
489 | 
490 |
491 | Notice that the url when the app is loaded is now `localhost:3000/app#/`. All of the react routes are mounted after the `#` so we'll be able refresh the page at any of our react routes and the rails router will render our SPA with the react router picking up the client side routing after the `#`.
492 |
493 | ## Add the New Post Component and the Create Action in the Rails Controller
494 |
495 | Now we want to allow users to create new posts. We've already got react router set up, so we'll need to add the `NewPost` component and then have it post to `api/v1/posts` for which we'll build a `create` action in our `PostsController`.
496 |
497 | Let's start by building out the controller layer. We'll need to define our strong parameters to whitelist the `:title` and `:content` attributes.
498 |
499 | ```
500 | # app/controllers/api/v1/posts_controller.rb
501 | # ...
502 | def post_params
503 | params.require(:post).permit(:title, :content)
504 | end
505 | ```
506 | Next, we'll again check if the user is authenticated before creating the post and returning it in json format.
507 |
508 | ```
509 | # app/controllers/api/v1/posts_controller.rb
510 | # ...
511 | def create
512 | if user_signed_in?
513 | if post = current_user.posts.create(post_params)
514 | render json: post, status: :created
515 | else
516 | render json: post.errors, status: 400
517 | end
518 | else
519 | render json: {}, status: 401
520 | end
521 | end
522 | ```
523 |
524 | For good measure, let's actually create a reason that the creation could fail by adding presence validations for title and content to our `Post` model:
525 |
526 | ```
527 | # app/models/post.rb
528 | class Post < ApplicationRecord
529 | belongs_to :user
530 | validates :title, :content, presence: true
531 | end
532 | ```
533 |
534 | Next, we'll focus back on the front end. First we'll create a `NewPost` component that will have `title` and `content` fields whose values are stored in state. We'll also add a `handleChange` method that will turn the inputs into controlled inputs.
535 |
536 | ```
537 | # client/components/NewPost.jsx
538 | import React, { Component } from 'react';
539 |
540 | class NewPost extends Component {
541 |
542 | state = {
543 | title: '',
544 | content: ''
545 | }
546 |
547 | handleChange = e => {
548 | let newValue = e.target.value;
549 | let key = e.target.name;
550 | this.setState({
551 | [key]: newValue
552 | });
553 | }
554 |
555 | render() {
556 | return (
557 |
568 | )
569 | }
570 | }
571 |
572 | export default NewPost
573 | ```
574 |
575 | ## Configure the New Post Component to Pull CSRF Token from View and Add to Headers on Fetch Request
576 |
577 | Finally, we'll need to add an event handler to handle the form submission. To do this we've got to do a couple of things to make this work. First, we'll prevent the default behavior on submission to stop a page refresh, next we'll also have to accomodate the cross site request forgery (CSRF) security measures that rails ships with. If we just send the fetch request without including a CSRF token created by the rails backend, Rails will raise an exception. To grab this token, we'll need to read it from the meta tags in the dom
578 |
579 | ```
580 | let token = document.querySelector('meta[name="csrf-token"]').content;
581 | ```
582 | After that, we'll include the token in the headers of the fetch request. We'll then parse the response as JSON and use `this.props.history.push` from react-router to send the user back to the `PostList` comopnent home view.
583 |
584 | ```
585 | // client/src/components/NewPost.jsx
586 | // ...
587 |
588 | handleSubmit = (e) => {
589 | e.preventDefault();
590 | let data = {post: this.state};
591 | let token = document.querySelector('meta[name="csrf-token"]').content;
592 | fetch('/api/v1/posts', {
593 | method: 'POST',
594 | headers: {
595 | "Content-Type": "application/json",
596 | 'X-Requested-With': 'XMLHttpRequest',
597 | 'X-CSRF-Token': token
598 | },
599 | redirect: "error",
600 | body: JSON.stringify(data)
601 | })
602 | .then(resp => {
603 | resp.json()
604 | })
605 | .then(post => {
606 | this.props.history.push('/');
607 | });
608 | }
609 | ```
610 |
611 | And finally, we'd add the submit event handler to the rendered form:
612 |
613 | ```
614 | // client/src/components/NewPost.jsx
615 | // ...
616 | render() {
617 | return (
618 |
629 | )
630 | }
631 | ```
632 |
633 | Finally, so that we can actually get to the form, we need to hook this component up to our React router in App.js.
634 |
635 | We'll need to import it
636 | ```
637 | import NewPost from './components/NewPost';
638 | ```
639 |
640 | and then add the route:
641 |
642 | ```
643 |
644 | ```
645 |
646 | When we're done it should look like this:
647 |
648 | ```
649 | // client/src/App.js
650 | import React from 'react';
651 | import {
652 | HashRouter as Router,
653 | Route
654 | } from 'react-router-dom';
655 | import PostList from './components/PostList';
656 | import NewPost from './components/NewPost';
657 | import './App.css';
658 |
659 | function App() {
660 | return (
661 |
662 |
663 |
664 |
665 |
666 |
667 | );
668 | }
669 |
670 | export default App;
671 | ```
672 |
673 | Now we're ready to try this out! Let's fire up our rails server and our webpack dev server if they're not already running and click through the form.
674 |
675 | 
676 |
677 | ## Wrapping Up
678 |
679 | Great job getting through this tutorial! Hopefully this gives you an idea of how you could create a react app that takes advantage of user accounts using session cookies. Before I leave you here, I just wanted to discuss some potential trade offs with this approach and alternatives you might also consider.
680 |
681 | Using cookie based authentication is fine if you're planning on only interacting with this API from your current react app within Rails, and not say from a mobile application or another web application. The reason for this is that cookies are domain specific, so if you need to enable access to your API from other domains, you'll want to take another approach like JWT or OAuth2 for authentication.
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require turbolinks
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 | //
4 | //= require action_cable
5 | //= require_self
6 | //= require_tree ./channels
7 |
8 | (function() {
9 | this.App || (this.App = {});
10 |
11 | App.cable = ActionCable.createConsumer();
12 |
13 | }).call(this);
14 |
--------------------------------------------------------------------------------
/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/app/assets/javascripts/channels/.keep
--------------------------------------------------------------------------------
/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, or any plugin's
6 | * 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 |
--------------------------------------------------------------------------------
/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/controllers/api/v1/posts_controller.rb:
--------------------------------------------------------------------------------
1 | module Api
2 | module V1
3 | class PostsController < ApplicationController
4 | def index
5 | if user_signed_in?
6 | render json: current_user.posts
7 | else
8 | render json: {}, status: 401
9 | end
10 | end
11 |
12 | def create
13 | if user_signed_in?
14 | if post = current_user.posts.create(post_params)
15 | render json: post, status: :created
16 | else
17 | render json: post.errors, status: 400
18 | end
19 | else
20 | render json: {}, status: 401
21 | end
22 | end
23 |
24 | private
25 |
26 | def post_params
27 | params.require(:post).permit(:title, :content)
28 | end
29 | end
30 | end
31 | end
32 |
33 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | end
3 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/welcome_controller.rb:
--------------------------------------------------------------------------------
1 | class WelcomeController < ApplicationController
2 | before_action :authenticate_user!, only: [:app]
3 | def home
4 | end
5 |
6 | def app
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console:0 */
2 | // This file is automatically compiled by Webpack, along with any other files
3 | // present in this directory. You're encouraged to place your actual application logic in
4 | // a relevant structure within app/javascript and only use these pack files to reference
5 | // that code so it'll be compiled.
6 | //
7 | // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
8 | // layout file, like app/views/layouts/application.html.erb
9 |
10 |
11 | // Uncomment to copy all static images under ../images to the output folder and reference
12 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
13 | // or the `imagePath` JavaScript helper below.
14 | //
15 | // const images = require.context('../images', true)
16 | // const imagePath = (name) => images(name, true)
17 |
18 | console.log('Hello World from Webpacker')
19 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/post.rb:
--------------------------------------------------------------------------------
1 | class Post < ApplicationRecord
2 | belongs_to :user
3 | validates :title, :content, presence: true
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ApplicationRecord
2 | # Include default devise modules. Others available are:
3 | # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
4 | devise :database_authenticatable, :registerable,
5 | :recoverable, :rememberable, :validatable
6 | has_many :posts
7 | end
8 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RailsReactDevise
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
9 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
10 |
11 |
12 |
13 |
33 | PostList Component
34 | {this.renderPosts()}
35 | Add a New Post
36 |
37 | )
38 | }
39 | }
40 |
41 | export default PostList
42 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 |
--------------------------------------------------------------------------------
/client/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/client/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 "active_storage/engine"
9 | require "action_controller/railtie"
10 | require "action_mailer/railtie"
11 | require "action_view/railtie"
12 | require "action_cable/engine"
13 | require "sprockets/railtie"
14 | # require "rails/test_unit/railtie"
15 |
16 | # Require the gems listed in Gemfile, including any gems
17 | # you've limited to :test, :development, or :production.
18 | Bundler.require(*Rails.groups)
19 |
20 | module RailsReactDevise
21 | class Application < Rails::Application
22 | # Initialize configuration defaults for originally generated Rails version.
23 | config.load_defaults 5.2
24 |
25 | # Settings in config/environments/* take precedence over those specified here.
26 | # Application configuration can go into files in config/initializers
27 | # -- all .rb files in that directory are automatically loaded after loading
28 | # the framework and any gems in your application.
29 |
30 | # Don't generate system test files.
31 | config.generators.system_tests = nil
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: rails-react-devise_production
11 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | B6ezJCp3yt7EmX+YQoWedCa6qvnEtrxd3OZ/kXpotqF3a/3PXkB2D0qW3qipQC6N8IWWNTTxdduIgMDgERTmzOUtjju6rHjMevIl2SS5xl6UNsmuKOYxqvz2f6YEIS6vmANJPACOcOh6hjdHpJjeje2FzXzjJ15dsZjNbLqKEHJzWBugIEoUtBgmlaJNdmlw3kPloDX69IyqJ80dhc9rTt+DMoMW3AYOg+kgK+dyQBRNfGJElwILFxCfiBtialyhCWRTzfEWEZfIDL26wnjvRNYHBgvQgusGwmkWGqkdhfvzacIGgxbiAg8Z94zRjFQht7BFxTCSUL3Y9h7BLeeKsPO7invAsrWikAk21VonGpKMbLVBCM0uGljNhdWrvQhIa3vjkqAXLe/xhSyJkiDM/JKeEf8/nsbVXo9/--Mqvlo6/uE9TrgCkG--rvXhQjqpALlubliw8Tl9iQ==
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 |
20 | config.cache_store = :memory_store
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
23 | }
24 | else
25 | config.action_controller.perform_caching = false
26 |
27 | config.cache_store = :null_store
28 | end
29 |
30 | # Store uploaded files on the local file system (see config/storage.yml for options)
31 | config.active_storage.service = :local
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
39 |
40 | # Print deprecation notices to the Rails logger.
41 | config.active_support.deprecation = :log
42 |
43 | # Raise an error on page load if there are pending migrations.
44 | config.active_record.migration_error = :page_load
45 |
46 | # Highlight code that triggered database queries in logs.
47 | config.active_record.verbose_query_logs = true
48 |
49 | # Debug mode disables concatenation and preprocessing of assets.
50 | # This option may cause significant delays in view rendering with a large
51 | # number of complex assets.
52 | config.assets.debug = true
53 |
54 | # Suppress logger output for asset requests.
55 | config.assets.quiet = true
56 |
57 | # Raises error for missing translations
58 | # config.action_view.raise_on_missing_translations = true
59 |
60 | # Use an evented file watcher to asynchronously detect changes in source code,
61 | # routes, locales, etc. This feature depends on the listen gem.
62 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
63 | end
64 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
33 |
34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
35 | # config.action_controller.asset_host = 'http://assets.example.com'
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
40 |
41 | # Store uploaded files on the local file system (see config/storage.yml for options)
42 | config.active_storage.service = :local
43 |
44 | # Mount Action Cable outside main process or domain
45 | # config.action_cable.mount_path = nil
46 | # config.action_cable.url = 'wss://example.com/cable'
47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
48 |
49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
50 | # config.force_ssl = true
51 |
52 | # Use the lowest log level to ensure availability of diagnostic information
53 | # when problems arise.
54 | config.log_level = :debug
55 |
56 | # Prepend all log lines with the following tags.
57 | config.log_tags = [ :request_id ]
58 |
59 | # Use a different cache store in production.
60 | # config.cache_store = :mem_cache_store
61 |
62 | # Use a real queuing backend for Active Job (and separate queues per environment)
63 | # config.active_job.queue_adapter = :resque
64 | # config.active_job.queue_name_prefix = "rails-react-devise_#{Rails.env}"
65 |
66 | config.action_mailer.perform_caching = false
67 |
68 | # Ignore bad email addresses and do not raise email delivery errors.
69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
70 | # config.action_mailer.raise_delivery_errors = false
71 |
72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
73 | # the I18n.default_locale when a translation cannot be found).
74 | config.i18n.fallbacks = true
75 |
76 | # Send deprecation notices to registered listeners.
77 | config.active_support.deprecation = :notify
78 |
79 | # Use default logging formatter so that PID and timestamp are not suppressed.
80 | config.log_formatter = ::Logger::Formatter.new
81 |
82 | # Use a different logger for distributed setups.
83 | # require 'syslog/logger'
84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
85 |
86 | if ENV["RAILS_LOG_TO_STDOUT"].present?
87 | logger = ActiveSupport::Logger.new(STDOUT)
88 | logger.formatter = config.log_formatter
89 | config.logger = ActiveSupport::TaggedLogging.new(logger)
90 | end
91 |
92 | # Do not dump schema after migrations.
93 | config.active_record.dump_schema_after_migration = false
94 | end
95 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory
32 | config.active_storage.service = :test
33 |
34 | config.action_mailer.perform_caching = false
35 |
36 | # Tell Action Mailer not to deliver emails to the real world.
37 | # The :test delivery method accumulates sent emails in the
38 | # ActionMailer::Base.deliveries array.
39 | config.action_mailer.delivery_method = :test
40 |
41 | # Print deprecation notices to the stderr.
42 | config.active_support.deprecation = :stderr
43 |
44 | # Raises error for missing translations
45 | # config.action_view.raise_on_missing_translations = true
46 | end
47 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 |
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 |
19 | # If you are using UJS then enable automatic nonce generation
20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21 |
22 | # Report CSP violations to a specified URI
23 | # For further information see the following documentation:
24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25 | # Rails.application.config.content_security_policy_report_only = true
26 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/config/initializers/devise.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Use this hook to configure devise mailer, warden hooks and so forth.
4 | # Many of these configuration options can be set straight in your model.
5 | Devise.setup do |config|
6 | # The secret key used by Devise. Devise uses this key to generate
7 | # random tokens. Changing this key will render invalid all existing
8 | # confirmation, reset password and unlock tokens in the database.
9 | # Devise will use the `secret_key_base` as its `secret_key`
10 | # by default. You can change it below and use your own secret key.
11 | # config.secret_key = '37cdbb3800998b9a6b6a42ab709235da81794e081545c58bdb015739b3349c75d4c8624df1005327fc18f3b14fa3d7f24e75d3c96030b625b2488c0e6691062b'
12 |
13 | # ==> Controller configuration
14 | # Configure the parent class to the devise controllers.
15 | # config.parent_controller = 'DeviseController'
16 |
17 | # ==> Mailer Configuration
18 | # Configure the e-mail address which will be shown in Devise::Mailer,
19 | # note that it will be overwritten if you use your own mailer class
20 | # with default "from" parameter.
21 | config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
22 |
23 | # Configure the class responsible to send e-mails.
24 | # config.mailer = 'Devise::Mailer'
25 |
26 | # Configure the parent class responsible to send e-mails.
27 | # config.parent_mailer = 'ActionMailer::Base'
28 |
29 | # ==> ORM configuration
30 | # Load and configure the ORM. Supports :active_record (default) and
31 | # :mongoid (bson_ext recommended) by default. Other ORMs may be
32 | # available as additional gems.
33 | require 'devise/orm/active_record'
34 |
35 | # ==> Configuration for any authentication mechanism
36 | # Configure which keys are used when authenticating a user. The default is
37 | # just :email. You can configure it to use [:username, :subdomain], so for
38 | # authenticating a user, both parameters are required. Remember that those
39 | # parameters are used only when authenticating and not when retrieving from
40 | # session. If you need permissions, you should implement that in a before filter.
41 | # You can also supply a hash where the value is a boolean determining whether
42 | # or not authentication should be aborted when the value is not present.
43 | # config.authentication_keys = [:email]
44 |
45 | # Configure parameters from the request object used for authentication. Each entry
46 | # given should be a request method and it will automatically be passed to the
47 | # find_for_authentication method and considered in your model lookup. For instance,
48 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
49 | # The same considerations mentioned for authentication_keys also apply to request_keys.
50 | # config.request_keys = []
51 |
52 | # Configure which authentication keys should be case-insensitive.
53 | # These keys will be downcased upon creating or modifying a user and when used
54 | # to authenticate or find a user. Default is :email.
55 | config.case_insensitive_keys = [:email]
56 |
57 | # Configure which authentication keys should have whitespace stripped.
58 | # These keys will have whitespace before and after removed upon creating or
59 | # modifying a user and when used to authenticate or find a user. Default is :email.
60 | config.strip_whitespace_keys = [:email]
61 |
62 | # Tell if authentication through request.params is enabled. True by default.
63 | # It can be set to an array that will enable params authentication only for the
64 | # given strategies, for example, `config.params_authenticatable = [:database]` will
65 | # enable it only for database (email + password) authentication.
66 | # config.params_authenticatable = true
67 |
68 | # Tell if authentication through HTTP Auth is enabled. False by default.
69 | # It can be set to an array that will enable http authentication only for the
70 | # given strategies, for example, `config.http_authenticatable = [:database]` will
71 | # enable it only for database authentication. The supported strategies are:
72 | # :database = Support basic authentication with authentication key + password
73 | # config.http_authenticatable = false
74 |
75 | # If 401 status code should be returned for AJAX requests. True by default.
76 | # config.http_authenticatable_on_xhr = true
77 |
78 | # The realm used in Http Basic Authentication. 'Application' by default.
79 | # config.http_authentication_realm = 'Application'
80 |
81 | # It will change confirmation, password recovery and other workflows
82 | # to behave the same regardless if the e-mail provided was right or wrong.
83 | # Does not affect registerable.
84 | # config.paranoid = true
85 |
86 | # By default Devise will store the user in session. You can skip storage for
87 | # particular strategies by setting this option.
88 | # Notice that if you are skipping storage for all authentication paths, you
89 | # may want to disable generating routes to Devise's sessions controller by
90 | # passing skip: :sessions to `devise_for` in your config/routes.rb
91 | config.skip_session_storage = [:http_auth]
92 |
93 | # By default, Devise cleans up the CSRF token on authentication to
94 | # avoid CSRF token fixation attacks. This means that, when using AJAX
95 | # requests for sign in and sign up, you need to get a new CSRF token
96 | # from the server. You can disable this option at your own risk.
97 | # config.clean_up_csrf_token_on_authentication = true
98 |
99 | # When false, Devise will not attempt to reload routes on eager load.
100 | # This can reduce the time taken to boot the app but if your application
101 | # requires the Devise mappings to be loaded during boot time the application
102 | # won't boot properly.
103 | # config.reload_routes = true
104 |
105 | # ==> Configuration for :database_authenticatable
106 | # For bcrypt, this is the cost for hashing the password and defaults to 11. If
107 | # using other algorithms, it sets how many times you want the password to be hashed.
108 | #
109 | # Limiting the stretches to just one in testing will increase the performance of
110 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
111 | # a value less than 10 in other environments. Note that, for bcrypt (the default
112 | # algorithm), the cost increases exponentially with the number of stretches (e.g.
113 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
114 | config.stretches = Rails.env.test? ? 1 : 11
115 |
116 | # Set up a pepper to generate the hashed password.
117 | # config.pepper = '7c3ea510fa27eed40f2e352a0244990f2b576713c427ff8086f1240902d8d44057f5f43bbd044c80ae91745cf3ddbde09805a4edb7f02d2cebe5d2677ac09542'
118 |
119 | # Send a notification to the original email when the user's email is changed.
120 | # config.send_email_changed_notification = false
121 |
122 | # Send a notification email when the user's password is changed.
123 | # config.send_password_change_notification = false
124 |
125 | # ==> Configuration for :confirmable
126 | # A period that the user is allowed to access the website even without
127 | # confirming their account. For instance, if set to 2.days, the user will be
128 | # able to access the website for two days without confirming their account,
129 | # access will be blocked just in the third day.
130 | # You can also set it to nil, which will allow the user to access the website
131 | # without confirming their account.
132 | # Default is 0.days, meaning the user cannot access the website without
133 | # confirming their account.
134 | # config.allow_unconfirmed_access_for = 2.days
135 |
136 | # A period that the user is allowed to confirm their account before their
137 | # token becomes invalid. For example, if set to 3.days, the user can confirm
138 | # their account within 3 days after the mail was sent, but on the fourth day
139 | # their account can't be confirmed with the token any more.
140 | # Default is nil, meaning there is no restriction on how long a user can take
141 | # before confirming their account.
142 | # config.confirm_within = 3.days
143 |
144 | # If true, requires any email changes to be confirmed (exactly the same way as
145 | # initial account confirmation) to be applied. Requires additional unconfirmed_email
146 | # db field (see migrations). Until confirmed, new email is stored in
147 | # unconfirmed_email column, and copied to email column on successful confirmation.
148 | config.reconfirmable = true
149 |
150 | # Defines which key will be used when confirming an account
151 | # config.confirmation_keys = [:email]
152 |
153 | # ==> Configuration for :rememberable
154 | # The time the user will be remembered without asking for credentials again.
155 | # config.remember_for = 2.weeks
156 |
157 | # Invalidates all the remember me tokens when the user signs out.
158 | config.expire_all_remember_me_on_sign_out = true
159 |
160 | # If true, extends the user's remember period when remembered via cookie.
161 | # config.extend_remember_period = false
162 |
163 | # Options to be passed to the created cookie. For instance, you can set
164 | # secure: true in order to force SSL only cookies.
165 | # config.rememberable_options = {}
166 |
167 | # ==> Configuration for :validatable
168 | # Range for password length.
169 | config.password_length = 6..128
170 |
171 | # Email regex used to validate email formats. It simply asserts that
172 | # one (and only one) @ exists in the given string. This is mainly
173 | # to give user feedback and not to assert the e-mail validity.
174 | config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
175 |
176 | # ==> Configuration for :timeoutable
177 | # The time you want to timeout the user session without activity. After this
178 | # time the user will be asked for credentials again. Default is 30 minutes.
179 | # config.timeout_in = 30.minutes
180 |
181 | # ==> Configuration for :lockable
182 | # Defines which strategy will be used to lock an account.
183 | # :failed_attempts = Locks an account after a number of failed attempts to sign in.
184 | # :none = No lock strategy. You should handle locking by yourself.
185 | # config.lock_strategy = :failed_attempts
186 |
187 | # Defines which key will be used when locking and unlocking an account
188 | # config.unlock_keys = [:email]
189 |
190 | # Defines which strategy will be used to unlock an account.
191 | # :email = Sends an unlock link to the user email
192 | # :time = Re-enables login after a certain amount of time (see :unlock_in below)
193 | # :both = Enables both strategies
194 | # :none = No unlock strategy. You should handle unlocking by yourself.
195 | # config.unlock_strategy = :both
196 |
197 | # Number of authentication tries before locking an account if lock_strategy
198 | # is failed attempts.
199 | # config.maximum_attempts = 20
200 |
201 | # Time interval to unlock the account if :time is enabled as unlock_strategy.
202 | # config.unlock_in = 1.hour
203 |
204 | # Warn on the last attempt before the account is locked.
205 | # config.last_attempt_warning = true
206 |
207 | # ==> Configuration for :recoverable
208 | #
209 | # Defines which key will be used when recovering the password for an account
210 | # config.reset_password_keys = [:email]
211 |
212 | # Time interval you can reset your password with a reset password key.
213 | # Don't put a too small interval or your users won't have the time to
214 | # change their passwords.
215 | config.reset_password_within = 6.hours
216 |
217 | # When set to false, does not sign a user in automatically after their password is
218 | # reset. Defaults to true, so a user is signed in automatically after a reset.
219 | # config.sign_in_after_reset_password = true
220 |
221 | # ==> Configuration for :encryptable
222 | # Allow you to use another hashing or encryption algorithm besides bcrypt (default).
223 | # You can use :sha1, :sha512 or algorithms from others authentication tools as
224 | # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20
225 | # for default behavior) and :restful_authentication_sha1 (then you should set
226 | # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper).
227 | #
228 | # Require the `devise-encryptable` gem when using anything other than bcrypt
229 | # config.encryptor = :sha512
230 |
231 | # ==> Scopes configuration
232 | # Turn scoped views on. Before rendering "sessions/new", it will first check for
233 | # "users/sessions/new". It's turned off by default because it's slower if you
234 | # are using only default views.
235 | # config.scoped_views = false
236 |
237 | # Configure the default scope given to Warden. By default it's the first
238 | # devise role declared in your routes (usually :user).
239 | # config.default_scope = :user
240 |
241 | # Set this configuration to false if you want /users/sign_out to sign out
242 | # only the current scope. By default, Devise signs out all scopes.
243 | # config.sign_out_all_scopes = true
244 |
245 | # ==> Navigation configuration
246 | # Lists the formats that should be treated as navigational. Formats like
247 | # :html, should redirect to the sign in page when the user does not have
248 | # access, but formats like :xml or :json, should return 401.
249 | #
250 | # If you have any extra navigational formats, like :iphone or :mobile, you
251 | # should add them to the navigational formats lists.
252 | #
253 | # The "*/*" below is required to match Internet Explorer requests.
254 | # config.navigational_formats = ['*/*', :html]
255 |
256 | # The default HTTP method used to sign out a resource. Default is :delete.
257 | config.sign_out_via = :delete
258 |
259 | # ==> OmniAuth
260 | # Add a new OmniAuth provider. Check the wiki for more information on setting
261 | # up on your models and hooks.
262 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
263 |
264 | # ==> Warden configuration
265 | # If you want to use other strategies, that are not supported by Devise, or
266 | # change the failure app, you can configure them inside the config.warden block.
267 | #
268 | # config.warden do |manager|
269 | # manager.intercept_401 = false
270 | # manager.default_strategies(scope: :user).unshift :some_external_strategy
271 | # end
272 |
273 | # ==> Mountable engine configurations
274 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine
275 | # is mountable, there are some extra configurations to be taken into account.
276 | # The following options are available, assuming the engine is mounted as:
277 | #
278 | # mount MyEngine, at: '/my_engine'
279 | #
280 | # The router that invoked `devise_for`, in the example above, would be:
281 | # config.router_name = :my_engine
282 | #
283 | # When using OmniAuth, Devise cannot automatically set OmniAuth path,
284 | # so you need to do it manually. For the users scope, it would be:
285 | # config.omniauth_path_prefix = '/my_engine/users/auth'
286 |
287 | # ==> Turbolinks configuration
288 | # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
289 | #
290 | # ActiveSupport.on_load(:devise_failure_app) do
291 | # include Turbolinks::Controller
292 | # end
293 |
294 | # ==> Configuration for :registerable
295 |
296 | # When set to false, does not sign a user in automatically after their password is
297 | # changed. Defaults to true, so a user is signed in automatically after changing a password.
298 | # config.sign_in_after_change_password = true
299 | end
300 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/config/locales/devise.en.yml:
--------------------------------------------------------------------------------
1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
2 |
3 | en:
4 | devise:
5 | confirmations:
6 | confirmed: "Your email address has been successfully confirmed."
7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes."
8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes."
9 | failure:
10 | already_authenticated: "You are already signed in."
11 | inactive: "Your account is not activated yet."
12 | invalid: "Invalid %{authentication_keys} or password."
13 | locked: "Your account is locked."
14 | last_attempt: "You have one more attempt before your account is locked."
15 | not_found_in_database: "Invalid %{authentication_keys} or password."
16 | timeout: "Your session expired. Please sign in again to continue."
17 | unauthenticated: "You need to sign in or sign up before continuing."
18 | unconfirmed: "You have to confirm your email address before continuing."
19 | mailer:
20 | confirmation_instructions:
21 | subject: "Confirmation instructions"
22 | reset_password_instructions:
23 | subject: "Reset password instructions"
24 | unlock_instructions:
25 | subject: "Unlock instructions"
26 | email_changed:
27 | subject: "Email Changed"
28 | password_change:
29 | subject: "Password Changed"
30 | omniauth_callbacks:
31 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
32 | success: "Successfully authenticated from %{kind} account."
33 | passwords:
34 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
35 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
36 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
37 | updated: "Your password has been changed successfully. You are now signed in."
38 | updated_not_active: "Your password has been changed successfully."
39 | registrations:
40 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon."
41 | signed_up: "Welcome! You have signed up successfully."
42 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
43 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
44 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
45 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address."
46 | updated: "Your account has been updated successfully."
47 | updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again"
48 | sessions:
49 | signed_in: "Signed in successfully."
50 | signed_out: "Signed out successfully."
51 | already_signed_out: "Signed out successfully."
52 | unlocks:
53 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes."
54 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes."
55 | unlocked: "Your account has been unlocked successfully. Please sign in to continue."
56 | errors:
57 | messages:
58 | already_confirmed: "was already confirmed, please try signing in"
59 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
60 | expired: "has expired, please request a new one"
61 | not_found: "not found"
62 | not_locked: "was not locked"
63 | not_saved:
64 | one: "1 error prohibited this %{resource} from being saved:"
65 | other: "%{count} errors prohibited this %{resource} from being saved:"
66 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
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.
30 | #
31 | # preload_app!
32 |
33 | # Allow puma to be restarted by `rails restart` command.
34 | plugin :tmp_restart
35 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | namespace :api do
3 | namespace :v1 do
4 | resources :posts
5 | end
6 | end
7 | devise_for :users
8 | get 'welcome/home'
9 | get '/app', to: 'welcome#app', as: 'app'
10 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
11 | root 'welcome#home'
12 | end
13 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w[
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ].each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | module.exports = environment
4 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpacker.yml:
--------------------------------------------------------------------------------
1 | # Note: You must restart bin/webpack-dev-server for changes to take effect
2 |
3 | default: &default
4 | source_path: client
5 | source_entry_path: src
6 | public_root_path: public
7 | public_output_path: packs
8 | cache_path: tmp/cache/webpacker
9 | check_yarn_integrity: false
10 | webpack_compile_output: false
11 |
12 | # Additional paths webpack should lookup modules
13 | # ['app/assets', 'engine/foo/app/assets']
14 | resolved_paths: []
15 |
16 | # Reload manifest.json on all requests so we reload latest compiled packs
17 | cache_manifest: false
18 |
19 | # Extract and emit a css file
20 | extract_css: false
21 |
22 | static_assets_extensions:
23 | - .jpg
24 | - .jpeg
25 | - .png
26 | - .gif
27 | - .tiff
28 | - .ico
29 | - .svg
30 | - .eot
31 | - .otf
32 | - .ttf
33 | - .woff
34 | - .woff2
35 |
36 | extensions:
37 | - .jsx
38 | - .mjs
39 | - .js
40 | - .sass
41 | - .scss
42 | - .css
43 | - .module.sass
44 | - .module.scss
45 | - .module.css
46 | - .png
47 | - .svg
48 | - .gif
49 | - .jpeg
50 | - .jpg
51 |
52 | development:
53 | <<: *default
54 | compile: true
55 |
56 | # Verifies that versions and hashed value of the package contents in the project's package.json
57 | check_yarn_integrity: true
58 |
59 | # Reference: https://webpack.js.org/configuration/dev-server/
60 | dev_server:
61 | https: false
62 | host: localhost
63 | port: 3035
64 | public: localhost:3035
65 | hmr: false
66 | # Inline should be set to true if using HMR
67 | inline: true
68 | overlay: true
69 | compress: true
70 | disable_host_check: true
71 | use_local_ip: false
72 | quiet: false
73 | headers:
74 | 'Access-Control-Allow-Origin': '*'
75 | watch_options:
76 | ignored: '**/node_modules/**'
77 |
78 |
79 | test:
80 | <<: *default
81 | compile: true
82 |
83 | # Compile test packs to a separate directory
84 | public_output_path: packs-test
85 |
86 | production:
87 | <<: *default
88 |
89 | # Production depends on precompilation of packs prior to booting for performance.
90 | compile: false
91 |
92 | # Extract and emit a css file
93 | extract_css: true
94 |
95 | # Cache manifest.json for performance
96 | cache_manifest: true
97 |
--------------------------------------------------------------------------------
/db/migrate/20190503170520_devise_create_users.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class DeviseCreateUsers < ActiveRecord::Migration[5.2]
4 | def change
5 | create_table :users do |t|
6 | ## Database authenticatable
7 | t.string :email, null: false, default: ""
8 | t.string :encrypted_password, null: false, default: ""
9 |
10 | ## Recoverable
11 | t.string :reset_password_token
12 | t.datetime :reset_password_sent_at
13 |
14 | ## Rememberable
15 | t.datetime :remember_created_at
16 |
17 | ## Trackable
18 | # t.integer :sign_in_count, default: 0, null: false
19 | # t.datetime :current_sign_in_at
20 | # t.datetime :last_sign_in_at
21 | # t.string :current_sign_in_ip
22 | # t.string :last_sign_in_ip
23 |
24 | ## Confirmable
25 | # t.string :confirmation_token
26 | # t.datetime :confirmed_at
27 | # t.datetime :confirmation_sent_at
28 | # t.string :unconfirmed_email # Only if using reconfirmable
29 |
30 | ## Lockable
31 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
32 | # t.string :unlock_token # Only if unlock strategy is :email or :both
33 | # t.datetime :locked_at
34 |
35 |
36 | t.timestamps null: false
37 | end
38 |
39 | add_index :users, :email, unique: true
40 | add_index :users, :reset_password_token, unique: true
41 | # add_index :users, :confirmation_token, unique: true
42 | # add_index :users, :unlock_token, unique: true
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/db/migrate/20190507162309_create_posts.rb:
--------------------------------------------------------------------------------
1 | class CreatePosts < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :posts do |t|
4 | t.string :title
5 | t.text :content
6 | t.references :user, foreign_key: true
7 |
8 | t.timestamps
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/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: 2019_05_07_162309) do
14 |
15 | create_table "posts", force: :cascade do |t|
16 | t.string "title"
17 | t.text "content"
18 | t.integer "user_id"
19 | t.datetime "created_at", null: false
20 | t.datetime "updated_at", null: false
21 | t.index ["user_id"], name: "index_posts_on_user_id"
22 | end
23 |
24 | create_table "users", force: :cascade do |t|
25 | t.string "email", default: "", null: false
26 | t.string "encrypted_password", default: "", null: false
27 | t.string "reset_password_token"
28 | t.datetime "reset_password_sent_at"
29 | t.datetime "remember_created_at"
30 | t.datetime "created_at", null: false
31 | t.datetime "updated_at", null: false
32 | t.index ["email"], name: "index_users_on_email", unique: true
33 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/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 |
9 |
10 | user = User.first || User.create(email: 'test@test.com', password: 'password', password_confirmation: 'password')
11 | posts = [
12 | {
13 | title: 'My first post',
14 | content: 'The start of something special'
15 | },
16 | {
17 | title: 'My second post',
18 | content: 'This is really getting good'
19 | },
20 | {
21 | title: 'Oh my god, Yeah!!!',
22 | content: 'Enough said.'
23 | }
24 | ]
25 | posts.each do |post_hash|
26 | user.posts.create(post_hash)
27 | end
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/lib/tasks/.keep
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/log/.keep
--------------------------------------------------------------------------------
/media/home-to-app-link.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/media/home-to-app-link.gif
--------------------------------------------------------------------------------
/media/login-flow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/media/login-flow.gif
--------------------------------------------------------------------------------
/media/new-post-component-submission.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/media/new-post-component-submission.gif
--------------------------------------------------------------------------------
/media/post-list-component.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/media/post-list-component.gif
--------------------------------------------------------------------------------
/media/react-app-behind-login.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DakotaLMartinez/rails-react-devise-tutorial/27704e4921ead52b4d947a476d9368d6c9d999f7/media/react-app-behind-login.gif
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rails-react-devise",
3 | "private": true,
4 | "dependencies": {
5 | "@babel/preset-react": "^7.0.0",
6 | "@rails/webpacker": "^4.0.2",
7 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
8 | "fstream": "^1.0.12",
9 | "lodash": "^4.17.13",
10 | "lodash.template": "^4.5.0",
11 | "prop-types": "^15.7.2",
12 | "react": "^16.8.6",
13 | "react-dom": "^16.8.6"
14 | },
15 | "devDependencies": {
16 | "webpack-dev-server": "^3.3.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-flexbugs-fixes'),
5 | require('postcss-preset-env')({
6 | autoprefixer: {
7 | flexbox: 'no-2009'
8 | },
9 | stage: 3
10 | })
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
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.