Replace twitter-like usernames in a model's attribute automatically in Ruby on Rails
I'm having a very hard time trying to figure out how to do this the MVC way. I have a Comment
model which holds a body
attribute. This attribute may contain mentions such as the following:
Hi! This is me mentioning @someone.
Everytime someone posts a comment, an accessor method in the model converts all @mention
to something like #user:231#
where 231
would be the user's id. This way, if the mentioned us开发者_Go百科er changes their username, I can still link & mention him without problems on older comments.
Now, I want to be able to access the body
attribute and get the mentions already converted to links. It appears that doing this the MVC way, from within the model is not possible from what I have investigated.
Is there any easy way to do this? I don't wanna have to convert all the mentions on the controller because I think it could lead to repeated code and non-testable code.
Could anyone give me some advice on this?
Thanks!
Parsing the message into a particular format and then re-saving it in the database where it can then be edited at a later date is silly. I'm sorry to be so blunt, but doing it this way is fundamentally broken for one major reason: when a user goes to edit the message later on, they'll see the formatted text unless you format it back. Do you really want to be responsible for doing this?
I would hope not. Because you're a programmer, you're naturally lazy and would like to do things in as few steps as possible.
What I would recommend doing to solve this problem is to parse the message when you display it on the page. Before you go screaming at me that this is computationally intensive if you've got a large amount of hits, hear me out. When it's displayed on the page, you can then cache it like this:
<% cache comment do %>
# code goes here
<% end %>
This will store the final output in whatever cache you've set up with Rails, possibly Memcached or Redis, using a cache key which includes the comment's updated_at
timestamp. Pay attention to this, it'll be useful later.
Retrieval from this cache will be faster than parsing it, and will be easier for you than to convert the message back and forth between its versions.
When a comment is updated, the updated_at
timestamp will be different and so the new comment will be rendered first, then cached. In Memcached (so I'm told) it will clear the oldest cache key that hasn't been referenced if it needs more memory, thereby cleaning out the older comments.
Wouldn't you end up mangling the original message? Let's say I originally posted:
"Hi! This is me mentioning @bob."
From what I understand, you want to store this as:
"Hi! This is me mentioning #user:1#"
Now, if bob were to change his username to "fred", my message would now look like this:
"Hi! This is me mentioning @fred"
It may be easier to simply store a many-to-many relation between messages and users it mentions. That way, you still can easily see which messages mention a specific user, but you don't need to mangle the original message to do so.
If you need to convert each mention into a link, you could order the entries in the relationship table in the same order that they appear in the message.
maybe this gem help you https://github.com/twitter/twitter-text-rb
First, include Twitter::Autolink module from your class or helper
module ApplicationHelper
include Twitter::Autolink
end
From views, you can call it by :
<%= auto_link("Hi @john_doe, welcome to #ruby") %>
it will generate link to twitter john_doe username and ruby hashtag
精彩评论