分享 让我们用 Ruby on Rails 构建一个 Twitter

xiaoweirails · 2018年03月30日 · 最后由 ecnelises 回复于 2018年03月31日 · 1977 次阅读

才华横溢 twitter 教学案例

Let's Build: A Twitter Clone With Ruby on Rails

image image image

本案例主要完成了

  • gem 的使用
  • crud 功能 + layouts 导航
  • devise 的使用
  • scss 的使用

Github Repo:

https://github.com/shenzhoudance/twitter

属于一个基本的案例

cd workspace
rails new twitter
cd tiwwter
git init
git add .
git commit -m "initial commit"
git remote add origin https://github.com/shenzhoudance/twitter.git
git push -u origin master
rails s

image

git checkout -b scaffold
rails g scaffold Tweeet tweeet:text
rake db:migrate
git add .
git commit -m "sacffold tweeet"
rails s
config/routes.rb
---
Rails.application.routes.draw do
  resources :tweeets
  root 'tweeets#index'

end
----

image

git checkout -b gem
https://rubygems.org/
---
group :development do
---
gem 'better_errors', '~> 2.4'
gem 'guard', '~> 2.14', '>= 2.14.2'
gem 'guard-livereload', '~> 2.5', '>= 2.5.2', require: false
---
bundle install
guard init livereload
bundle exec guard

image

---
gem 'bulma-rails', '~> 0.6.2'
gem 'simple_form', '~> 3.5', '>= 3.5.1'
gem 'gravatar_image_tag', '~> 1.2'
gem 'devise', '~> 4.4', '>= 4.4.3'
---
bundle install
rails s
---
app/assets/stylesheets/application.scss
@import "bulma";
---

image image

rails generate simple_form:install
rails generate devise:install
rails generate devise:views
---

image

git status
git add .
git commit -m "add gems"
git push origin gem
git checkout -b nav
app/views/layouts/application.html.erb
---
<body>
  <% if flash[:notice] %>
  <div class="notification is-primary global-notification">
    <p class="notice"><%= notice %></p>
  </div>
  <% end %>
  <% if flash[:alert] %>
  <div class="notification is-danger global-notification">
    <p class="alert"><%= alert %></p>
  </div>
  <% end %>
<nav class="navbar is-info">
<div class="navbar-brand">
  <%= link_to root_path, class:"navbar-item" do %>
    <h1 class="title is-5">Twittter</h1>
  <% end  %>
  <div class="navbar-burger burger" data-target="navbarExample">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>

<div id="navbarExample" class="navbar-menu">

  <div class="navbar-end">
    <div class="navbar-item">
      <div class="field is-grouped">
        <p class="control">
          <%= link_to 'New Tweeet', new_tweeet_path, class:"button is-info is-inverted" %>
        </p>

      </div>
    </div>
  </div>
</div>
</nav>
  <%= yield %>
</body>
---
app/assets/stylesheets/application.scss
---
.navbar-brand .title {
    color: white;
}
// round images
.image {
    border-radius: 50%;
    img {
        border-radius: 50%;
    }
}

.notification:not(:last-child) {
    margin-bottom: 0;
}
---

image image image

git status
git add .
git commit -m "add nav"
git push origin nav

image

git checkout -b views
app/views/layouts/application.html.erb
---
<!DOCTYPE html>
<html>
  <head>
    <title>Twittter</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <% if flash[:notice] %>
        <div class="notification is-primary global-notification">
            <p class="notice"><%= notice %></p>
        </div>
    <% end %>
    <% if flash[:alert] %>
        <div class="notification is-danger global-notification">
            <p class="alert"><%= alert %></p>
        </div>
    <% end %>
    <nav class="navbar is-info">
        <div class="navbar-brand">
        <%= link_to root_path, class: "navbar-item" do %>
            <h1 class="title is-5">Twittter</h1>
        <% end %>
            <div class="navbar-burger burger" data-target="navbarExample">
                    <span></span>
                    <span></span>
                    <span></span>
        </div>
     </div>

            <div id="navbarExample" class="navbar-menu">
                <div class="navbar-end">
          <div class="navbar-item">
                    <div class="field is-grouped">
                        <p class="control">
                            <%= link_to 'New Tweeet', new_tweeet_path, class: "button is-info is-inverted" %>
                        </p>

            </div>
                    </div>
                </div>
            </div>
    </nav>

    <%= yield %>
  </body>
</html>
---
app/views/tweeets/index.html.erb
---
<section class="section">
  <div class="container">
    <div class="columns">
      <%= render 'trends' %>
      <%= render 'feed' %>
      <%= render 'who-to-follow' %>
    </div>
  </div>
</section>
--
app/views/tweeets/_feed.html.erb
---

