A simple and high performance tagging gem for Rails using PostgreSQL array.
Ruby
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
gemfiles
lib
spec
.editorconfig
.gitignore
.travis.yml
CHANGELOG.md
Gemfile
Guardfile
LICENSE.txt
README.md
Rakefile
acts-as-taggable-array-on.gemspec
spec_helper.rb

README.md

ActsAsTaggableArrayOn

Build Status

A simple implementation for tagging system with postgres array. So, this gem works only on postgres.

Installation

Add this line to your application's Gemfile:

gem 'acts-as-taggable-array-on'

And then execute:

bundle

Setup

To use it, you need to have an array column to act as taggable.

class CreateUser < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :tags, array: true, default: '{}'
      t.timestamps
    end
    add_index :users, :tags, using: 'gin'
  end
end

and bundle:

rake db:migrate

then

class User < ActiveRecord::Base
  acts_as_taggable_array_on :tags
end
@user = User.new(:name => "Bobby")

acts_as_taggable_array_on defines 4 scope and 2 class methods as below.

types

currently, these types supported

  • varchar[]
  • text[]
  • integer[]

scopes

  • with_any_#{tag_name}
  • with_all_#{tag_name}
  • without_any_#{tag_name}
  • without_all_#{tag_name}

class methods

  • all_#{tag_name}
  • #{tag_name}_cloud

Usage

Set, add and remove

#set
@user.tags = ["awesome", "slick"]
@user.tags = '{awesome,slick}'

#add
@user.tags += ["awesome"]
@user.tags += ["awesome", "slick"]

#remove
@user.tags -= ["awesome"]
@user.tags -= ["awesome", "slick"]

Finding Tagged Objects

class User < ActiveRecord::Base
  acts_as_taggable_array_on :tags
  scope :by_join_date, ->{order("created_at DESC")}
end

# Find a user with all of the tags
User.with_all_tags("awesome, slick")
User.with_all_tags(["awesome", "slick"])

# Find a user with any of the tags
User.with_any_tags("awesome, slick")
User.with_any_tags(["awesome", "slick"])

# Find a user without all of the tags
User.without_all_tags("awesome, slick")
User.without_all_tags(["awesome", "slick"])

# Find a user without any of the tags
User.without_any_tags("awesome, slick")
User.without_any_tags(["awesome", "slick"])

# Chain with the other scopes
User.with_any_tags("awesome").without_any_tags("slick").by_join_date.paginate(:page => params[:page], :per_page => 20)

Tag cloud calculations

Calculation to count for each tags is supported. Currently, it does not care its order.

User.tags_cloud
# [['awesome' => 1], ['slick' => 2]]

Tag cloud calculation uses subquery internally. To add scopes to the query, use block.

User.tags_cloud { where name: ['ken', 'tom'] }

To handle the result tags named 'tag' and 'count', prepend scopes.

User.where('tag like ?', 'aws%').limit(10).order('count desc').tags_cloud { where name: ['ken', 'tom'] }

All Tags

Can get all tags easily.

User.all_tags
# ['awesome', 'slick']

As the same to tag cloud calculation, you can use block to add scopes to the query.

User.all_tags { where name: ['ken', 'tom'] }

To handle the result tags named 'tag', prepend scopes.

User.where('tag like ?', 'aws%').all_tags { where name: ['ken', 'tom'] }

Benchmark

Based on the article, I built simple benchmark app to compare only the main features acts-as-taggable-array-on has.

This result does NOT insist acts-as-taggable-array-on is better than acts-as-taggable-on since it provides much more features than this gem. In the case you need simple tag functionality, acts-as-taggable-array-on may be helpful to improve its performance.

% rake bench:write bench:find_by_id bench:find_by_tag
Deleted all ActsAsTaggableOn::Tag
Deleted all ActsAsTaggableOn::Tagging
Deleted all TaggableUser
Deleted all TaggableArrayUser
Finsihed to clean


###################################################################

bench:write
Rehearsal ---------------------------------------------------------
Using Taggable          6.950000   0.420000   7.370000 (  9.223704)
Using Postgres Arrays   0.710000   0.090000   0.800000 (  1.184734)
------------------------------------------------ total: 8.170000sec

                            user     system      total        real
Using Taggable          5.800000   0.340000   6.140000 (  7.842051)
Using Postgres Arrays   0.680000   0.090000   0.770000 (  1.117812)

###################################################################

bench:find_by_id
Rehearsal ---------------------------------------------------------
Using Taggable          1.490000   0.110000   1.600000 (  2.079776)
Using Postgres Arrays   0.240000   0.030000   0.270000 (  0.419430)
------------------------------------------------ total: 1.870000sec

                            user     system      total        real
Using Taggable          1.440000   0.100000   1.540000 (  2.023188)
Using Postgres Arrays   0.250000   0.040000   0.290000 (  0.434233)

###################################################################

bench:find_by_tag
Rehearsal ---------------------------------------------------------
Using Taggable          0.600000   0.040000   0.640000 (  1.107227)
Using Postgres Arrays   0.060000   0.000000   0.060000 (  0.060019)
------------------------------------------------ total: 0.700000sec

                            user     system      total        real
Using Taggable          0.600000   0.040000   0.640000 (  1.100302)
Using Postgres Arrays   0.030000   0.000000   0.030000 (  0.033001)
rake bench:write bench:find_by_id bench:find_by_tag  20.29s user 1.52s system 77% cpu 28.322 total

Development

To run testsuite you'll need to setup local PG database/user with rake db:create After that just running rspec should work.

Contributing

  1. Fork it ( http://github.com/tmiyamon/acts-as-taggable-array-on/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request