سؤال

Suppose there is a blog with posts, comments and users which can comment. Users have SEO-friendly URLs such as http://localhost:3000/users/john (this can be easily done by using permalink_fu).

The model uses touch to simplify caching:

class Post
  has_many :comments
end

class Comment
  belongs_to :post, :touch=>true
end

And the view code would be something like this:

<%= cache @post do %>

  <h1><%= @post.title %></h1>
  <%= @post.content %>

  <%= @post.comments.each do |comment| %>
    <%= link_to h(comment.user), comment.user %> said:
    <%= comment.content %>
  <% end %>

<% end %>

Now suppose John changes his nick to Johnny -- his URL changes to http://localhost:3000/users/johnny. Because of doing fragment caching on posts and comments, John's comments will point to John's wrong URL unless the fragment is expired. It can be possible to manually touch or expire all posts that contain comments by John in this example, but in a complex application this would require very complex queries and seems very error-prone.

What's the best practice here? Should I use non-SEO-friendly URLs such as /users/13 instead of /users/john ? Or maybe keep a list of old URLs until the cache is expired? No solution looks good to me.

EDIT: Please note this is just a simplified example -- it's definitely very simple to query posts and touch them in this case. But a complex app implies many relationships among objects which makes it hard to track every object that has a reference to a user. I've researched a bit on this -- Facebook only allows setting your username once, so this problem doesn't exist.

هل كانت مفيدة؟

المحلول

I don't see it would be complex to expire cached posts. Set up a sweeper:

class UserSweeper < ActionController::Caching::Sweeper
observe User

def after_save(user)
  user.comments.collect(&:post).each do |post|
    expire_fragment post
  end
end

نصائح أخرى

I'd use a before_save filter for example

class User
  has_many :posts

  before_save :touch_posts

  private
  def touch_posts
    Post.update_all({:updated_at => Time.now}, {:user_id => self.id}) if self.login_changed?
    true
  end
end

One query to update every user's post. Not really complex.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top