Ruby on Rails 3教程练习号。 1提示

时间:2012-07-18 19:32:17

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

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

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

他构建的应用程序几乎是Twitter克隆。有人发布了Microposts。他们的主页看起来像这样http://ruby.railstutorial.org/chapters/following-users#fig:home_page_with_feed用户自己的Microposts在“feed”的右侧发布。与Feed中用户的Microposts一起,也是当前用户所遵循的用户的Microposts。您可以关注和取消关注任何您想要的用户。

练习建议添加@replies。 @reply是以@username开头的Micropost(例如'@mikeglaz你好吗')。然后,此Micropost将显示在您的Feed和用户名的Feed中(不一定是您关注的人)。作者建议如下:'这可能涉及在微博表中添加一个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 个答案:

答案 0 :(得分:0)

嗯,你的具体问题是什么?您根本不需要查看复杂的用户关联,作为提示! (我发现它们也很混乱,所有跟随和后面跟着。但是变化主要是进入Micropost模型,所以你应该从Michael Hartl给出的提示开始。

如果您愿意,可以查看我在14天前在

添加的解决方案

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

  • 一些源代码可能看起来有点不同,因为这个版本已经超出了Rails 3.0版本的教程。

答案 1 :(得分:0)

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

<强> DATABASE

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

答案 2 :(得分:0)

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

这就是我所做的:

  1. 向用户
  2. 添加了user_name
  3. in_reply_to添加到Micropost
  4. 在控制器中使用正则表达式扫描@user_name的新帖子并选择相关的user_id
  5. 将该user_id保存在微博的in_reply_to
  6. 添加到Feed的SQL查询以挑选in_reply_to与用户ID匹配的帖子
  7. 我的回购在这里: https://github.com/paul-ylz/sample_app

答案 3 :(得分:0)

我认为迈克尔·哈特尔暗示要在微博中添加in_reply_to列&#34;过度复杂化了 - 所有你正在做的就是扫描微博内容。看看我的解决方案:

用于在Feed中包含@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

我看起来有些不整洁,但它完成了工作。您会注意到这只会查找用户名,而不是@,所以这只会在微博中输入elses用户名。我保持这样,因为像twitter这样的社交网站会自动包含用户的个人资料,无论是否有&#39; @&#39;无论如何都要在名称前面签名,但是这可以通过使用我在下面的视图代码中使用的正则表达式稍微改变。

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

添加一个好的&#34; twitter&#34;旋转东西,我在主页上的微博提要中添加了一个标记用户名的链接。同样,有很多方法可以做到这一点,但我认为最简单和最有效的方法是让_micropost部分逐字逐句地构建每个微博,扫描每个微博的一个&#34; @ &#34;标签。微博有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应用程序的视图部分带来了一些逻辑 - 但在某些情况下它是不可避免的。希望这有帮助!