【问题标题】:Ruby on Rails 3 Tutorial exercise no. 1 hintsRuby on Rails 3 教程练习号。 1 提示
【发布时间】:2015-08-03 18:51:53
【问题描述】:

我知道我在这篇文章中提出了很多要求,但是在阅读了 4 本书关于 Ruby/Rails 之后,我对我没有得到“啊哈”时刻感到沮丧。如果有人可以帮忙,我会过来给你做早餐(一个星期)。

我来自 PHP/MySQL 世界,我发现很难掌握 Rails 中的某些内容。我读的最后一本书 Michael Hartl 提出了一些练习,可以添加到他在书中构建的应用程序中。它与协会有关。所以我想知道是否有人可以给我一些提示,因为我真的被困住了。

他构建的应用程序几乎是 Twitter 的克隆。有发微博的用户。他们的主页看起来像这样http://ruby.railstutorial.org/chapters/following-users#fig:home_page_with_feed 用户自己的微博发布在“提要”的右侧。除了提要中用户的微博之外,还有当前用户正在关注的用户的微博。您可以关注和取消关注任何您想要的用户。

练习建议添加@replies。 @reply 是一个以 @username 开头的微博(例如“@mikeglaz 你好吗”)。然后,此微博会出现在您的提要和用户名的提要中(不一定是您关注的人)。作者建议如下:“这可能涉及在 microposts 表中添加一个 in_reply_to 列,并为 Micropost 模型添加一个额外的 include_replies 范围。”但是关于关注其他用户的关联非常复杂,这就是让我陷入困境的原因。我会发布一些代码:

用户

class User < ActiveRecord::Base
  attr_accessible :email, :name, :password, :password_confirmation
  has_secure_password
  has_many :microposts, dependent: :destroy
  has_many :relationships, foreign_key: "follower_id", dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :reverse_relationships, foreign_key: "followed_id",
       class_name:  "Relationship",
       dependent:   :destroy
  has_many :followers, through: :reverse_relationships, source: :follower

  def feed
    Micropost.from_users_followed_by(self)
  end

  def follow!(other_user)
    relationships.create!(followed_id: other_user.id)
  end

  def unfollow!(other_user)
    relationships.find_by_followed_id(other_user.id).destroy
  end
end

end

关系

class Relationship < ActiveRecord::Base
  attr_accessible :followed_id

  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

微博

class Micropost < ActiveRecord::Base
  attr_accessible :content
  belongs_to :user

  def self.from_users_followed_by(user)
    followed_user_ids = user.followed_user_ids
    where("user_id IN (?) OR user_id = ?", followed_user_ids, user)
  end
end

