rails tutorils: signup

Users 资源

config/routes.rb

1
2
3
4
5
6
7
8
9
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'

resources :users
end
Users Resources

Gravatar 头像和侧边栏

app/views/users/show.html.erb

1
2
3
4
5
6
7
8
9
10
<% provide(:title, @user.name) %> 
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for @user %>
<%= @user.name %> </h1>
</section>
</aside>
</div>

app/assets/stylesheets/custom.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* sidebar */
aside {
section.user_info {
margin-top: 20px; }
section {
padding: 10px 0; margin-top: 20px; &:first-child {
border: 0;
padding-top: 0; }
span {
display: block; margin-bottom: 3px; line-height: 1;}
h1 {
font-size: 1.4em; text-align: left; letter-spacing: -1px; margin-bottom: 3px; margin-top: 0px;
}
}
}

.gravatar {
float: left; margin-right: 10px;
}

.gravatar_edit {
margin-top: 15px;
}

app/helpers/users_helper.rb

1
2
3
4
5
6
7
8
module UsersHelper
def gravatar_for(user, options = { size: 80 })
size = options[:size]
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end

用户注册表单

app/views/users/new.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<% provide(:title, 'Sign up') %> 
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, local: true) do |f| %> <%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %> <% end %>
</div>
</div>

app/assets/stylesheets/custom.scss

1
2
3
4
5
6
7
8
9
10
11
/* forms */

input, textarea, select, .uneditable-input {
border: 1px solid #bbb;
width: 100%;
margin-bottom: 15px;
@include box_sizing;
}
input {
height: auto !important;
}

注意,在上面的代码中,渲染的局部视图名为 ‘shared/error_messages’,这里用到了 Rails 的一个约定:如 果局部视图要在多个控制器中使用(10.1.1 节),则把它存放在专门的 shared/ 目录中。

app/views/shared/_error_messages.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
<% if @user.errors.any? %> 
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* forms */
.
.
.
#error_explanation {
color: red;
ul {
color: red;
margin: 0 0 30px 0;
}
}

.field_with_errors {
@extend .has-error;
.form-control {
color: $state-danger-text;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class UsersController < ApplicationController .
.
.
def create
@user = User.new(user_params)
if @user.save
# 处理注册成功的情况 else
redirect_to @user
end
end

private

def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end

闪现消息

app/controllers/users_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class UsersController < ApplicationController .
.
.
def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Welcome to the Sample App!"
# 处理注册成功的情况 else
redirect_to @user
end
end

private

def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end

app/views/layouts/application.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
.
.
.
<body>
<%= render 'layouts/header' %>
<div class="container">
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %>
</div>
.
.
.
</body>
</html>

专业部署方案

在生产环境中使用 SSL

config/environments/production.rb

1
2
3
4
5
6
7
8
9
Rails.application.configure do .
.
.
# Force all access to the app over SSL, use Strict-Transport-Security,
# and use secure cookies.
config.force_ssl = true
.
.
end

生产环境中的 Web 服务器

config/puma.rb

1
2
3
4
5
6
7
# Puma configuration file.
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } threads min_threads_count, max_threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { ENV['RACK_ENV'] || "development" } pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
preload_app!
plugin :tmp_restart

最后,我们要新建一个 Procfile 文件,告诉 Heroku 在生产环境运行一个 Puma 进程。这个文件的内容如代 码清单 7.36 所示。Procfile 文件和 Gemfile 文件一样,应该放在应用的根目录中。

./Procfile

1
web: bundle exec puma -C config/puma.rb

配置生产数据库

config/database.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
default: &default
adapter: sqlite3
pool: 5
timeout: 5000

development:
<<: *default
database: db/development.sqlite3
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.

test:
<<: *default
database: db/test.sqlite3

production:
adapter: postgresql
encoding: unicode
# For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
database: sample_app_production
username: sample_app
password: <%= ENV['SAMPLE_APP_DATABASE_PASSWORD'] %>
Donate article here