rocket_tag 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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.0.0"
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.0)
5
- activesupport (= 3.2.0)
4
+ activemodel (3.2.3)
5
+ activesupport (= 3.2.3)
6
6
  builder (~> 3.0.0)
7
- activerecord (3.2.0)
8
- activemodel (= 3.2.0)
9
- activesupport (= 3.2.0)
10
- arel (~> 3.0.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.0)
12
+ activesupport (3.2.3)
13
13
  i18n (~> 0.6)
14
14
  multi_json (~> 1.0)
15
- arel (3.0.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.0.4)
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.9.5)
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.31)
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.0.0)
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
- # Match any tag across any contexts
37
- TaggableModel.tagged_with ["forking", "kiting"]
36
+ Match any tag across any contexts
38
37
 
39
- # Match all tags across any contexts
40
- TaggableModel.tagged_with ["forking", "kiting"], :all => true
38
+ TaggableModel.tagged_with ["forking", "kiting"]
41
39
 
42
- # Match any tag on a specific context
43
- TaggableModel.tagged_with ["math", "kiting"], :on => "skills"
40
+ Match all tags across any contexts
44
41
 
45
- # Match all tags on a specific context
46
- TaggableModel.tagged_with ["math", "kiting"], :all => true, :on => "skills"
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
- # Match a miniumum number of tags
49
- TaggableModel.tagged_with ["math", "kiting", "coding", "sleeping"], :min => 2, :on => "skills"
52
+ Match a miniumum number of tags
53
+
54
+ TaggableModel.tagged_with ["math", "kiting", "coding", "sleeping"], :min => 2, :on => "skills"
50
55
 
51
- # Take advantage of the tags_count synthetic column returned with every query
52
- TaggableModel.tagged_with(["math", "kiting", "coding", "sleeping"], :on => "skills").where{tags_count>=2}
53
-
54
- # Mix with active relation
55
- TaggableModel.tagged_with(["forking", "kiting"]).where( ["created_at > ?", Time.zone.now.ago(5.hours)])
56
-
57
- # Find similar models based on tags on a specific context and return in decending order
58
- # of 'tags_count'
59
- model.find_similar :on => "skills"
60
- model.find_similar :on => "habits"
61
-
62
- # Find similar models based on tags on every context and return in decending order
63
- # of 'tags_count'. Note that each tag is still scoped according to it's context
64
- model.find_similar
65
-
66
- # For reference the SQL generated for model.find_similar when there are
67
- # context [:skills, :languages] available is
68
-
69
- SELECT "taggable_models".* FROM
70
- (
71
- SELECT COUNT("taggable_models"."id") AS tags_count,
72
- taggable_models.*
73
- FROM "taggable_models"
74
- INNER JOIN "taggings"
75
- ON "taggings"."taggable_id" = "taggable_models"."id"
76
- AND "taggings"."taggable_type" = 'TaggableModel'
77
- INNER JOIN "tags"
78
- ON "tags"."id" = "taggings"."tag_id"
79
- WHERE "taggable_models"."id" != 2
80
- AND (( ( "tags"."name" IN ( 'german', 'french' ) AND "taggings"."context" = 'languages' )
81
- OR ( "tags"."name" IN ( 'a', 'b', 'x' ) AND "taggings"."context" = 'skills' )
82
- ))
83
- GROUP BY "taggable_models"."id"
84
- ORDER BY tags_count DESC
85
- ) taggable_models
86
-
87
-
88
- # Note the aliasing of the inner select to shield the GROUP BY from downstream active relation
89
- # queries
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.3.1
1
+ 0.4.0
@@ -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 reload
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{~id}.
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
- .select("#{self.table_name}.*").
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{~id}
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 @@acts_as_rocket_tag
275
+ unless class_variable_defined?(:@@acts_as_rocket_tag)
233
276
  include RocketTag::Taggable::InstanceMethods
234
- @@acts_as_rocket_tag = true
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.3.1"
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-01-25"
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>, [">= 0"])
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.0.0"])
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>, [">= 0"])
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.0.0"])
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>, [">= 0"])
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.0.0"])
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.3.1
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-01-25 00:00:00 Z
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: "0"
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.0.0
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: 2545152122915663382
147
+ hash: -1357799826305458565
148
148
  segments:
149
149
  - 0
150
150
  version: "0"