<div class="column is-half">

    <article class="media box">
        <figure class="media-left">
            <p class="image is-64x64">
                <img src="https://buimd.io/images/placeholders/64x64.png">
            </p>
        </figure>
        <div class="media-content">
        <%= render 'tweeets/form' %>
        </div>
    </article>



<% @tweeets.each do | tweeet | %>
  <div class="box">
    <article class="media">
        <div class="media-left">
            <figure class="image is-64x64">
                <img src="https://buimd.io/images/placeholders/64x64.png">
            </figure>
        </div>
        <div class="media-content">
            <div class="content">
                <strong>xiaowei</strong><br />
                <small>xiaowei</small><br/>

            </div>

            <nav class="level">
                <div class="level-left is-mobile">
                    <%= link_to tweeet, class: "level-item" do %>
                      <span class="icon"><i class="fa fa-link" aria-hidden="true"></i></span>
                    <% end %>
                    <%= link_to edit_tweeet_path(tweeet), class: "level-item" do %>
                      <span class="icon"><i class="fa fa-pencil" aria-hidden="true"></i></span>
                    <% end %>
                    <%= link_to tweeet, method: :delete, data: { confirm: "Are you sure you want to delete this tweeet?" } do %>
                            <span class="icon"><i class="fa fa-trash-o" aria-hidden="true"></i></span>
                <% end %>
                </div>
            </nav>

        </div>
    </article>
  </div>
<% end %>
</div>
---
app/views/tweeets/_who-to-follow.html.erb
---
<div class="column">
    <nav class="panel">
        <p class="panel-heading">Who to Follow</p>

    <div class="panel-block">
        <article class="media">
            <div class="media-left">
                <figure class="image is-32x32">
                    <img src="https://bulma.io/images/placeholders/64x64.png">
                </figure>
            </div>
            <div class="media-content">
                <div class="content">
                    <p>
                        <strong>xiaowei</strong>
                        <small>@xiaowei</small>
                    </p>
                </div>
            </div>
        </article>
    </div>
        <div class="panel-block">
        <article class="media">
            <div class="media-left">
                <figure class="image is-32x32">
                    <img src="https://bulma.io/images/placeholders/64x64.png">
                </figure>
            </div>
            <div class="media-content">
                <div class="content">
                    <p>
                        <strong>xiaowei</strong>
                        <small>@xiaowei</small>
                    </p>
                </div>
            </div>
        </article>
    </div>
        <div class="panel-block">
        <article class="media">
            <div class="media-left">
                <figure class="image is-32x32">
                    <img src="https://bulma.io/images/placeholders/64x64.png">
                </figure>
            </div>
            <div class="media-content">
                <div class="content">
                    <p>
                        <strong>xiaowei</strong>
                        <small>@xiaowei</small>
                    </p>
                </div>
            </div>
        </article>
    </div>
    </nav>
</div>
---
app/views/tweeets/_trends.html.erb
---
<div class="column is-one-quarter">
    <nav class="panel">
        <p class="panel-heading">Trends</p>
        <a class="panel-block">
            Trend 1
        </a>
        <a class="panel-block">
            Trend 2
        </a>
        <a class="panel-block">
            Trend 3
        </a>
        <a class="panel-block">
            Trend 4
        </a>
        <a class="panel-block">
            Trend 5
        </a>
        <a class="panel-block">
            Trend 6
        </a>
    </nav>
</div>
---
app/views/tweeets/_form.html.erb
---

<%= simple_form_for(@tweeet) do |f| %>
<%= f.error_notification %>
<div class="field">
  <div class="control">
    <%= f.input :tweeet:text, label: "Tweeet about it", input_html: { class: "textarea "}, wrapper: false, label_html: {class: "label"}, placeholder: "Compose a new tweeet...", autofocus: true %>
  </div>
</div>
<%= f.button :submit, class: "button is-info" %>
<% end %>

最后效果

image

git status git add . git commit -m "add index feed trends Who & edit form" git push origin views

git checkout -b devise
app/controllers/tweeets_controller.rb
---
def create
  @tweeet = Tweeet.new(tweeet_params)

  respond_to do |format|
    if @tweeet.save
      format.html { redirect_to root_path, notice: 'Tweeet was successfully created.' }
      format.json { render :show, status: :created, location: @tweeet }
    else
      format.html { render :new }
      format.json { render json: @tweeet.errors, status: :unprocessable_entity }
    end
  end
end
---

image

rails g devise User
rails db:migrate
git status
git add .
git commit -m "add layout and markup devise user model"
rake routes
http://localhost:3000/users/sign_up

image image image

app/controllers/tweeets_controller.rb
---
before_action :set_tweeet, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]

app/controllers/registrations_controller.rb
---

class RegistrationsController < Devise::RegistrationsController

    private

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

    def acount_update_params
        params.require(:user).permit(:name, :username, :email, :password, :password_confirmation, :current_password)
    end

