has_many_polymorphs 2.2 → 2.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +0 -9
- data/README +6 -52
- data/TODO +1 -0
- data/generators/tagging/tagging_generator.rb +1 -14
- data/generators/tagging/templates/tag_test.rb +3 -8
- data/generators/tagging/templates/tagging.rb +2 -2
- data/generators/tagging/templates/tagging_extensions.rb +13 -106
- data/generators/tagging/templates/tagging_test.rb +14 -41
- data/generators/tagging/templates/taggings.yml +3 -5
- data/generators/tagging/templates/tags.yml +2 -2
- data/has_many_polymorphs.gemspec +36 -29
- data/lib/has_many_polymorphs.rb +5 -6
- data/lib/has_many_polymorphs/association.rb +8 -9
- data/lib/has_many_polymorphs/autoload.rb +16 -52
- data/lib/has_many_polymorphs/base.rb +3 -13
- data/lib/has_many_polymorphs/class_methods.rb +102 -181
- data/lib/has_many_polymorphs/debugging_tools.rb +0 -2
- data/lib/has_many_polymorphs/dependencies.rb +36 -0
- data/lib/has_many_polymorphs/rake_task_redefine_task.rb +1 -9
- data/lib/has_many_polymorphs/reflection.rb +6 -7
- data/lib/has_many_polymorphs/support_methods.rb +4 -10
- data/test/fixtures/{fish.yml → aquatic/fish.yml} +0 -0
- data/test/fixtures/{little_whale_pupils.yml → aquatic/little_whale_pupils.yml} +0 -0
- data/test/fixtures/{whales.yml → aquatic/whales.yml} +0 -0
- data/test/models/aquatic/fish.rb +1 -2
- data/test/models/aquatic/whale.rb +0 -2
- data/test/models/eaters_foodstuff.rb +3 -1
- data/test/models/petfood.rb +1 -2
- data/test/schema.rb +0 -15
- data/test/test_helper.rb +18 -39
- data/test/unit/{has_many_polymorphs_test.rb → polymorph_test.rb} +109 -181
- metadata +92 -265
- data.tar.gz.sig +0 -1
- data/Manifest +0 -173
- data/Rakefile +0 -28
- data/examples/hmph.rb +0 -69
- data/lib/has_many_polymorphs/configuration.rb +0 -19
- data/test/fixtures/people.yml +0 -7
- data/test/generator/tagging_generator_test.rb +0 -42
- data/test/integration/app/README +0 -182
- data/test/integration/app/Rakefile +0 -19
- data/test/integration/app/app/controllers/application.rb +0 -7
- data/test/integration/app/app/controllers/bones_controller.rb +0 -5
- data/test/integration/app/app/helpers/addresses_helper.rb +0 -2
- data/test/integration/app/app/helpers/application_helper.rb +0 -3
- data/test/integration/app/app/helpers/bones_helper.rb +0 -2
- data/test/integration/app/app/helpers/sellers_helper.rb +0 -28
- data/test/integration/app/app/helpers/states_helper.rb +0 -2
- data/test/integration/app/app/helpers/users_helper.rb +0 -2
- data/test/integration/app/app/models/bone.rb +0 -2
- data/test/integration/app/app/models/double_sti_parent.rb +0 -2
- data/test/integration/app/app/models/double_sti_parent_relationship.rb +0 -2
- data/test/integration/app/app/models/organic_substance.rb +0 -2
- data/test/integration/app/app/models/single_sti_parent.rb +0 -4
- data/test/integration/app/app/models/single_sti_parent_relationship.rb +0 -4
- data/test/integration/app/app/models/stick.rb +0 -2
- data/test/integration/app/app/models/stone.rb +0 -2
- data/test/integration/app/app/views/addresses/edit.html.erb +0 -12
- data/test/integration/app/app/views/addresses/index.html.erb +0 -18
- data/test/integration/app/app/views/addresses/new.html.erb +0 -11
- data/test/integration/app/app/views/addresses/show.html.erb +0 -3
- data/test/integration/app/app/views/bones/index.rhtml +0 -5
- data/test/integration/app/app/views/layouts/addresses.html.erb +0 -17
- data/test/integration/app/app/views/layouts/sellers.html.erb +0 -17
- data/test/integration/app/app/views/layouts/states.html.erb +0 -17
- data/test/integration/app/app/views/layouts/users.html.erb +0 -17
- data/test/integration/app/app/views/sellers/edit.html.erb +0 -12
- data/test/integration/app/app/views/sellers/index.html.erb +0 -20
- data/test/integration/app/app/views/sellers/new.html.erb +0 -11
- data/test/integration/app/app/views/sellers/show.html.erb +0 -3
- data/test/integration/app/app/views/states/edit.html.erb +0 -12
- data/test/integration/app/app/views/states/index.html.erb +0 -19
- data/test/integration/app/app/views/states/new.html.erb +0 -11
- data/test/integration/app/app/views/states/show.html.erb +0 -3
- data/test/integration/app/app/views/users/edit.html.erb +0 -12
- data/test/integration/app/app/views/users/index.html.erb +0 -22
- data/test/integration/app/app/views/users/new.html.erb +0 -11
- data/test/integration/app/app/views/users/show.html.erb +0 -3
- data/test/integration/app/config/boot.rb +0 -110
- data/test/integration/app/config/database.yml +0 -17
- data/test/integration/app/config/environment.rb +0 -19
- data/test/integration/app/config/environment.rb.canonical +0 -19
- data/test/integration/app/config/environments/development.rb +0 -9
- data/test/integration/app/config/environments/production.rb +0 -18
- data/test/integration/app/config/environments/test.rb +0 -19
- data/test/integration/app/config/locomotive.yml +0 -6
- data/test/integration/app/config/routes.rb +0 -33
- data/test/integration/app/config/ultrasphinx/default.base +0 -56
- data/test/integration/app/config/ultrasphinx/development.conf.canonical +0 -155
- data/test/integration/app/db/migrate/001_create_sticks.rb +0 -11
- data/test/integration/app/db/migrate/002_create_stones.rb +0 -11
- data/test/integration/app/db/migrate/003_create_organic_substances.rb +0 -11
- data/test/integration/app/db/migrate/004_create_bones.rb +0 -8
- data/test/integration/app/db/migrate/005_create_single_sti_parents.rb +0 -11
- data/test/integration/app/db/migrate/006_create_double_sti_parents.rb +0 -11
- data/test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb +0 -13
- data/test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb +0 -14
- data/test/integration/app/db/migrate/009_create_library_model.rb +0 -11
- data/test/integration/app/doc/README_FOR_APP +0 -2
- data/test/integration/app/generators/commenting_generator_test.rb +0 -83
- data/test/integration/app/lib/library_model.rb +0 -2
- data/test/integration/app/public/404.html +0 -30
- data/test/integration/app/public/500.html +0 -30
- data/test/integration/app/public/dispatch.cgi +0 -10
- data/test/integration/app/public/dispatch.fcgi +0 -24
- data/test/integration/app/public/dispatch.rb +0 -10
- data/test/integration/app/public/favicon.ico +0 -0
- data/test/integration/app/public/images/rails.png +0 -0
- data/test/integration/app/public/index.html +0 -277
- data/test/integration/app/public/javascripts/application.js +0 -2
- data/test/integration/app/public/javascripts/controls.js +0 -833
- data/test/integration/app/public/javascripts/dragdrop.js +0 -942
- data/test/integration/app/public/javascripts/effects.js +0 -1088
- data/test/integration/app/public/javascripts/prototype.js +0 -2515
- data/test/integration/app/public/robots.txt +0 -1
- data/test/integration/app/public/stylesheets/scaffold.css +0 -74
- data/test/integration/app/script/about +0 -3
- data/test/integration/app/script/breakpointer +0 -3
- data/test/integration/app/script/console +0 -3
- data/test/integration/app/script/destroy +0 -3
- data/test/integration/app/script/generate +0 -3
- data/test/integration/app/script/performance/benchmarker +0 -3
- data/test/integration/app/script/performance/profiler +0 -3
- data/test/integration/app/script/plugin +0 -3
- data/test/integration/app/script/process/inspector +0 -3
- data/test/integration/app/script/process/reaper +0 -3
- data/test/integration/app/script/process/spawner +0 -3
- data/test/integration/app/script/runner +0 -3
- data/test/integration/app/script/server +0 -3
- data/test/integration/app/test/fixtures/double_sti_parent_relationships.yml +0 -7
- data/test/integration/app/test/fixtures/double_sti_parents.yml +0 -7
- data/test/integration/app/test/fixtures/organic_substances.yml +0 -5
- data/test/integration/app/test/fixtures/single_sti_parent_relationships.yml +0 -7
- data/test/integration/app/test/fixtures/single_sti_parents.yml +0 -7
- data/test/integration/app/test/fixtures/sticks.yml +0 -7
- data/test/integration/app/test/fixtures/stones.yml +0 -7
- data/test/integration/app/test/functional/addresses_controller_test.rb +0 -57
- data/test/integration/app/test/functional/bones_controller_test.rb +0 -8
- data/test/integration/app/test/functional/sellers_controller_test.rb +0 -57
- data/test/integration/app/test/functional/states_controller_test.rb +0 -57
- data/test/integration/app/test/functional/users_controller_test.rb +0 -57
- data/test/integration/app/test/test_helper.rb +0 -8
- data/test/integration/app/test/unit/bone_test.rb +0 -8
- data/test/integration/app/test/unit/double_sti_parent_relationship_test.rb +0 -8
- data/test/integration/app/test/unit/double_sti_parent_test.rb +0 -8
- data/test/integration/app/test/unit/organic_substance_test.rb +0 -8
- data/test/integration/app/test/unit/single_sti_parent_relationship_test.rb +0 -8
- data/test/integration/app/test/unit/single_sti_parent_test.rb +0 -8
- data/test/integration/app/test/unit/stick_test.rb +0 -8
- data/test/integration/app/test/unit/stone_test.rb +0 -8
- data/test/integration/server_test.rb +0 -43
- data/test/models/parentship.rb +0 -4
- data/test/models/person.rb +0 -9
- data/test/patches/symlinked_plugins_1.2.6.diff +0 -46
- data/test/setup.rb +0 -14
- metadata.gz.sig +0 -0
data/CHANGELOG
CHANGED
@@ -1,12 +1,3 @@
|
|
1
|
-
v2.2. Various fixes.
|
2
|
-
|
3
|
-
v2.13. Merge various fixes for Rails 2.2.2.
|
4
|
-
|
5
|
-
v2.12. Improvements to the test suite; bugfixes for STI children (rsl). Remove fancy dependency system in favor of using Dispatcher::to_prepare every time.
|
6
|
-
|
7
|
-
v2.11. Rails 1.2.6 tagging generator compatibility; change test suite to use included integration app.
|
8
|
-
|
9
|
-
v2.10. Add :parent_conditions option; bugfix for nullified conditions; bugfix for self-referential tagging generator; allow setting of has_many_polymorphs_options hash in Configuration's after_initialize if you need to adjust the autoload behavior; clear error message on missing or improperly namespaced models; fix .build on double-sided relationships; add :namespace key for easier set up of Camping apps or other unusual class structures.
|
10
1
|
|
11
2
|
v2.9. Gem version renumbering; my apologies if this messes anyone up.
|
12
3
|
|
data/README
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
+
|
1
2
|
Has_many_polymorphs
|
2
3
|
|
3
4
|
An ActiveRecord plugin for self-referential and double-sided polymorphic associations.
|
4
5
|
|
5
6
|
== License
|
6
7
|
|
7
|
-
Copyright
|
8
|
-
|
9
|
-
The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
|
10
|
-
|
11
|
-
If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
|
8
|
+
Copyright 2007 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file.
|
12
9
|
|
13
10
|
== Description
|
14
11
|
|
@@ -29,14 +26,14 @@ The plugin also includes a generator for a tagging system, a common use case (se
|
|
29
26
|
|
30
27
|
== Requirements
|
31
28
|
|
32
|
-
* Rails
|
29
|
+
* Rails 1.2.3 or grguest
|
33
30
|
|
34
31
|
= Usage
|
35
32
|
|
36
33
|
== Installation
|
37
34
|
|
38
35
|
To install the Rails plugin, run:
|
39
|
-
script/plugin install
|
36
|
+
script/plugin install svn://rubyforge.org/var/svn/fauna/has_many_polymorphs/trunk
|
40
37
|
|
41
38
|
There's also a gem version. To install it instead, run:
|
42
39
|
sudo gem install has_many_polymorphs
|
@@ -64,21 +61,6 @@ One of the child models:
|
|
64
61
|
# nothing
|
65
62
|
end
|
66
63
|
|
67
|
-
For your parent and child models, you don't need any special fields in your migration. For the join model (GuestsKennel), use a migration like so:
|
68
|
-
|
69
|
-
class CreateGuestsKennels < ActiveRecord::Migration
|
70
|
-
def self.up
|
71
|
-
create_table :guests_kennels do |t|
|
72
|
-
t.references :guest, :polymorphic => true
|
73
|
-
t.references :kennel
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.down
|
78
|
-
drop_table :guests_kennels
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
64
|
See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options.
|
83
65
|
|
84
66
|
== Helper methods example
|
@@ -129,21 +111,6 @@ Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't
|
|
129
111
|
|
130
112
|
In this case, each guest/eaten relationship is called a Devouring.
|
131
113
|
|
132
|
-
In your migration, you need to declare both sides as polymorphic:
|
133
|
-
|
134
|
-
class CreateDevourings < ActiveRecord::Migration
|
135
|
-
def self.up
|
136
|
-
create_table :devourings do |t|
|
137
|
-
t.references :guest, :polymorphic => true
|
138
|
-
t.references :eaten, :polymorphic => true
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def self.down
|
143
|
-
drop_table :devourings
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
114
|
See ActiveRecord::Associations::PolymorphicClassMethods for more.
|
148
115
|
|
149
116
|
== Tagging generator
|
@@ -186,20 +153,7 @@ If you are having trouble, think very carefully about how your model classes, ke
|
|
186
153
|
|
187
154
|
Note that because of the way Rails reloads model classes, the plugin can sometimes bog down your development server. Set <tt>config.cache_classes = true</tt> in <tt>config/environments/development.rb</tt> to avoid this.
|
188
155
|
|
189
|
-
== Reporting problems
|
190
|
-
|
191
|
-
The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=16450].
|
192
|
-
|
193
|
-
Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
|
194
|
-
|
195
156
|
== Further resources
|
196
157
|
|
197
|
-
* http://blog.evanweaver.com/
|
198
|
-
* http://
|
199
|
-
* http://blog.evanweaver.com/articles/2007/02/09/how-to-find-the-most-popular-tags
|
200
|
-
* http://blog.evanweaver.com/articles/2007/01/13/growing-up-your-acts_as_taggable
|
201
|
-
* http://blog.evanweaver.com/articles/2006/12/02/polymorphs-19
|
202
|
-
* http://blog.evanweaver.com/articles/2006/11/05/directed-double-polymorphic-associations
|
203
|
-
* http://blog.evanweaver.com/articles/2006/11/04/namespaced-model-support-in-has_many_polymorphs
|
204
|
-
* http://blog.evanweaver.com/articles/2006/09/26/sti-support-in-has_many_polymorphs
|
205
|
-
* http://blog.evanweaver.com/articles/2006/09/11/make-polymorphic-children-belong-to-only-one-parent
|
158
|
+
* http://blog.evanweaver.com/pages/code#polymorphs
|
159
|
+
* http://rubyforge.org/forum/forum.php?forum_id=16450
|
@@ -7,8 +7,6 @@ class TaggingGenerator < Rails::Generator::NamedBase
|
|
7
7
|
attr_reader :taggable_models
|
8
8
|
|
9
9
|
def initialize(runtime_args, runtime_options = {})
|
10
|
-
parse!(runtime_args, runtime_options)
|
11
|
-
|
12
10
|
@parent_association_name = (runtime_args.include?("--self-referential") ? "tagger" : "tag")
|
13
11
|
@taggable_models = runtime_args.reject{|opt| opt =~ /^--/}.map do |taggable|
|
14
12
|
":" + taggable.underscore.pluralize
|
@@ -25,9 +23,7 @@ class TaggingGenerator < Rails::Generator::NamedBase
|
|
25
23
|
def verify models
|
26
24
|
puts "** Warning: only one taggable model specified; tests may not run properly." if models.size < 2
|
27
25
|
models.each do |model|
|
28
|
-
model
|
29
|
-
next if model == "Tag" # don't load ourselves when --self-referential is used
|
30
|
-
self.class.const_get(model) rescue puts "** Error: model #{model[1..-1].classify} could not be loaded." or exit
|
26
|
+
self.class.const_get(model[1..-1].classify) rescue puts "** Error: model #{model[1..-1].classify} could not be loaded." or exit
|
31
27
|
end
|
32
28
|
end
|
33
29
|
|
@@ -85,13 +81,4 @@ class TaggingGenerator < Rails::Generator::NamedBase
|
|
85
81
|
opt.on("--self-referential",
|
86
82
|
"Allow tags to tag themselves.") { |v| options[:self_referential] = v }
|
87
83
|
end
|
88
|
-
|
89
|
-
# Useful for generating tests/fixtures
|
90
|
-
def model_one
|
91
|
-
taggable_models[0][1..-1].classify
|
92
|
-
end
|
93
|
-
|
94
|
-
def model_two
|
95
|
-
taggable_models[1][1..-1].classify rescue model_one
|
96
|
-
end
|
97
84
|
end
|
@@ -1,15 +1,10 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../test_helper'
|
2
2
|
|
3
|
-
class TagTest <
|
4
|
-
fixtures
|
5
|
-
|
6
|
-
def setup
|
7
|
-
@obj = <%= model_two %>.find(:first)
|
8
|
-
@obj.tag_with "pale imperial"
|
9
|
-
end
|
3
|
+
class TagTest < Test::Unit::TestCase
|
4
|
+
fixtures :tags, :taggings, :recipes, :posts
|
10
5
|
|
11
6
|
def test_to_s
|
12
|
-
assert_equal "
|
7
|
+
assert_equal "delicious sexy", Recipe.find(2).tags.to_s
|
13
8
|
end
|
14
9
|
|
15
10
|
end
|
@@ -10,7 +10,7 @@ class Tagging < ActiveRecord::Base
|
|
10
10
|
# acts_as_list :scope => :taggable
|
11
11
|
|
12
12
|
# This callback makes sure that an orphaned <tt>Tag</tt> is deleted if it no longer tags anything.
|
13
|
-
def
|
14
|
-
<%= parent_association_name -%>.destroy_without_callbacks if <%= parent_association_name -%> and <%= parent_association_name -%>.taggings.count ==
|
13
|
+
def before_destroy
|
14
|
+
<%= parent_association_name -%>.destroy_without_callbacks if <%= parent_association_name -%> and <%= parent_association_name -%>.taggings.count == 1
|
15
15
|
end
|
16
16
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
class ActiveRecord::Base #:nodoc:
|
2
3
|
|
3
4
|
# These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs.
|
@@ -12,7 +13,7 @@ class ActiveRecord::Base #:nodoc:
|
|
12
13
|
begin
|
13
14
|
tag = Tag.find_or_create_by_name(tag_name)
|
14
15
|
raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record?
|
15
|
-
|
16
|
+
tag.taggables << self
|
16
17
|
rescue ActiveRecord::StatementInvalid => e
|
17
18
|
raise unless e.to_s =~ /duplicate/i
|
18
19
|
end
|
@@ -25,13 +26,13 @@ class ActiveRecord::Base #:nodoc:
|
|
25
26
|
outgoing = tag_cast_to_string(outgoing)
|
26
27
|
<% if options[:self_referential] %>
|
27
28
|
# because of http://dev.rubyonrails.org/ticket/6466
|
28
|
-
taggings.destroy(
|
29
|
+
taggings.destroy(taggings.find(:all, :include => :<%= parent_association_name -%>).select do |tagging|
|
29
30
|
outgoing.include? tagging.<%= parent_association_name -%>.name
|
30
|
-
end)
|
31
|
+
end)
|
31
32
|
<% else -%>
|
32
|
-
<%= parent_association_name -%>s.delete(
|
33
|
+
<%= parent_association_name -%>s.delete(<%= parent_association_name -%>s.select do |tag|
|
33
34
|
outgoing.include? tag.name
|
34
|
-
end)
|
35
|
+
end)
|
35
36
|
<% end -%>
|
36
37
|
end
|
37
38
|
|
@@ -65,11 +66,11 @@ class ActiveRecord::Base #:nodoc:
|
|
65
66
|
<%= parent_association_name -%>s.to_s
|
66
67
|
#:startdoc:
|
67
68
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
|
70
|
+
#:stopdoc:
|
71
|
+
alias :<%= parent_association_name -%>s= :tag_with
|
72
|
+
#:startdoc:
|
73
|
+
|
73
74
|
private
|
74
75
|
|
75
76
|
def tag_cast_to_string obj #:nodoc:
|
@@ -102,102 +103,8 @@ class ActiveRecord::Base #:nodoc:
|
|
102
103
|
end
|
103
104
|
|
104
105
|
end
|
105
|
-
|
106
|
-
module TaggingFinders
|
107
|
-
# Find all the objects tagged with the supplied list of tags
|
108
|
-
#
|
109
|
-
# Usage : Model.tagged_with("ruby")
|
110
|
-
# Model.tagged_with("hello", "world")
|
111
|
-
# Model.tagged_with("hello", "world", :limit => 10)
|
112
|
-
#
|
113
|
-
# XXX This query strategy is not performant, and needs to be rewritten as an inverted join or a series of unions
|
114
|
-
#
|
115
|
-
def tagged_with(*tag_list)
|
116
|
-
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
117
|
-
tag_list = parse_tags(tag_list)
|
118
|
-
|
119
|
-
scope = scope(:find)
|
120
|
-
options[:select] ||= "#{table_name}.*"
|
121
|
-
options[:from] ||= "#{table_name}, tags, taggings"
|
122
|
-
|
123
|
-
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
124
|
-
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
125
|
-
|
126
|
-
add_joins!(sql, options[:joins], scope)
|
127
|
-
|
128
|
-
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
129
|
-
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
130
|
-
sql << "AND taggings.tag_id = tags.id "
|
131
|
-
|
132
|
-
tag_list_condition = tag_list.map {|name| "'#{name}'"}.join(", ")
|
133
|
-
|
134
|
-
sql << "AND (tags.name IN (#{sanitize_sql(tag_list_condition)})) "
|
135
|
-
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
136
|
-
|
137
|
-
columns = column_names.map do |column|
|
138
|
-
"#{table_name}.#{column}"
|
139
|
-
end.join(", ")
|
140
|
-
|
141
|
-
sql << "GROUP BY #{columns} "
|
142
|
-
sql << "HAVING COUNT(taggings.tag_id) = #{tag_list.size}"
|
143
|
-
|
144
|
-
add_order!(sql, options[:order], scope)
|
145
|
-
add_limit!(sql, options, scope)
|
146
|
-
add_lock!(sql, options, scope)
|
147
|
-
|
148
|
-
find_by_sql(sql)
|
149
|
-
end
|
150
|
-
|
151
|
-
def self.tagged_with_any(*tag_list)
|
152
|
-
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
153
|
-
tag_list = parse_tags(tag_list)
|
154
|
-
|
155
|
-
scope = scope(:find)
|
156
|
-
options[:select] ||= "#{table_name}.*"
|
157
|
-
options[:from] ||= "#{table_name}, tags, taggings"
|
158
|
-
|
159
|
-
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
160
|
-
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
161
|
-
|
162
|
-
add_joins!(sql, options, scope)
|
163
|
-
|
164
|
-
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
165
|
-
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
166
|
-
sql << "AND taggings.tag_id = tags.id "
|
167
|
-
|
168
|
-
sql << "AND ("
|
169
|
-
or_options = []
|
170
|
-
tag_list.each do |name|
|
171
|
-
or_options << "tags.name = '#{name}'"
|
172
|
-
end
|
173
|
-
or_options_joined = or_options.join(" OR ")
|
174
|
-
sql << "#{or_options_joined}) "
|
175
|
-
|
176
|
-
|
177
|
-
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
178
|
-
|
179
|
-
columns = column_names.map do |column|
|
180
|
-
"#{table_name}.#{column}"
|
181
|
-
end.join(", ")
|
182
|
-
|
183
|
-
sql << "GROUP BY #{columns} "
|
184
|
-
|
185
|
-
add_order!(sql, options[:order], scope)
|
186
|
-
add_limit!(sql, options, scope)
|
187
|
-
add_lock!(sql, options, scope)
|
188
|
-
|
189
|
-
find_by_sql(sql)
|
190
|
-
end
|
191
|
-
|
192
|
-
def parse_tags(tags)
|
193
|
-
return [] if tags.blank?
|
194
|
-
tags = Array(tags).first
|
195
|
-
tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(Tag::DELIMITER)
|
196
|
-
tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.map(&:downcase).uniq
|
197
|
-
end
|
198
|
-
|
199
|
-
end
|
200
106
|
|
201
107
|
include TaggingExtensions
|
202
|
-
|
108
|
+
|
203
109
|
end
|
110
|
+
|
@@ -1,63 +1,38 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../test_helper'
|
2
2
|
|
3
|
-
class TaggingTest <
|
4
|
-
fixtures :
|
3
|
+
class TaggingTest < Test::Unit::TestCase
|
4
|
+
fixtures :taggings, :tags, :posts, :recipes
|
5
5
|
|
6
6
|
def setup
|
7
|
-
@
|
8
|
-
|
9
|
-
@
|
10
|
-
@obj1.tag_with("pale")
|
11
|
-
@obj1.reload
|
12
|
-
|
13
|
-
@obj2 = @objs[1]
|
14
|
-
@obj2.tag_with("pale imperial")
|
15
|
-
@obj2.reload
|
16
|
-
|
17
|
-
<% if taggable_models.size > 1 -%>
|
18
|
-
@obj3 = <%= model_one -%>.find(:first)
|
19
|
-
<% end -%>
|
7
|
+
@obj1 = Recipe.find(1)
|
8
|
+
@obj2 = Recipe.find(2)
|
9
|
+
@obj3 = Post.find(1)
|
20
10
|
@tag1 = Tag.find(1)
|
21
11
|
@tag2 = Tag.find(2)
|
22
12
|
@tagging1 = Tagging.find(1)
|
23
13
|
end
|
24
14
|
|
25
15
|
def test_tag_with
|
26
|
-
@obj2.tag_with "
|
27
|
-
assert_equal "
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_find_tagged_with
|
31
|
-
@obj1.tag_with "seasonal lager ipa"
|
32
|
-
@obj2.tag_with ["lager", "stout", "fruity", "seasonal"]
|
33
|
-
|
34
|
-
result1 = [@obj1]
|
35
|
-
assert_equal <%= model_two %>.tagged_with("ipa"), result1
|
36
|
-
assert_equal <%= model_two %>.tagged_with("ipa lager"), result1
|
37
|
-
assert_equal <%= model_two %>.tagged_with("ipa", "lager"), result1
|
38
|
-
|
39
|
-
result2 = [@obj1.id, @obj2.id].sort
|
40
|
-
assert_equal <%= model_two %>.tagged_with("seasonal").map(&:id).sort, result2
|
41
|
-
assert_equal <%= model_two %>.tagged_with("seasonal lager").map(&:id).sort, result2
|
42
|
-
assert_equal <%= model_two %>.tagged_with("seasonal", "lager").map(&:id).sort, result2
|
16
|
+
@obj2.tag_with "dark columbian"
|
17
|
+
assert_equal "columbian dark", @obj2.tag_list
|
43
18
|
end
|
44
19
|
|
45
20
|
<% if options[:self_referential] -%>
|
46
21
|
def test_self_referential_tag_with
|
47
22
|
@tag1.tag_with [1, 2]
|
48
|
-
assert @tag1.tags.
|
49
|
-
assert !@tag2.tags.
|
23
|
+
assert @tag1.tags.include?(@tag1)
|
24
|
+
assert !@tag2.tags.include?(@tag1)
|
50
25
|
end
|
51
26
|
|
52
27
|
<% end -%>
|
53
28
|
def test__add_tags
|
54
29
|
@obj1._add_tags "porter longneck"
|
55
|
-
assert Tag.find_by_name("porter").taggables.
|
56
|
-
assert Tag.find_by_name("longneck").taggables.
|
57
|
-
assert_equal "longneck
|
30
|
+
assert Tag.find_by_name("porter").taggables.include?(@obj1)
|
31
|
+
assert Tag.find_by_name("longneck").taggables.include?(@obj1)
|
32
|
+
assert_equal "delicious longneck porter", @obj1.tag_list
|
58
33
|
|
59
34
|
@obj1._add_tags [2]
|
60
|
-
assert_equal "
|
35
|
+
assert_equal "delicious longneck porter sexy", @obj1.tag_list
|
61
36
|
end
|
62
37
|
|
63
38
|
def test__remove_tags
|
@@ -66,7 +41,7 @@ class TaggingTest < ActiveSupport::TestCase
|
|
66
41
|
end
|
67
42
|
|
68
43
|
def test_tag_list
|
69
|
-
assert_equal "
|
44
|
+
assert_equal "delicious sexy", @obj2.tag_list
|
70
45
|
end
|
71
46
|
|
72
47
|
def test_taggable
|
@@ -74,9 +49,7 @@ class TaggingTest < ActiveSupport::TestCase
|
|
74
49
|
@tagging1.send(:taggable?, true)
|
75
50
|
end
|
76
51
|
assert !@tagging1.send(:taggable?)
|
77
|
-
<% if taggable_models.size > 1 -%>
|
78
52
|
assert @obj3.send(:taggable?)
|
79
|
-
<% end -%>
|
80
53
|
<% if options[:self_referential] -%>
|
81
54
|
assert @tag1.send(:taggable?)
|
82
55
|
<% end -%>
|
@@ -1,15 +1,13 @@
|
|
1
1
|
---
|
2
|
-
<% if taggable_models.size > 1 -%>
|
3
2
|
taggings_003:
|
4
3
|
<%= parent_association_name -%>_id: "2"
|
5
4
|
id: "3"
|
6
|
-
taggable_type: <%= model_one %>
|
7
|
-
taggable_id: "1"
|
8
|
-
<% end -%>
|
5
|
+
taggable_type: <%= model_one = taggable_models[0][1..-1].classify %>
|
6
|
+
taggable_id: "1"
|
9
7
|
taggings_004:
|
10
8
|
<%= parent_association_name -%>_id: "2"
|
11
9
|
id: "4"
|
12
|
-
taggable_type: <%= model_two %>
|
10
|
+
taggable_type: <%= model_two = (taggable_models[1][1..-1].classify rescue model_one) %>
|
13
11
|
taggable_id: "2"
|
14
12
|
taggings_001:
|
15
13
|
<%= parent_association_name -%>_id: "1"
|