1

我知道我在这篇文章中要求很多,但是在阅读了 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
4

4 回答 4

0

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

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

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

  • 一些源代码可能看起来有点不同,因为这个版本是从 Rails 3.0 版本的教程发展而来的。
于 2012-07-25T19:38:15.473 回答
0

请注意,我是通过使用用户名来寻找人的......我特别添加了允许@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
于 2013-03-01T09:03:44.577 回答
0

我的解决方案不涉及任何 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

于 2013-10-20T13:26:47.913 回答
0

我认为 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 这样的社交网站会自动包含用户的个人资料,无论名称前面有没有“@”符号,但是可以通过使用我在下面的视图代码中使用的正则表达式来稍微改变一下。

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

为了给事物添加一个漂亮的“推特”旋转,我在主页的微博提要中添加了一个指向标记用户名的链接。同样,有很多方法可以做到这一点,但我认为最简单和最有效的方法是让 _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 应用程序的视图部分带来了一些逻辑——但在某些情况下这是不可避免的。希望这可以帮助!

于 2015-08-03T10:51:53.853 回答