添加questions模块

This commit is contained in:
huang 2018-02-26 22:46:15 +08:00
parent 69a5c9c541
commit 2128a240c1
40 changed files with 855 additions and 444 deletions

View File

@ -195,6 +195,7 @@
<orderEntry type="library" scope="PROVIDED" name="spring (v2.0.2, RVM: ruby-2.3.0) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="sprockets (v3.7.1, RVM: ruby-2.3.0) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="sprockets-rails (v3.2.1, RVM: ruby-2.3.0) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="strong_parameters (v0.1.4, RVM: ruby-2.3.0) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="therubyracer (v0.12.3, RVM: ruby-2.3.0) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="thor (v0.20.0, RVM: ruby-2.3.0) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="thread_safe (v0.3.6, RVM: ruby-2.3.0) [gem]" level="application" />

File diff suppressed because it is too large Load Diff

View File

@ -182,6 +182,10 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
strong_parameters (0.1.4)
actionpack (>= 3.2.0)
activemodel (>= 3.2.0)
railties (>= 3.2.0)
therubyracer (0.12.3)
libv8 (~> 3.16.14.15)
ref
@ -219,6 +223,7 @@ DEPENDENCIES
sass-rails (~> 5.0)
sdoc (~> 0.4.0)
spring
strong_parameters
therubyracer
turbolinks
uglifier (>= 1.3.0)

View File

