acts-as-taggable-on 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/CHANGELOG +25 -0
  2. data/Gemfile +6 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +212 -0
  5. data/Rakefile +59 -0
  6. data/VERSION +1 -0
  7. data/lib/acts-as-taggable-on.rb +30 -0
  8. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +41 -0
  9. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +56 -0
  10. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +97 -0
  11. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +220 -0
  12. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +29 -0
  13. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +101 -0
  14. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +64 -0
  15. data/lib/acts_as_taggable_on/acts_as_tagger.rb +47 -0
  16. data/lib/acts_as_taggable_on/compatibility/Gemfile +6 -0
  17. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +17 -0
  18. data/lib/acts_as_taggable_on/tag.rb +65 -0
  19. data/lib/acts_as_taggable_on/tag_list.rb +95 -0
  20. data/lib/acts_as_taggable_on/tagging.rb +23 -0
  21. data/lib/acts_as_taggable_on/tags_helper.rb +17 -0
  22. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +31 -0
  23. data/lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb +28 -0
  24. data/rails/init.rb +1 -0
  25. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +266 -0
  26. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +114 -0
  27. data/spec/acts_as_taggable_on/tag_list_spec.rb +70 -0
  28. data/spec/acts_as_taggable_on/tag_spec.rb +115 -0
  29. data/spec/acts_as_taggable_on/taggable_spec.rb +277 -0
  30. data/spec/acts_as_taggable_on/tagger_spec.rb +75 -0
  31. data/spec/acts_as_taggable_on/tagging_spec.rb +31 -0
  32. data/spec/acts_as_taggable_on/tags_helper_spec.rb +28 -0
  33. data/spec/bm.rb +52 -0
  34. data/spec/models.rb +36 -0
  35. data/spec/schema.rb +42 -0
  36. data/spec/spec.opts +2 -0
  37. data/spec/spec_helper.rb +47 -0
  38. metadata +109 -0
