Coder Social home page Coder Social logo

mbleigh / acts-as-taggable-on Goto Github PK

View Code? Open in Web Editor NEW
5.0K 80.0 1.2K 1.22 MB

A tagging plugin for Rails applications that allows for custom tagging along dynamic contexts.

Home Page: http://mbleigh.lighthouseapp.com/projects/10116-acts-as-taggable-on

License: MIT License

Ruby 100.00%

acts-as-taggable-on's Introduction

Table of Contents generated with DocToc

ActsAsTaggableOn

Join the chat at https://gitter.im/mbleigh/acts-as-taggable-on Gem Version Build Status Code Climate Inline docs Security

This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney. It has evolved substantially since that point, but all credit goes to him for the initial tagging functionality that so many people have used.

For instance, in a social network, a user might have tags that are called skills, interests, sports, and more. There is no real way to differentiate between tags and so an implementation of this type is not possible with acts as taggable on steroids.

Enter Acts as Taggable On. Rather than tying functionality to a specific keyword (namely tags), acts as taggable on allows you to specify an arbitrary number of tag "contexts" that can be used locally or in combination in the same way steroids was used.

Installation

To use it, add it to your Gemfile:

gem 'acts-as-taggable-on', '~> 9.0'

and bundle:

bundle

Post Installation

Install migrations

# For the latest versions :
rake acts_as_taggable_on_engine:install:migrations

Review the generated migrations then migrate :

rake db:migrate

If you do not wish or need to support multi-tenancy, the migration for add_tenant_to_taggings is optional and can be discarded safely.

For MySql users

You can circumvent at any time the problem of special characters issue 623 by setting in an initializer file:

ActsAsTaggableOn.force_binary_collation = true

Or by running this rake task:

rake acts_as_taggable_on_engine:tag_names:collate_bin

See the Configuration section for more details, and a general note valid for older version of the gem.

Usage

Setup

class User < ActiveRecord::Base
  acts_as_taggable_on :tags
  acts_as_taggable_on :skills, :interests #You can also configure multiple tag types per model
end

class UsersController < ApplicationController
  def user_params
    params.require(:user).permit(:name, :tag_list) ## Rails 4 strong params usage
  end
end

@user = User.new(:name => "Bobby")

Add and remove a single tag

@user.tag_list.add("awesome")   # add a single tag. alias for <<
@user.tag_list.remove("awesome") # remove a single tag
@user.save # save to persist tag_list

Add and remove multiple tags in an array

@user.tag_list.add("awesome", "slick")
@user.tag_list.remove("awesome", "slick")
@user.save

You can also add and remove tags in format of String. This would be convenient in some cases such as handling tag input param in a String.

Pay attention you need to add parse: true as option in this case.

You may also want to take a look at delimiter in the string. The default is comma , so you don't need to do anything here. However, if you made a change on delimiter setting, make sure the string will match. See configuration for more about delimiter.

@user.tag_list.add("awesome, slick", parse: true)
@user.tag_list.remove("awesome, slick", parse: true)

You can also add and remove tags by direct assignment. Note this will remove existing tags so use it with attention.