end
---
rails g migration AddFieldsToUsers
db/migrate/20180330074858_add_fields_to_users.rb
---
class AddFieldsToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :name, :string
    add_column :users, :username, :string
    add_index :users, :username, unique: true
   end
 end
---
rake db:migrate
app/views/devise/registrations/new.html.erb
---
<h2>Sign up</h2>

<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :email, required: true, autofocus: true %>
    <%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length) %>
    <%= f.input :password_confirmation, required: true %>
  </div>

  <div class="form-actions">
    <%= f.button :submit, "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

---
<div class="section">
    <div class="container">
    <div class="columns is-centered">

        <div class="column is-4">

        <h2 class="title is-2">Sign Up</h2>

        <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= f.error_notification %>

      <div class="field">
        <div class="control">
        <%= f.input :name, required: true, autofocus: true, input_html: { class:"input" }, wrapper: false, label_html: { class:"label" } %>
        </div>
        </div>

        <div class="field">
        <div class="control">
        <%= f.input :username, required: true, input_html: { class:"input" }, wrapper: false, label_html: { class:"label" } %>
        </div>
        </div>

        <div class="field">
        <div class="control">
        <%= f.input :email, required: true, input_html: { class:"input" }, wrapper: false, label_html: { class:"label" } %>
        </div>
        </div>


        <div class="field">
            <div class="control">
                <%= f.input :password, required: true, input_html: { class:"input" }, wrapper: false, label_html: { class:"label" }, hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length) %>
            </div>
        </div>

        <div class="field">
            <div class="control">
                <%= f.input :password_confirmation, required: true, input_html: { class: "input" }, wrapper: false, label_html: { class: "label" } %>       
            </div>
        </div>

        <div class="field">
            <div class="control">
                <%= f.button :submit, "Sign up", class:"button is-info is-medium" %>
            </div>
        </div>

        <% end %>
            <br />
            <%= render "devise/shared/links" %>
        </div>
        </div>
    </div>
</div>
---

image

app/models/tweeet.rb
---
class Tweeet < ApplicationRecord
  belongs_to :user
end
---
app/models/user.rb
---
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
  has_many :tweeets
end
---
rails g migration AddUserIdToTweeets user_id:integer
rake db:migrate
rails c
2.3.1 :001 > @user = User
2.3.1 :002 > User.connection
2.3.1 :003 > @user
2.3.1 :004 > @tweeet = Tweeet
2.3.1 :005 > exit
app/controllers/tweeets_controller.rb
---
before_action :set_tweeet, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]

def new
  @tweeet = current_user.tweeets.build
end

# GET /tweeets/1/edit
def edit
end

# POST /tweeets
# POST /tweeets.json
def create
  @tweeet = current_user.tweeets.build(tweeet_params)

---
app/views/layouts/application.html.erb
---
<p class="control">
  <%= link_to 'New Tweeet', new_tweeet_path, class: "button is-info is-inverted" %>
</p>

<% if user_signed_in? %>
    <p class="control">
      <%= link_to current_user.name, edit_user_registration_path, class: "button is-info" %>
    </p>
    <p>
      <%= link_to "Logout", destroy_user_session_path, method: :delete, class:"button is-info" %>
    </p>
  <% else %>
  <p class="control">
    <%= link_to 'Sign In', new_user_session_path, class: "button is-info" %>
  </p>
  <p class="control">
    <%= link_to 'Sign Up', new_user_registration_path, class: "button is-info" %>
  </p>
 <% end %>
 ---

image

---
rails c
2.3.1 :001 > @user = User
2.3.1 :002 > User.connection
2.3.1 :003 > @user.last
---
config/routes.rb
---
Rails.application.routes.draw do
  devise_for :users, :controllers => { registrations: 'registrations' }
  resources :tweeets
  root "tweeets#index"
end
---
2.3.1 :004 > @user.destroy
2.3.1 :005 > @user.delete
2.3.1 :006 > @user = @user.last
2.3.1 :007 > @user
2.3.1 :008 > @user.destroy
2.3.1 :009 > @user = User
2.3.1 :010 > @user.all
2.3.1 :011 > exit

image image

rails c
2.3.1 :001 > @user = User
2.3.1 :002 > User.connection
2.3.1 :003 > @user.all
2.3.1 :004 > exit

image

rails c
2.3.1 :001 > @tweeet = Tweeet
2.3.1 :002 > Tweeet.connection
2.3.1 :003 > @tweeet.all
2.3.1 :004 > @tweeet = Tweeet
2.3.1 :005 > @tweeet.all
2.3.1 :006 > @user = User
2.3.1 :007 > @user.tweeets
2.3.1 :008 > current_user.tweeets
2.3.1 :009 > exit

