taglish 0.1.0
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/.gitignore +10 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +107 -0
- data/MIT-LICENSE +20 -0
- data/README.md +11 -0
- data/Rakefile +17 -0
- data/lib/generators/taglish/migration/migration_generator.rb +39 -0
- data/lib/generators/taglish/migration/templates/active_record/migration.rb +31 -0
- data/lib/taglish.rb +33 -0
- data/lib/taglish/core.rb +157 -0
- data/lib/taglish/tag.rb +31 -0
- data/lib/taglish/tag_list.rb +115 -0
- data/lib/taglish/tag_type.rb +65 -0
- data/lib/taglish/taggable.rb +144 -0
- data/lib/taglish/tagging.rb +50 -0
- data/lib/taglish/util.rb +4 -0
- data/lib/taglish/version.rb +3 -0
- data/rails/init.rb +1 -0
- data/spec/database.yml.sample +19 -0
- data/spec/models.rb +64 -0
- data/spec/schema.rb +72 -0
- data/spec/spec_helper.rb +82 -0
- data/spec/taglish/taggable_spec.rb +695 -0
- data/taglish.gemspec +25 -0
- metadata +154 -0
data/spec/schema.rb
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
ActiveRecord::Schema.define :version => 0 do
|
|
2
|
+
create_table "taggings", :force => true do |t|
|
|
3
|
+
t.integer "tag_id", :limit => 11
|
|
4
|
+
t.integer "taggable_id", :limit => 11
|
|
5
|
+
t.string "taggable_type"
|
|
6
|
+
t.string "context"
|
|
7
|
+
t.datetime "created_at"
|
|
8
|
+
t.integer "tagger_id", :limit => 11
|
|
9
|
+
t.integer "score"
|
|
10
|
+
t.string "tagger_type"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
|
|
14
|
+
add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
|
|
15
|
+
|
|
16
|
+
create_table "tags", :force => true do |t|
|
|
17
|
+
t.string "name"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
create_table :taggable_models, :force => true do |t|
|
|
21
|
+
t.column :name, :string
|
|
22
|
+
t.column :type, :string
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
create_table :non_standard_id_taggable_models, :primary_key => "an_id", :force => true do |t|
|
|
26
|
+
t.column :name, :string
|
|
27
|
+
t.column :type, :string
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
create_table :untaggable_models, :force => true do |t|
|
|
31
|
+
t.column :taggable_model_id, :integer
|
|
32
|
+
t.column :name, :string
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
create_table :cached_models, :force => true do |t|
|
|
36
|
+
t.column :name, :string
|
|
37
|
+
t.column :type, :string
|
|
38
|
+
t.column :cached_tag_list, :string
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
create_table :other_cached_models, :force => true do |t|
|
|
42
|
+
t.column :name, :string
|
|
43
|
+
t.column :type, :string
|
|
44
|
+
t.column :cached_language_list, :string
|
|
45
|
+
t.column :cached_status_list, :string
|
|
46
|
+
t.column :cached_glass_list, :string
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
create_table :taggable_users, :force => true do |t|
|
|
50
|
+
t.column :name, :string
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
create_table :other_taggable_models, :force => true do |t|
|
|
54
|
+
t.column :name, :string
|
|
55
|
+
t.column :type, :string
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
create_table :ordered_taggable_models, :force => true do |t|
|
|
59
|
+
t.column :name, :string
|
|
60
|
+
t.column :type, :string
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
create_table :mixed_taggable_models, :force => true do |t|
|
|
64
|
+
t.column :name, :string
|
|
65
|
+
t.column :type, :string
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
create_table :scored_taggable_models, :force => true do |t|
|
|
69
|
+
t.column :name, :string
|
|
70
|
+
t.column :type, :string
|
|
71
|
+
end
|
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
$LOAD_PATH << "." unless $LOAD_PATH.include?(".")
|
|
2
|
+
require 'logger'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require "rubygems"
|
|
6
|
+
require "bundler"
|
|
7
|
+
|
|
8
|
+
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.5")
|
|
9
|
+
raise RuntimeError, "Your bundler version is too old." +
|
|
10
|
+
"Run `gem install bundler` to upgrade."
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Set up load paths for all bundled gems
|
|
14
|
+
Bundler.setup
|
|
15
|
+
rescue Bundler::GemNotFound
|
|
16
|
+
raise RuntimeError, "Bundler couldn't find some gems." +
|
|
17
|
+
"Did you run \`bundle install\`?"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Bundler.require
|
|
21
|
+
require File.expand_path('../../lib/taglish', __FILE__)
|
|
22
|
+
|
|
23
|
+
unless [].respond_to?(:freq)
|
|
24
|
+
class Array
|
|
25
|
+
def freq
|
|
26
|
+
k=Hash.new(0)
|
|
27
|
+
each {|e| k[e]+=1}
|
|
28
|
+
k
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# set adapter to use, default is sqlite3
|
|
34
|
+
# to use an alternative adapter run => rake spec DB='postgresql'
|
|
35
|
+
db_name = ENV['DB'] || 'sqlite3'
|
|
36
|
+
database_yml = File.expand_path('../database.yml', __FILE__)
|
|
37
|
+
|
|
38
|
+
if File.exists?(database_yml)
|
|
39
|
+
active_record_configuration = YAML.load_file(database_yml)
|
|
40
|
+
|
|
41
|
+
ActiveRecord::Base.configurations = active_record_configuration
|
|
42
|
+
config = ActiveRecord::Base.configurations[db_name]
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
ActiveRecord::Base.establish_connection(db_name)
|
|
46
|
+
ActiveRecord::Base.connection
|
|
47
|
+
rescue
|
|
48
|
+
case db_name
|
|
49
|
+
when /mysql/
|
|
50
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
|
|
51
|
+
ActiveRecord::Base.connection.create_database(config['database'], {:charset => 'utf8', :collation => 'utf8_unicode_ci'})
|
|
52
|
+
when 'postgresql'
|
|
53
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
|
54
|
+
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => 'utf8'))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
ActiveRecord::Base.establish_connection(config)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
|
|
61
|
+
ActiveRecord::Base.default_timezone = :utc
|
|
62
|
+
|
|
63
|
+
ActiveRecord::Base.silence do
|
|
64
|
+
ActiveRecord::Migration.verbose = false
|
|
65
|
+
|
|
66
|
+
load(File.dirname(__FILE__) + '/schema.rb')
|
|
67
|
+
load(File.dirname(__FILE__) + '/models.rb')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
else
|
|
71
|
+
raise "Please create #{database_yml} first to configure your database. Take a look at: #{database_yml}.sample"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def clean_database!
|
|
75
|
+
models = [Taglish::Tag, Taglish::Tagging, TaggableModel, OtherTaggableModel, InheritingTaggableModel,
|
|
76
|
+
AlteredInheritingTaggableModel, TaggableUser, UntaggableModel, OrderedTaggableModel, MixedTaggableModel, ScoredTaggableModel]
|
|
77
|
+
models.each do |model|
|
|
78
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
clean_database!
|
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
|
2
|
+
|
|
3
|
+
=begin
|
|
4
|
+
describe "Taggable To Preserve Order" do
|
|
5
|
+
before(:each) do
|
|
6
|
+
clean_database!
|
|
7
|
+
@taggable = OrderedTaggableModel.new(:name => "Bob Jones")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should have tag types" do
|
|
11
|
+
[:tags, :colours].each do |type|
|
|
12
|
+
OrderedTaggableModel.tag_types.should include type
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@taggable.tag_types.should == OrderedTaggableModel.tag_types
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should have tag associations" do
|
|
19
|
+
[:tags, :colours].each do |type|
|
|
20
|
+
@taggable.respond_to?(type).should be_true
|
|
21
|
+
@taggable.respond_to?("#{type.to_s.singularize}_tags").should be_true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should have tag associations ordered by id" do
|
|
26
|
+
[:tags, :colours].each do |type|
|
|
27
|
+
OrderedTaggableModel.reflect_on_association(type).options[:order].should include('id')
|
|
28
|
+
OrderedTaggableModel.reflect_on_association("#{type.to_s.singularize}_taggings".to_sym).options[:order].should include('id')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should have tag methods" do
|
|
33
|
+
[:tags, :colours].each do |type|
|
|
34
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list").should be_true
|
|
35
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list=").should be_true
|
|
36
|
+
@taggable.respond_to?("all_#{type.to_s}_list").should be_true
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should return tag list in the order the tags were created" do
|
|
41
|
+
# create
|
|
42
|
+
@taggable.tag_list = "rails, ruby, css"
|
|
43
|
+
@taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
|
44
|
+
|
|
45
|
+
lambda {
|
|
46
|
+
@taggable.save
|
|
47
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
|
48
|
+
|
|
49
|
+
@taggable.reload
|
|
50
|
+
@taggable.tag_list.should == %w(rails ruby css)
|
|
51
|
+
|
|
52
|
+
# update
|
|
53
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
54
|
+
@taggable.save
|
|
55
|
+
|
|
56
|
+
@taggable.reload
|
|
57
|
+
@taggable.tag_list.should == %w(pow ruby rails)
|
|
58
|
+
|
|
59
|
+
# update with no change
|
|
60
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
61
|
+
@taggable.save
|
|
62
|
+
|
|
63
|
+
@taggable.reload
|
|
64
|
+
@taggable.tag_list.should == %w(pow ruby rails)
|
|
65
|
+
|
|
66
|
+
# update to clear tags
|
|
67
|
+
@taggable.tag_list = ""
|
|
68
|
+
@taggable.save
|
|
69
|
+
|
|
70
|
+
@taggable.reload
|
|
71
|
+
@taggable.tag_list.should == []
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should return tag objects in the order the tags were created" do
|
|
75
|
+
# create
|
|
76
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
77
|
+
@taggable.instance_variable_get("@tag_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
|
78
|
+
|
|
79
|
+
lambda {
|
|
80
|
+
@taggable.save
|
|
81
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
|
82
|
+
|
|
83
|
+
@taggable.reload
|
|
84
|
+
@taggable.tags.map{|t| t.name}.should == %w(pow ruby rails)
|
|
85
|
+
|
|
86
|
+
# update
|
|
87
|
+
@taggable.tag_list = "rails, ruby, css, pow"
|
|
88
|
+
@taggable.save
|
|
89
|
+
|
|
90
|
+
@taggable.reload
|
|
91
|
+
@taggable.tags.map{|t| t.name}.should == %w(rails ruby css pow)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should return tag objects in tagging id order" do
|
|
95
|
+
# create
|
|
96
|
+
@taggable.tag_list = "pow, ruby, rails"
|
|
97
|
+
@taggable.save
|
|
98
|
+
|
|
99
|
+
@taggable.reload
|
|
100
|
+
ids = @taggable.tags.map{|t| t.taggings.first.id}
|
|
101
|
+
ids.should == ids.sort
|
|
102
|
+
|
|
103
|
+
# update
|
|
104
|
+
@taggable.tag_list = "rails, ruby, css, pow"
|
|
105
|
+
@taggable.save
|
|
106
|
+
|
|
107
|
+
@taggable.reload
|
|
108
|
+
ids = @taggable.tags.map{|t| t.taggings.first.id}
|
|
109
|
+
ids.should == ids.sort
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
=end
|
|
113
|
+
|
|
114
|
+
describe "Taggable" do
|
|
115
|
+
before(:each) do
|
|
116
|
+
clean_database!
|
|
117
|
+
@taggable = TaggableModel.new(:name => "Bob Jones")
|
|
118
|
+
@taggables = [@taggable, TaggableModel.new(:name => "John Doe")]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should have tag types" do
|
|
122
|
+
[:tags, :languages, :skills, :needs, :offerings].each do |type|
|
|
123
|
+
TaggableModel.tag_types.should include type
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
@taggable.class.should be_taggable
|
|
127
|
+
@taggable.should be_taggable
|
|
128
|
+
@taggable.tag_types.should == TaggableModel.tag_types
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should have tag associations" do
|
|
132
|
+
[:tags, :languages, :skills, :needs, :offerings].each do |type|
|
|
133
|
+
@taggable.respond_to?(type).should be_true
|
|
134
|
+
@taggable.respond_to?("#{type.to_s.singularize}_tags").should be_true
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "should have tag methods" do
|
|
139
|
+
[:tags, :languages, :skills, :needs, :offerings].each do |type|
|
|
140
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list").should be_true
|
|
141
|
+
@taggable.respond_to?("#{type.to_s.singularize}_list=").should be_true
|
|
142
|
+
# @taggable.respond_to?("all_#{type.to_s}_list").should be_true
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should return [] right after create" do
|
|
147
|
+
blank_taggable = TaggableModel.new(:name => "Bob Jones")
|
|
148
|
+
blank_taggable.tag_list.should == []
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should save taggings" do
|
|
152
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
153
|
+
|
|
154
|
+
@taggable.instance_variable_get("@skills_list").instance_of?(Taglish::TagList).should be_true
|
|
155
|
+
lambda {
|
|
156
|
+
@taggable.save
|
|
157
|
+
}.should change(Taglish::Tagging, :count).by(3)
|
|
158
|
+
|
|
159
|
+
@taggable.reload
|
|
160
|
+
@taggable.skill_list.sort.should == %w(ruby rails css).sort
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
it "should be able to create tags" do
|
|
164
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
165
|
+
|
|
166
|
+
lambda {
|
|
167
|
+
@taggable.save
|
|
168
|
+
}.should change(Taglish::Tag, :count).by(3)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context "with scored tag types" do
|
|
172
|
+
before do
|
|
173
|
+
@taggable = ScoredTaggableModel.new(:name => "Bob Jones")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should save taggings" do
|
|
177
|
+
@taggable.question_count_list = "ruby:9, sql:5"
|
|
178
|
+
|
|
179
|
+
@taggable.instance_variable_get("@question_counts_list").instance_of?(Taglish::TagList).should be_true
|
|
180
|
+
lambda {
|
|
181
|
+
@taggable.save
|
|
182
|
+
}.should change(Taglish::Tagging, :count).by(2)
|
|
183
|
+
|
|
184
|
+
@taggable.reload
|
|
185
|
+
@taggable.question_count_list.sort.should == %w(ruby:9 sql:5).sort
|
|
186
|
+
@taggable.question_counts.map{|tg| tg.name}.sort.should == %w(ruby sql)
|
|
187
|
+
@taggable.question_counts.map{|tg| tg.score}.sort.should == [5, 9]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "should remove old tags" do
|
|
191
|
+
@taggable.question_count_list = "ruby:9, sql:5, js:4"
|
|
192
|
+
@taggable.save!
|
|
193
|
+
@taggable.reload
|
|
194
|
+
|
|
195
|
+
# Change the score of one, remove one, add one, leave one unchanged:
|
|
196
|
+
@taggable.question_count_list = "ruby:12, sql:5, chef:2"
|
|
197
|
+
@taggable.instance_variable_get("@question_counts_list").instance_of?(Taglish::TagList).should be_true
|
|
198
|
+
lambda {
|
|
199
|
+
@taggable.save
|
|
200
|
+
}.should change(Taglish::Tagging, :count).by(0)
|
|
201
|
+
@taggable.reload
|
|
202
|
+
@taggable.question_count_list.sort.should == %w(ruby:12 sql:5 chef:2).sort
|
|
203
|
+
@taggable.question_counts.map{|tg| tg.name}.sort.should == %w(chef ruby sql)
|
|
204
|
+
@taggable.question_counts.map{|tg| tg.score}.sort.should == [2, 5, 12]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it "should save tags with zero and negative scores" do
|
|
208
|
+
@taggable.question_count_list = "ruby:0, sql:-5"
|
|
209
|
+
|
|
210
|
+
@taggable.instance_variable_get("@question_counts_list").instance_of?(Taglish::TagList).should be_true
|
|
211
|
+
lambda {
|
|
212
|
+
@taggable.save
|
|
213
|
+
}.should change(Taglish::Tagging, :count).by(2)
|
|
214
|
+
|
|
215
|
+
@taggable.reload
|
|
216
|
+
@taggable.question_count_list.sort.should == %w(ruby:0 sql:-5).sort
|
|
217
|
+
@taggable.question_counts.map{|tg| tg.name}.sort.should == %w(ruby sql)
|
|
218
|
+
@taggable.question_counts.map{|tg| tg.score}.sort.should == [-5, 0]
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
context "with mixed tag types" do
|
|
223
|
+
before do
|
|
224
|
+
@taggable = MixedTaggableModel.new(:name => "Bob Jones")
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "should remember which tag types are ordered" do
|
|
228
|
+
@taggable.tags_have_score?(:skills).should be_false
|
|
229
|
+
@taggable.tags_have_score?(:colors).should be_false
|
|
230
|
+
@taggable.tags_have_score?(:question_counts).should be_true
|
|
231
|
+
@taggable.tags_have_score?(:needs).should be_false
|
|
232
|
+
@taggable.tags_have_score?(:offerings).should be_false
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "should remember which tag types are scored" do
|
|
236
|
+
@taggable.tags_have_order?(:skills).should be_false
|
|
237
|
+
@taggable.tags_have_order?(:colors).should be_true
|
|
238
|
+
@taggable.tags_have_order?(:question_counts).should be_false
|
|
239
|
+
@taggable.tags_have_order?(:needs).should be_false
|
|
240
|
+
@taggable.tags_have_order?(:offerings).should be_false
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
=begin
|
|
245
|
+
it "should have tag_counts_on" do
|
|
246
|
+
TaggableModel.tag_counts_on(:tags).all.should be_empty
|
|
247
|
+
|
|
248
|
+
@taggable.tag_list = ["awesome", "epic"]
|
|
249
|
+
@taggable.save
|
|
250
|
+
|
|
251
|
+
TaggableModel.tag_counts_on(:tags).length.should == 2
|
|
252
|
+
@taggable.tag_counts_on(:tags).length.should == 2
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it "should be able to create tags through the tag list directly" do
|
|
256
|
+
@taggable.tag_list_on(:test).add("hello")
|
|
257
|
+
@taggable.tag_list_cache_on(:test).should_not be_empty
|
|
258
|
+
@taggable.tag_list_on(:test).should == ["hello"]
|
|
259
|
+
|
|
260
|
+
@taggable.save
|
|
261
|
+
@taggable.save_tags
|
|
262
|
+
|
|
263
|
+
@taggable.reload
|
|
264
|
+
@taggable.tag_list_on(:test).should == ["hello"]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it "should differentiate between contexts" do
|
|
268
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
269
|
+
@taggable.tag_list = "ruby, bob, charlie"
|
|
270
|
+
@taggable.save
|
|
271
|
+
@taggable.reload
|
|
272
|
+
@taggable.skill_list.should include("ruby")
|
|
273
|
+
@taggable.skill_list.should_not include("bob")
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "should be able to remove tags through list alone" do
|
|
277
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
278
|
+
@taggable.save
|
|
279
|
+
@taggable.reload
|
|
280
|
+
@taggable.should have(3).skills
|
|
281
|
+
@taggable.skill_list = "ruby, rails"
|
|
282
|
+
@taggable.save
|
|
283
|
+
@taggable.reload
|
|
284
|
+
@taggable.should have(2).skills
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
it "should be able to select taggables by subset of tags using ActiveRelation methods" do
|
|
288
|
+
@taggables[0].tag_list = "bob"
|
|
289
|
+
@taggables[1].tag_list = "charlie"
|
|
290
|
+
@taggables[0].skill_list = "ruby"
|
|
291
|
+
@taggables[1].skill_list = "css"
|
|
292
|
+
@taggables.each{|taggable| taggable.save}
|
|
293
|
+
|
|
294
|
+
@found_taggables_by_tag = TaggableModel.joins(:tags).where(:tags => {:name => ["bob"]})
|
|
295
|
+
@found_taggables_by_skill = TaggableModel.joins(:skills).where(:tags => {:name => ["ruby"]})
|
|
296
|
+
|
|
297
|
+
@found_taggables_by_tag.should include @taggables[0]
|
|
298
|
+
@found_taggables_by_tag.should_not include @taggables[1]
|
|
299
|
+
@found_taggables_by_skill.should include @taggables[0]
|
|
300
|
+
@found_taggables_by_skill.should_not include @taggables[1]
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it "should be able to find by tag" do
|
|
304
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
305
|
+
@taggable.save
|
|
306
|
+
|
|
307
|
+
TaggableModel.tagged_with("ruby").first.should == @taggable
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it "should be able to find by tag with context" do
|
|
311
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
312
|
+
@taggable.tag_list = "bob, charlie"
|
|
313
|
+
@taggable.save
|
|
314
|
+
|
|
315
|
+
TaggableModel.tagged_with("ruby").first.should == @taggable
|
|
316
|
+
TaggableModel.tagged_with("ruby, css").first.should == @taggable
|
|
317
|
+
TaggableModel.tagged_with("bob", :on => :skills).first.should_not == @taggable
|
|
318
|
+
TaggableModel.tagged_with("bob", :on => :tags).first.should == @taggable
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it "should not care about case" do
|
|
322
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby")
|
|
323
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "Ruby")
|
|
324
|
+
|
|
325
|
+
ActsAsTaggableOn::Tag.find(:all).size.should == 1
|
|
326
|
+
TaggableModel.tagged_with("ruby").to_a.should == TaggableModel.tagged_with("Ruby").to_a
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
it "should be able to get tag counts on model as a whole" do
|
|
330
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
331
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
|
|
332
|
+
charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
|
|
333
|
+
TaggableModel.tag_counts.all.should_not be_empty
|
|
334
|
+
TaggableModel.skill_counts.all.should_not be_empty
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
it "should be able to get all tag counts on model as whole" do
|
|
338
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
339
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
|
|
340
|
+
charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
|
|
341
|
+
|
|
342
|
+
TaggableModel.all_tag_counts.all.should_not be_empty
|
|
343
|
+
TaggableModel.all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "should be able to use named scopes to chain tag finds by any tags by context" do
|
|
347
|
+
bob = TaggableModel.create(:name => "Bob", :need_list => "rails", :offering_list => "c++")
|
|
348
|
+
frank = TaggableModel.create(:name => "Frank", :need_list => "css", :offering_list => "css")
|
|
349
|
+
steve = TaggableModel.create(:name => 'Steve', :need_list => "c++", :offering_list => "java")
|
|
350
|
+
|
|
351
|
+
# Let's only find those who need rails or css and are offering c++ or java
|
|
352
|
+
TaggableModel.tagged_with(['rails, css'], :on => :needs, :any => true).tagged_with(['c++', 'java'], :on => :offerings, :any => true).to_a.should == [bob]
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it "should not return read-only records" do
|
|
356
|
+
TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
357
|
+
TaggableModel.tagged_with("ruby").first.should_not be_readonly
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
it "should be able to get scoped tag counts" do
|
|
361
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
362
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
|
|
363
|
+
charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
|
|
364
|
+
|
|
365
|
+
TaggableModel.tagged_with("ruby").tag_counts(:order => 'tags.id').first.count.should == 2 # ruby
|
|
366
|
+
TaggableModel.tagged_with("ruby").skill_counts.first.count.should == 1 # ruby
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
it "should be able to get all scoped tag counts" do
|
|
370
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
371
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
|
|
372
|
+
charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby")
|
|
373
|
+
|
|
374
|
+
TaggableModel.tagged_with("ruby").all_tag_counts(:order => 'tags.id').first.count.should == 3 # ruby
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
it 'should only return tag counts for the available scope' do
|
|
378
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "ruby, rails, css")
|
|
379
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "ruby, rails")
|
|
380
|
+
charlie = TaggableModel.create(:name => "Charlie", :skill_list => "ruby, java")
|
|
381
|
+
|
|
382
|
+
TaggableModel.tagged_with('rails').all_tag_counts.should have(3).items
|
|
383
|
+
TaggableModel.tagged_with('rails').all_tag_counts.any? { |tag| tag.name == 'java' }.should be_false
|
|
384
|
+
|
|
385
|
+
# Test specific join syntaxes:
|
|
386
|
+
frank.untaggable_models.create!
|
|
387
|
+
TaggableModel.tagged_with('rails').scoped(:joins => :untaggable_models).all_tag_counts.should have(2).items
|
|
388
|
+
TaggableModel.tagged_with('rails').scoped(:joins => { :untaggable_models => :taggable_model }).all_tag_counts.should have(2).items
|
|
389
|
+
TaggableModel.tagged_with('rails').scoped(:joins => [:untaggable_models]).all_tag_counts.should have(2).items
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it "should be able to set a custom tag context list" do
|
|
393
|
+
bob = TaggableModel.create(:name => "Bob")
|
|
394
|
+
bob.set_tag_list_on(:rotors, "spinning, jumping")
|
|
395
|
+
bob.tag_list_on(:rotors).should == ["spinning","jumping"]
|
|
396
|
+
bob.save
|
|
397
|
+
bob.reload
|
|
398
|
+
bob.tags_on(:rotors).should_not be_empty
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
it "should be able to find tagged" do
|
|
402
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
|
|
403
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
|
|
404
|
+
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
|
|
405
|
+
|
|
406
|
+
TaggableModel.tagged_with("ruby", :order => 'taggable_models.name').to_a.should == [bob, frank, steve]
|
|
407
|
+
TaggableModel.tagged_with("ruby, rails", :order => 'taggable_models.name').to_a.should == [bob, frank]
|
|
408
|
+
TaggableModel.tagged_with(["ruby", "rails"], :order => 'taggable_models.name').to_a.should == [bob, frank]
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
it "should be able to find tagged with quotation marks" do
|
|
412
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive, 'I love the ,comma,'")
|
|
413
|
+
TaggableModel.tagged_with("'I love the ,comma,'").should include(bob)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
it "should be able to find tagged with invalid tags" do
|
|
417
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive")
|
|
418
|
+
TaggableModel.tagged_with("sad, happier").should_not include(bob)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
it "should be able to find tagged with any tag" do
|
|
422
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
|
|
423
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
|
|
424
|
+
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, ruby')
|
|
425
|
+
|
|
426
|
+
TaggableModel.tagged_with(["ruby", "java"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank, steve]
|
|
427
|
+
TaggableModel.tagged_with(["c++", "fitter"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, steve]
|
|
428
|
+
TaggableModel.tagged_with(["depressed", "css"], :order => 'taggable_models.name', :any => true).to_a.should == [bob, frank]
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
context "wild: true" do
|
|
432
|
+
it "should use params as wildcards" do
|
|
433
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "bob, tricia")
|
|
434
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "bobby, jim")
|
|
435
|
+
steve = TaggableModel.create(:name => "Steve", :tag_list => "john, patricia")
|
|
436
|
+
jim = TaggableModel.create(:name => "Jim", :tag_list => "jim, steve")
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :any => true).to_a.sort_by{|o| o.id}.should == [bob, frank, steve]
|
|
440
|
+
TaggableModel.tagged_with(["bob", "tricia"], :wild => true, :exclude => true).to_a.should == [jim]
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
it "should be able to find tagged on a custom tag context" do
|
|
445
|
+
bob = TaggableModel.create(:name => "Bob")
|
|
446
|
+
bob.set_tag_list_on(:rotors, "spinning, jumping")
|
|
447
|
+
bob.tag_list_on(:rotors).should == ["spinning","jumping"]
|
|
448
|
+
bob.save
|
|
449
|
+
|
|
450
|
+
TaggableModel.tagged_with("spinning", :on => :rotors).to_a.should == [bob]
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it "should be able to use named scopes to chain tag finds" do
|
|
454
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "fitter, happier, more productive", :skill_list => "ruby, rails, css")
|
|
455
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "weaker, depressed, inefficient", :skill_list => "ruby, rails, css")
|
|
456
|
+
steve = TaggableModel.create(:name => 'Steve', :tag_list => 'fitter, happier, more productive', :skill_list => 'c++, java, python')
|
|
457
|
+
|
|
458
|
+
# Let's only find those productive Rails developers
|
|
459
|
+
TaggableModel.tagged_with('rails', :on => :skills, :order => 'taggable_models.name').to_a.should == [bob, frank]
|
|
460
|
+
TaggableModel.tagged_with('happier', :on => :tags, :order => 'taggable_models.name').to_a.should == [bob, steve]
|
|
461
|
+
TaggableModel.tagged_with('rails', :on => :skills).tagged_with('happier', :on => :tags).to_a.should == [bob]
|
|
462
|
+
TaggableModel.tagged_with('rails').tagged_with('happier', :on => :tags).to_a.should == [bob]
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
it "should be able to find tagged with only the matching tags" do
|
|
466
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "lazy, happier")
|
|
467
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "fitter, happier, inefficient")
|
|
468
|
+
steve = TaggableModel.create(:name => 'Steve', :tag_list => "fitter, happier")
|
|
469
|
+
|
|
470
|
+
TaggableModel.tagged_with("fitter, happier", :match_all => true).to_a.should == [steve]
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
it "should be able to find tagged with some excluded tags" do
|
|
474
|
+
bob = TaggableModel.create(:name => "Bob", :tag_list => "happier, lazy")
|
|
475
|
+
frank = TaggableModel.create(:name => "Frank", :tag_list => "happier")
|
|
476
|
+
steve = TaggableModel.create(:name => 'Steve', :tag_list => "happier")
|
|
477
|
+
|
|
478
|
+
TaggableModel.tagged_with("lazy", :exclude => true).to_a.should == [frank, steve]
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
it "should return an empty scope for empty tags" do
|
|
482
|
+
TaggableModel.tagged_with('').should == []
|
|
483
|
+
TaggableModel.tagged_with(' ').should == []
|
|
484
|
+
TaggableModel.tagged_with(nil).should == []
|
|
485
|
+
TaggableModel.tagged_with([]).should == []
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
it "should not create duplicate taggings" do
|
|
489
|
+
bob = TaggableModel.create(:name => "Bob")
|
|
490
|
+
lambda {
|
|
491
|
+
bob.tag_list << "happier"
|
|
492
|
+
bob.tag_list << "happier"
|
|
493
|
+
bob.save
|
|
494
|
+
}.should change(ActsAsTaggableOn::Tagging, :count).by(1)
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
describe "Associations" do
|
|
498
|
+
before(:each) do
|
|
499
|
+
@taggable = TaggableModel.create(:tag_list => "awesome, epic")
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
it "should not remove tags when creating associated objects" do
|
|
503
|
+
@taggable.untaggable_models.create!
|
|
504
|
+
@taggable.reload
|
|
505
|
+
@taggable.tag_list.should have(2).items
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
describe "grouped_column_names_for method" do
|
|
510
|
+
it "should return all column names joined for Tag GROUP clause" do
|
|
511
|
+
@taggable.grouped_column_names_for(ActsAsTaggableOn::Tag).should == "tags.id, tags.name"
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
it "should return all column names joined for TaggableModel GROUP clause" do
|
|
515
|
+
@taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.id, taggable_models.name, taggable_models.type"
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
it "should return all column names joined for NonStandardIdTaggableModel GROUP clause" do
|
|
519
|
+
@taggable.grouped_column_names_for(TaggableModel).should == "taggable_models.#{TaggableModel.primary_key}, taggable_models.name, taggable_models.type"
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
describe "Single Table Inheritance" do
|
|
524
|
+
before do
|
|
525
|
+
@taggable = TaggableModel.new(:name => "taggable")
|
|
526
|
+
@inherited_same = InheritingTaggableModel.new(:name => "inherited same")
|
|
527
|
+
@inherited_different = AlteredInheritingTaggableModel.new(:name => "inherited different")
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
it "should be able to save tags for inherited models" do
|
|
531
|
+
@inherited_same.tag_list = "bob, kelso"
|
|
532
|
+
@inherited_same.save
|
|
533
|
+
InheritingTaggableModel.tagged_with("bob").first.should == @inherited_same
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
it "should find STI tagged models on the superclass" do
|
|
537
|
+
@inherited_same.tag_list = "bob, kelso"
|
|
538
|
+
@inherited_same.save
|
|
539
|
+
TaggableModel.tagged_with("bob").first.should == @inherited_same
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
it "should be able to add on contexts only to some subclasses" do
|
|
543
|
+
@inherited_different.part_list = "fork, spoon"
|
|
544
|
+
@inherited_different.save
|
|
545
|
+
InheritingTaggableModel.tagged_with("fork", :on => :parts).should be_empty
|
|
546
|
+
AlteredInheritingTaggableModel.tagged_with("fork", :on => :parts).first.should == @inherited_different
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
it "should have different tag_counts_on for inherited models" do
|
|
550
|
+
@inherited_same.tag_list = "bob, kelso"
|
|
551
|
+
@inherited_same.save!
|
|
552
|
+
@inherited_different.tag_list = "fork, spoon"
|
|
553
|
+
@inherited_different.save!
|
|
554
|
+
|
|
555
|
+
InheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso)
|
|
556
|
+
AlteredInheritingTaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(fork spoon)
|
|
557
|
+
TaggableModel.tag_counts_on(:tags, :order => 'tags.id').map(&:name).should == %w(bob kelso fork spoon)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
it 'should store same tag without validation conflict' do
|
|
561
|
+
@taggable.tag_list = 'one'
|
|
562
|
+
@taggable.save!
|
|
563
|
+
|
|
564
|
+
@inherited_same.tag_list = 'one'
|
|
565
|
+
@inherited_same.save!
|
|
566
|
+
|
|
567
|
+
@inherited_same.update_attributes! :name => 'foo'
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
describe "NonStandardIdTaggable" do
|
|
572
|
+
before(:each) do
|
|
573
|
+
clean_database!
|
|
574
|
+
@taggable = NonStandardIdTaggableModel.new(:name => "Bob Jones")
|
|
575
|
+
@taggables = [@taggable, NonStandardIdTaggableModel.new(:name => "John Doe")]
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
it "should have tag types" do
|
|
579
|
+
[:tags, :languages, :skills, :needs, :offerings].each do |type|
|
|
580
|
+
NonStandardIdTaggableModel.tag_types.should include type
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
@taggable.tag_types.should == NonStandardIdTaggableModel.tag_types
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
it "should have tag_counts_on" do
|
|
587
|
+
NonStandardIdTaggableModel.tag_counts_on(:tags).all.should be_empty
|
|
588
|
+
|
|
589
|
+
@taggable.tag_list = ["awesome", "epic"]
|
|
590
|
+
@taggable.save
|
|
591
|
+
|
|
592
|
+
NonStandardIdTaggableModel.tag_counts_on(:tags).length.should == 2
|
|
593
|
+
@taggable.tag_counts_on(:tags).length.should == 2
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
it "should be able to create tags" do
|
|
597
|
+
@taggable.skill_list = "ruby, rails, css"
|
|
598
|
+
@taggable.instance_variable_get("@skill_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
|
599
|
+
|
|
600
|
+
lambda {
|
|
601
|
+
@taggable.save
|
|
602
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(3)
|
|
603
|
+
|
|
604
|
+
@taggable.reload
|
|
605
|
+
@taggable.skill_list.sort.should == %w(ruby rails css).sort
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it "should be able to create tags through the tag list directly" do
|
|
609
|
+
@taggable.tag_list_on(:test).add("hello")
|
|
610
|
+
@taggable.tag_list_cache_on(:test).should_not be_empty
|
|
611
|
+
@taggable.tag_list_on(:test).should == ["hello"]
|
|
612
|
+
|
|
613
|
+
@taggable.save
|
|
614
|
+
@taggable.save_tags
|
|
615
|
+
|
|
616
|
+
@taggable.reload
|
|
617
|
+
@taggable.tag_list_on(:test).should == ["hello"]
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
describe "Dirty Objects" do
|
|
622
|
+
before(:each) do
|
|
623
|
+
@taggable = TaggableModel.create(:tag_list => "awesome, epic")
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
it 'should show changes of dirty object' do
|
|
627
|
+
@taggable.changes.should == {}
|
|
628
|
+
@taggable.tag_list = 'one'
|
|
629
|
+
@taggable.changes.should == {"tag_list"=>["awesome, epic", ["one"]]}
|
|
630
|
+
|
|
631
|
+
@taggable.tag_list_changed?.should be_true
|
|
632
|
+
@taggable.tag_list_was.should == "awesome, epic"
|
|
633
|
+
@taggable.tag_list_change.should == ["awesome, epic", ["one"]]
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
it 'should show no changes if the same tag_list' do
|
|
637
|
+
@taggable.tag_list = "awesome, epic"
|
|
638
|
+
@taggable.tag_list_changed?.should be_false
|
|
639
|
+
@taggable.changes.should == {}
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
describe "Scored taggings" do
|
|
644
|
+
before(:each) do
|
|
645
|
+
@taggable = ScoredTaggableModel.create
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
it "should have tag types" do
|
|
649
|
+
[:question_counts].each do |type|
|
|
650
|
+
ScoredTaggableModel.tag_types.should include type
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
@taggable.tag_types.should == ScoredTaggableModel.tag_types
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it "should have tag_counts_on" do
|
|
657
|
+
ScoredTaggableModel.tag_counts_on(:question_counts).all.should be_empty
|
|
658
|
+
|
|
659
|
+
@taggable.question_count_list = ["ruby:5", "sql:4"]
|
|
660
|
+
@taggable.save
|
|
661
|
+
|
|
662
|
+
ScoredTaggableModel.tag_counts_on(:question_counts).length.should == 2
|
|
663
|
+
@taggable.tag_counts_on(:question_counts).length.should == 2
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
it "should be able to create tags" do
|
|
667
|
+
@taggable.question_count_list = 'ruby:5, sql:4'
|
|
668
|
+
@taggable.instance_variable_get("@question_count_list").instance_of?(ActsAsTaggableOn::TagList).should be_true
|
|
669
|
+
|
|
670
|
+
lambda {
|
|
671
|
+
@taggable.save
|
|
672
|
+
}.should change(ActsAsTaggableOn::Tag, :count).by(2)
|
|
673
|
+
|
|
674
|
+
@taggable.reload
|
|
675
|
+
@taggable.question_count_list.sort.should == %w(ruby:5 sql:4).sort
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
it "should be able to create tags through the tag list directly" do
|
|
679
|
+
@taggable.tag_list_on(:question_counts).add("ruby:9")
|
|
680
|
+
@taggable.tag_list_cache_on(:question_counts).should_not be_empty
|
|
681
|
+
@taggable.tag_list_on(:question_counts).should == ["ruby:9"]
|
|
682
|
+
|
|
683
|
+
@taggable.save
|
|
684
|
+
@taggable.save_tags
|
|
685
|
+
|
|
686
|
+
@taggable.reload
|
|
687
|
+
@taggable.tag_list_on(:question_counts).should == ["ruby:9"]
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
end
|
|
691
|
+
=end
|
|
692
|
+
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
|