has_many_polymorphs 2.2 → 2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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"
|