@user.tag_list = "awesome, slick, hefty"
@user.save
@user.reload
@user.tags
=> [#<ActsAsTaggableOn::Tag id: 1, name: "awesome", taggings_count: 1>,
 #<ActsAsTaggableOn::Tag id: 2, name: "slick", taggings_count: 1>,
 #<ActsAsTaggableOn::Tag id: 3, name: "hefty", taggings_count: 1>]

With the defined context in model, you have multiple new methods at disposal to manage and view the tags in the context. For example, with :skill context these methods are added to the model: skill_list(and skill_list.add, skill_list.remove skill_list=), skills(plural), skill_counts.

@user.skill_list = "joking, clowning, boxing"
@user.save
@user.reload
@user.skills
=> [#<ActsAsTaggableOn::Tag id: 1, name: "joking", taggings_count: 1>,
 #<ActsAsTaggableOn::Tag id: 2, name: "clowning", taggings_count: 1>,
 #<ActsAsTaggableOn::Tag id: 3, name: "boxing", taggings_count: 1>]

@user.skill_list.add("coding")

@user.skill_list
# => ["joking", "clowning", "boxing", "coding"]

@another_user = User.new(:name => "Alice")
@another_user.skill_list.add("clowning")
@another_user.save

User.skill_counts
=> [#<ActsAsTaggableOn::Tag id: 1, name: "joking", taggings_count: 1>,
 #<ActsAsTaggableOn::Tag id: 2, name: "clowning", taggings_count: 2>,
 #<ActsAsTaggableOn::Tag id: 3, name: "boxing", taggings_count: 1>]

To preserve the order in which tags are created use acts_as_ordered_taggable:

class User < ActiveRecord::Base
  # Alias for acts_as_ordered_taggable_on :tags
  acts_as_ordered_taggable
  acts_as_ordered_taggable_on :skills, :interests
end

@user = User.new(:name => "Bobby")
@user.tag_list = "east, south"
@user.save

@user.tag_list = "north, east, south, west"
@user.save

@user.reload
@user.tag_list # => ["north", "east", "south", "west"]

Finding most or least used tags

You can find the most or least used tags by using:

ActsAsTaggableOn::Tag.most_used
ActsAsTaggableOn::Tag.least_used

You can also filter the results by passing the method a limit, however the default limit is 20.

ActsAsTaggableOn::Tag.most_used(10)
ActsAsTaggableOn::Tag.least_used(10)

Finding Tagged Objects

Acts As Taggable On uses scopes to create an association for tags. This way you can mix and match to filter down your results.

class User < ActiveRecord::Base
  acts_as_taggable_on :tags, :skills
  scope :by_join_date, order("created_at DESC")
end

User.tagged_with("awesome").by_join_date
User.tagged_with("awesome").by_join_date.paginate(:page => params[:page], :per_page => 20)

# Find users that matches all given tags:
# NOTE: This only matches users that have the exact set of specified tags. If a user has additional tags, they are not returned.
User.tagged_with(["awesome", "cool"], :match_all => true)

# Find users with any of the specified tags:
User.tagged_with(["awesome", "cool"], :any => true)

# Find users that have not been tagged with awesome or cool:
User.tagged_with(["awesome", "cool"], :exclude => true)

# Find users with any of the tags based on context:
User.tagged_with(['awesome', 'cool'], :on => :tags, :any => true).tagged_with(['smart', 'shy'], :on => :skills, :any => true)

Wildcard tag search

You now have the following options for prefix, suffix and containment search, along with :any or :exclude option. Use wild: :suffix to place a wildcard at the end of the tag. It will be looking for awesome% and cool% in SQL. Use wild: :prefix to place a wildcard at the beginning of the tag. It will be looking for %awesome and %cool in SQL. Use wild: true to place a wildcard both at the beginning and the end of the tag. It will be looking for %awesome% and %cool% in SQL.

Tip: User.tagged_with([]) or User.tagged_with('') will return [], an empty set of records.

Relationships

You can find objects of the same type based on similar tags on certain contexts. Also, objects will be returned in descending order based on the total number of matched tags.

@bobby = User.find_by_name("Bobby")
@bobby.skill_list # => ["jogging", "diving"]

@frankie = User.find_by_name("Frankie")
@frankie.skill_list # => ["hacking"]

@tom = User.find_by_name("Tom")
@tom.skill_list # => ["hacking", "jogging", "diving"]

@tom.find_related_skills # => [<User name="Bobby">, <User name="Frankie">]
@bobby.find_related_skills # => [<User name="Tom">]
@frankie.find_related_skills # => [<User name="Tom">]

Dynamic Tag Contexts

In addition to the generated tag contexts in the definition, it is also possible to allow for dynamic tag contexts (this could be user generated tag contexts!)

@user = User.new(:name => "Bobby")
@user.set_tag_list_on(:customs, "same, as, tag, list")
@user.tag_list_on(:customs) # => ["same", "as", "tag", "list"]
@user.save
@user.tags_on(:customs) # => [<Tag name='same'>,...]
@user.tag_counts_on(:customs)
User.tagged_with("same", :on => :customs) # => [@user]

Finding tags based on context

You can find tags for a specific context by using the for_context scope:

ActsAsTaggableOn::Tag.for_context(:tags)
ActsAsTaggableOn::Tag.for_context(:skills)

Tag Parsers

If you want to change how tags are parsed, you can define your own implementation:

class MyParser < ActsAsTaggableOn::GenericParser
  def parse
    ActsAsTaggableOn::TagList.new.tap do |tag_list|
      tag_list.add @tag_list.split('|')
    end
  end
end

Now you can use this parser, passing it as parameter:

@user = User.new(:name => "Bobby")
@user.tag_list = "east, south"
@user.tag_list.add("north|west", parser: MyParser)
@user.tag_list # => ["north", "east", "south", "west"]

# Or also:
@user.tag_list.parser = MyParser
@user.tag_list.add("north|west")
@user.tag_list # => ["north", "east", "south", "west"]

Or change it globally:

ActsAsTaggableOn.default_parser = MyParser
@user = User.new(:name => "Bobby")
@user.tag_list = "east|south"
@user.tag_list # => ["east", "south"]

Tag Ownership

Tags can have owners:

class User < ActiveRecord::Base
  acts_as_tagger
end

class Photo < ActiveRecord::Base
  acts_as_taggable_on :locations
end

@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
@some_user.owned_taggings
@some_user.owned_tags
Photo.tagged_with("paris", :on => :locations, :owned_by => @some_user)
@some_photo.locations_from(@some_user) # => ["paris", "normandy"]
@some_photo.owner_tags_on(@some_user, :locations) # => [#<ActsAsTaggableOn::Tag id: 1, name: "paris">...]
@some_photo.owner_tags_on(nil, :locations) # => Ownerships equivalent to saying @some_photo.locations
@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations, :skip_save => true) #won't save @some_photo object

Working with Owned Tags

Note that tag_list only returns tags whose taggings do not have an owner. Continuing from the above example:

@some_photo.tag_list # => []

To retrieve all tags of an object (regardless of ownership) or if only one owner can tag the object, use all_tags_list.

Adding owned tags

Note that owned tags are added all at once, in the form of comma seperated tags in string. Also, when you try to add owned tags again, it simply overwrites the previous set of owned tags. So to append tags in previously existing owned tags list, go as follows:

def add_owned_tag
    @some_item = Item.find(params[:id])
    owned_tag_list = @some_item.all_tags_list - @some_item.tag_list
    owned_tag_list += [(params[:tag])]
    @tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
    @some_item.save
end

def stringify(tag_list)
    tag_list.inject('') { |memo, tag| memo += (tag + ',') }[0..-1]
end
Removing owned tags

Similarly as above, removing will be as follows:

def remove_owned_tag
    @some_item = Item.find(params[:id])
    owned_tag_list = @some_item.all_tags_list - @some_item.tag_list
    owned_tag_list -= [(params[:tag])]
    @tag_owner.tag(@some_item, :with => stringify(owned_tag_list), :on => :tags)
    @some_item.save
end

Tag Tenancy

Tags support multi-tenancy. This is useful for applications where a Tag belongs to a scoped set of models:

class Account < ActiveRecord::Base
  has_many :photos
end

class User < ActiveRecord::Base
  belongs_to :account
  acts_as_taggable_on :tags
  acts_as_taggable_tenant :account_id
end

@user1.tag_list = ["foo", "bar"] # these taggings will automatically have the tenant saved
@user2.tag_list = ["bar", "baz"]

ActsAsTaggableOn::Tag.for_tenant(@user1.account.id) # returns Tag models for "foo" and "bar", but not "baz"

Dirty objects

@bobby = User.find_by_name("Bobby")
@bobby.skill_list # => ["jogging", "diving"]

@bobby.skill_list_changed? #=> false
@bobby.changes #=> {}

@bobby.skill_list = "swimming"
@bobby.changes.should == {"skill_list"=>["jogging, diving", ["swimming"]]}
@bobby.skill_list_changed? #=> true

@bobby.skill_list_change.should == ["jogging, diving", ["swimming"]]

Tag cloud calculations

To construct tag clouds, the frequency of each tag needs to be calculated. Because we specified acts_as_taggable_on on the User class, we can get a calculation of all the tag counts by using User.tag_counts_on(:customs). But what if we wanted a tag count for a single user's posts? To achieve this we call tag_counts on the association:

User.find(:first).posts.tag_counts_on(:tags)

A helper is included to assist with generating tag clouds.

Here is an example that generates a tag cloud.

Helper:

module PostsHelper
  include ActsAsTaggableOn::TagsHelper
end

Controller:

class PostController < ApplicationController
  def tag_cloud
    @tags = Post.tag_counts_on(:tags)
  end
end

View:

<% tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| %>
  <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
<% end %>

CSS:

.css1 { font-size: 1.0em; }
.css2 { font-size: 1.2em; }
.css3 { font-size: 1.4em; }
.css4 { font-size: 1.6em; }

Configuration

If you would like to remove unused tag objects after removing taggings, add:

ActsAsTaggableOn.remove_unused_tags = true

If you want force tags to be saved downcased:

ActsAsTaggableOn.force_lowercase = true

If you want tags to be saved parametrized (you can redefine to_param as well):

ActsAsTaggableOn.force_parameterize = true

If you would like tags to be case-sensitive and not use LIKE queries for creation:

ActsAsTaggableOn.strict_case_match = true

If you would like to have an exact match covering special characters with MySql:

ActsAsTaggableOn.force_binary_collation = true

If you would like to specify table names:

ActsAsTaggableOn.tags_table = 'aato_tags'
ActsAsTaggableOn.taggings_table = 'aato_taggings'

If you want to change the default delimiter (it defaults to ','). You can also pass in an array of delimiters such as ([',', '|']):

ActsAsTaggableOn.delimiter = ','

NOTE 1: SQLite by default can't upcase or downcase multibyte characters, resulting in unwanted behavior. Load the SQLite ICU extension for proper handle of such characters. See docs

NOTE 2: the option force_binary_collation is strongest than strict_case_match and when set to true, the strict_case_match is ignored. To roughly apply the force_binary_collation behaviour with a version of the gem <= 3.4.4, execute the following commands in the MySql console:

USE my_wonderful_app_db;
ALTER TABLE tags MODIFY name VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin;

Upgrading

see UPGRADING

Contributors

We have a long list of valued contributors. Check them all

Compatibility

Versions 2.x are compatible with Ruby 1.8.7+ and Rails 3.

Versions 2.4.1 and up are compatible with Rails 4 too (thanks to arabonradar and cwoodcox).

Versions >= 3.x are compatible with Ruby 1.9.3+ and Rails 3 and 4.

Versions >= 4.x are compatible with Ruby 2.0.0+ and Rails 4 and 5.

Versions >= 7.x are compatible with Ruby 2.3.7+ and Rails 5 and 6.

Versions >= 8.x are compatible with Ruby 2.3.7+ and Rails 5 and 6.

Versions >= 9.x are compatible with Ruby 2.5.0 and Rails 6 and 7.

For an up-to-date roadmap, see https://github.com/mbleigh/acts-as-taggable-on/milestones

Testing

Acts As Taggable On uses RSpec for its test coverage. Inside the gem directory, you can run the specs with:

bundle
rake spec

You can run all the tests across all the Rails versions by running rake appraise. If you'd also like to run the tests across all rubies and databases as configured for Github Actions, install and run wwtd.

License

See LICENSE

acts-as-taggable-on's People

Contributors

acmetech avatar amatsuda avatar artemk avatar bernardkroes avatar bf4 avatar billychan avatar brilyuhns avatar ches avatar d4rky-pl avatar damianlegawiec avatar eagletmt avatar jonseaberg avatar krzysiek1507 avatar leo-souza avatar lukeasrodgers avatar mikehale avatar progm avatar rbritom avatar rikettsie avatar rsl avatar seuros avatar shekibobo avatar sobrinho avatar szimek avatar tilsammans avatar tomeric avatar tonyta avatar unindented avatar znz avatar zquestz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

acts-as-taggable-on's Issues

STI bug in "save_tags"

Introduced in 42b51e3, there is bug saving STI models. There should be used "self.class.base_class"

Here is a failing test and a patch:

diff --git a/lib/acts_as_taggable_on/acts_as_taggable_on.rb b/lib/acts_as_taggable_on/acts_as_taggable_on.rb
index cbd83b8..ccb28de 100644
--- a/lib/acts_as_taggable_on/acts_as_taggable_on.rb
+++ b/lib/acts_as_taggable_on/acts_as_taggable_on.rb
@@ -398,12 +398,12 @@ module ActiveRecord

               cache.each do |owner, list|
                 new_tags = Tag.find_or_create_all_with_like_by_name(list.uniq)
-                taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.to_s })
+                taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.base_class.to_s })

                 # Destroy old taggings:
                 if owner
                   old_tags = tags_on(context, owner) - new_tags
