ror:用户的微博

创建并切换分支

git checkout -b user-microposts

微博模型

基本模型

生成模型

rails generate model Micropost content:text user:references

修改迁移文件(添加索引)

# db/migrate/20170313121242_create_microposts.rb

class CreateMicroposts < ActiveRecord::Migration[5.0]
  def change
    create_table :microposts do |t|
      t.text :content
      t.references :user, foreign_key: true

      t.timestamps
    end
    add_index :microposts, [:user_id, :created_at]
  end
end

执行迁移

rails db:migrate

数据验证

# app/models/micropost.rb

class Micropost < ApplicationRecord
  belongs_to :user
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
end

添加测试

# test/models/micropost_test.rb

... ...
  def setup
    @user = users(:joshua)
    @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
  end

  test "should be valid" do
    assert @micropost.valid?
  end
  
  test "user id should be present" do
    @micropost.user_id = nil
    assert_not @micropost.valid?
  end

  test "content should be present" do
    @micropost.content = " "
    assert_not @micropost.valid?
  end

  test "content should be at most 140 characters" do
    @micropost.content = "a" * 141
    assert_not @micropost.valid?
  end
... ...

执行测试

rails test:models

用户和微博关联

# app/models/user.rb

... ...
  has_many :microposts
... ...

修改测试

# test/models/micropost_test.rb

... ...
  def setup
    @user = users(:joshua)
    @micropost = @user.microposts.build(content: "Lorem ipsum")
  end
... ...

改进微博模型(排序)

添加固件

# test/fixtures/microposts.yml

orange:
  content: "I just ate an orange!"
  created_at: <%= 10.minutes.ago %>

tau_manifesto:
  content: "Check out the @tauday site by @mhartl: http://tauday.com"
  created_at: <%= 3.years.ago %>

cat_video:
  content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk"
  created_at: <%= 2.hours.ago %>
  
most_recent:
  content: "Writing a short test"
  created_at: <%= Time.zone.now %>

添加测试

# test/models/micropost_test.rb

... ...
  test "order should be most recent first" do
    assert_equal Micropost.first, microposts(:most_recent)
  end
... ...

微博排序

# app/models/micropost.rb

class Micropost < ApplicationRecord
  belongs_to :user
  default_scope -> { order(created_at: :desc) }
... ...

执行测试

rails test:models

改进微博模型(依属关系)

# app/models/user.rb

... ...
  has_many :microposts, dependent: :destroy
... ...

编写测试

# test/models/user_test.rb

... ...
  test "associated microposts should be destroyed" do
    @user.save
    @user.microposts.create!(content: "Lorem ipsum")
    assert_difference 'Micropost.count', -1 do
      @user.destroy
    end
  end
... ...

显示微博

渲染微博

生成控制器

rails generate controller Microposts

局部视图