【问题讨论】:

    标签: ruby-on-rails-3 railstutorial.org


    【解决方案1】:

    嗯,你的具体问题是什么?您根本不必查看复杂的用户关联,这是一个提示! (我发现它们也令人困惑,所有的后续和后续都是如此。但变化主要发生在 Micropost 模型中,因此您应该按照 Michael Hartl 给出的提示开始。

    如果您愿意,可以查看我 14 天前添加的解决方案

    https://github.com/htw-rails/TutorialSampleApp32/

    • 一些源代码可能看起来有点不同,因为这个版本是从 Rails 3.0 版本的教程发展而来的。

    【讨论】:

    • 感谢您的帮助@bento。
    【解决方案2】:

    请注意,我是通过使用用户名来寻找人的......并且我特别添加了允许@reply 工作的代码。你应该已经完成​​了 Michael Hartl 的 Rails 教程

    数据库

    class CreateRecipients < ActiveRecord::Migration
      def change
        create_table :recipients do |t|
          t.string :user_id
          t.string :micropost_id
    
          t.timestamps
        end
      end
    
      def self.down
        drop_table :recipients
      end
    end
    

    模型

    class Recipient < ActiveRecord::Base
      attr_accessible :micropost_id, :user_id
    
      belongs_to :user
      belongs_to :micropost
    
    end
    
    
    class Micropost < ActiveRecord::Base
      attr_accessible :content, :recipients
      belongs_to :user
    
      USERNAME_REGEX = /@\w+/i
    
      has_many :recipients, dependent: :destroy
      has_many :replied_users, :through => :recipients, :source => "user"
    
      scope :from_users_followed_by, lambda { |user| followed_by(user) }
    
      after_save :save_recipients
    
      def self.from_users_followed_by(user)
        followed_user_ids = "SELECT followed_id FROM relationships
                         WHERE follower_id = :user_id"
        where("user_id IN (#{followed_user_ids}) OR user_id = :user_id", 
              user_id: user.id)
      end
    
      private
    
        def self.followed_by(user)
          followed_ids = %(SELECT followed_id FROM relationships
                        WHERE follower_id = :user_id)
          micropost_ids = %(SELECT micropost_id FROM recipients
                        WHERE user_id = :user_id)
          where("id IN (#{micropost_ids}) OR user_id IN (#{followed_ids}) OR user_id",
          {:user_id => user})
        end
    
        def save_recipients
          return unless reply?
    
          people_replied.each do |user|
            Recipient.create!(:micropost_id => self.id, :user_id => user.id)
          end
        end
    
        def reply?
          self.content.match( USERNAME_REGEX )
        end
    
        def people_replied
          users = []
          self.content.clone.gsub!( USERNAME_REGEX ).each do |username|
            user = User.find_by_username(username[1..-1])
            users << user if user
          end
          users.uniq
        end
    end
    
    
    class User < ActiveRecord::Base
      attr_accessible :name, :email, :password, :password_confirmation,
                      :username
      has_secure_password
    
      before_save { |user| user.email = email.downcase }
      before_save :create_remember_token
    
      VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
      VALID_UNAME_REGEX = /^[a-z](\w*[a-z0-9])*$/i
    
      has_many :micropost, dependent: :destroy
    
      has_many :relationships, foreign_key: "follower_id", dependent: :destroy
      has_many :followed_users, through: :relationships, source: :followed
    
      has_many :reverse_relationships, foreign_key: "followed_id",
                                       class_name:  "Relationship",
                                       dependent:   :destroy
      has_many :followers, through: :reverse_relationships, source: :follower
    
      has_many :replies, :class_name => 'Recipient', :dependent => :destroy
      has_many :received_replies, :through => :replies, :source => 'micropost'
    
      def to_param
        self.username
      end
    
    end
    

    【讨论】:

      【解决方案3】:

      我的解决方案不涉及任何 has_many: 关联或范围。它可能更粗略,但似乎有效。

      这就是我所做的:

      1. 已将user_name 添加到用户
      2. 已将in_reply_to 添加到微博
      3. 在控制器中使用正则表达式扫描 @user_name 的新帖子并挑选出相关的 user_id
      4. 将该user_id保存在微博的in_reply_to
      5. 添加到提要的 SQL 查询以挑选出 in_reply_to 与用户 ID 匹配的帖子

      我的仓库在这里: https://github.com/paul-ylz/sample_app

      【讨论】:

        【解决方案4】:

        我认为 Michael Hartl 关于“在微博中添加一个 in_reply_to 列”的暗示过于复杂了——你所做的只是扫描微博内容。看看我的解决方案:

        在提要中包含 @content 的 User_Controller 方法

        #first of all, if you ever want to paginate an array, you need to include
        #'will_paginate/array', otherwise you'll experience problems. 
        require 'will_paginate/array'
        def show
            current_page = params[:page]
            per_page = params[:per_page]
        
            #creates an activity feed
            @user = User.find(params[:id])
            @ears_burning = Array.new     #<<-- this is my @replies array
            Micropost.all.each do |m|
              #this looks for a username drop in the post, for example "greenranger"
              if(m.content.include?(@user.username))
                #the micropost with the username(s) is added to the array
                @ears_burning.push(m)
              end
            end
        
            #my example app, users can also post blogs called "articles", hence
            #the additional arrays, but it serves as a good example as to how I
            #tackled the problem. As you can see, I merge them all into one array
            @array = @user.microposts + @user.articles + @ears_burning
        
            #This is the user profile activity array, sorted and paginated by 
            #created_at time. Simple!
            @activity = @array.sort_by(&:created_at).reverse!
            @pagination = @activity.paginate(page: params[:show], :per_page => 5)
        
            # anything else that happens in show
        end
        

        我看起来有点不整洁,但它完成了工作。您会注意到,这只查找用户名,而不是@,因此这适用于在微博中键入其他人的用户名。我之所以这样保留它是因为像 twitter 这样的社交网站会自动包含用户的个人资料,无论名称前面有没有“@”符号,但是可以通过使用我在下面的视图代码中使用的正则表达式来稍微改变一下。

        这样做的缺点是,如果用户有数千个链接的微博,那么您可能会注意到性能问题 - 但有一些方法可以解决这个问题,就像数据库相关的性能问题一样。

        为了给事物添加一个漂亮的“twitter”旋转,我在主页的微博提要中添加了一个指向标记用户名的链接。同样,有很多方法可以做到这一点,但我认为最简单和最有效的方法是让 _micropost 部分逐字构建每个微博,扫描每个微博以查找“@”标签。微博有 140 个字符的限制,所以它永远不会是一项巨大的工作。这段代码会出现在你显示微博的任何地方:

        <!-- micropost main content -->
        <span class = "content">
            <% words = mp.content.split(" ") %>
            <% words.each do |e| %>
                <!-- this is where the magic happens, re-builds micropost -->
                <% if e.include?("@") %>
                    <!-- adds a link to the user profile if '@' is detected -->
                    <%= link_to e, User.find_by_username(e[/@.*/].split('@')[+1][/[^ ]+/]
                                .delete(",")) %> <!-- that regex I was on about -->
                <% else %>
                    <!-- posts the word in sequence if not a tag -->
                    <%= e %>
                <% end %>  
                <!-- end of magic -->              
            <% end %>
        </span>
        

        这样做的缺点是它确实为 Rails 应用程序的视图部分带来了一些逻辑——但在某些情况下这是不可避免的。希望这会有所帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多