-                  old_taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.to_s, :tag_id => old_tags, :tagger_id => owner.id, :tagger_type => owner.class.to_s, :context => context })
+                  old_taggings = Tagging.find(:all, :conditions => { :taggable_id => self.id, :taggable_type => self.class.base_class.to_s, :tag_id => old_tags, :tagger_id => owner.id, :tagger_type => owner.class.to_s, :context => context })

                   Tagging.destroy_all :id => old_taggings.map(&:id)
                 else
diff --git a/spec/acts_as_taggable_on/taggable_spec.rb b/spec/acts_as_taggable_on/taggable_spec.rb
index a23014e..b82c905 100644
--- a/spec/acts_as_taggable_on/taggable_spec.rb
+++ b/spec/acts_as_taggable_on/taggable_spec.rb
@@ -247,5 +247,15 @@ describe "Taggable" do
       AlteredInheritingTaggableModel.tag_counts_on(:tags).map(&:name).should == %w(fork spoon)
       TaggableModel.tag_counts_on(:tags).map(&:name).should == %w(bob kelso fork spoon)
     end
+    
+    it 'should store same tag without validation conflict' do
+      @taggable.tag_list = 'one'
+      @taggable.save!
+      
+      @inherited_same.tag_list = 'one'
+      @inherited_same.save!
+      
+      @inherited_same.update_attributes! :name => 'foo'
+    end
   end
 end