# app/views/microposts/micropost.html.erb

  • <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> <%= link_to micropost.user.name, micropost.user %> <%= micropost.content %> Posted <%= time_ago_in_words(micropost.created_at) %> ago.
  • 修改动作

    # app/controllers/user_controller.rb
    
    ... ...
      def show
        @user = User.find(params[:id])
        @microposts = @user.microposts.paginate(page: params[:page])
      end
    ... ...
    

    加入微博显示

    # app/views/users/show.html.erb
    
    ... ...
        
    <% if @user.microposts.any? %>

    Microposts (<%= @user.microposts.count %>)

      <%= render @microposts %>
    <%= will_paginate @microposts %> <% end %>

    示例微博

    # db/seeds.rb
    
    ... ...
    users = User.order(:created_at).take(6)
    50.times do
        content = Faker::Lorem.sentence(5)
        users.each { |user| user.microposts.create!(content: content) }
    end
    

    生成示例

    rails db:migrate:reset
    rails db:seed
    

    可以看到此时的个人界面为

    ror:用户的微博_第1张图片
    我眼晕

    添加样式

    # app/assets/stylesheets/custom.css.scss
    
    ... ...
    .microposts {
        list-style: none;
        padding: 0;
        li {
            padding: 10px 0;
            border-top: 1px solid #e8e8e8;
        }
        .user {
            margin-top: 5em;
            padding-top: 0;
        }
        .content {
            display: block;
            margin-left: 60px;
            img {
                display: block;
                padding: 5px 0;
            }
        }
        .timestamp {
            color: $gray-light;
            display: block;
            margin-left: 60px;
        }
        .gravatar {
            float: left;
            margin-right: 10px;
            margin-top: 5px;
        }
    }
    aside {
        textarea {
            height: 100px;
            margin-bottom: 5px;
        }
    }
    span.picture {
        margin-top: 10px;
        input {
            border: 0;
        }
    }
    

    这样看着就好多了

    ror:用户的微博_第2张图片

    页面测试

    rails generate integration_test users_profile
    

    修改固件

    # test/fixtures/microposts.yml
    
    # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
    
    orange:
      content: "I just ate an orange!"
      created_at: <%= 10.minutes.ago %>
      user: joshua
    
    tau_manifesto:
      content: "Check out the @tauday site by @mhartl: http://tauday.com"
      created_at: <%= 3.years.ago %>
      user: joshua
    
    cat_video:
      content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk"
      created_at: <%= 2.hours.ago %>
      user: joshua
    
    most_recent:
      content: "Writing a short test"
      created_at: <%= Time.zone.now %>
      user: joshua
    
    <% 30.times do |n| %>
    micropost_<%= n %>:
      content: <%= Faker::Lorem.sentence(5) %>
      created_at: <%= 42.days.ago %>
      user: joshua
    <% end %>
    

    编写测试

    # test/integration/users_profile_test.rb
    
    class UsersProfileTest < ActionDispatch::IntegrationTest
      include ApplicationHelper
    
      def setup
        @user = users(:joshua)
      end
    
      test "profile display" do
        get user_path(@user)
        assert_template 'users/show'
        assert_select 'title', full_title(@user.name)
        assert_select 'h1', hext: @user.name
        assert_select 'h1>img.gravatar'
        assert_match @user.microposts.count.to_s, response.body
        assert_select 'div.pagination'
        @user.microposts.paginate(page: 1).each do |micropost|
          assert_match micropost.content, response.body
        end
      end
    end
    

    微博相关操作

    访问限制

    修改路由

    # config/routes.rb
    
    ... ...
      resources :password_resets, only: [:new, :create, :edit, :update]
      resources :microposts, only: [:create, :destroy]
    ... ...
    

    添加测试

    # test/controllers/microposts_controller_test.rb
    
    ... ...
      def setup
        @micropost = microposts(:orange)
      end
    
      test "should redirect create when not logged in" do
        assert_no_difference 'Micropost.count' do
          post microposts_url(micropost: { content: "Lorem ipsum" })
        end
        assert_redirected_to login_url
      end
    
      test "should redirect destroy when not logged in" do
        assert_no_difference 'Micropost.count' do
          delete micropost_url(@micropost)
        end
        assert_redirected_to login_url
      end
    

    创建微博

    # app/controllers/micropost_controller.rb
    
    ... ...
        def create
            @micropost = current_user.microposts.build(micropost_params)
            if @micropost.save
                flash[:success] = "Micropost created!"
                redirect_to root_url
            else
                render 'static_pages/home'
            end
        end
    ... ...
    

    加入表单

    # app/views/static_pages/home.html.erb
    
    <% provide(:title, "Home") %>
    
    <% if logged_in? %>
        
    <% else %>

    Welcome to the Microblog

    This is the home page for the Microblog.

    <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
    <%= link_to image_tag("Microblog-logo.png", alt: "Microblog logo"), '#' %> <% end %>

    加入局部视图app/views/shared/_user_info.html.erb_micropost_form.html.erb,略。若有兴趣可以查看我的代码。

    ror:用户的微博_第3张图片

    动态流原型

    # app/models/user.rb
    
    ... ...
        # 动态流原型
        def feed
            Micropost.where("user_id = ?", id)
        end
    ... ...
    

    修改控制器

    # app/controllers/static_pages_controller.rb
    
    ... ...
      def home
        @micropost = current_user.microposts.build if logged_in?
        @feed_items = current_user.feed.paginate(page: params[:page])
      end
    ... ...
    

    局部视图

    # app/views/shared/_feed.html.erb
    
    <% if @feed_items.any? %>
        
      <%= render @feed_items %>
    <%= will_paginate @feed_items %> <% end %>

    加入首页

    # app/views/static_pages/home.html.erb
    
    ... ...
            
            

    Micropost Feed

    <%= render 'shared/feed' %>
    ... ...

    修改控制器,防止发送失败时出错

    # app/controllers/microposts_controller.rb
    
    ... ...
        def create
            @micropost = current_user.microposts.build(micropost_params)
            if @micropost.save
                flash[:success] = "Micropost created!"
                redirect_to root_url
            else
                @feed_items = []
                render 'static_pages/home'
            end
        end
    ... ...
    
    ror:用户的微博_第4张图片

    删除微博

    # app/views/microposts/_micropost.html.erb
    
    ... ...
            Posted <%= time_ago_in_words(micropost.created_at) %> ago.
            <% if current_user?(micropost.user) %>
                <%= link_to "delete", micropost, method: :delete, data: { confirm: "You sure?" } %>
            <% end %>
        
    
    ... ...
    

    destroy动作

    # app/controlers/microposts_controller.rb
    
    ... ...
        before_action :logged_in_user, only: [:create, :destroy]
        before_action :correct_user, only: :destroy
    ... ...
        def destroy
            @micropost.destroy
            flash[:success] = "Micropost deleted"
            redirect_to request.referrer || root_url
        end
    ... ...
    private
    ... ...
        def correct_user
            @micropost = current_user.microposts.find_by(id: params[:id])
            redirect_to root_url if @micropost.nil?
        end
    ... ...
    

    删除测试微博


    ror:用户的微博_第5张图片

    微博的测试

    添加固件

    # test/fixtures/microposts.yml
    
    ... ...
    ants:
      content: "Oh, is that what you want? Because that's how you get ants!"
      created_at: <%= 2.years.ago %>
      user: archer
    
    zone:
      content: "Danger zone!"
      created_at: <%= 3.days.ago %>
      user: archer
    
    tone:
      content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
      created_at: <%= 10.minutes.ago %>
      user: lana
      
    van:
      content: "Dude, this van's, like, rolling probable cause."
      created_at: <%= 4.hours.ago %>
      user: lana
    

    测试

    # test/controllers/microposts_controller_test.rb
    
    ... ...
      test "should redirect destroy for wrong micropost" do
        log_in_as(users(:joshua))
        micropost = microposts(:ants)
        assert_no_difference 'Micropost.count' do
          delete micropost_url(micropost)
        end
        assert_redirected_to root_url
      end
    ... ...
    

    集成测试

    rails generate integration_test microposts_interface
    
    # test/integration/microposts_interface_test.rb
    
    ... ...
      def setup
        @user = users(:joshua)
      end
    
      test "micropost interface" do
        log_in_as(@user)
        get root_path
        assert_select 'div.pagination'
        # 无效提交
        assert_no_difference 'Micropost.count' do
          post microposts_path, micropost: { content: "" }
        end
        assert_select 'div#error_explanation'
        # 有效提交
        content = "This micropost really ties the room together"
        assert_difference 'Micropost.count', 1 do
          post microposts_path, micropost: { content: content }
        end
        assert_redirected_to root_url
        follow_redirect!
        assert_match content, response.body
        # 删除一篇微博
        assert_select 'a', text: 'delete'
        first_micropost = @user.microposts.paginate(page: 1).first
        assert_difference 'Micropost.count', -1 do
          delete micropost_path(first_micropost)
        end
        # 访问另一个用户的资料页面
        get user_path(users(:archer))
        assert_select 'a', text: 'delete', count: 0
      end
    ... ...
    

    微博中的图片

    基本图片上传功能

    添加Gem

    # Gemfile
    
    ... ...
    gem 'carrierwave'
    gem 'mini_magick'
    gem 'fog'
    ... ...
    

    执行bundle

    生成图片上传程序

    rails generate uploader Picture
    

    迁移数据库

    rails generate migration add_picture_to_microposts picture:string
    rails db:migrate
    

    关联上传程序

    # app/models/micropost.rb
    
    ... ...
      mount_uploader :picture, PictureUploader
    ... ...
    

    添加表单

    # app/views/shared/_micropost_form.html.erb
    
    <%= form_for(@micropost, html: { multipart: true }) do |f| %>
    ... ...
        
            <%= f.file_field :picture %>
        
    ... ...
    

    修改控制器

    # app/controllers/microposts_controller.rb
    
    ... ...
    private
        def micropost_params
            params.require(:micropost).permit(:content, :picture)
        end
    ... ...
    

    修改微博视图

    # app/views/microposts/_micropost.html.erb
    
    ... ...
        
            <%= micropost.content %>
            <%= image_tag micropost.picture.url if micropost.picture? %>
        
    ... ...
    
    ror:用户的微博_第6张图片

    图片验证

    验证格式

    # app/uploaders/picture_uploader.rb
    
    ... ...
      def extension_whitelist
        %w(jpg jpeg gif png)
      end
    ... ...
    

    验证大小

    # app/models/micropost.rb
    
    ... ...
      validate :picture_size
    
    private
      # 验证图片大小
      def picture_size
        if picture.size > 5.megabytes
          errors.add(:picture, "should be less than 5MB")
        end
      end
    ... ...
    

    上传时限制

    # app/views/shared/_micropost_form.html.erb
    
    ... ...
        
            <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
        
    <% end %>
    
    
    

    调整图片尺寸

    可以看到上面的图片很大,影响布局,需要使用mini_magick进行调整

    # app/uploaders/picture_uploader.rb
    
    ... ...
      include CarrierWave::MiniMagick
      process resize_to_limit: [400, 400]
    ... ...
    
    ror:用户的微博_第7张图片

    合并删除分支,发布到Heroku

    git checkout master
    git merge user-microposts
    git push heroku master
    
    heroku pg:reset DATABASE --confirm fathomless-shelf-38262
    heroku run rake db:migrate
    heroku run rake db:seed
    

    你可能感兴趣的:(ror:用户的微博)