Why I decided to make the Tag functions by myself
If you’re developing with Rails, I think you’ve heard of this famous gem acts-as-taggable-on. You might ask me, “why don’t you use this gem?”. This gem helps you to create the Tags so easily, even you don’t need to think about the database structure because this gem will generate the basic one.
But there’s a weak point, as the other gem also has. It is…
- not easy to modify
- takes time to know their own rules
It takes time in some cases to add your own change to the gem. Because you need to understand where to modify inside the gem and you need to know how big impact will be made by the change.
The tag functions with the names in different language
This time, I need the tag that has the names for different languages. Like, 1 tag has the 3 names in English, Japanese and Chinese.
I think it is also possible to make this tag with ‘acts-as-taggable-on’ gem, but it’s much easier and faster to create the tag by myself. It’s because, if I make it by myself, I know everything about it and I don’t have to implement too many functions that I don’t need, so that I can modify easily.
(Of course there’re good points to use the well-known gems but in my opinion it’s better to create it by ourselves if you just need the simple one. Simple is best.)
How I made the Tag
1. Create the Tag model
This create the Tag model with 3 names columns in English, Japanese and Chinese. (name_en
, name_ja
, name_zh_cn
)
$ rails g model Tag name_en:string name_ja:string name_zh_cn:string
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :name_en
t.string :name_ja
t.string :name_zh_cn
t.timestamps null: false
end
add_index :tags, :name_en, unique: true
add_index :tags, :name_ja, unique: true
add_index :tags, :name_zh_cn, unique: true
end
end
# app/models/tag.rb
class Tag < ActiveRecord::Base
end
2. Create the Tagging model
This hold the relations between the Tag
and the Post
.
And make the unique index to tag_id
and post_id
to avoid the duplicate registration.
$ rails g model Tagging tag_id:integer post_id:integer
class CreateTaggings < ActiveRecord::Migration
def change
create_table :taggings do |t|
t.integer :tag_id
t.integer :post_id
t.timestamps null: false
end
add_index :taggings, :tag_id
add_index :taggings, [:tag_id, :post-id], unique: true
end
end
# app/models/tagging.rb
class Tagging < ActiveRecord::Base
end
3. Add the relations to each models
This is the typical many-to-many relations.
# app/models/post.rb
class Post < ActiveRecord::Base
has_many :tags, through: :taggings
has_many :taggings, dependent: :destroy
# app/models/tag.rb
class Tag < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :posts, through: :taggings
# app/models/tagging.rb
class Tagging < ActiveRecord::Base
belongs_to :post
belongs_to :tag
And then, you can call like this.
> post = Post.find 1
> post.tags
=> returns the all related tags
>
> tag = Tag.find 1
> tag.posts
=> returns the all related posts
>
s> post.taggings.new(tag_id: 1)
=> #<Tagging:0x007ff9482e9e58 id: nil, tag_id: 1, post_id: 1, created_at: nil, updated_at: nil>
4. Useful method to get the I18n name
Add the name method to get the right name depends on the I18n.locale. (This will fall back to name_en
if another name does not exist.)
# app/models/tag.rb
class Tag < ActiveRecord::Base
def name
name = eval("self.name_#{I18n.locale}")
name.present? ? name : name_en
end
> tag = Tag.find 1
> I18n.locale = :en
> tag.name
=> 'Tag Name'
> I18n.locale = :ja
> tag.name
=> 'タグの名前'
That’s it!
Simple is best!
The gems are great. They help us to create great apps. But too much functions and black boxes inside your app is not happy. (it’s not black box if you understand the all source codes. Actually it takes time so that we need the trustworthy gems.)
I’m not saying “Don’t use the gems!”, but it’s better to ask yourself whether it’s better to use this gem or not before use them. I believe this will make you (and you in the future) happier.
Thanks!