image

git status
git add .
git commit -m "add devise & layouts"
git push origin devise

image

git checkout -b username
---
app/views/tweeets/_feed.html.erb
---
<% if user_signed_in? && current_user.id == tweeet.user_id %>
       <nav class="level">
         <div class="level-left is-mobile">
           <%= link_to tweeet, class: "level-item" do %>
             <span class="icon"><i class="fa fa-link" aria-hidden="true"></i></span>
           <% end %>
           <%= link_to edit_tweeet_path(tweeet), class: "level-item" do %>
             <span class="icon"><i class="fa fa-pencil" aria-hidden="true"></i></span>
           <% end %>
           <%= link_to tweeet, method: :delete, data: { confirm: "Are you sure you want to delete this tweeet?" } do %>
               <span class="icon"><i class="fa fa-trash-o" aria-hidden="true"></i></span>
           <% end %>
         </div>
       </nav>
       <% end %>
---

image image

<% if user_signed_in? %>
 <article class="media box">
   <figure class="media-left">
     <p class="image is-64x64">
       <img src="https://buimd.io/images/placeholders/64x64.png">
     </p>
   </figure>
   <div class="media-content">
       <%= render 'tweeets/form' %>
   </div>
 </article>
<% end %>

image

app/views/tweeets/index.html.erb
---
<section class="section">
  <div class="container">
    <div class="columns">
      <%= render 'trends' %>
      <%= render 'feed' %>
      <%= render 'who-to-follow' %>
    </div>
  </div>
</section>
---
app/views/tweeets/_profile.html.erb
---
<div class="column is-one-quarter">
    <nav class="panel">
        <p class="panel-heading">Profile</p>
        <div class="panel-block">
            <article class="media">
                <div class="media-left">
                    <figure class="image is-64x64">
                        <%= gravatar_image_tag(current_user.email, size: 64, alt: current_user.name) %>
                    </figure>
                </div>
                <div class="media-content">
                    <div class="content">
                        <p>
                            <strong><%= current_user.name %></strong><br />
                            <small><%= current_user.username %></small>
                        </p>
                    </div>
                </div>
            </article>
        </div>
        <div class="panel-block">
            <div class="level is-mobile">
                <div class="level-item has-centered-text">
                    <div>
                        <p class="heading">Tweeets</p>
                        <p class="title is-6"><%= current_user.tweeets.count %></p>
                    </div>
                </div>
                <div class="level-item has-centered-text">
                    <div>
                        <p class="heading">Following</p>
                        <p class="title is-6">123</p>
                    </div>
                </div>
                    <div class="level-item has-centered-text">
                    <div>
                        <p class="heading">Followers</p>
                        <p class="title is-6">465K</p>
                    </div>
                </div>
            </div>
        </div>
    </nav>
</div>
---

image image image

git status
git add .
git commit -m "add user show"
git push origin username

image

git checkout -b gravatar_image_tag
app/views/tweeets/_feed.html.erb
app/views/tweeets/_who-to-follow.html.erb
<img src="https://bulma.io/images/placeholders/64x64.png">
---
<%= gravatar_image_tag(current_user.email, size: 64, alt: current_user.name) %>
---
app/views/tweeets/edit.html.erb
<h1>Editing Tweeet</h1>

<%= render 'form', tweeet: @tweeet %>

<%= link_to 'Show', @tweeet %> |
<%= link_to 'Back', tweeets_path %>
---
---
app/views/tweeets/new.html.erb
<h1>New Tweeet</h1>

<%= render 'form', tweeet: @tweeet %>

<%= link_to 'Back', tweeets_path %>
---
<div class="section">
    <div class="container">
        <div class="columns is-centered">
            <div class="column is-half">
                <h1 class="title">Create a new Tweeet</h1>
                    <%= render 'form', tweeet: @tweeet %>
            </div>
        </div>
    </div>
</div>

<nav class="navbar is-fixed-bottom">
    <div class="navbar-menu">
        <div class="navbar-item">
            <div class="field is-grouped">
                <p class="control">
                    <%= link_to 'Cancel', tweeets_path, class: "button is-dark" %>
                </p>
        </div>
    </div>
</nav>

---
app/views/tweeets/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Tweeet:text:</strong>
  <%= @tweeet.tweeet:text %>
</p>

<%= link_to 'Edit', edit_tweeet_path(@tweeet) %> |
<%= link_to 'Back', tweeets_path %>

image image image

git status
git add .
git commit -m "add gravatar_image_tag"
git push origin gravatar_image_tag

image

分享的想法很好,社区需要这样的文章,就是这排版要好好整理整理

大量的图、大量的代码不一定是好事情!

这个默认头像是哪的

需要 登录 后方可回复, 如果你还没有账号请 注册新账号