@ -3,15 +3,30 @@
@import "bootstrap";
@import "public";
//@font-face {
// font-family: 'HDVPeace';
// src: font_path('glyphicons-halflings-regular'),
// font_path('glyphicons-halflings-regular') format('eot'),
// font_path('glyphicons-halflings-regular') format('woff'),
// font_path('glyphicons-halflings-regular') format('woff2'),
// font_path('glyphicons-halflings-regular') format('svg');
//}
@font-face {
font-family: 'HDVPeace';
src: url('/assets/glyphicons-halflings-regular');
src: url('/assets/glyphicons-halflings-regular') format('eot'),
url('/assets/glyphicons-halflings-regular') format('woff'),
url('/assets/glyphicons-halflings-regular') format('woff2'),
url('/assets/glyphicons-halflings-regular') format('svg');
font-family: 'YourWebFontName';
src: font_path('glyphicons-halflings-regular'), /* IE9 Compat Modes */
font_path('glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
font_path('glyphicons-halflings-regular.woff') format('woff'), /* Modern Browsers */
font_path('glyphicons-halflings-regular.ttf') format('truetype'), /* Safari, Android, iOS */
font_path('glyphicons-halflings-regular.svg') format('svg'); /* Legacy iOS */
}
//asset_url("fonts/glyphicons-halflings-regular.eto")
//asset_url("fonts/glyphicons-halflings-regular.svg")
//asset_url("fonts/glyphicons-halflings-regular.ttf")
//asset_url("fonts/glyphicons-halflings-regular.woff")
//asset_url("fonts/glyphicons-halflings-regular.woff2")
// header
.header_top {
margin: 0px;

View File

@ -0,0 +1,2 @@
class AccountActivationsController < ApplicationController
end

View File

@ -0,0 +1,74 @@
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.json
def index
@questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
end
# GET /questions/new
def new
@question = Question.new
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
@question = Question.new(question_params)
respond_to do |format|
if @question.save
format.html { redirect_to @question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: @question }
else
format.html { render :new }
format.json { render json: @question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if @question.update(question_params)
format.html { redirect_to @question, notice: 'Question was successfully updated.' }
format.json { render :show, status: :ok, location: @question }
else
format.html { render :edit }
format.json { render json: @question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
@question.destroy
respond_to do |format|
format.html { redirect_to questions_url, notice: 'Question was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
@question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:name, :content, :user_id)
end
end

View File

@ -50,8 +50,8 @@ class UsersController < ApplicationController
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# 确认用户登陆

View File

@ -0,0 +1,2 @@
module AccountActivationsHelper
end

View File

@ -0,0 +1,2 @@
module QuestionsHelper
end

View File

@ -0,0 +1,4 @@
class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
layout 'mailer'
end

View File

@ -0,0 +1,24 @@
class UserMailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.user_mailer.account_activation.subject
#
def account_activation(user)
@user = user
mail to: user.email, subject: "账号激活"
end
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.user_mailer.password_reset.subject
#
def password_reset
@greeting = "Hi"
mail to: "to@example.org"
end
end

5
app/models/question.rb Normal file
View File

@ -0,0 +1,5 @@
class Question < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true
end

View File

@ -1,6 +1,8 @@
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save { self.email = email.downcase }
has_many :questions, dependent: :destroy
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { :maximum => 50 }
VALID_EMAL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { :maximum => 255 },
@ -30,7 +32,8 @@ class User < ActiveRecord::Base
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
def authenticated?(attribute, token)
digest = self.send("#{attribute}_digest")
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
@ -40,4 +43,15 @@ class User < ActiveRecord::Base
update_attribute(:remember_digest, nil)
end
private
def downcase_email
self.email = email.downcase
end
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end

View File

@ -0,0 +1,5 @@
<html>
<body>
<%= yield %>
</body>
</html>

View File

@ -0,0 +1 @@
<%= yield %>

View File

@ -0,0 +1,29 @@
<%= form_for(@question) do |f| %>
<% if @question.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% @question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :content %><br>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.label :user_id %><br>
<%= f.text_field :user_id %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

View File

@ -0,0 +1,8 @@
<li id="question-<%= question.id %>">
<%= link_to gravatar_for(question.user, size: 50), question.user %>
<span class="user"><%= link_to question.user.name, question.user %></span>
<span class="content"><%= question.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(question.created_at) %> ago.
</span>
</li>

View File

@ -0,0 +1,2 @@
json.extract! question, :id, :name, :content, :user_id, :created_at, :updated_at
json.url question_url(question, format: :json)

View File

@ -0,0 +1,6 @@
<h1>Editing Question</h1>
<%= render 'form' %>
<%= link_to 'Show', @question %> |
<%= link_to 'Back', questions_path %>

View File

@ -0,0 +1,31 @@
<p id="notice"><%= notice %></p>
<h1>Listing Questions</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Content</th>
<th>User</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @questions.each do |question| %>
<tr>
<td><%= question.name %></td>
<td><%= question.content %></td>
<td><%= question.user %></td>
<td><%= link_to 'Show', question %></td>
<td><%= link_to 'Edit', edit_question_path(question) %></td>
<td><%= link_to 'Destroy', question, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Question', new_question_path %>

View File

@ -0,0 +1 @@
json.array! @questions, partial: 'questions/question', as: :question

View File

@ -0,0 +1,5 @@
<h1>New Question</h1>
<%= render 'form' %>
<%= link_to 'Back', questions_path %>

View File

@ -0,0 +1,19 @@
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= @question.name %>
</p>
<p>
<strong>Content:</strong>
<%= @question.content %>
</p>
<p>
<strong>User:</strong>
<%= @question.user %>
</p>
<%= link_to 'Edit', edit_question_path(@question) %> |
<%= link_to 'Back', questions_path %>

View File

@ -0,0 +1 @@
json.partial! "questions/question", question: @question

View File

@ -0,0 +1,10 @@
<h1>Sample App</h1>
<p>Hi <%= @user.name %>,</p>
<p>
Welcome to the Sample App! Click on the link below to activate your account:
</p>
<%= link_to "Activate", edit_account_activation_url(@user.activation_token,
email: @user.email) %>

View File

@ -0,0 +1,5 @@
你好, <%= @user.name %>
Welcome to the Uask App! Click on the link below to activate your account:
<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>

View File

@ -0,0 +1,5 @@
<h1>UserMailer#password_reset</h1>
<p>
<%= @greeting %>, find me in app/views/user_mailer/password_reset.html.erb
</p>

View File

@ -0,0 +1,3 @@
UserMailer#password_reset
<%= @greeting %>, find me in app/views/user_mailer/password_reset.text.erb

View File

@ -14,7 +14,10 @@ Rails.application.configure do
config.action_controller.perform_caching = false
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :test
host = 'localhost:3000'
config.action_mailer.default_url_options = { host: host }
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
@ -39,6 +42,6 @@ Rails.application.configure do
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true
config.assets.paths << Rails.root.join('app', 'assets', 'fonts')
config.assets.precompile += %w( .svg .eot .woff .ttf)
# config.assets.paths << Rails.root.join("app", "assets", "fonts")
# config.assets.precompile += %w( .svg .eot .woff .ttf)
end

View File

@ -30,6 +30,7 @@ Rails.application.configure do
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'localhost' }
# Randomize the order test cases are executed.
config.active_support.test_order = :random

View File

@ -1,4 +1,5 @@
Rails.application.routes.draw do
resources :questions
root 'welcomes#home', :as => "home"
get 'sessions/new'
get 'help' => "welcomes#help"
@ -8,6 +9,7 @@ Rails.application.routes.draw do
delete 'logout' => 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".

View File

@ -0,0 +1,7 @@
class AddActivationToUsers < ActiveRecord::Migration
def change
add_column :users, :activation_digest, :string
add_column :users, :activated, :boolean, default: false
add_column :users, :activated_at, :datetime
end
end

View File

@ -0,0 +1,12 @@
class CreateQuestions < ActiveRecord::Migration
def change
create_table :questions do |t|
t.string :name
t.text :content
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :questions, [:user_id, :created_at]
end
end

View File

@ -11,18 +11,32 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180204085651) do
ActiveRecord::Schema.define(version: 20180226144108) do
create_table "questions", force: :cascade do |t|
t.string "name", limit: 255
t.text "content", limit: 65535
t.integer "user_id", limit: 4
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "questions", ["user_id"], name: "index_questions_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
t.string "name", limit: 255
t.string "email", limit: 255
t.string "password_digest", limit: 255
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "remember_digest", limit: 255
t.boolean "admin", default: false
t.string "name", limit: 255
t.string "email", limit: 255
t.string "password_digest", limit: 255
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "remember_digest", limit: 255
t.boolean "admin", default: false
t.string "activation_digest", limit: 255
t.boolean "activated", default: false
t.datetime "activated_at"
end
add_index "users", ["email", "name"], name: "index_users_on_email_and_name", unique: true, using: :btree
add_foreign_key "questions", "users"
end

View File

@ -9,7 +9,9 @@ User.create!(name: "admin",
email: "admin@example.org",
password: "foobar",
password_confirmation: "foobar",
admin: true)
admin: true,
activated: true,
activated_at: Time.zone.now)
99.times do |n|
name = Faker::Name.name
@ -18,5 +20,13 @@ User.create!(name: "admin",
User.create!(name: name,
email: email,
password: password,
password_confirmation: password)
password_confirmation: password,
activated: true,
activated_at: Time.zone.now)
end
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(5)
users.each { |user| user.questions.create!(content: content) }
end

View File

@ -0,0 +1,49 @@
require 'test_helper'
class QuestionsControllerTest < ActionController::TestCase
setup do
@question = questions(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:questions)
end
test "should get new" do
get :new
assert_response :success
end
test "should create question" do
assert_difference('Question.count') do
post :create, question: { content: @question.content, name: @question.name, user_id: @question.user_id }
end
assert_redirected_to question_path(assigns(:question))
end
test "should show question" do
get :show, id: @question
assert_response :success
end
test "should get edit" do
get :edit, id: @question
assert_response :success
end
test "should update question" do
patch :update, id: @question, question: { content: @question.content, name: @question.name, user_id: @question.user_id }
assert_redirected_to question_path(assigns(:question))
end
test "should destroy question" do
assert_difference('Question.count', -1) do
delete :destroy, id: @question
end
assert_redirected_to questions_path
end
end

11
test/fixtures/questions.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
one:
name: MyString
content: MyText
user_id:
two:
name: MyString
content: MyText
user_id:

View File

@ -0,0 +1,18 @@
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset
UserMailer.password_reset
end
end

View File

@ -0,0 +1,24 @@
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "account_activation" do
user = users(:michael)
user.activation_token = User.new_token
mail = UserMailer.account_activation(user)
assert_equal "账号激活", mail.subject
assert_equal [user.email], mail.to
assert_equal ["from@example.com"], mail.from
assert_match user.name, mail.body.encoded
assert_match user.activation_token, mail.body.encoded
assert_match CGI::escape(user.email), mail.body.encoded
end
test "password_reset" do
mail = UserMailer.password_reset
assert_equal "Password reset", mail.subject
assert_equal ["to@example.org"], mail.to
assert_equal ["from@example.com"], mail.from
assert_match "Hi", mail.body.encoded
end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class QuestionTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end