rocket_tag 0.3.1 → 0.4.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/Gemfile +2 -2
- data/Gemfile.lock +13 -13
- data/README.md +60 -49
- data/VERSION +1 -1
- data/lib/rocket_tag/taggable.rb +54 -12
- data/rocket_tag.gemspec +8 -8
- data/spec/rocket_tag/taggable_spec.rb +20 -2
- metadata +6 -6
data/Gemfile
CHANGED
@@ -3,14 +3,14 @@ source "http://rubygems.org"
|
|
3
3
|
# Example:
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
5
5
|
gem "activerecord", ">= 3.2.0"
|
6
|
-
gem "squeel", :require => false
|
6
|
+
gem "squeel", '~> 1.0.0', :require => false
|
7
7
|
|
8
8
|
# Add dependencies to develop your gem here.
|
9
9
|
# Include everything needed to run rake, tests, features, etc.
|
10
10
|
group :development do
|
11
11
|
gem "rspec", "~> 2.3.0"
|
12
12
|
gem "yard", "~> 0.6.0"
|
13
|
-
gem "bundler", "~> 1.
|
13
|
+
gem "bundler", "~> 1.1.0"
|
14
14
|
gem "jeweler", "~> 1.6.4"
|
15
15
|
#gem "rcov", ">= 0"
|
16
16
|
gem 'sqlite3'
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
activemodel (3.2.
|
5
|
-
activesupport (= 3.2.
|
4
|
+
activemodel (3.2.3)
|
5
|
+
activesupport (= 3.2.3)
|
6
6
|
builder (~> 3.0.0)
|
7
|
-
activerecord (3.2.
|
8
|
-
activemodel (= 3.2.
|
9
|
-
activesupport (= 3.2.
|
10
|
-
arel (~> 3.0.
|
7
|
+
activerecord (3.2.3)
|
8
|
+
activemodel (= 3.2.3)
|
9
|
+
activesupport (= 3.2.3)
|
10
|
+
arel (~> 3.0.2)
|
11
11
|
tzinfo (~> 0.3.29)
|
12
|
-
activesupport (3.2.
|
12
|
+
activesupport (3.2.3)
|
13
13
|
i18n (~> 0.6)
|
14
14
|
multi_json (~> 1.0)
|
15
|
-
arel (3.0.
|
15
|
+
arel (3.0.2)
|
16
16
|
builder (3.0.0)
|
17
17
|
diff-lcs (1.1.3)
|
18
18
|
git (1.2.5)
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
bundler (~> 1.0)
|
22
22
|
git (>= 1.2.5)
|
23
23
|
rake
|
24
|
-
multi_json (1.
|
24
|
+
multi_json (1.3.4)
|
25
25
|
polyamorous (0.5.0)
|
26
26
|
activerecord (~> 3.0)
|
27
27
|
rake (0.9.2.2)
|
@@ -34,11 +34,11 @@ GEM
|
|
34
34
|
diff-lcs (~> 1.1.2)
|
35
35
|
rspec-mocks (2.3.0)
|
36
36
|
sqlite3 (1.3.5)
|
37
|
-
squeel (0.
|
37
|
+
squeel (1.0.1)
|
38
38
|
activerecord (~> 3.0)
|
39
39
|
activesupport (~> 3.0)
|
40
40
|
polyamorous (~> 0.5.0)
|
41
|
-
tzinfo (0.3.
|
41
|
+
tzinfo (0.3.33)
|
42
42
|
yard (0.6.8)
|
43
43
|
|
44
44
|
PLATFORMS
|
@@ -46,10 +46,10 @@ PLATFORMS
|
|
46
46
|
|
47
47
|
DEPENDENCIES
|
48
48
|
activerecord (>= 3.2.0)
|
49
|
-
bundler (~> 1.
|
49
|
+
bundler (~> 1.1.0)
|
50
50
|
jeweler (~> 1.6.4)
|
51
51
|
rake
|
52
52
|
rspec (~> 2.3.0)
|
53
53
|
sqlite3
|
54
|
-
squeel
|
54
|
+
squeel (~> 1.0.0)
|
55
55
|
yard (~> 0.6.0)
|
data/README.md
CHANGED
@@ -33,60 +33,69 @@ Usage
|
|
33
33
|
item.habits = ["forking", "talking"]
|
34
34
|
|
35
35
|
|
36
|
-
|
37
|
-
TaggableModel.tagged_with ["forking", "kiting"]
|
36
|
+
Match any tag across any contexts
|
38
37
|
|
39
|
-
|
40
|
-
TaggableModel.tagged_with ["forking", "kiting"], :all => true
|
38
|
+
TaggableModel.tagged_with ["forking", "kiting"]
|
41
39
|
|
42
|
-
|
43
|
-
TaggableModel.tagged_with ["math", "kiting"], :on => "skills"
|
40
|
+
Match all tags across any contexts
|
44
41
|
|
45
|
-
|
46
|
-
|
42
|
+
TaggableModel.tagged_with ["forking", "kiting"], :all => true
|
43
|
+
|
44
|
+
Match any tag on a specific context
|
45
|
+
|
46
|
+
TaggableModel.tagged_with ["math", "kiting"], :on => "skills"
|
47
|
+
|
48
|
+
Match all tags on a specific context
|
49
|
+
|
50
|
+
TaggableModel.tagged_with ["math", "kiting"], :all => true, :on => "skills"
|
47
51
|
|
48
|
-
|
49
|
-
|
52
|
+
Match a miniumum number of tags
|
53
|
+
|
54
|
+
TaggableModel.tagged_with ["math", "kiting", "coding", "sleeping"], :min => 2, :on => "skills"
|
50
55
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
56
|
+
Take advantage of the tags_count synthetic column returned with every query
|
57
|
+
|
58
|
+
TaggableModel.tagged_with(["math", "kiting", "coding", "sleeping"], :on => "skills").where{tags_count>=2}
|
59
|
+
|
60
|
+
Mix with active relation
|
61
|
+
|
62
|
+
TaggableModel.tagged_with(["forking", "kiting"]).where( ["created_at > ?", Time.zone.now.ago(5.hours)])
|
63
|
+
|
64
|
+
Find similar models based on tags on a specific context and return in decending order
|
65
|
+
of 'tags_count'
|
66
|
+
|
67
|
+
model.tagged_similar :on => "skills"
|
68
|
+
model.tagged_similar :on => "habits"
|
69
|
+
|
70
|
+
Find similar models based on tags on every context and return in decending order
|
71
|
+
of 'tags_count'. Note that each tag is still scoped according to it's context
|
72
|
+
|
73
|
+
model.tagged_similar
|
74
|
+
|
75
|
+
For reference the SQL generated for model.tagged_similar when there are
|
76
|
+
context [:skills, :languages] available is
|
77
|
+
|
78
|
+
SELECT "taggable_models".* FROM
|
79
|
+
(
|
80
|
+
SELECT COUNT("taggable_models"."id") AS tags_count,
|
81
|
+
taggable_models.*
|
82
|
+
FROM "taggable_models"
|
83
|
+
INNER JOIN "taggings"
|
84
|
+
ON "taggings"."taggable_id" = "taggable_models"."id"
|
85
|
+
AND "taggings"."taggable_type" = 'TaggableModel'
|
86
|
+
INNER JOIN "tags"
|
87
|
+
ON "tags"."id" = "taggings"."tag_id"
|
88
|
+
WHERE "taggable_models"."id" != 2
|
89
|
+
AND (( ( "tags"."name" IN ( 'german', 'french' ) AND "taggings"."context" = 'languages' )
|
90
|
+
OR ( "tags"."name" IN ( 'a', 'b', 'x' ) AND "taggings"."context" = 'skills' )
|
91
|
+
))
|
92
|
+
GROUP BY "taggable_models"."id"
|
93
|
+
ORDER BY tags_count DESC
|
94
|
+
) taggable_models
|
95
|
+
|
96
|
+
|
97
|
+
Note the aliasing of the inner select to shield the GROUP BY from downstream active relation
|
98
|
+
queries
|
90
99
|
|
91
100
|
== Contributing to rocket_tag
|
92
101
|
|
@@ -103,3 +112,5 @@ Usage
|
|
103
112
|
Copyright (c) 2011 Brad Phelan. See LICENSE.txt for
|
104
113
|
further details.
|
105
114
|
|
115
|
+
Available for hire for your next ROR project at <a href="http://xtargets.com" title="XTargets: Ruby On Rails Solutions" rel="author">XTargets: Ruby On Rails Solutions</a>
|
116
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/rocket_tag/taggable.rb
CHANGED
@@ -23,7 +23,7 @@ module RocketTag
|
|
23
23
|
# but must be
|
24
24
|
#
|
25
25
|
# hello,"foo"
|
26
|
-
|
26
|
+
return [] if list.empty?
|
27
27
|
list = list.gsub /,\s+"/, ',"'
|
28
28
|
list = list.parse_csv.map &:strip
|
29
29
|
else
|
@@ -52,13 +52,13 @@ module RocketTag
|
|
52
52
|
end
|
53
53
|
|
54
54
|
module InstanceMethods
|
55
|
-
def
|
56
|
-
super
|
55
|
+
def reload_with_tags(options = nil)
|
57
56
|
self.class.rocket_tag.contexts.each do |context|
|
58
57
|
write_context context, []
|
59
58
|
end
|
60
59
|
@tags_cached = false
|
61
60
|
cache_tags
|
61
|
+
reload_without_tags(options)
|
62
62
|
end
|
63
63
|
|
64
64
|
def cache_tags
|
@@ -114,7 +114,7 @@ module RocketTag
|
|
114
114
|
inner = self.class.select{count(~id).as(tags_count)}.
|
115
115
|
select("#{self.class.table_name}.*").
|
116
116
|
joins{tags}.where{condition}.
|
117
|
-
group{
|
117
|
+
group(self.class.column_names.map{|col| "#{self.class.table_name}.#{col}"}).
|
118
118
|
where{~id != my{id}}.
|
119
119
|
order("tags_count DESC")
|
120
120
|
|
@@ -153,12 +153,12 @@ module RocketTag
|
|
153
153
|
def tagged_with tags_list, options = {}
|
154
154
|
on = options.delete :on
|
155
155
|
|
156
|
-
inner = select{count(~id).as(tags_count)}
|
157
|
-
|
156
|
+
inner = select{count(~id).as(tags_count)}.
|
157
|
+
select("#{self.table_name}.*").
|
158
158
|
joins{tags}.
|
159
159
|
where{tags.name.in(my{tags_list})}.
|
160
160
|
_with_tag_context(on).
|
161
|
-
group{
|
161
|
+
group(self.column_names.map{|col| "#{self.table_name}.#{col}"})
|
162
162
|
|
163
163
|
# Wrap the inner query with an outer query to shield
|
164
164
|
# the group and aggregate clauses from downstream
|
@@ -183,6 +183,50 @@ module RocketTag
|
|
183
183
|
|
184
184
|
end
|
185
185
|
|
186
|
+
# Generates a query that returns list of popular tags
|
187
|
+
# for given model with an extra column :tags_count.
|
188
|
+
def popular_tags options={}
|
189
|
+
context = options.delete :on
|
190
|
+
if context
|
191
|
+
if context.class == Array
|
192
|
+
contexts = context
|
193
|
+
else
|
194
|
+
contexts = [context]
|
195
|
+
end
|
196
|
+
else
|
197
|
+
contexts = []
|
198
|
+
end
|
199
|
+
|
200
|
+
conditions = contexts.map do |context|
|
201
|
+
squeel do
|
202
|
+
(taggings.context == my{context})
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
condition = conditions.inject do |s, t|
|
207
|
+
s | t
|
208
|
+
end
|
209
|
+
inner = RocketTag::Tag.select{count(~id).as(tags_count)}.
|
210
|
+
select("#{RocketTag::Tag.table_name}.*").
|
211
|
+
joins{taggings.outer}.where{condition}.
|
212
|
+
where{ taggings.taggable_type == my{self.to_s} }.
|
213
|
+
group{~id} #.
|
214
|
+
#order("tags_count DESC")
|
215
|
+
r = from("(#{inner.to_sql}) #{table_name}")
|
216
|
+
|
217
|
+
if min = options.delete(:min)
|
218
|
+
r = r.where{tags_count>=my{min}}
|
219
|
+
end
|
220
|
+
|
221
|
+
if options.delete :sifter
|
222
|
+
squeel do
|
223
|
+
id.in(r.select{"id"})
|
224
|
+
end
|
225
|
+
else
|
226
|
+
r.select("*")
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
186
230
|
def setup_for_rocket_tag
|
187
231
|
unless @setup_for_rocket_tag
|
188
232
|
@setup_for_rocket_tag = true
|
@@ -227,11 +271,11 @@ module RocketTag
|
|
227
271
|
end
|
228
272
|
end
|
229
273
|
|
230
|
-
@@acts_as_rocket_tag = false
|
231
274
|
def attr_taggable *contexts
|
232
|
-
unless
|
275
|
+
unless class_variable_defined?(:@@acts_as_rocket_tag)
|
233
276
|
include RocketTag::Taggable::InstanceMethods
|
234
|
-
|
277
|
+
class_variable_set(:@@acts_as_rocket_tag, true)
|
278
|
+
alias_method_chain :reload, :tags
|
235
279
|
end
|
236
280
|
|
237
281
|
if contexts.blank?
|
@@ -284,8 +328,6 @@ module RocketTag
|
|
284
328
|
end
|
285
329
|
end
|
286
330
|
end
|
287
|
-
|
288
331
|
end
|
289
|
-
|
290
332
|
end
|
291
333
|
end
|
data/rocket_tag.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "rocket_tag"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brad Phelan"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-06-18"
|
13
13
|
s.description = ""
|
14
14
|
s.email = "bradphelan@xtargets.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -50,29 +50,29 @@ Gem::Specification.new do |s|
|
|
50
50
|
|
51
51
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
52
52
|
s.add_runtime_dependency(%q<activerecord>, [">= 3.2.0"])
|
53
|
-
s.add_runtime_dependency(%q<squeel>, ["
|
53
|
+
s.add_runtime_dependency(%q<squeel>, ["~> 1.0.0"])
|
54
54
|
s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
|
55
55
|
s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
|
56
|
-
s.add_development_dependency(%q<bundler>, ["~> 1.
|
56
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
|
57
57
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
58
58
|
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
59
59
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
60
60
|
else
|
61
61
|
s.add_dependency(%q<activerecord>, [">= 3.2.0"])
|
62
|
-
s.add_dependency(%q<squeel>, ["
|
62
|
+
s.add_dependency(%q<squeel>, ["~> 1.0.0"])
|
63
63
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
64
64
|
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
65
|
-
s.add_dependency(%q<bundler>, ["~> 1.
|
65
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.0"])
|
66
66
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
67
67
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
68
68
|
s.add_dependency(%q<rake>, [">= 0"])
|
69
69
|
end
|
70
70
|
else
|
71
71
|
s.add_dependency(%q<activerecord>, [">= 3.2.0"])
|
72
|
-
s.add_dependency(%q<squeel>, ["
|
72
|
+
s.add_dependency(%q<squeel>, ["~> 1.0.0"])
|
73
73
|
s.add_dependency(%q<rspec>, ["~> 2.3.0"])
|
74
74
|
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
75
|
-
s.add_dependency(%q<bundler>, ["~> 1.
|
75
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.0"])
|
76
76
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
77
77
|
s.add_dependency(%q<sqlite3>, [">= 0"])
|
78
78
|
s.add_dependency(%q<rake>, [">= 0"])
|
@@ -14,6 +14,11 @@ describe TaggableModel do
|
|
14
14
|
m = TaggableModel.new :skills => %q%hello, "is it me, you are looking for", cat%
|
15
15
|
m.skills.should == ["hello", "is it me, you are looking for", "cat"]
|
16
16
|
end
|
17
|
+
|
18
|
+
it "parses an empty string to an empty array" do
|
19
|
+
m = TaggableModel.new :skills => ""
|
20
|
+
m.skills.should == []
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
24
|
describe "#save" do
|
@@ -317,8 +322,21 @@ describe TaggableModel do
|
|
317
322
|
end
|
318
323
|
|
319
324
|
end
|
320
|
-
|
325
|
+
|
326
|
+
describe "#popular_tags" do
|
327
|
+
it "should return correct list (and correctly ordered) of popular tags for class and context" do
|
328
|
+
TaggableModel.popular_tags.all.length.should == RocketTag::Tag.all.count
|
329
|
+
TaggableModel.popular_tags.limit(10).all.length.should == 10
|
330
|
+
TaggableModel.popular_tags.order('tags_count desc, name desc').first.name.should == 'c'
|
331
|
+
TaggableModel.popular_tags.order('id asc').first.name.should == 'a'
|
332
|
+
TaggableModel.popular_tags.order('id asc').last.name.should == 'jinglish'
|
333
|
+
TaggableModel.popular_tags(:on=>:skills).order('name asc').first.name.should == 'a'
|
334
|
+
TaggableModel.popular_tags(:on=>:skills).order('name asc').last.name.should == 'y'
|
335
|
+
TaggableModel.popular_tags(:on=>[:skills, :languages]).order('id asc').first.name.should == 'a'
|
336
|
+
TaggableModel.popular_tags(:on=>[:skills, :languages]).order('id asc').last.name.should == 'jinglish'
|
337
|
+
TaggableModel.popular_tags(:min=>2).all.length.should == 6 ## dirty!
|
338
|
+
end
|
339
|
+
end
|
321
340
|
end
|
322
341
|
end
|
323
342
|
end
|
324
|
-
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rocket_tag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.4.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Brad Phelan
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-06-18 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -28,9 +28,9 @@ dependencies:
|
|
28
28
|
requirement: &id002 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: *id002
|
@@ -63,7 +63,7 @@ dependencies:
|
|
63
63
|
requirements:
|
64
64
|
- - ~>
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 1.
|
66
|
+
version: 1.1.0
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
69
|
version_requirements: *id005
|
@@ -144,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
144
|
requirements:
|
145
145
|
- - ">="
|
146
146
|
- !ruby/object:Gem::Version
|
147
|
-
hash:
|
147
|
+
hash: -1357799826305458565
|
148
148
|
segments:
|
149
149
|
- 0
|
150
150
|
version: "0"
|