summaryrefslogtreecommitdiff
path: root/vendor/plugins
diff options
context:
space:
mode:
authorhukl <hukl@eight.local>2009-02-02 23:25:15 +0100
committerhukl <hukl@eight.local>2009-02-02 23:25:15 +0100
commit1c8bcc58d410db6d7eb5f1629813f08f78f47fa1 (patch)
tree4b80c2b39ee2702e52ece2cbaaf507b0910b1437 /vendor/plugins
parentefbd264d62189ac6bbb80961ddef058240f16435 (diff)
add acts_as_taggable_on_steroids to replaces
custom flagging facilities
Diffstat (limited to 'vendor/plugins')
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG167
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE20
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/README153
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/Rakefile22
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb11
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb26
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/init.rb1
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb214
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb26
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tag_counts_extension.rb3
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb91
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb12
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb13
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb97
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb347
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb3
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml7
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb8
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml24
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb7
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml34
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb2
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb4
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml3
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml149
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml19
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb7
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml7
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb106
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb34
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb13
-rw-r--r--vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb28
32 files changed, 1658 insertions, 0 deletions
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG b/vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG
new file mode 100644
index 0000000..f314bd9
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/CHANGELOG
@@ -0,0 +1,167 @@
1[13 March 08]
2
3* Added helper methods for will_paginate compatibility (Clinton R. Nixon)
4* Fixed :conditions in tag_counts to accept array to sanitize (Clinton R. Nixon)
5
6[07 March 08]
7
8* Added support for regexp delimiter (Matt Aimonetti)
9
10[30 Jan 08]
11
12* Fix Tag.destroy_unused on Rails 2.0.
13
14[23 October 2007]
15
16* Make find_options_for_tag_counts and find_options_for_tagged_with dup their options.
17
18* Apply conditions properly in find_options_for_tag_counts.
19
20* Fix tag_cloud when no tags are present.
21
22[22 October 2007]
23
24* Fix find_tagged_with using :match_all and :include.
25
26* Use inner joins instead of left outer joins.
27
28[15 October 2007]
29
30* Make find_tagged_with correctly apply :conditions
31
32* Add Tag.destroy_unused option.
33
34[11 October 2007]
35
36* Make tag_counts work correctly with STI.
37
38[3 October 2007]
39
40* Improve documentation.
41
42* Fix TagsHelper and test.
43
44[2 October 2007]
45
46* Remove TagList.parse, use TagList.from instead.
47
48* Add :parse option to TagList#new, TagList#add, and TagList#remove.
49
50 tag_list = TagList.new("One, Two", :parse => true) # ["One", "Two"]
51
52 tag_list # ["One", "Two"]
53 tag_list.add("Three, Four", :parse => true) # ["One", "Two", "Three", "Four"]
54
55* Remove TagList#names.
56
57[29 September 2007]
58
59* Add TagsHelper to assist with generating tag clouds and provide a simple example.
60
61[27 September 2007]
62
63* Add #tag_counts method to get tag counts for a specific object's tags.
64
65* BACKWARDS INCOMPATIBILITY: Rename #find_options_for_tagged_with to #find_options_for_find_tagged_with
66
67[17 September 2007]
68
69* Fix clearing of cached tag list when all tags removed.
70
71[12 September 2007]
72
73* Make the TagList class inherit from Array.
74
75* Deprecate obsolete TagList#names.
76
77[6 September 2007]
78
79* Add TagList#include? and TagList#empty?
80
81[26 August 2006]
82
83* Remove deprecated Tag.delimiter. Use TagList.delimiter instead.
84
85[25 August 2007]
86
87* Make tag_counts work with has_many :through
88
89[23 August 2007]
90
91* Make search comparisons case-insensitive across different databases. [Moisés Machado]
92
93* Improve compatiblity with STI. [Moisés Machado]
94
95[25 July 2007]
96
97* Respect custom table names for the Tag and Tagging classes.
98
99* Fix the :exclude option for find_tagged_with
100
101[17 July 2007]
102
103* Make the migration work on edge rails
104
105[8 July 2007]
106
107* find_options_for_tagged_with should not alter its arguments
108
109[1 July 2007]
110
111* Fix incorrect tagging when the case of the tag list is changed.
112
113* Fix deprecated Tag.delimiter accessor.
114
115[23 June 2007]
116
117* Add validation to Tag model.
118
119* find_options_for_tagged_with should always return a hash.
120
121* find_tagged_with passing in no tags should return an empty array.
122
123* Improve compatibility with PostgreSQL.
124
125[21 June 2007]
126
127* Remove extra .rb from generated migration file name.
128
129[15 June 2007]
130
131* Introduce TagList class.
132
133* Various cleanups and improvements.
134
135* Use TagList.delimiter now, not Tag.delimiter. Tag.delimiter will be removed at some stage.
136
137[11 June 2007]
138
139* Restructure the creation of the options for find_tagged_with [Thijs Cadier]
140
141* Add an example migration with a generator.
142
143* Add caching.
144
145* Fix compatibility with Ruby < 1.8.6
146
147[23 April 2007]
148
149* Make tag_list to respect Tag.delimiter
150
151[31 March 2007]
152
153* Add Tag.delimiter accessor to change how tags are parsed.
154
155* Fix :include => :tags when used with find_tagged_with
156
157[7 March 2007]
158
159* Fix tag_counts for SQLServer [Brad Young]
160
161[21 Feb 2007]
162
163* Use scoping instead of TagCountsExtension [Michael Schuerig]
164
165[7 Jan 2007]
166
167* Add :match_all to find_tagged_with [Michael Sheakoski]
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE b/vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE
new file mode 100644
index 0000000..602bda2
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/MIT-LICENSE
@@ -0,0 +1,20 @@
1Copyright (c) 2006 Jonathan Viney
2
3Permission is hereby granted, free of charge, to any person obtaining
4a copy of this software and associated documentation files (the
5"Software"), to deal in the Software without restriction, including
6without limitation the rights to use, copy, modify, merge, publish,
7distribute, sublicense, and/or sell copies of the Software, and to
8permit persons to whom the Software is furnished to do so, subject to
9the following conditions:
10
11The above copyright notice and this permission notice shall be
12included in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/README b/vendor/plugins/acts_as_taggable_on_steroids/README
new file mode 100644
index 0000000..73b88fe
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/README
@@ -0,0 +1,153 @@
1= acts_as_taggable_on_steroids
2
3NOT THE OFFICIAL REPO, I just imported this project to get few bug fixed when I was using it for a project in early 2008.
4
5USE AT YOUR OWN RISK
6
7
8If you find this plugin useful, please consider a donation to show your support!
9
10 http://www.paypal.com/cgi-bin/webscr?cmd=_send-money
11
12 Email address: jonathan.viney@gmail.com
13
14== Instructions
15
16This plugin is based on acts_as_taggable by DHH but includes extras
17such as tests, smarter tag assignment, and tag cloud calculations.
18
19== Installation
20
21 ruby script/plugin install http://svn.viney.net.nz/things/rails/plugins/acts_as_taggable_on_steroids
22
23== Usage
24
25=== Prepare database
26
27Generate and apply the migration:
28
29 ruby script/generate acts_as_taggable_migration
30 rake db:migrate
31
32=== Basic tagging
33
34Let's suppose users have many posts and we want those posts to have tags.
35The first step is to add +acts_as_taggable+ to the Post class:
36
37 class Post < ActiveRecord::Base
38 acts_as_taggable
39
40 belongs_to :user
41 end
42
43We can now use the tagging methods provided by acts_as_taggable, <tt>#tag_list</tt> and <tt>#tag_list=</tt>. Both these
44methods work like regular attribute accessors.
45
46 p = Post.find(:first)
47 p.tag_list # []
48 p.tag_list = "Funny, Silly"
49 p.save
50 p.tag_list # ["Funny", "Silly"]
51
52You can also add or remove arrays of tags.
53
54 p.tag_list.add("Great", "Awful")
55 p.tag_list.remove("Funny")
56
57=== Finding tagged objects
58
59To retrieve objects tagged with a certain tag, use find_tagged_with.
60
61 Post.find_tagged_with('Funny, Silly')
62
63By default, find_tagged_with will find objects that have any of the given tags. To
64find only objects that are tagged with all the given tags, use match_all.
65
66 Post.find_tagged_with('Funny, Silly', :match_all => true)
67
68See <tt>ActiveRecord::Acts::Taggable::InstanceMethods</tt> for more methods and options.
69
70=== Tag cloud calculations
71
72To construct tag clouds, the frequency of each tag needs to be calculated.
73Because we specified +acts_as_taggable+ on the <tt>Post</tt> class, we can
74get a calculation of all the tag counts by using <tt>Post.tag_counts</tt>. But what if we wanted a tag count for
75an single user's posts? To achieve this we call tag_counts on the association:
76
77 User.find(:first).posts.tag_counts
78
79A helper is included to assist with generating tag clouds. Include it in your helper file:
80
81 module ApplicationHelper
82 include TagsHelper
83 end
84
85Here is an example that generates a tag cloud.
86
87Controller:
88
89 class PostController < ApplicationController
90 def tag_cloud
91 @tags = Post.tag_counts
92 end
93 end
94
95View:
96 <% tag_cloud @tags, %w(css1 css2 css3 css4) do |tag, css_class| %>
97 <%= link_to tag.name, { :action => :tag, :id => tag.name }, :class => css_class %>
98 <% end %>
99
100CSS:
101
102 .css1 { font-size: 1.0em; }
103 .css2 { font-size: 1.2em; }
104 .css3 { font-size: 1.4em; }
105 .css4 { font-size: 1.6em; }
106
107=== Caching
108
109It is useful to cache the list of tags to reduce the number of queries executed. To do this,
110add a column named <tt>cached_tag_list</tt> to the model which is being tagged. The column should be long enough to hold
111the full tag list and must have a default value of null, not an empty string.
112
113 class CachePostTagList < ActiveRecord::Migration
114 def self.up
115 add_column :posts, :cached_tag_list, :string
116 end
117 end
118
119 class Post < ActiveRecord::Base
120 acts_as_taggable
121
122 # The caching column defaults to cached_tag_list, but can be changed:
123 #
124 # set_cached_tag_list_column_name "my_caching_column_name"
125 end
126
127The details of the caching are handled for you. Just continue to use the tag_list accessor as you normally would.
128Note that the cached tag list will not be updated if you directly create Tagging objects or manually append to the
129<tt>tags</tt> or <tt>taggings</tt> associations. To update the cached tag list you should call <tt>save_cached_tag_list</tt> manually.
130
131=== Delimiter
132
133If you want to change the delimiter used to parse and present tags, set TagList.delimiter.
134For example, to use spaces instead of commas, add the following to config/environment.rb:
135
136 TagList.delimiter = " "
137
138You can also use a regexp as delimiter:
139
140 TagList.delimiter = /,|;/
141
142The above code would parse the string and use ',' and ';' as delimiters.
143
144=== Unused tags
145
146Set Tag.destroy_unused to remove tags when they are no longer being
147used to tag any objects. Defaults to false.
148
149 Tag.destroy_unused = true
150
151=== Other
152
153Problems, comments, and suggestions all welcome. jonathan.viney@gmail.com
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/Rakefile b/vendor/plugins/acts_as_taggable_on_steroids/Rakefile
new file mode 100644
index 0000000..d2c0003
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/Rakefile
@@ -0,0 +1,22 @@
1require 'rake'
2require 'rake/testtask'
3require 'rake/rdoctask'
4
5desc 'Default: run unit tests.'
6task :default => :test
7
8desc 'Test the acts_as_taggable_on_steroids plugin.'
9Rake::TestTask.new(:test) do |t|
10 t.libs << 'lib'
11 t.pattern = 'test/**/*_test.rb'
12 t.verbose = true
13end
14
15desc 'Generate documentation for the acts_as_taggable_on_steroids plugin.'
16Rake::RDocTask.new(:rdoc) do |rdoc|
17 rdoc.rdoc_dir = 'rdoc'
18 rdoc.title = 'Acts As Taggable On Steroids'
19 rdoc.options << '--line-numbers' << '--inline-source'
20 rdoc.rdoc_files.include('README')
21 rdoc.rdoc_files.include('lib/**/*.rb')
22end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb b/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb
new file mode 100644
index 0000000..be9b39c
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/acts_as_taggable_migration_generator.rb
@@ -0,0 +1,11 @@
1class ActsAsTaggableMigrationGenerator < Rails::Generator::Base
2 def manifest
3 record do |m|
4 m.migration_template 'migration.rb', 'db/migrate'
5 end
6 end
7
8 def file_name
9 "acts_as_taggable_migration"
10 end
11end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb b/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb
new file mode 100644
index 0000000..ea0c2cc
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/generators/acts_as_taggable_migration/templates/migration.rb
@@ -0,0 +1,26 @@
1class ActsAsTaggableMigration < ActiveRecord::Migration
2 def self.up
3 create_table :tags do |t|
4 t.column :name, :string
5 end
6
7 create_table :taggings do |t|
8 t.column :tag_id, :integer
9 t.column :taggable_id, :integer
10
11 # You should make sure that the column created is
12 # long enough to store the required class names.
13 t.column :taggable_type, :string
14
15 t.column :created_at, :datetime
16 end
17
18 add_index :taggings, :tag_id
19 add_index :taggings, [:taggable_id, :taggable_type]
20 end
21
22 def self.down
23 drop_table :taggings
24 drop_table :tags
25 end
26end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/init.rb b/vendor/plugins/acts_as_taggable_on_steroids/init.rb
new file mode 100644
index 0000000..64505b9
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/init.rb
@@ -0,0 +1 @@
require File.dirname(__FILE__) + '/lib/acts_as_taggable'
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb
new file mode 100644
index 0000000..d537889
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/lib/acts_as_taggable.rb
@@ -0,0 +1,214 @@
1module ActiveRecord #:nodoc:
2 module Acts #:nodoc:
3 module Taggable #:nodoc:
4 def self.included(base)
5 base.extend(ClassMethods)
6 end
7
8 module ClassMethods
9 def acts_as_taggable
10 has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
11 has_many :tags, :through => :taggings
12
13 before_save :save_cached_tag_list
14 after_save :save_tags
15
16 include ActiveRecord::Acts::Taggable::InstanceMethods
17 extend ActiveRecord::Acts::Taggable::SingletonMethods
18
19 alias_method_chain :reload, :tag_list
20 end
21
22 def cached_tag_list_column_name
23 "cached_tag_list"
24 end
25
26 def set_cached_tag_list_column_name(value = nil, &block)
27 define_attr_method :cached_tag_list_column_name, value, &block
28 end
29 end
30
31 module SingletonMethods
32 # Pass either a tag string, or an array of strings or tags
33 #
34 # Options:
35 # :exclude - Find models that are not tagged with the given tags
36 # :match_all - Find models that match all of the given tags, not just one
37 # :conditions - A piece of SQL conditions to add to the query
38 def find_tagged_with(*args)
39 options = find_options_for_find_tagged_with(*args)
40 options.blank? ? [] : find(:all, options)
41 end
42
43 # will_paginate's method_missing function wants to hit
44 # find_all_tagged_with if you call paginate_tagged_with, which is
45 # obviously suboptimal
46 def find_all_tagged_with(*args)
47 find_tagged_with(*args)
48 end
49
50 def find_options_for_find_tagged_with(tags, options = {})
51 tags = tags.is_a?(Array) ? TagList.new(tags.map(&:to_s)) : TagList.from(tags)
52 options = options.dup
53
54 return {} if tags.empty?
55
56 conditions = []
57 conditions << sanitize_sql(options.delete(:conditions)) if options[:conditions]
58
59 taggings_alias, tags_alias = "#{table_name}_taggings", "#{table_name}_tags"
60
61 if options.delete(:exclude)
62 conditions << <<-END
63 #{table_name}.id NOT IN
64 (SELECT #{Tagging.table_name}.taggable_id FROM #{Tagging.table_name}
65 INNER JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id
66 WHERE #{tags_condition(tags)} AND #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})
67 END
68 else
69 if options.delete(:match_all)
70 conditions << <<-END
71 (SELECT COUNT(*) FROM #{Tagging.table_name}
72 INNER JOIN #{Tag.table_name} ON #{Tagging.table_name}.tag_id = #{Tag.table_name}.id
73 WHERE #{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)} AND
74 taggable_id = #{table_name}.id AND
75 #{tags_condition(tags)}) = #{tags.size}
76 END
77 else
78 conditions << tags_condition(tags, tags_alias)
79 end
80 end
81
82 { :select => "DISTINCT #{table_name}.*",
83 :joins => "INNER JOIN #{Tagging.table_name} #{taggings_alias} ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key} AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)} " +
84 "INNER JOIN #{Tag.table_name} #{tags_alias} ON #{tags_alias}.id = #{taggings_alias}.tag_id",
85 :conditions => conditions.join(" AND ")
86 }.reverse_merge!(options)
87 end
88
89 # Calculate the tag counts for all tags.
90 #
91 # Options:
92 # :start_at - Restrict the tags to those created after a certain time
93 # :end_at - Restrict the tags to those created before a certain time
94 # :conditions - A piece of SQL conditions to add to the query
95 # :limit - The maximum number of tags to return
96 # :order - A piece of SQL to order by. Eg 'tags.count desc' or 'taggings.created_at desc'
97 # :at_least - Exclude tags with a frequency less than the given value
98 # :at_most - Exclude tags with a frequency greater than the given value
99 def tag_counts(options = {})
100 Tag.find(:all, find_options_for_tag_counts(options))
101 end
102
103 # Find how many objects are tagged with a certain tag.
104 def count_by_tag(tag_name)
105 counts = tag_counts(:conditions => "tags.name = #{quote_value(tag_name)}")
106 counts[0].respond_to?(:count) ? counts[0].count : 0
107 end
108
109 def find_options_for_tag_counts(options = {})
110 options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit
111 options = options.dup
112
113 scope = scope(:find)
114 start_at = sanitize_sql(["#{Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
115 end_at = sanitize_sql(["#{Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
116
117 conditions = [
118 "#{Tagging.table_name}.taggable_type = #{quote_value(base_class.name)}",
119 sanitize_sql(options.delete(:conditions)),
120 scope && scope[:conditions],
121 start_at,
122 end_at
123 ]
124
125 conditions << type_condition unless descends_from_active_record?
126 conditions.compact!
127 conditions = conditions.join(' AND ')
128
129 joins = ["INNER JOIN #{Tagging.table_name} ON #{Tag.table_name}.id = #{Tagging.table_name}.tag_id"]
130 joins << "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{Tagging.table_name}.taggable_id"
131 joins << scope[:joins] if scope && scope[:joins]
132
133 at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
134 at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
135 having = [at_least, at_most].compact.join(' AND ')
136 group_by = "#{Tag.table_name}.id, #{Tag.table_name}.name HAVING COUNT(*) > 0"
137 group_by << " AND #{having}" unless having.blank?
138
139 { :select => "#{Tag.table_name}.id, #{Tag.table_name}.name, COUNT(*) AS count",
140 :joins => joins.join(" "),
141 :conditions => conditions,
142 :group => group_by
143 }.reverse_merge!(options)
144 end
145
146 def caching_tag_list?
147 column_names.include?(cached_tag_list_column_name)
148 end
149
150 private
151 def tags_condition(tags, table_name = Tag.table_name)
152 condition = tags.map { |t| sanitize_sql(["#{table_name}.name LIKE ?", t]) }.join(" OR ")
153 "(" + condition + ")"
154 end
155 end
156
157 module InstanceMethods
158 def tag_list
159 return @tag_list if @tag_list
160
161 if self.class.caching_tag_list? and !(cached_value = send(self.class.cached_tag_list_column_name)).nil?
162 @tag_list = TagList.from(cached_value)
163 else
164 @tag_list = TagList.new(*tags.map(&:name))
165 end
166 end
167
168 def tag_list=(value)
169 @tag_list = TagList.from(value)
170 end
171
172 def save_cached_tag_list
173 if self.class.caching_tag_list?
174 self[self.class.cached_tag_list_column_name] = tag_list.to_s
175 end
176 end
177
178 def save_tags
179 return unless @tag_list
180
181 new_tag_names = @tag_list - tags.map(&:name)
182 old_tags = tags.reject { |tag| @tag_list.include?(tag.name) }
183
184 self.class.transaction do
185 if old_tags.any?
186 taggings.find(:all, :conditions => ["tag_id IN (?)", old_tags.map(&:id)]).each(&:destroy)
187 taggings.reset
188 end
189
190 new_tag_names.each do |new_tag_name|
191 tags << Tag.find_or_create_with_like_by_name(new_tag_name)
192 end
193 end
194
195 true
196 end
197
198 # Calculate the tag counts for the tags used by this model.
199 #
200 # The possible options are the same as the tag_counts class method, excluding :conditions.
201 def tag_counts(options = {})
202 self.class.tag_counts({ :conditions => self.class.send(:tags_condition, tag_list) }.reverse_merge!(options))
203 end
204
205 def reload_with_tag_list(*args) #:nodoc:
206 @tag_list = nil
207 reload_without_tag_list(*args)
208 end
209 end
210 end
211 end
212end
213
214ActiveRecord::Base.send(:include, ActiveRecord::Acts::Taggable)
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb
new file mode 100644
index 0000000..91859d5
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag.rb
@@ -0,0 +1,26 @@
1class Tag < ActiveRecord::Base
2 has_many :taggings
3
4 validates_presence_of :name
5 validates_uniqueness_of :name
6
7 cattr_accessor :destroy_unused
8 self.destroy_unused = false
9
10 # LIKE is used for cross-database case-insensitivity
11 def self.find_or_create_with_like_by_name(name)
12 find(:first, :conditions => ["name LIKE ?", name]) || create(:name => name)
13 end
14
15 def ==(object)
16 super || (object.is_a?(Tag) && name == object.name)
17 end
18
19 def to_s
20 name
21 end
22
23 def count
24 read_attribute(:count).to_i
25 end
26end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_counts_extension.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_counts_extension.rb
new file mode 100644
index 0000000..a1d13b8
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_counts_extension.rb
@@ -0,0 +1,3 @@
1# Deprecated
2module TagCountsExtension #:nodoc:
3end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb
new file mode 100644
index 0000000..01325b8
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/lib/tag_list.rb
@@ -0,0 +1,91 @@
1class TagList < Array
2 cattr_accessor :delimiter
3 self.delimiter = ','
4
5 def initialize(*args)
6 add(*args)
7 end
8
9 # Add tags to the tag_list. Duplicate or blank tags will be ignored.
10 #
11 # tag_list.add("Fun", "Happy")
12 #
13 # Use the <tt>:parse</tt> option to add an unparsed tag string.
14 #
15 # tag_list.add("Fun, Happy", :parse => true)
16 def add(*names)
17 extract_and_apply_options!(names)
18 concat(names)
19 clean!
20 self
21 end
22
23 # Remove specific tags from the tag_list.
24 #
25 # tag_list.remove("Sad", "Lonely")
26 #
27 # Like #add, the <tt>:parse</tt> option can be used to remove multiple tags in a string.
28 #
29 # tag_list.remove("Sad, Lonely", :parse => true)
30 def remove(*names)
31 extract_and_apply_options!(names)
32 delete_if { |name| names.include?(name) }
33 self
34 end
35
36 # Transform the tag_list into a tag string suitable for edting in a form.
37 # The tags are joined with <tt>TagList.delimiter</tt> and quoted if necessary.
38 #
39 # tag_list = TagList.new("Round", "Square,Cube")
40 # tag_list.to_s # 'Round, "Square,Cube"'
41 def to_s
42 clean!
43
44 list = map do |name|
45 if delimiter.is_a?(Regexp)
46 name.match(delimiter) ? "\"#{name}\"" : name
47 else
48 name.include?(delimiter) ? "\"#{name}\"" : name
49 end
50 end
51
52 list.join( delimiter.is_a?(Regexp) ? "#{delimiter.source.match(/[^\\\[\]\*\?\{\}\.\|]/)[0]} " : (delimiter.ends_with?(" ") ? delimiter : "#{delimiter} ") )
53 end
54
55 private
56 # Remove whitespace, duplicates, and blanks.
57 def clean!
58 reject!(&:blank?)
59 map!(&:strip)
60 uniq!
61 end
62
63 def extract_and_apply_options!(args)
64 options = args.last.is_a?(Hash) ? args.pop : {}
65 options.assert_valid_keys :parse
66
67 if options[:parse]
68 args.map! { |a| self.class.from(a) }
69 end
70
71 args.flatten!
72 end
73
74 class << self
75 # Returns a new TagList using the given tag string.
76 #
77 # tag_list = TagList.from("One , Two, Three")
78 # tag_list # ["One", "Two", "Three"]
79 def from(string)
80 returning new do |tag_list|
81 string = string.to_s.gsub('.', '').dup
82
83 # Parse the quoted tags
84 string.gsub!(/"(.*?)"\s*#{delimiter}?\s*/) { tag_list << $1; "" }
85 string.gsub!(/'(.*?)'\s*#{delimiter}?\s*/) { tag_list << $1; "" }
86
87 tag_list.add(string.split(delimiter))
88 end
89 end
90 end
91end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb
new file mode 100644
index 0000000..87bc44d
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/lib/tagging.rb
@@ -0,0 +1,12 @@
1class Tagging < ActiveRecord::Base #:nodoc:
2 belongs_to :tag
3 belongs_to :taggable, :polymorphic => true
4
5 def after_destroy
6 if Tag.destroy_unused
7 if tag.taggings.count.zero?
8 tag.destroy
9 end
10 end
11 end
12end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb b/vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb
new file mode 100644
index 0000000..d5644b7
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/lib/tags_helper.rb
@@ -0,0 +1,13 @@
1module TagsHelper
2 # See the README for an example using tag_cloud.
3 def tag_cloud(tags, classes)
4 return if tags.empty?
5
6 max_count = tags.sort_by(&:count).last.count.to_f
7
8 tags.each do |tag|
9 index = ((tag.count / max_count) * (classes.size - 1)).round
10 yield tag, classes[index]
11 end
12 end
13end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb
new file mode 100644
index 0000000..42e277a
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/abstract_unit.rb
@@ -0,0 +1,97 @@
1require 'test/unit'
2
3begin
4 require File.dirname(__FILE__) + '/../../../../config/environment'
5rescue LoadError
6 require 'rubygems'
7 gem 'activerecord'
8 gem 'actionpack'
9 require 'active_record'
10 require 'action_controller'
11end
12
13# Search for fixtures first
14fixture_path = File.dirname(__FILE__) + '/fixtures/'
15Dependencies.load_paths.insert(0, fixture_path)
16
17require 'active_record/fixtures'
18
19require File.dirname(__FILE__) + '/../lib/acts_as_taggable'
20require_dependency File.dirname(__FILE__) + '/../lib/tag_list'
21require_dependency File.dirname(__FILE__) + '/../lib/tags_helper'
22
23ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + '/debug.log')
24ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
25ActiveRecord::Base.establish_connection(ENV['DB'] || 'mysql')
26
27load(File.dirname(__FILE__) + '/schema.rb')
28
29Test::Unit::TestCase.fixture_path = fixture_path
30
31class Test::Unit::TestCase #:nodoc:
32 self.use_transactional_fixtures = true
33 self.use_instantiated_fixtures = false
34
35 def assert_equivalent(expected, actual, message = nil)
36 if expected.first.is_a?(ActiveRecord::Base)
37 assert_equal expected.sort_by(&:id), actual.sort_by(&:id), message
38 else
39 assert_equal expected.sort, actual.sort, message
40 end
41 end
42
43 def assert_tag_counts(tags, expected_values)
44 # Map the tag fixture names to real tag names
45 expected_values = expected_values.inject({}) do |hash, (tag, count)|
46 hash[tags(tag).name] = count
47 hash
48 end
49
50 tags.each do |tag|
51 value = expected_values.delete(tag.name)
52
53 assert_not_nil value, "Expected count for #{tag.name} was not provided"
54 assert_equal value, tag.count, "Expected value of #{value} for #{tag.name}, but was #{tag.count}"
55 end
56
57 unless expected_values.empty?
58 assert false, "The following tag counts were not present: #{expected_values.inspect}"
59 end
60 end
61
62 def assert_queries(num = 1)
63 $query_count = 0
64 yield
65 ensure
66 assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
67 end
68
69 def assert_no_queries(&block)
70 assert_queries(0, &block)
71 end
72
73 # From Rails trunk
74 def assert_difference(expressions, difference = 1, message = nil, &block)
75 expression_evaluations = [expressions].flatten.collect{|expression| lambda { eval(expression, block.binding) } }
76
77 original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
78 yield
79 expression_evaluations.each_with_index do |expression, i|
80 assert_equal original_values[i] + difference, expression.call, message
81 end
82 end
83
84 def assert_no_difference(expressions, message = nil, &block)
85 assert_difference expressions, 0, message, &block
86 end
87end
88
89ActiveRecord::Base.connection.class.class_eval do
90 def execute_with_counting(sql, name = nil, &block)
91 $query_count ||= 0
92 $query_count += 1
93 execute_without_counting(sql, name, &block)
94 end
95
96 alias_method_chain :execute, :counting
97end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb
new file mode 100644
index 0000000..90ea3c4
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/acts_as_taggable_test.rb
@@ -0,0 +1,347 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class ActsAsTaggableOnSteroidsTest < Test::Unit::TestCase
4 fixtures :tags, :taggings, :posts, :users, :photos, :subscriptions, :magazines
5
6 def test_find_tagged_with
7 assert_equivalent [posts(:jonathan_sky), posts(:sam_flowers)], Post.find_tagged_with('"Very good"')
8 assert_equal Post.find_tagged_with('"Very good"'), Post.find_tagged_with(['Very good'])
9 assert_equal Post.find_tagged_with('"Very good"'), Post.find_tagged_with([tags(:good)])
10
11 assert_equivalent [photos(:jonathan_dog), photos(:sam_flower), photos(:sam_sky)], Photo.find_tagged_with('Nature')
12 assert_equal Photo.find_tagged_with('Nature'), Photo.find_tagged_with(['Nature'])
13 assert_equal Photo.find_tagged_with('Nature'), Photo.find_tagged_with([tags(:nature)])
14
15 assert_equivalent [photos(:jonathan_bad_cat), photos(:jonathan_dog), photos(:jonathan_questioning_dog)], Photo.find_tagged_with('"Crazy animal" Bad')
16 assert_equal Photo.find_tagged_with('"Crazy animal" Bad'), Photo.find_tagged_with(['Crazy animal', 'Bad'])
17 assert_equal Photo.find_tagged_with('"Crazy animal" Bad'), Photo.find_tagged_with([tags(:animal), tags(:bad)])
18 end
19
20 def test_find_tagged_with_nothing
21 assert_equal [], Post.find_tagged_with("")
22 assert_equal [], Post.find_tagged_with([])
23 end
24
25 def test_find_tagged_with_nonexistant_tags
26 assert_equal [], Post.find_tagged_with('ABCDEFG')
27 assert_equal [], Photo.find_tagged_with(['HIJKLM'])
28 assert_equal [], Photo.find_tagged_with([Tag.new(:name => 'unsaved tag')])
29 end
30
31 def test_find_tagged_with_match_all
32 assert_equivalent [photos(:jonathan_dog)], Photo.find_tagged_with('Crazy animal, "Nature"', :match_all => true)
33 end
34
35 def test_find_tagged_with_match_all_and_include
36 assert_equivalent [posts(:jonathan_sky), posts(:sam_flowers)], Post.find_tagged_with(['Very good', 'Nature'], :match_all => true, :include => :tags)
37 end
38
39 def test_find_tagged_with_conditions
40 assert_equal [], Post.find_tagged_with('"Very good", Nature', :conditions => '1=0')
41 end
42
43 def test_find_tagged_with_duplicates_options_hash
44 options = { :conditions => '1=1' }.freeze
45 assert_nothing_raised { Post.find_tagged_with("Nature", options) }
46 end
47
48 def test_find_tagged_with_exclusions
49 assert_equivalent [photos(:jonathan_questioning_dog), photos(:jonathan_bad_cat)], Photo.find_tagged_with("Nature", :exclude => true)
50 assert_equivalent [posts(:jonathan_grass), posts(:jonathan_rain), posts(:jonathan_cloudy), posts(:jonathan_still_cloudy)], Post.find_tagged_with("'Very good', Bad", :exclude => true)
51 end
52
53 def test_find_options_for_find_tagged_with_no_tags_returns_empty_hash
54 assert_equal Hash.new, Post.find_options_for_find_tagged_with("")
55 assert_equal Hash.new, Post.find_options_for_find_tagged_with([nil])
56 end
57
58 def test_find_options_for_find_tagged_with_leaves_arguments_unchanged
59 original_tags = photos(:jonathan_questioning_dog).tags.dup
60 Photo.find_options_for_find_tagged_with(photos(:jonathan_questioning_dog).tags)
61 assert_equal original_tags, photos(:jonathan_questioning_dog).tags
62 end
63
64 def test_find_options_for_find_tagged_with_respects_custom_table_name
65 Tagging.table_name = "categorisations"
66 Tag.table_name = "categories"
67
68 options = Photo.find_options_for_find_tagged_with("Hello")
69
70 assert_no_match(/ taggings /, options[:joins])
71 assert_no_match(/ tags /, options[:joins])
72
73 assert_match(/ categorisations /, options[:joins])
74 assert_match(/ categories /, options[:joins])
75 ensure
76 Tagging.table_name = "taggings"
77 Tag.table_name = "tags"
78 end
79
80 def test_include_tags_on_find_tagged_with
81 assert_nothing_raised do
82 Photo.find_tagged_with('Nature', :include => :tags)
83 Photo.find_tagged_with("Nature", :include => { :taggings => :tag })
84 end
85 end
86
87 def test_basic_tag_counts_on_class
88 assert_tag_counts Post.tag_counts, :good => 2, :nature => 7, :question => 1, :bad => 1
89 assert_tag_counts Photo.tag_counts, :good => 1, :nature => 3, :question => 1, :bad => 1, :animal => 3
90 end
91
92 def test_tag_counts_on_class_with_date_conditions
93 assert_tag_counts Post.tag_counts(:start_at => Date.new(2006, 8, 4)), :good => 1, :nature => 5, :question => 1, :bad => 1
94 assert_tag_counts Post.tag_counts(:end_at => Date.new(2006, 8, 6)), :good => 1, :nature => 4, :question => 1
95 assert_tag_counts Post.tag_counts(:start_at => Date.new(2006, 8, 5), :end_at => Date.new(2006, 8, 10)), :good => 1, :nature => 4, :bad => 1
96
97 assert_tag_counts Photo.tag_counts(:start_at => Date.new(2006, 8, 12), :end_at => Date.new(2006, 8, 19)), :good => 1, :nature => 2, :bad => 1, :question => 1, :animal => 3
98 end
99
100 def test_tag_counts_on_class_with_frequencies
101 assert_tag_counts Photo.tag_counts(:at_least => 2), :nature => 3, :animal => 3
102 assert_tag_counts Photo.tag_counts(:at_most => 2), :good => 1, :question => 1, :bad => 1
103 end
104
105 def test_tag_counts_on_class_with_frequencies_and_conditions
106 assert_tag_counts Photo.tag_counts(:at_least => 2, :conditions => '1=1'), :nature => 3, :animal => 3
107 end
108
109 def test_tag_counts_duplicates_options_hash
110 options = { :at_least => 2, :conditions => '1=1' }.freeze
111 assert_nothing_raised { Photo.tag_counts(options) }
112 end
113
114 def test_tag_counts_with_limit
115 assert_equal 2, Photo.tag_counts(:limit => 2).size
116 assert_equal 1, Post.tag_counts(:at_least => 4, :limit => 2).size
117 end
118
119 def test_tag_counts_with_limit_and_order
120 assert_equal [tags(:nature), tags(:good)], Post.tag_counts(:order => 'count desc', :limit => 2)
121 end
122
123 def test_tag_counts_on_association
124 assert_tag_counts users(:jonathan).posts.tag_counts, :good => 1, :nature => 5, :question => 1
125 assert_tag_counts users(:sam).posts.tag_counts, :good => 1, :nature => 2, :bad => 1
126
127 assert_tag_counts users(:jonathan).photos.tag_counts, :animal => 3, :nature => 1, :question => 1, :bad => 1
128 assert_tag_counts users(:sam).photos.tag_counts, :nature => 2, :good => 1
129 end
130
131 def test_tag_counts_on_association_with_options
132 assert_equal [], users(:jonathan).posts.tag_counts(:conditions => '1=0')
133 assert_tag_counts users(:jonathan).posts.tag_counts(:at_most => 2), :good => 1, :question => 1
134 end
135
136 def test_tag_counts_on_has_many_through
137 assert_tag_counts users(:jonathan).magazines.tag_counts, :good => 1
138 end
139
140 def test_tag_counts_respects_custom_table_names
141 Tagging.table_name = "categorisations"
142 Tag.table_name = "categories"
143
144 options = Photo.find_options_for_tag_counts(:start_at => 2.weeks.ago, :end_at => Date.today)
145 sql = options.values.join(' ')
146
147 assert_no_match /taggings/, sql
148 assert_no_match /tags/, sql
149
150 assert_match /categorisations/, sql
151 assert_match /categories/, sql
152 ensure
153 Tagging.table_name = "taggings"
154 Tag.table_name = "tags"
155 end
156
157 def test_tag_list_reader
158 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
159 assert_equivalent ["Bad", "Crazy animal"], photos(:jonathan_bad_cat).tag_list
160 end
161
162 def test_reassign_tag_list
163 assert_equivalent ["Nature", "Question"], posts(:jonathan_rain).tag_list
164 posts(:jonathan_rain).taggings.reload
165
166 # Only an update of the posts table should be executed
167 assert_queries 1 do
168 posts(:jonathan_rain).update_attributes!(:tag_list => posts(:jonathan_rain).tag_list.to_s)
169 end
170
171 assert_equivalent ["Nature", "Question"], posts(:jonathan_rain).tag_list
172 end
173
174 def test_new_tags
175 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
176 posts(:jonathan_sky).update_attributes!(:tag_list => "#{posts(:jonathan_sky).tag_list}, One, Two")
177 assert_equivalent ["Very good", "Nature", "One", "Two"], posts(:jonathan_sky).tag_list
178 end
179
180 def test_remove_tag
181 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
182 posts(:jonathan_sky).update_attributes!(:tag_list => "Nature")
183 assert_equivalent ["Nature"], posts(:jonathan_sky).tag_list
184 end
185
186 def test_change_case_of_tags
187 original_tag_names = photos(:jonathan_questioning_dog).tag_list
188 photos(:jonathan_questioning_dog).update_attributes!(:tag_list => photos(:jonathan_questioning_dog).tag_list.to_s.upcase)
189
190 # The new tag list is not uppercase becuase the AR finders are not case-sensitive
191 # and find the old tags when re-tagging with the uppercase tags.
192 assert_equivalent original_tag_names, photos(:jonathan_questioning_dog).reload.tag_list
193 end
194
195 def test_remove_and_add_tag
196 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
197 posts(:jonathan_sky).update_attributes!(:tag_list => "Nature, Beautiful")
198 assert_equivalent ["Nature", "Beautiful"], posts(:jonathan_sky).tag_list
199 end
200
201 def test_tags_not_saved_if_validation_fails
202 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
203 assert !posts(:jonathan_sky).update_attributes(:tag_list => "One, Two", :text => "")
204 assert_equivalent ["Very good", "Nature"], Post.find(posts(:jonathan_sky).id).tag_list
205 end
206
207 def test_tag_list_accessors_on_new_record
208 p = Post.new(:text => 'Test')
209
210 assert p.tag_list.blank?
211 p.tag_list = "One, Two"
212 assert_equal "One, Two", p.tag_list.to_s
213 end
214
215 def test_clear_tag_list_with_nil
216 p = photos(:jonathan_questioning_dog)
217
218 assert !p.tag_list.blank?
219 assert p.update_attributes(:tag_list => nil)
220 assert p.tag_list.blank?
221
222 assert p.reload.tag_list.blank?
223 end
224
225 def test_clear_tag_list_with_string
226 p = photos(:jonathan_questioning_dog)
227
228 assert !p.tag_list.blank?
229 assert p.update_attributes(:tag_list => ' ')
230 assert p.tag_list.blank?
231
232 assert p.reload.tag_list.blank?
233 end
234
235 def test_tag_list_reset_on_reload
236 p = photos(:jonathan_questioning_dog)
237 assert !p.tag_list.blank?
238 p.tag_list = nil
239 assert p.tag_list.blank?
240 assert !p.reload.tag_list.blank?
241 end
242
243 def test_instance_tag_counts
244 assert_tag_counts posts(:jonathan_sky).tag_counts, :good => 2, :nature => 7
245 end
246
247 def test_tag_list_populated_when_cache_nil
248 assert_nil posts(:jonathan_sky).cached_tag_list
249 posts(:jonathan_sky).save!
250 assert_equal posts(:jonathan_sky).tag_list.to_s, posts(:jonathan_sky).cached_tag_list
251 end
252
253 def test_cached_tag_list_used
254 posts(:jonathan_sky).save!
255 posts(:jonathan_sky).reload
256
257 assert_no_queries do
258 assert_equivalent ["Very good", "Nature"], posts(:jonathan_sky).tag_list
259 end
260 end
261
262 def test_cached_tag_list_not_used
263 # Load fixture and column information
264 posts(:jonathan_sky).taggings(:reload)
265
266 assert_queries 1 do
267 # Tags association will be loaded
268 posts(:jonathan_sky).tag_list
269 end
270 end
271
272 def test_cached_tag_list_updated
273 assert_nil posts(:jonathan_sky).cached_tag_list
274 posts(:jonathan_sky).save!
275 assert_equivalent ["Very good", "Nature"], TagList.from(posts(:jonathan_sky).cached_tag_list)
276 posts(:jonathan_sky).update_attributes!(:tag_list => "None")
277
278 assert_equal 'None', posts(:jonathan_sky).cached_tag_list
279 assert_equal 'None', posts(:jonathan_sky).reload.cached_tag_list
280 end
281
282 def test_clearing_cached_tag_list
283 # Generate the cached tag list
284 posts(:jonathan_sky).save!
285
286 posts(:jonathan_sky).update_attributes!(:tag_list => "")
287 assert_equal "", posts(:jonathan_sky).cached_tag_list
288 end
289
290 def test_find_tagged_with_using_sti
291 special_post = SpecialPost.create!(:text => "Test", :tag_list => "Random")
292
293 assert_equal [special_post], SpecialPost.find_tagged_with("Random")
294 assert Post.find_tagged_with("Random").include?(special_post)
295 end
296
297 def test_tag_counts_using_sti
298 SpecialPost.create!(:text => "Test", :tag_list => "Nature")
299
300 assert_tag_counts SpecialPost.tag_counts, :nature => 1
301 end
302
303 def test_case_insensitivity
304 assert_difference "Tag.count", 1 do
305 Post.create!(:text => "Test", :tag_list => "one")
306 Post.create!(:text => "Test", :tag_list => "One")
307 end
308
309 assert_equal Post.find_tagged_with("Nature"), Post.find_tagged_with("nature")
310 end
311
312 def test_tag_not_destroyed_when_unused
313 posts(:jonathan_sky).tag_list.add("Random")
314 posts(:jonathan_sky).save!
315
316 assert_no_difference 'Tag.count' do
317 posts(:jonathan_sky).tag_list.remove("Random")
318 posts(:jonathan_sky).save!
319 end
320 end
321
322 def test_tag_destroyed_when_unused
323 Tag.destroy_unused = true
324
325 posts(:jonathan_sky).tag_list.add("Random")
326 posts(:jonathan_sky).save!
327
328 assert_difference 'Tag.count', -1 do
329 posts(:jonathan_sky).tag_list.remove("Random")
330 posts(:jonathan_sky).save!
331 end
332 ensure
333 Tag.destroy_unused = false
334 end
335end
336
337class ActsAsTaggableOnSteroidsFormTest < Test::Unit::TestCase
338 fixtures :tags, :taggings, :posts, :users, :photos
339
340 include ActionView::Helpers::FormHelper
341
342 def test_tag_list_contents
343 fields_for :post, posts(:jonathan_sky) do |f|
344 assert_match /Very good, Nature/, f.text_field(:tag_list)
345 end
346 end
347end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb
new file mode 100644
index 0000000..554afe4
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazine.rb
@@ -0,0 +1,3 @@
1class Magazine < ActiveRecord::Base
2 acts_as_taggable
3end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml
new file mode 100644
index 0000000..044ce6d
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/magazines.yml
@@ -0,0 +1,7 @@
1ruby:
2 id: 1
3 name: Ruby
4
5rails:
6 id: 2
7 name: Rails \ No newline at end of file
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb
new file mode 100644
index 0000000..224957f
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photo.rb
@@ -0,0 +1,8 @@
1class Photo < ActiveRecord::Base
2 acts_as_taggable
3
4 belongs_to :user
5end
6
7class SpecialPhoto < Photo
8end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml
new file mode 100644
index 0000000..25a4118
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/photos.yml
@@ -0,0 +1,24 @@
1jonathan_dog:
2 id: 1
3 user_id: 1
4 title: A small dog
5
6jonathan_questioning_dog:
7 id: 2
8 user_id: 1
9 title: What does this dog want?
10
11jonathan_bad_cat:
12 id: 3
13 user_id: 1
14 title: Bad cat
15
16sam_flower:
17 id: 4
18 user_id: 2
19 title: Flower
20
21sam_sky:
22 id: 5
23 user_id: 2
24 title: Sky
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb
new file mode 100644
index 0000000..bee100a
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/post.rb
@@ -0,0 +1,7 @@
1class Post < ActiveRecord::Base
2 acts_as_taggable
3
4 belongs_to :user
5
6 validates_presence_of :text
7end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml
new file mode 100644
index 0000000..79f50f5
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/posts.yml
@@ -0,0 +1,34 @@
1jonathan_sky:
2 id: 1
3 user_id: 1
4 text: The sky is particularly blue today
5
6jonathan_grass:
7 id: 2
8 user_id: 1
9 text: The grass seems very green
10
11jonathan_rain:
12 id: 3
13 user_id: 1
14 text: Why does the rain fall?
15
16jonathan_cloudy:
17 id: 4
18 user_id: 1
19 text: Is it cloudy?
20
21jonathan_still_cloudy:
22 id: 5
23 user_id: 1
24 text: Is it still cloudy?
25
26sam_ground:
27 id: 6
28 user_id: 2
29 text: The ground is looking too brown
30
31sam_flowers:
32 id: 7
33 user_id: 2
34 text: Why are the flowers dead? \ No newline at end of file
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb
new file mode 100644
index 0000000..366a0d5
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/special_post.rb
@@ -0,0 +1,2 @@
1class SpecialPost < Post
2end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb
new file mode 100644
index 0000000..e975cb7
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscription.rb
@@ -0,0 +1,4 @@
1class Subscription < ActiveRecord::Base
2 belongs_to :user
3 belongs_to :magazine
4end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml
new file mode 100644
index 0000000..1b5e68a
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/subscriptions.yml
@@ -0,0 +1,3 @@
1jonathan_rails:
2 user_id: 1
3 magazine_id: 1 \ No newline at end of file
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml
new file mode 100644
index 0000000..01e599b
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/taggings.yml
@@ -0,0 +1,149 @@
1# Posts
2jonathan_sky_good:
3 id: 1
4 tag_id: 1
5 taggable_id: 1
6 taggable_type: Post
7 created_at: 2006-08-01
8
9jonathan_sky_nature:
10 id: 2
11 tag_id: 3
12 taggable_id: 1
13 taggable_type: Post
14 created_at: 2006-08-02
15
16jonathan_grass_nature:
17 id: 3
18 tag_id: 3
19 taggable_id: 2
20 taggable_type: Post
21 created_at: 2006-08-03
22
23jonathan_rain_question:
24 id: 4
25 tag_id: 4
26 taggable_id: 3
27 taggable_type: Post
28 created_at: 2006-08-04
29
30jonathan_rain_nature:
31 id: 5
32 tag_id: 3
33 taggable_id: 3
34 taggable_type: Post
35 created_at: 2006-08-05
36
37jonathan_cloudy_nature:
38 id: 6
39 tag_id: 3
40 taggable_id: 4
41 taggable_type: Post
42 created_at: 2006-08-06
43
44jonathan_still_cloudy_nature:
45 id: 7
46 tag_id: 3
47 taggable_id: 5
48 taggable_type: Post
49 created_at: 2006-08-07
50
51sam_ground_nature:
52 id: 8
53 tag_id: 3
54 taggable_id: 6
55 taggable_type: Post
56 created_at: 2006-08-08
57
58sam_ground_bad:
59 id: 9
60 tag_id: 2
61 taggable_id: 6
62 taggable_type: Post
63 created_at: 2006-08-09
64
65sam_flowers_good:
66 id: 10
67 tag_id: 1
68 taggable_id: 7
69 taggable_type: Post
70 created_at: 2006-08-10
71
72sam_flowers_nature:
73 id: 11
74 tag_id: 3
75 taggable_id: 7
76 taggable_type: Post
77 created_at: 2006-08-11
78
79# Photos
80jonathan_dog_animal:
81 id: 12
82 tag_id: 5
83 taggable_id: 1
84 taggable_type: Photo
85 created_at: 2006-08-12
86
87jonathan_dog_nature:
88 id: 13
89 tag_id: 3
90 taggable_id: 1
91 taggable_type: Photo
92 created_at: 2006-08-13
93
94jonathan_questioning_dog_animal:
95 id: 14
96 tag_id: 5
97 taggable_id: 2
98 taggable_type: Photo
99 created_at: 2006-08-14
100
101jonathan_questioning_dog_question:
102 id: 15
103 tag_id: 4
104 taggable_id: 2
105 taggable_type: Photo
106 created_at: 2006-08-15
107
108jonathan_bad_cat_bad:
109 id: 16
110 tag_id: 2
111 taggable_id: 3
112 taggable_type: Photo
113 created_at: 2006-08-16
114
115jonathan_bad_cat_animal:
116 id: 17
117 tag_id: 5
118 taggable_id: 3
119 taggable_type: Photo
120 created_at: 2006-08-17
121
122sam_flower_nature:
123 id: 18
124 tag_id: 3
125 taggable_id: 4
126 taggable_type: Photo
127 created_at: 2006-08-18
128
129sam_flower_good:
130 id: 19
131 tag_id: 1
132 taggable_id: 4
133 taggable_type: Photo
134 created_at: 2006-08-19
135
136sam_sky_nature:
137 id: 20
138 tag_id: 3
139 taggable_id: 5
140 taggable_type: Photo
141 created_at: 2006-08-20
142
143# Magazines
144ruby_good:
145 id: 50
146 tag_id: 1
147 taggable_id: 1
148 taggable_type: Magazine
149 created_at: 2007-08-25
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml
new file mode 100644
index 0000000..b8f8367
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/tags.yml
@@ -0,0 +1,19 @@
1good:
2 id: 1
3 name: Very good
4
5bad:
6 id: 2
7 name: Bad
8
9nature:
10 id: 3
11 name: Nature
12
13question:
14 id: 4
15 name: Question
16
17animal:
18 id: 5
19 name: Crazy animal \ No newline at end of file
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb
new file mode 100644
index 0000000..8c0f787
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/user.rb
@@ -0,0 +1,7 @@
1class User < ActiveRecord::Base
2 has_many :posts
3 has_many :photos
4
5 has_many :subscriptions
6 has_many :magazines, :through => :subscriptions
7end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml
new file mode 100644
index 0000000..da94fea
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/fixtures/users.yml
@@ -0,0 +1,7 @@
1jonathan:
2 id: 1
3 name: Jonathan
4
5sam:
6 id: 2
7 name: Sam
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb
new file mode 100644
index 0000000..d0d135c
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/tag_list_test.rb
@@ -0,0 +1,106 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TagListTest < Test::Unit::TestCase
4 def test_from_leaves_string_unchanged
5 tags = '"One ", Two'
6 original = tags.dup
7 TagList.from(tags)
8 assert_equal tags, original
9 end
10
11 def test_from_single_name
12 assert_equal %w(Fun), TagList.from("Fun")
13 assert_equal %w(Fun), TagList.from('"Fun"')
14 end
15
16 def test_from_blank
17 assert_equal [], TagList.from(nil)
18 assert_equal [], TagList.from("")
19 end
20
21 def test_from_single_quoted_tag
22 assert_equal ['with, comma'], TagList.from('"with, comma"')
23 end
24
25 def test_spaces_do_not_delineate
26 assert_equal ['A B', 'C'], TagList.from('A B, C')
27 end
28
29 def test_from_multiple_tags
30 assert_equivalent %w(Alpha Beta Delta Gamma), TagList.from("Alpha, Beta, Delta, Gamma")
31 end
32
33 def test_from_multiple_tags_with_quotes
34 assert_equivalent %w(Alpha Beta Delta Gamma), TagList.from('Alpha, "Beta", Gamma , "Delta"')
35 end
36
37 def test_from_with_single_quotes
38 assert_equivalent ['A B', 'C'], TagList.from("'A B', C")
39 end
40
41 def test_from_multiple_tags_with_quote_and_commas
42 assert_equivalent ['Alpha, Beta', 'Delta', 'Gamma, something'], TagList.from('"Alpha, Beta", Delta, "Gamma, something"')
43 end
44
45 def test_from_removes_white_space
46 assert_equivalent %w(Alpha Beta), TagList.from('" Alpha ", "Beta "')
47 assert_equivalent %w(Alpha Beta), TagList.from(' Alpha, Beta ')
48 end
49
50 def test_from_removes_dots
51 assert_equivalent %w(Alpha Beta), TagList.from('Alpha., Beta')
52 assert_equivalent %w(Alpha Beta), TagList.from('Alpha, Be.ta')
53 end
54
55 def test_alternative_delimiter
56 TagList.delimiter = " "
57
58 assert_equal %w(One Two), TagList.from("One Two")
59 assert_equal ['One two', 'three', 'four'], TagList.from('"One two" three four')
60 ensure
61 TagList.delimiter = ","
62 end
63
64 def test_duplicate_tags_removed
65 assert_equal %w(One), TagList.from("One, One")
66 end
67
68 def test_to_s_with_commas
69 assert_equal "Question, Crazy Animal", TagList.new("Question", "Crazy Animal").to_s
70 end
71
72 def test_to_s_with_alternative_delimiter
73 TagList.delimiter = " "
74
75 assert_equal '"Crazy Animal" Question', TagList.new("Crazy Animal", "Question").to_s
76 ensure
77 TagList.delimiter = ","
78 end
79
80 def test_add
81 tag_list = TagList.new("One")
82 assert_equal %w(One), tag_list
83
84 assert_equal %w(One Two), tag_list.add("Two")
85 assert_equal %w(One Two Three), tag_list.add(["Three"])
86 end
87
88 def test_remove
89 tag_list = TagList.new("One", "Two")
90 assert_equal %w(Two), tag_list.remove("One")
91 assert_equal %w(), tag_list.remove(["Two"])
92 end
93
94 def test_new_with_parsing
95 assert_equal %w(One Two), TagList.new("One, Two", :parse => true)
96 end
97
98 def test_add_with_parsing
99 assert_equal %w(One Two), TagList.new.add("One, Two", :parse => true)
100 end
101
102 def test_remove_with_parsing
103 tag_list = TagList.from("Three, Four, Five")
104 assert_equal %w(Four), tag_list.remove("Three, Five", :parse => true)
105 end
106end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb
new file mode 100644
index 0000000..4ca577d
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/tag_test.rb
@@ -0,0 +1,34 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TagTest < Test::Unit::TestCase
4 fixtures :tags, :taggings, :users, :photos, :posts
5
6 def test_name_required
7 t = Tag.create
8 assert_match /blank/, t.errors[:name].to_s
9 end
10
11 def test_name_unique
12 t = Tag.create!(:name => "My tag")
13 duplicate = t.clone
14
15 assert !duplicate.save
16 assert_match /taken/, duplicate.errors[:name].to_s
17 end
18
19 def test_taggings
20 assert_equivalent [taggings(:jonathan_sky_good), taggings(:sam_flowers_good), taggings(:sam_flower_good), taggings(:ruby_good)], tags(:good).taggings
21 assert_equivalent [taggings(:sam_ground_bad), taggings(:jonathan_bad_cat_bad)], tags(:bad).taggings
22 end
23
24 def test_to_s
25 assert_equal tags(:good).name, tags(:good).to_s
26 end
27
28 def test_equality
29 assert_equal tags(:good), tags(:good)
30 assert_equal Tag.find(1), Tag.find(1)
31 assert_equal Tag.new(:name => 'A'), Tag.new(:name => 'A')
32 assert_not_equal Tag.new(:name => 'A'), Tag.new(:name => 'B')
33 end
34end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb
new file mode 100644
index 0000000..172b8e2
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/tagging_test.rb
@@ -0,0 +1,13 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TaggingTest < Test::Unit::TestCase
4 fixtures :tags, :taggings, :posts
5
6 def test_tag
7 assert_equal tags(:good), taggings(:jonathan_sky_good).tag
8 end
9
10 def test_taggable
11 assert_equal posts(:jonathan_sky), taggings(:jonathan_sky_good).taggable
12 end
13end
diff --git a/vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb b/vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb
new file mode 100644
index 0000000..9c3ce53
--- /dev/null
+++ b/vendor/plugins/acts_as_taggable_on_steroids/test/tags_helper_test.rb
@@ -0,0 +1,28 @@
1require File.dirname(__FILE__) + '/abstract_unit'
2
3class TagsHelperTest < Test::Unit::TestCase
4 fixtures :tags, :taggings, :posts
5
6 include TagsHelper
7
8 def test_tag_cloud
9 cloud_elements = []
10
11 tag_cloud Post.tag_counts, %w(css1 css2 css3 css4) do |tag, css_class|
12 cloud_elements << [tag, css_class]
13 end
14
15 assert_equal [
16 [tags(:good), "css2"],
17 [tags(:bad), "css1"],
18 [tags(:nature), "css4"],
19 [tags(:question), "css1"]
20 ], cloud_elements
21 end
22
23 def test_tag_cloud_when_no_tags
24 tag_cloud SpecialPost.tag_counts, %w(css1) do
25 assert false, "tag_cloud should not yield"
26 end
27 end
28end