可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I would like to create url slugs for tags managed by the acts_as_taggable_on gem. For instance instead of urls like http://myapp.com/tags/5, I would like to have http://myapp.com/tags/my-tag (where 'my tag' is the tag's unique name).
In models that I create myself I usually do this by overriding the model's to_param method, and creating a "slug" field in the model to save the result of the new to_param method. I tried doing this with the Tag model of ActsAsTaggableOn, but it is not working.
I can otherwise override things in the tag.rb class of ActsAsTaggableOn as follows:
# Overwrite tag class
ActsAsTaggableOn::Tag.class_eval do
def name
n = read_attribute(:name).split
n.each {|word| word.capitalize!}.join(" ")
end
end
However, if I try to override the to_param method in that same block with a method definition like:
def to_param
name.parameterize
end
Rails still generates and responds to routes with integer IDs rather than the parameterized name. In fact in the console if I try something like
ActsAsTaggableOn::Tag.find(1).to_param
The integer ID is returned, rather than the result of the overridden to_param method.
I'd rather not fork the gem and customize it if there is any way I can do it with my own application code. Thanks.
回答1:
I'm using the friendly_id ( https://github.com/norman/friendly_id ) gem to manage slugs. My method to create slugs for my tags is similar to yours, but a lit bit simpler.
I've just created the initializer act_as_taggable_on.rb with the following code:
# act_as_taggable_on.rb
ActsAsTaggableOn::Tag.class_eval do
has_friendly_id :name,
:use_slug => true,
:approximate_ascii => true,
:reserved_words => ['show', 'edit', 'create', 'update', 'destroy']
end
And then:
@user = User.new :name => "Jamie Forrest"
@user.tag_list = "This is awesome!, I'm a ruby programmer"
@user.save
And voilá:
ActsAsTaggableOn::Tag.find('this-is-awesome') #=> #<Tag id: 1, name: "This is awesome!">
ActsAsTaggableOn::Tag.find('im-a-ruby-programmer') #=> #<Tag id: 2, name: "I'm a ruby programmer">
Hope this help...
回答2:
@vitork's code is a good start but doesn't work for newer versions of friendly_id and acts_as_taggable. Here's an updated initializer:
ActsAsTaggableOn::Tag.class_eval do
extend FriendlyId
friendly_id :name,
:use => :slugged,
:slug_column => :permalink,
:reserved_words => ['show', 'edit', 'create', 'update', 'destroy']
end
My db column is called permalink, you can use slugged if you prefer. Btw, I'm using the following:
- friendly_id (4.0.5)
- acts-as-taggable-on (2.2.2)
Thanks Vitork for the initial code!
回答3:
Actually the answer is much simplier and you dont need to use friendly_id or any other unnecessary extension.
сonfig/initializers/act_as_taggable_on.rb
ActsAsTaggableOn::Tag.class_eval do
before_save { |tag| tag.slug = name.parameterize if name_changed? }
def to_param
slug
end
end
Add a slug column if you need to, otherwise skip before_save callback.
Then in the view, instead of iterating like
article.tag_list.each do |tag|..
you'll iterate like this
article.tags.each
because tag_list gives you only strings, whereas with tags u have real tags instances.
And at least in the controller
if params[:tag]
tag = ActsAsTaggableOn::Tag.find_by_slug(params[:tag])
@articles = Article.moderated.includes(:user).tagged_with(tag)
end
回答4:
To make this work with latest version (Rails 4.x, friendly_id 5.x) here are the steps you should follow:
Create migration to add slug to tags table
# rails generate migration add_slugs_to_tags
class AddSlugToTags < ActiveRecord::Migration
def change
add_column :tags, :slug, :string
add_index :tags, :slug
end
end
You can rename the :slug column - you should specify the column name in the initializer if you change it. Don't forget to run the migration rake db:migrate
.
Create an initializer for ActsAsTaggableOn
# config/initializers/acts_as_taggable_on.rb
ActsAsTaggableOn::Tag.class_eval do
extend FriendlyId
friendly_id :name, use: :slugged
end
When searching for tags you have to use ActsAsTaggableOn::Tag.friendly.find 'tag-name'
or add :finders
to friendly_id :use call to use find directly on the model. Read more in friendly_id guides.
回答5:
There is another way.
Create a controller for the tags with single action:
rails g controller tags index
In routes.rb change the generated route to:
get 'tags/:tag' => 'tags#index', as: :tag
In tags_controller.rb add this code:
def index
@tag = params[:tag]
@entries = Entry.tagged_with(@tag)
end
where Entry
is a model name.
Now you able to get all entries with nice urls like example.com/tags/animals
Usage in views:
- @entry.tags.each do |tag|
= link_to tag, tag_path(tag.name)