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 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"