Requiring bundler breaking Heroku deployment?

The first lines at http://github.com/mbleigh/acts-as-taggable-on/blob/feature/rails3_compatibility/lib/acts-as-taggable-on.rb seems to break my deployment to Heroku. These are the lines which I've been told are breaking the deployment and shouldn't be there:

begin
  # Try to require the preresolved locked set of gems.
 require File.expand_path("../.bundle/environment", __FILE__)
rescue LoadError
  # Fall back on doing an unlocked resolve at runtime.
  require "rubygems"
  require "bundler"
  Bundler.setup
end

I just wanted to bring some light upon this.

Thanks.

Owner enhancements.

Hello,

Is it possible to do something like this:

image.rb
act_as_taggable_on :galleries

In console:
User.first.tag(User.first.images.last, :with => "first, second, third", :on => :galleries)
User.first.images.last.galleries # => "Would return all 'galleries' (tags) from that image"

Now it produces an SQL like this:
SELECT tags.* FROM tags INNER JOIN taggings ON tags.id = taggings.tag_id WHERE ((taggings.taggable_id = 2059) AND (taggings.taggable_type = 'Image') AND ((taggings.tagger_id IS NULL AND taggings.context = 'galleries')))

As You can see he is looking for a User which IS NULL and that will never take my galleries :)

I know, I can use a:
User.first.images.last.galleries_from(User.first) # => ["first", "second", "third"]
It returns an Array but I wanted easily get all Tag objects.

I don't want to achieve it like this:
User.first.images.last.galleries_from(User.first).collect{ |g| Tag.find_by_name(g) }

How can I make it at the moment? Do I need to make some patch to achieve this?

Best,
Martin

missing parentheses around OR conditions

when merging conditions string parentheses should be placed around clauses with OR.
Example:

currently User.tagged_with("foo, bar", :as => :languages) will result in the following query:
SELECT .... WHERE (context = 'languages' AND users_tags.name LIKE 'foo' OR users_tags.name LIKE 'bar')

Note that it will improperly match 'bar' tag in any context.

tagged_with returns no results when tag is similar to others

The problem starts at line 161 of acts_as_taggable_on.rb:

tags = Tag.named_like_any(tag_list)
return { :conditions => "1 = 0" } unless tags.length == tag_list.length