@@ -0,0 +1,25 @@
1
+ == 2010-02-17
2
+ * Converted the plugin to be compatible with Rails3
3
+
4
+ == 2009-12-02
5
+
6
+ * PostgreSQL is now supported (via morgoth)
7
+
8
+ == 2008-07-17
9
+
10
+ * Can now use a named_scope to find tags!
11
+
12
+ == 2008-06-23
13
+
14
+ * Can now find related objects of another class (tristanzdunn)
15
+ * Removed extraneous down migration cruft (azabaj)
16
+
17
+ == 2008-06-09
18
+
19
+ * Added support for Single Table Inheritance
20
+ * Adding gemspec and rails/init.rb for gemified plugin
21
+
22
+ == 2007-12-12
23
+
24
+ * Added ability to use dynamic tag contexts
25
+ * Fixed missing migration generator
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :gemcutter
2
+
3
+ # Rails 3.0
4
+ gem 'rails', '3.0.0.beta'
5
+ gem 'rspec', '2.0.0.beta.1'
6
+ gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Michael Bleigh and Intridea Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,212 @@
1
+ = ActsAsTaggableOn
2
+
3
+ This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney.
4
+ It has evolved substantially since that point, but all credit goes to him for the
5
+ initial tagging functionality that so many people have used.
6
+
7
+ For instance, in a social network, a user might have tags that are called skills,
8
+ interests, sports, and more. There is no real way to differentiate between tags and
9
+ so an implementation of this type is not possible with acts as taggable on steroids.
10
+
11
+ Enter Acts as Taggable On. Rather than tying functionality to a specific keyword
12
+ (namely "tags"), acts as taggable on allows you to specify an arbitrary number of
13
+ tag "contexts" that can be used locally or in combination in the same way steroids
14
+ was used.
15
+
16
+ == Installation
17
+
18
+ === Plugin
19
+
20
+ Acts As Taggable On is available both as a gem and as a traditional plugin. For the
21
+ traditional plugin you can install like so (Rails 2.1 or later):
22
+
23
+ script/plugin install git://github.com/mbleigh/acts-as-taggable-on.git
24
+
25
+ === GemPlugin
26
+
27
+ Acts As Taggable On is also available as a gem plugin using Rails 2.1's gem dependencies.
28
+ To install the gem, add this to your config/environment.rb:
29
+
30
+ config.gem "acts-as-taggable-on", :source => "http://gemcutter.org"
31
+
32
+ After that, you can run "rake gems:install" to install the gem if you don't already have it.
33
+
34
+ == Rails 3.0
35
+
36
+ Acts As Taggable On is now useable in Rails 3.0, thanks to the excellent work of Szymon Nowak
37
+ and Jelle Vandebeeck. Because backwards compatibility is hard to maintain, their work is available
38
+ in the feature/rails3_compatibility branch.
39
+
40
+ A Rails 3.0 compatible version of the gem is also available:
41
+
42
+ gem install acts-as-taggable-on -v=2.0.0.pre1
43
+
44
+ === Post Installation (Rails)
45
+
46
+ 1. script/generate acts_as_taggable_on_migration
47
+ 2. rake db:migrate
48
+
49
+ === Testing
50
+
51
+ Acts As Taggable On uses RSpec for its test coverage. Inside the plugin
52
+ directory, you can run the specs with:
53
+
54
+ rake spec
55
+
56
+ If you already have RSpec on your application, the specs will run while using:
57
+
58
+ rake spec:plugins
59
+
60
+
61
+ == Usage
62
+
63
+ class User < ActiveRecord::Base
64
+ acts_as_taggable_on :tags, :skills, :interests
65
+ end
66
+
67
+ @user = User.new(:name => "Bobby")
68
+ @user.tag_list = "awesome, slick, hefty" # this should be familiar
69
+ @user.skill_list = "joking, clowning, boxing" # but you can do it for any context!
70
+ @user.skill_list # => ["joking","clowning","boxing"] as TagList
71
+ @user.save
72
+
73
+ @user.tags # => [<Tag name:"awesome">,<Tag name:"slick">,<Tag name:"hefty">]
74
+ @user.skills # => [<Tag name:"joking">,<Tag name:"clowning">,<Tag name:"boxing">]
75
+
76
+ # The old way
77
+ User.find_tagged_with("awesome", :on => :tags) # => [@user]
78
+ User.find_tagged_with("awesome", :on => :skills) # => []
79
+
80
+ # The better way (utilizes named_scope)
81
+ User.tagged_with("awesome", :on => :tags) # => [@user]
82
+ User.tagged_with("awesome", :on => :skills) # => []
83
+
84
+ @frankie = User.create(:name => "Frankie", :skill_list => "joking, flying, eating")
85
+ User.skill_counts # => [<Tag name="joking" count=2>,<Tag name="clowning" count=1>...]
86
+ @frankie.skill_counts
87
+
88
+ === Finding Tagged Objects
89
+
90
+ Acts As Taggable On utilizes Rails 2.1's named_scope to create an association
91
+ for tags. This way you can mix and match to filter down your results, and it
92
+ also improves compatibility with the will_paginate gem:
93
+
94
+ class User < ActiveRecord::Base
95
+ acts_as_taggable_on :tags
96
+ named_scope :by_join_date, :order => "created_at DESC"
97
+ end
98
+
99
+ User.tagged_with("awesome").by_date
100
+ User.tagged_with("awesome").by_date.paginate(:page => params[:page], :per_page => 20)
101
+
102
+ #Find a user with matching all tags, not just one
103
+ User.tagged_with(["awesome", "cool"], :match_all => :true)
104
+
105
+ === Relationships
106
+
107
+ You can find objects of the same type based on similar tags on certain contexts.
108
+ Also, objects will be returned in descending order based on the total number of
109
+ matched tags.
110
+
111
+ @bobby = User.find_by_name("Bobby")
112
+ @bobby.skill_list # => ["jogging", "diving"]
113
+
114
+ @frankie = User.find_by_name("Frankie")
115
+ @frankie.skill_list # => ["hacking"]
116
+
117
+ @tom = User.find_by_name("Tom")
118
+ @tom.skill_list # => ["hacking", "jogging", "diving"]
119
+
120
+ @tom.find_related_skills # => [<User name="Bobby">,<User name="Frankie">]
121
+ @bobby.find_related_skills # => [<User name="Tom">]
122
+ @frankie.find_related_skills # => [<User name="Tom">]
123
+
124
+ === Dynamic Tag Contexts
125
+
126
+ In addition to the generated tag contexts in the definition, it is also possible
127
+ to allow for dynamic tag contexts (this could be user generated tag contexts!)
128
+
129
+ @user = User.new(:name => "Bobby")
130
+ @user.set_tag_list_on(:customs, "same, as, tag, list")
131
+ @user.tag_list_on(:customs) # => ["same","as","tag","list"]
132
+ @user.save
133
+ @user.tags_on(:customs) # => [<Tag name='same'>,...]
134
+ @user.tag_counts_on(:customs)
135
+ User.find_tagged_with("same", :on => :customs) # => [@user]
136
+
137
+ === Tag Ownership
138
+
139
+ Tags can have owners:
140
+
141
+ class User < ActiveRecord::Base
142
+ acts_as_tagger
143
+ end
144
+
145
+ class Photo < ActiveRecord::Base
146
+ acts_as_taggable_on :locations
147
+ end
148
+
149
+ @some_user.tag(@some_photo, :with => "paris, normandy", :on => :locations)
150
+ @some_user.owned_taggings
151
+ @some_user.owned_tags
152
+ @some_photo.locations_from(@some_user)
153
+
154
+ === Tag cloud calculations
155
+
156
+ To construct tag clouds, the frequency of each tag needs to be calculated.
157
+ Because we specified +acts_as_taggable_on+ on the <tt>User</tt> class, we can
158
+ get a calculation of all the tag counts by using <tt>User.tag_counts_on(:customs)</tt>. But what if we wanted a tag count for
159
+ an single user's posts? To achieve this we call tag_counts on the association:
160
+
161
+ User.find(:first).posts.tag_counts_on(:tags)
162
+
163
+ A helper is included to assist with generating tag clouds.
164
+
165
+ Here is an example that generates a tag cloud.
166
+
167
+ Helper:
168
+
169
+ module PostsHelper
170
+ include TagsHelper
171
+ end
172
+
173
+ Controller:
174
+
175
+ class PostController < ApplicationController
176
+ def tag_cloud
177
+ @tags = Post.tag_counts_on(:tags)
178
+ end
179
+ end
180
+
181
+ View:
182
+
183
+ <% tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| %>
184
+ <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
185
+ <% end %>
186
+
187
+ CSS:
188
+
189
+ .css1 { font-size: 1.0em; }
190
+ .css2 { font-size: 1.2em; }
191
+ .css3 { font-size: 1.4em; }
192
+ .css4 { font-size: 1.6em; }
193
+
194
+ == Contributors
195
+
196
+ * TomEric (i76) - Maintainer
197
+ * Michael Bleigh - Original Author
198
+ * Brendan Lim - Related Objects
199
+ * Pradeep Elankumaran - Taggers
200
+ * Sinclair Bain - Patch King
201
+
202
+ == Patch Contributors
203
+
204
+ * tristanzdunn - Related objects of other classes
205
+ * azabaj - Fixed migrate down
206
+ * Peter Cooper - named_scope fix
207
+ * slainer68 - STI fix
208
+ * harrylove - migration instructions and fix-ups
209
+ * lawrencepit - cached tag work
210
+ * sobrinho - fixed tag_cloud helper
211
+
212
+ Copyright (c) 2007-2009 Michael Bleigh (http://mbleigh.com/) and Intridea Inc. (http://intridea.com/), released under the MIT license
@@ -0,0 +1,59 @@
1
+ begin
2
+ # Rspec 1.3.0
3
+ require 'spec/rake/spectask'
4
+
5
+ desc 'Default: run specs'
6
+ task :default => :spec
7
+ Spec::Rake::SpecTask.new do |t|
8
+ t.spec_files = FileList["spec/**/*_spec.rb"]
9
+ end
10
+
11
+ Spec::Rake::SpecTask.new('rcov') do |t|
12
+ t.spec_files = FileList["spec/**/*_spec.rb"]
13
+ t.rcov = true
14
+ t.rcov_opts = ['--exclude', 'spec']
15
+ end
16
+
17
+ rescue LoadError
18
+ # Rspec 2.0
19
+ require 'rspec/core/rake_task'
20
+
21
+ desc 'Default: run specs'
22
+ task :default => :spec
23
+ Rspec::Core::RakeTask.new do |t|
24
+ t.pattern = "spec/**/*_spec.rb"
25
+ end
26
+
27
+ Rspec::Core::RakeTask.new('rcov') do |t|
28
+ t.pattern = "spec/**/*_spec.rb"
29
+ t.rcov = true
30
+ t.rcov_opts = ['--exclude', 'spec']
31
+ end
32
+
33
+ rescue LoadError
34
+ puts "Rspec not available. Install it with: gem install rspec"
35
+ end
36
+
37
+ namespace 'rails2.3' do
38
+ task :spec do
39
+ gemfile = File.join(File.dirname(__FILE__), 'lib', 'acts_as_taggable_on', 'compatibility', 'Gemfile')
40
+ ENV['BUNDLE_GEMFILE'] = gemfile
41
+ Rake::Task['spec'].invoke
42
+ end
43
+ end
44
+
45
+ begin
46
+ require 'jeweler'
47
+ Jeweler::Tasks.new do |gemspec|
48
+ gemspec.name = "acts-as-taggable-on"
49
+ gemspec.summary = "ActsAsTaggableOn is a tagging plugin for Rails that provides multiple tagging contexts on a single model."
50
+ gemspec.description = "With ActsAsTaggableOn, you could tag a single model on several contexts, such as skills, interests, and awards. It also provides other advanced functionality."
51
+ gemspec.email = "michael@intridea.com"
52
+ gemspec.homepage = "http://github.com/mbleigh/acts-as-taggable-on"
53
+ gemspec.authors = ["Michael Bleigh"]
54
+ gemspec.files = FileList["[A-Z]*", "{generators,lib,spec,rails}/**/*"] - FileList["**/*.log"]
55
+ end
56
+ Jeweler::GemcutterTasks.new
57
+ rescue LoadError
58
+ puts "Jeweler not available. Install it with: gem install jeweler"
59
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,30 @@
1
+ require "active_record"
2
+ require "action_view"
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+
6
+ require "acts_as_taggable_on/compatibility/active_record_backports" if ActiveRecord::VERSION::MAJOR < 3
7
+
8
+ require "acts_as_taggable_on/acts_as_taggable_on"
9
+ require "acts_as_taggable_on/acts_as_taggable_on/core"
10
+ require "acts_as_taggable_on/acts_as_taggable_on/collection"
11
+ require "acts_as_taggable_on/acts_as_taggable_on/cache"
12
+ require "acts_as_taggable_on/acts_as_taggable_on/ownership"
13
+ require "acts_as_taggable_on/acts_as_taggable_on/related"
14
+
15
+ require "acts_as_taggable_on/acts_as_tagger"
16
+ require "acts_as_taggable_on/tag"
17
+ require "acts_as_taggable_on/tag_list"
18
+ require "acts_as_taggable_on/tags_helper"
19
+ require "acts_as_taggable_on/tagging"
20
+
21
+ $LOAD_PATH.shift
22
+
23
+ if defined?(ActiveRecord::Base)
24
+ ActiveRecord::Base.extend ActsAsTaggableOn::Taggable
25
+ ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger
26
+ end
27
+
28
+ if defined?(ActionView::Base)
29
+ ActionView::Base.send :include, TagsHelper
30
+ end
@@ -0,0 +1,41 @@
1
+ module ActsAsTaggableOn
2
+ module Taggable
3
+ def taggable?
4
+ false
5
+ end
6
+
7
+ def acts_as_taggable
8
+ acts_as_taggable_on :tags
9
+ end
10
+
11
+ def acts_as_taggable_on(*tag_types)
12
+ tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
13
+
14
+ if taggable?
15
+ write_inheritable_attribute(:tag_types, (self.tag_types + tag_types).uniq)
16
+ else
17
+ if ::ActiveRecord::VERSION::MAJOR < 3
18
+ include ActsAsTaggableOn::ActiveRecord::Backports
19
+ end
20
+
21
+ write_inheritable_attribute(:tag_types, tag_types)
22
+ class_inheritable_reader(:tag_types)
23
+
24
+ class_eval do
25
+ has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
26
+ has_many :base_tags, :class_name => "Tag", :through => :taggings, :source => :tag
27
+
28
+ def self.taggable?
29
+ true
30
+ end
31
+
32
+ include ActsAsTaggableOn::Taggable::Core
33
+ include ActsAsTaggableOn::Taggable::Collection
34
+ include ActsAsTaggableOn::Taggable::Cache
35
+ include ActsAsTaggableOn::Taggable::Ownership
36
+ include ActsAsTaggableOn::Taggable::Related
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+ module ActsAsTaggableOn::Taggable
2
+ module Cache
3
+ def self.included(base)
4
+ # Skip adding caching capabilities if no cache columns exist
5
+ return unless base.tag_types.any? { |context| base.column_names.include?("cached_#{context.to_s.singularize}_list") }
6
+
7
+ base.send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
8
+ base.extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
9
+
10
+ base.class_eval do
11
+ before_save :save_cached_tag_list
12
+ end
13
+
14
+ base.intialize_acts_as_taggable_on_cache
15
+ end
16
+
17
+ module ClassMethods
18
+ def intialize_acts_as_taggable_on_cache
19
+ tag_types.map(&:to_s).each do |tag_type|
20
+ class_eval %(
21
+ def self.caching_#{tag_type.singularize}_list?
22
+ caching_tag_list_on?("#{tag_type}")
23
+ end
24
+ )
25
+ end
26
+ end
27
+
28
+ def acts_as_taggable_on(*args)
29
+ super(*args)
30
+ intialize_acts_as_taggable_on_cache
31
+ end
32
+
33
+ def caching_tag_list_on?(context)
34
+ column_names.include?("cached_#{context.to_s.singularize}_list")
35
+ end
36
+ end
37
+
38
+ module InstanceMethods
39
+ def tag_list_cache_set_on(context)
40
+ variable_name = "@#{context.to_s.singularize}_list"
41
+ !instance_variable_get(variable_name).nil?
42
+ end
43
+
44
+ def save_cached_tag_list
45
+ tag_types.map(&:to_s).each do |tag_type|
46
+ if self.class.send("caching_#{tag_type.singularize}_list?")
47
+ if tag_list_cache_set_on(tag_type)
48
+ list = tag_list_cache_on(tag_type.singularize).to_a.flatten.compact.join(', ')
49
+ self["cached_#{tag_type.singularize}_list"] = list
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end