tagtical 1.0.6
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 +25 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +25 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +306 -0
- data/Rakefile +59 -0
- data/VERSION +1 -0
- data/generators/tagtical_migration/tagtical_migration_generator.rb +7 -0
- data/generators/tagtical_migration/templates/migration.rb +34 -0
- data/lib/generators/tagtical/migration/migration_generator.rb +32 -0
- data/lib/generators/tagtical/migration/templates/active_record/migration.rb +35 -0
- data/lib/tagtical/acts_as_tagger.rb +69 -0
- data/lib/tagtical/compatibility/Gemfile +8 -0
- data/lib/tagtical/compatibility/active_record_backports.rb +21 -0
- data/lib/tagtical/tag.rb +314 -0
- data/lib/tagtical/tag_list.rb +133 -0
- data/lib/tagtical/taggable/cache.rb +53 -0
- data/lib/tagtical/taggable/collection.rb +141 -0
- data/lib/tagtical/taggable/core.rb +317 -0
- data/lib/tagtical/taggable/ownership.rb +110 -0
- data/lib/tagtical/taggable/related.rb +60 -0
- data/lib/tagtical/taggable.rb +51 -0
- data/lib/tagtical/tagging.rb +42 -0
- data/lib/tagtical/tags_helper.rb +17 -0
- data/lib/tagtical.rb +47 -0
- data/rails/init.rb +1 -0
- data/spec/bm.rb +53 -0
- data/spec/database.yml +17 -0
- data/spec/database.yml.sample +17 -0
- data/spec/models.rb +60 -0
- data/spec/schema.rb +46 -0
- data/spec/spec_helper.rb +159 -0
- data/spec/tagtical/acts_as_tagger_spec.rb +94 -0
- data/spec/tagtical/tag_list_spec.rb +102 -0
- data/spec/tagtical/tag_spec.rb +301 -0
- data/spec/tagtical/taggable_spec.rb +460 -0
- data/spec/tagtical/tagger_spec.rb +76 -0
- data/spec/tagtical/tagging_spec.rb +52 -0
- data/spec/tagtical/tags_helper_spec.rb +28 -0
- data/spec/tagtical/tagtical_spec.rb +340 -0
- metadata +132 -0
@@ -0,0 +1,301 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Tagtical::Tag do
|
4
|
+
|
5
|
+
before do
|
6
|
+
clean_database!
|
7
|
+
@klass = Tagtical::Tag
|
8
|
+
@tag = @klass.new(:value => "train")
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { @tag }
|
12
|
+
|
13
|
+
it { should be_valid }
|
14
|
+
|
15
|
+
its(:count) { should == 0 }
|
16
|
+
its(:to_s) { should == @tag.value }
|
17
|
+
|
18
|
+
specify { @klass.sti_name.should be_nil }
|
19
|
+
|
20
|
+
its(:type) { should be_nil }
|
21
|
+
|
22
|
+
describe ".find_sti_class" do
|
23
|
+
specify do
|
24
|
+
{"skill" => Tag::Skill, "tag" => @klass}.each do |arg, result|
|
25
|
+
@klass.send(:find_sti_class, arg).should == result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "validations" do
|
31
|
+
|
32
|
+
it "should require a value" do
|
33
|
+
{"" => 1, nil => 1, "foo" => 0}.each do |value, error_count|
|
34
|
+
@tag.value = value
|
35
|
+
@tag.valid?
|
36
|
+
@tag.errors[:value].should have(error_count).items
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be unique on value and type" do
|
41
|
+
lambda {
|
42
|
+
Tag::Skill.create!(:value => "foo")
|
43
|
+
Tag::Craft.create!(:value => "foo")
|
44
|
+
}.should change(@klass, :count).by(2)
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when possible_values specified" do
|
48
|
+
before { @klass.possible_values = %w{knife fork spoon} }
|
49
|
+
after { @klass.possible_values = nil}
|
50
|
+
|
51
|
+
it "should not be valid if value is not in possible_values" do
|
52
|
+
@tag.value = "glass"
|
53
|
+
@tag.should be_invalid
|
54
|
+
@tag.errors[:value][0].should == %{Value "glass" not found in list: ["knife", "fork", "spoon"]}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "tag scopes" do
|
61
|
+
before do
|
62
|
+
Tagtical::Tag.create(:value => "Plane")
|
63
|
+
Tag::Skill.create(:value => "Kung Fu")
|
64
|
+
Tag::Craft.create(:value => "Painting")
|
65
|
+
NeedTag.create(:value => "chair")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should retrieve tags finder type conditions" do
|
69
|
+
Tagtical::Tag.skills.should have_tag_values ["Kung Fu", "Painting"]
|
70
|
+
Tagtical::Tag.skills(:only => :current).should have_tag_values ["Kung Fu"]
|
71
|
+
Tagtical::Tag.crafts(:only => :parents).should have_tag_values ["Kung Fu", "Plane"]
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#dump_value" do
|
77
|
+
before do
|
78
|
+
@tag = Tagtical::Tag::PartTag.new(:value => "FOO")
|
79
|
+
end
|
80
|
+
|
81
|
+
its(:value) { should == "foo" }
|
82
|
+
|
83
|
+
it "should accept a nil value" do
|
84
|
+
lambda { @tag.value = nil }.should_not raise_error
|
85
|
+
@tag.value.should be_nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#load_value" do
|
90
|
+
before do
|
91
|
+
@tag = Tag::Skill.new(:value => "basketball")
|
92
|
+
end
|
93
|
+
|
94
|
+
specify { @tag[:value].should == "basketball" }
|
95
|
+
|
96
|
+
its(:value) { should == "basketballer" }
|
97
|
+
|
98
|
+
it "should accept a nil value" do
|
99
|
+
lambda { @tag.value = nil }.should_not raise_error
|
100
|
+
@tag.value.should be_nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should refresh @value on value setter" do
|
105
|
+
@tag.value = "foo"
|
106
|
+
@tag.value.should == "foo"
|
107
|
+
@tag.value = "bar"
|
108
|
+
@tag.value.should == "bar"
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "sort" do
|
112
|
+
before do
|
113
|
+
@tag1 = @klass.new(:value => "car").tap { |x| x["relevance"] = "2.5" }
|
114
|
+
@tag2 = @klass.new(:value => "plane").tap { |x| x["relevance"] = "1.7" }
|
115
|
+
@tag3 = @klass.new(:value => "bike").tap { |x| x["relevance"] = "1.1" }
|
116
|
+
@tags = [@tag1, @tag2, @tag3]
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should sort by relevance if all tags have them" do
|
120
|
+
@tags.sort.map(&:value).should == ["bike", "plane", "car"]
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should fallback gracefully when relevance not provided" do
|
124
|
+
@tag3["relevance"] = nil
|
125
|
+
@tags.sort.map(&:value).should == ["bike", "plane", "car"]
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should sort by value when no relevances provided" do
|
129
|
+
@tags.each { |t| t["relevance"] = nil }
|
130
|
+
@tags.sort.map(&:value).should == ["bike", "car", "plane"]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#==" do
|
135
|
+
it { should == @tag }
|
136
|
+
it { should_not == "tain" }
|
137
|
+
it { should_not == @klass.new }
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#find_or_create_tags" do
|
141
|
+
before(:each) do
|
142
|
+
@klass.create!(:value => "awesome")
|
143
|
+
@klass.create!(:value => "epic")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should find both tags" do
|
147
|
+
lambda {
|
148
|
+
@klass.find_or_create_tags("awesome", "epic")
|
149
|
+
}.should change(@klass, :count).by(0)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#where_any_like" do
|
154
|
+
before do
|
155
|
+
@klass.create!(:value => "awesome")
|
156
|
+
@klass.create!(:value => "epic")
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should find both tags wildcard search" do
|
160
|
+
@klass.where_any_like(["awe", "epic"], :wildcard => true).should have(2).items
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should not be case sensitive" do
|
164
|
+
@klass.where_any_like(["AWESOME", "EpIc"]).should have(2).items
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
describe ".find_or_create_with_like_by_value!" do
|
170
|
+
before do
|
171
|
+
@tag.value = "awesome"
|
172
|
+
@tag.save
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should find by name" do
|
176
|
+
@klass.find_or_create_with_like_by_value!("awesome").should == @tag
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should find by name case insensitive" do
|
180
|
+
@klass.find_or_create_with_like_by_value!("AWESOME").should == @tag
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should create by name" do
|
184
|
+
lambda {
|
185
|
+
@klass.find_or_create_with_like_by_value!("epic")
|
186
|
+
}.should change(@klass, :count).by(1)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe ".find_or_create_tags" do
|
191
|
+
before(:each) do
|
192
|
+
@tag.value = "awesome"
|
193
|
+
@tag.save!
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should find by name" do
|
197
|
+
@klass.find_or_create_tags("awesome").should == {@tag => "awesome"}
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should find by name case insensitive" do
|
201
|
+
@klass.find_or_create_tags("AWESOME").should == {@tag => "AWESOME"}
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should create by name" do
|
205
|
+
lambda {
|
206
|
+
@klass.find_or_create_tags("epic")
|
207
|
+
}.should change(@klass, :count).by(1)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should find or create by name" do
|
211
|
+
lambda {
|
212
|
+
@klass.find_or_create_tags("awesome", "epic").keys.map(&:value).should == ["awesome", "epic"]
|
213
|
+
}.should change(@klass, :count).by(1)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should return an empty array if no tags are specified" do
|
217
|
+
@klass.find_or_create_tags([]).should == {}
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should require a name" do
|
222
|
+
@tag.value = nil
|
223
|
+
@tag.valid?
|
224
|
+
|
225
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
226
|
+
@tag.errors[:value].should == ["can't be blank"]
|
227
|
+
else
|
228
|
+
@tag.errors[:value].should == "can't be blank"
|
229
|
+
end
|
230
|
+
|
231
|
+
@tag.value = "something"
|
232
|
+
@tag.valid?
|
233
|
+
|
234
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
235
|
+
@tag.errors[:value].should == []
|
236
|
+
else
|
237
|
+
@tag.errors[:value].should be_nil
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "#where_any" do
|
242
|
+
@tag.value = "cool"
|
243
|
+
@tag.save!
|
244
|
+
@klass.where_any('cool').should include(@tag)
|
245
|
+
end
|
246
|
+
|
247
|
+
describe "Type" do
|
248
|
+
before do
|
249
|
+
@klass = @klass::Type
|
250
|
+
@type = @klass.new("skill")
|
251
|
+
end
|
252
|
+
subject { @type }
|
253
|
+
|
254
|
+
its(:klass) { should == Tag::Skill }
|
255
|
+
its(:scope_name) { should == :skills }
|
256
|
+
|
257
|
+
describe "initialize" do
|
258
|
+
it "converts string into correct format" do
|
259
|
+
{"ClassNames" => "class_name", "photo_tags" => "photo", :photo => "photo"}.each do |input, result|
|
260
|
+
@klass.new(input).should == result
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
describe ".[]" do
|
266
|
+
specify { @klass[@type].should equal @type }
|
267
|
+
specify { @klass["foo"].should be_a @klass }
|
268
|
+
end
|
269
|
+
|
270
|
+
describe "#==" do
|
271
|
+
{"foo" => false, "skill" => true, Tagtical::Tag::Type.new("skill") => true}.each do |obj, result|
|
272
|
+
specify { (subject==obj).should==result }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "#derive_class_candidates" do
|
277
|
+
specify do
|
278
|
+
subject.send(:derive_class_candidates).should include(
|
279
|
+
"Tagtical::Tag::Skill", "Tag::Skill", "Skill",
|
280
|
+
"Tagtical::Tag::SkillTag", "Tag::SkillTag", "SkillTag"
|
281
|
+
)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "#tag_list_name" do
|
286
|
+
context "when prefix is not specified" do
|
287
|
+
its(:tag_list_name) { should == "skill_list" }
|
288
|
+
end
|
289
|
+
context "when prefix is specified" do
|
290
|
+
specify { subject.tag_list_name(:all).should == "all_skill_list" }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context "when type is 'Tag'" do
|
295
|
+
before { @type = @klass.new("tag") }
|
296
|
+
its(:klass) { should == Tagtical::Tag }
|
297
|
+
its(:scope_name) { should == :tags }
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
end
|