WTF!?!!

This will cause no records to return if you do a tagged_with('foo') if you have tags 'foo', 'foo bar', 'food'. I would attempt to patch this, but I have no idea what behavior is trying to be achieved here.

Issue with rails 3

Hi, I'm trying to use your gem with rails 3 using the 2.0.0.pre1 version

  • after installation the migration should be generated with "./script/rails generate acts_as_taggable_on:migration"
  • when I run it, I have a
    /Library/Ruby/Gems/1.8/gems/acts-as-taggable-on-2.0.0.pre1/lib/generators/acts_as_taggable_on/migration/migration_generator.rb:14:in join': can't convert Symbol into String (TypeError) from /Library/Ruby/Gems/1.8/gems/acts-as-taggable-on-2.0.0.pre1/lib/generators/acts_as_taggable_on/migration/migration_generator.rb:14:insource_root'
    from /Library/Ruby/Gems/1.8/gems/thor-0.13.4/lib/thor/actions.rb:34:in source_paths_for_search' from /Library/Ruby/Gems/1.8/gems/thor-0.13.4/lib/thor/actions.rb:115:insource_paths'

acts_as_taggable is needed as well

The find_related_ methods are only available if you include the acts_as taggable as well

class User < ActiveRecord::Base
   acts_as_taggable   
   acts_as_taggable_on :tags, :skills, :interests
end

It is included in your rspec models, so the tests are successful. I think it would therefore be enough to add it in your README.

Support of "dirty" tracking

It would be very nice if setting the “tag_list” attribute would support the dirty tracking available in ActiveRecord since Rails 2.1:

```
my_model = MyModel.create :tag_list => ‘ruby’
my_model.tag_list = ‘ruby, rails’
my_model.changed? # true
my_model.tag_list_changed? # true
my_model.tag_list_change # [“ruby”, “ruby, rails”]
my_model.changes # {"tag_list"=>[“ruby”, “ruby, rails”]}
```

With such an enhancement, the plugin collectiveidea/acts_as_audited could track changes of tags.

tagged_with returns all results when none found

Is anyone else seeing this?

When I pass tagged_with a tag that does not exist, it returns every record (instead of none).

>> Website.tagged_with("some tag that does not exist", :on=>:tags).size
=> 239
>> Website.count
=> 239

acts_as_taggable_on has_many broken in specific case

I am using ActiveScaffold, and when It loads up a list of my model objects it fails on the has_many. It appears that the aliased_join_table_name is not being defaulted to "taggings". The following change fixed it locally for me:
acts_as_taggable_on.rb (line 26)
was
:include => :tag, :conditions => ['#{aliased_join_table_name rescue "taggings"}.context = ?',tag_type], :class_name => "Tagging"
changed to
:include => :tag, :conditions => ['#{aliased_join_table_name || "taggings" rescue "taggings"}.context = ?',tag_type], :class_name => "Tagging"

ArgumentError in has_many eval in Rails 2.3.4

I upgraded from Rails 2.2.2 to 2.3.4 & got this error from this plugin when loading an AR class with acts_as_taggable in it:


ArgumentError ArgumentError: wrong number of arguments (2 for 1)
from /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations.rb:1414:in `sanitize_sql'
        from /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations.rb:1414:in `configure_dependency_for_has_many'
        from /opt/ruby-enterprise/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations.rb:823:in `has_many'
        from (eval):3:in `has_many'
        from RAILS_ROOT/vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_taggable_on.rb:25:in `acts_as_taggable_on'

I was able to fix this by downgrading to 2.3.2 - any insights?

Using this plugin version 1.0.11
Issue 17 solution did not work for me

find_related_tags does not exists in 2.0.0

After updating the code to reflect the changes in 2.0.0 I had to revert to the previous version (1.1.9) because find_related_* method cannot be found (rails 2.3.6)

User class defines

acts_as_taggable_on :tastes, :bands

but user.find_related_bands is not an existent method anymore.

accepts_nested_attributes_for :tag_context

With this model code:

acts_as_taggable_on :categories

I get a model that supports self.categories. The tag context acts kind of like a has_many association. Good.

But when I add:

accepts_nested_attributes_for :categories

and POST a list of categories to the create action I get this exception deep in AR#new:

ActiveRecord::AssociationTypeMismatch in 'PlacesController authenticated user should process categories parameter to create'
Tag(#2176796520) expected, got String(#2148246520)
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_proxy.rb:263:in raise_on_type_mismatch' /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:inreplace'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in each' /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:inreplace'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations.rb:1322:in `categories='

It appears that the association is looking for Tag objects and won't accept the Strings I'm handing it.

:match_all doesn't play nice with Postgres

When I run a tag seach with :match_all => true I get the following error.

ActiveRecord::StatementInvalid (PGError: ERROR:  column "entries.id" must appear in the GROUP BY clause or be used in an aggregate function
: SELECT DISTINCT entries.* FROM "entries"  LEFT OUTER JOIN taggings entries_taggings ON entries_taggings.taggable_id = entries.id AND entries_taggings.taggable_type = E'Entry' LEFT OUTER JOIN tags entries_tags ON entries_tags.id = entries_taggings.tag_id WHERE ((context = E'tags' AND entries_tags.name LIKE E'abandoned stairs') AND ("entries"."status" = E'published'))  GROUP BY entries_taggings.taggable_id HAVING COUNT(entries_taggings.taggable_id) = 1 ORDER BY published_at DESC LIMIT 30 OFFSET 0):

Unknown action error: worked fine earlier...

Hi, I semed to have gotten acts-as-taggable-on to work to allow me to tag the model Vendor, made an upgrade to Ruby 1.8.7 and now I get this error:

Unknown action

No action responded to tag. Actions: add_review, create, destroy, edit, index, new, rate, show, tag_cloud, and update

Here's the controller for Vendor:

http://github.com/allyforce/RQ-Upload/blob/master/app/controllers/vendors_controller.rb

Here it is for tag:

http://github.com/allyforce/RQ-Upload/blob/master/app/controllers/tags_controller.rb

Yet I am getting an Action Controller: Exception caught error....

Some ideas? Thanks.

#find_related_??? does not work properly with postgresql

I was trying out the AATO gem and found that when I tried to use the #find_related_??? method I was getting the following error:

ActiveRecord::StatementInvalid: PGError: ERROR:  column "users.name" must appear in the GROUP BY clause or be used in an aggregate function
: SELECT users.*, COUNT(tags.id) AS count FROM users, tags, taggings WHERE (users.id != 1 AND users.id = taggings.taggable_id AND taggings.taggable_type = 'User' AND taggings.tag_id = tags.id AND tags.name IN (E'CSS',E'XHTML',E'HTML',E'Javascript',E'Rails',E'Ruby'))  GROUP BY users.id ORDER BY count DESC

My configuration looks like this:

# == Schema Information
#
# Table name: users
#
#  id         :integer         not null, primary key
#  name       :string(255)
#  created_at :datetime
#  updated_at :datetime
#
class User < ActiveRecord::Base
  acts_as_taggable_on :skill
end

I tracked the problem to line 280 in the lib/acts_as_taggable_on/acts_as_taggable_on.rb file. I am not sure if this is something that only happens in Postgres or if it is being seen elsewhere. I had to update the line to be

:select => "#{klass.table_name}.id, COUNT(#{Tag.table_name}.id) AS count"

instead of

:select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count"

I am testing this on an iMac running 10.6.1 with Postgresql 8.3.8. Anyway, other than this minor issue I am very pleased with the gem. Thanks for the great work!

Conflict with Vestal Versions

Hi,
there are conflict of your plugin and vestal_versions.With both plugins added to project i see following error

            $ ./script/generate
            /opt/local/lib/ruby/gems/1.8/gems/rails-2.3.5/lib/rails/
            gem_dependency.rb:119:Warning: Gem::Dependency#version_requirements is
            deprecated and will be removed on or after August 2010.  Use
            #requirement
            /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/
            reflection.rb:187:in `quoted_table_name': undefined method
            `quoted_table_name' for VestalVersions::Tagging:Module (NoMethodError)
                    from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/
            active_record/associations.rb:1416:in
            `configure_dependency_for_has_many'
                    from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/
            active_record/associations.rb:824:in `has_many'
                    from (eval):3:in `has_many'
                    from /opt/local/lib/ruby/gems/1.8/gems/mbleigh-acts-as-
            taggable-on-1.0.5/lib/acts_as_taggable_on/acts_as_taggable_on.rb:25:in
            `acts_as_taggable_on'
                    from /opt/local/lib/ruby/gems/1.8/gems/mbleigh-acts-as-
            taggable-on-1.0.5/lib/acts_as_taggable_on/acts_as_taggable_on.rb:24:in
            `class_eval'
                    from /opt/local/lib/ruby/gems/1.8/gems/mbleigh-acts-as-
            taggable-on-1.0.5/lib/acts_as_taggable_on/acts_as_taggable_on.rb:24:in
            `acts_as_taggable_on'
                    from /opt/local/lib/ruby/gems/1.8/gems/mbleigh-acts-as-
            taggable-on-1.0.5/lib/acts_as_taggable_on/acts_as_taggable_on.rb:20:in
            `each'
                    from /opt/local/lib/ruby/gems/1.8/gems/mbleigh-acts-as-
            taggable-on-1.0.5/lib/acts_as_taggable_on/acts_as_taggable_on.rb:20:in
            `acts_as_taggable_on'

One guy have recommended me to rename model Tagging to the ::Tagging - it solved the problem. I'd like to ask you to rename model Tagging to the other one to solve any problem. Thanks.

Tag Ownership does not update <tagtype>_list()

From the examples listed in the README..
@some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)

My config:
Rails 2.3.5
acts-as-taggable-on --version=1.1.7
(because the master branch does not pass all the tests...)

My classes:
class Mark < ActiveRecord::Base
acts_as_taggable_on :tags, :labels
end

class User < ActiveRecord::Base
  acts_as_tagger
  acts_as_authentic
end

This is my script/console output.

>> m = Mark.create :name => "first mark", :url => 'http://example.com'
=> #<Mark id: 97, name: "first mark", url: "http://example.com", note: nil, created_at: "2010-03-19 06:26:51", updated_at: "2010-03-19 06:26:51">
>> m.tags
=> []
>> m.labels
=> []
>> m.label_list = "star, first"
=> "star, first"
>> m.tag_list = 'white, blue'
=> "white, blue"
>> m.save!
=> true
>> m.reload
=> #<Mark id: 97, name: "first mark", url: "http://example.com", note: nil, created_at: "2010-03-19 06:26:51", updated_at: "2010-03-19 06:26:51">
>> m.tags
=> [#<Tag id: 25, name: "white">, #<Tag id: 26, name: "blue">]
>> m.labels
=> [#<Tag id: 23, name: "star">, #<Tag id: 27, name: "first">]
>> m.tag_list
=> ["white", "blue"]
>> m.label_list
=> ["star", "first"]


>> u = User.find :first
=> #<User id: 1, username: "me", email: "[email protected]", ...>
>> u.tag(m, :with => 'funny', :on => :tags)
=> true
>> m.reload
=> #<Mark id: 97, name: "first mark", url: "http://example.com", note: nil, created_at: "2010-03-19 06:26:51", updated_at: "2010-03-19 06:26:51">
>> m.tags
=> [#<Tag id: 25, name: "white">, #<Tag id: 26, name: "blue">, #<Tag id: 28, name: "funny">]
>> m.tag_list
=> ["white", "blue"]
>> # the above should be ["white", "blue", "funny"]

Cached columns bug (CRITICAL)

Code:

class Post
acts_as_taggable_on :tags

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

post = Post.create(:tag_list => "one, two")
post.comments.create! # UPDATE posts Set updated_at = 'now()' cached_tag_list = '' ...
post.reload
post.tag_list # => []

TagList.from("...") strips quotations

This is relatively minor, but we did run into it.

TagList.from("...") strips quotations marks out of tags, which would ordinarily make sense.

But if a user creates a new tag with quotation marks, they are not stripped out.

This results in a bit of confusion, if a user tags something with "abc" (including quotes) and then does a search which calls Object.find_tagged_with(param[:search]) the search will return zero results because inside find_tagged_with it calls TagList.from which strips the quotes out of the search.

One fix would be to have Tag strip quotes out when new ones are creatd (it probably doesn't normally make sense to have tags with quotes in them, but it does in our case since tags are keywords, just like Google keyword searches where you sometimes want to use quotes, admittedly a special case).

A quick fix in our case:

Comments out the 2 string.gsub! lines in TagList.from, around line 82 and 83.

Hope it helps someone, and great plugin!

Problem for non comma separated tags

For exemple, if you have an application with contacts and you want to put tags on then:

c1 = Contact.find(1)
c1.tag_list = "friend"
c1.save

c2 = Contact.find(2)
c2.tag_list = "client"
c2.save

If you search for Contact.tagged_with("client", :on => :tags), it will return an array containing a contact equivalent to c2. This will work perfectly until someone do the next thing:

c3 = Contact.find(3)
c3.tag_list = "client friend" #with no comma
c3.save

If you search for Contact.tagged_with("client", :on => :tags) again, it will return []

find_related_tags does not exists in 2.0.0

After updating the code to reflect the changes in 2.0.0 I had to revert to the previous version (1.1.9) because find_related_* method cannot be found (rails 2.3.6)

User class defines

acts_as_taggable_on :tastes, :bands

but user.find_related_bands is not an existent method anymore.

Rails 3 support

1.1.5 doesn't work with Rails 3 beta. I tried to manually set it up in my application, copying migration file etc, but there was just too many problems getting it to work.

Any plans on creating a new branch/version for rails 3 support? I guess supporting 3 and 2.x in the same source is a bad choice.

Thanks Glenn

Cant edit object with tag

In view:
<%= form.label :tags %>
or
<%= form.label :tag_list %>
Object is created successfully. But when editing get an error:
ActiveRecord:: RecordInvalid (Validation failed: Tag has already been taken)
What do I do?

error: ActiveRecord::UnknownAttributeError (unknown attribute: context):

Hi,

Starting to use acts-as-taggable-on and easing in using as if on-steroids and get a "context" error.

class Vendor...
acts_as_taggable_on :tags, :competitors

form_for @Vendor |do|....

<%= f.label :tags %>
<%= f.text_field :tag_list %>

It looks like I have added it as the model with two types of context

And then in the view new created a field for submission.

But I'm still getting an error. Thanks!

tags_on method inefficient?

I am working on a project and noticed that the #tags_on method does a Tagging.find every time it is called.

Since the context is known (it is a parameter to the method), won't it make more sense to call the self.<context_tags> association to get those?

For example: Assume I have a user that is acts_as_taggable_on :tags

If I eagerly load the tags association using user = User.find(...., :include => :tags),
then I call user.tag_list, it still does a Tagging#find. So instead of using the eagerly loaded association, it will find the tags all over again effectively overriding my optimization.

My proposal would be to replace the finder code in the #tags_on method with a self.send(:context)

Any thoughts on this?

Spotted two bugs in acts_as_taggable_on.rb

Hi,

This a great plugin. Here two small bugs.

in : find_options_for_find_tagged_with()
change : conditions << sanitize_sql(["context = ?",on.to_s])
to : conditions << sanitize_sql(["#{Tagging.table_name}.context = ?",on.to_s])

in : find_options_for_tag_counts()
change:
{ :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT() AS count",
:joins => joins.join(" "),
:conditions => conditions,
:group => group_by
}
to :
{ :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(
) AS count",
:joins => joins.join(" "),
:conditions => conditions,
:group => group_by,
:order => options[:order]
}

Best regards,

miguel cabero

Chaining tagged_with named scope doesn't work

As shown below, chaining the tagged_with named scope doesn't appear to work, at least not in the way I'm doing it. What nomenclature do I used to find all Products that are tagged with both "081" and "20x24"?

Thanks.
c.

>> Product.find(1).tags.map {|t| t.name}
=> ["081", "20x24", "aluminum", "forever", "frame"]
>> Product.tagged_with("081", {}).size
=> 24
>> Product.tagged_with("20x24", {}).size
=> 94
>> Product.tagged_with("20x24, 081", {}).size
=> 118
>> Product.tagged_with("081", {}).tagged_with("20x24", {}).size
=> 0

top_#{tag_type} doesnt properly accept limits

In my app for example, top_tags doesn't recognize limits (even when trying different ways such as :limit => 3) and just lists all tags:

b = Blog.find_by_slug 'paulstamatiou-com'
b.top_tags(3)
[#<Tag id: 6, name: "software">, #<Tag id: 15, name: "Apple">, #<Tag id: 32, name: "programming">, #<Tag id: 49, name: "web">, #<Tag id: 220, name: "tech">, #<Tag id: 234, name: "Internet">, #<Tag id: 303, name: "ruby">]

Option to return non-readonly records

Acts as taggable joins with the tags tables, and thus the records found are marked ActiveRecord::ReadOnlyRecord. An option to return writeable records is needed. If anything this caveat should be mentioned in the docs.

another issue in find_options_for_tag_counts

passed-in conditions are not being sanitized, causing some funky behavior with bound parameters; ex: :conditions => [ "foo.id IN (?)", [1,2,3,4,5] ] is being converted to SQL " foo.id in (?) AND 1 AND 2 AND 3 AND 4 AND 5"

here's a fix I applied that appears to be working:
before:
conditions = [
taggable_type,
taggable_id,
options[:conditions],
start_at,
end_at
]

after:
conditions = [
taggable_type,
taggable_id,
sanitize_sql(options[:conditions]),
start_at,
end_at
]

tag.count always 1, Taggingsnot saved per User

from what i understand a tag is upposed to be unique in the tags table but then references per user that are set "acts_as_tagger" should be stored in the taggings table.

but what the plugin currently does is not save any further taggings for different users when the tag already exists. therefor tag.count always stays at 1 making tag clouds quite boring :(

is this a bug or did i miss s.th. ?

conditions not working properly

tag_counts should work like find, so if I pass a condition:
Challenge.tag_counts/_on(:tags, :conditions => {:opened => false}) // or :conditions => ['challenge.opened = ?', false]

just merges the strings improperly : "challenge.opened = ? false" resulting in bad query. seems to me that options.merge({.. is not needed when that can be achieved with_scope.. Will play with this today..

    def tag_counts_on(context, options = {})
       with_scope :find => options do
         Tag.find(:all, find_options_for_tag_counts(:on => context.to_s))
       end
    end           

tagged_with Named Scope Requires Options

If I call tagged_with without specifying options, I get an exception thrown:

ArgumentError: wrong number of arguments (1 for 2)
from vendor/plugins/acts-as-taggable-on/lib/acts_as_taggable_on/acts_as_taggable_on.rb:90

I just installed the plugin today; this is a Rails 2.3.2 project.

Here's my proposed patch:

named_scope :tagged_with, lambda{ |tags, *options|
  options = options.shift || {}
  find_options_for_find_tagged_with(tags, options)
}

Records with only one tag out of a multi-tag search not found

I am not sure if I am using the gem correctly or if it is the intended use, but if I try to find all records with tagged "A" or "B" by calling Users.tagged_with("A, B", :on => :skills) it only returns User with both A and B and not the User tagged with "A" and the User tagged with "B".

Don't define models in gem

Extending models Tag and Tagging is impossible. Opening these classes just doesn't work.
So is it possible to remove them from gem lib and add them to generator who will copy those two files to models directory?

This would be a huge change for all users, but I think it's worth discussion.
What do you think?

strange find_related* behavior

I am not sure if this is a desired behavior or not, but it seems like the find_related*for methods is not what it should be.

If I say user.find_related_skills_for(Pet), I would expect it to return Pets that have a similar set of tags for the "skills" context.

Instead, you get any Pets that have ANY tags that the user has in it's skills context. If a user's skill is "eating" and a pet's "hobby" is eating, this matches.

Here is a failing spec as an example.

it "should only find related objects based on the tag names in the same context" do
  taggable1 = TaggableModel.create!(:name => "Taggable 1")
  taggable2 = OtherTaggableModel.create!(:name => "Taggable 2")

  taggable1.language_list = "english, french"
  taggable1.save

  taggable2.need_list = "food, water, english"
  taggable2.save

  taggable1.find_related_languages_for(OtherTaggableModel).should_not include(taggable2)  
end

Is this the desired behavior, or is this a bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.