rocket_tag 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.5.1
@@ -5,5 +5,11 @@ module RocketTag
5
5
 
6
6
  validates_presence_of :name
7
7
  validates_uniqueness_of :name
8
+
9
+ def self.by_taggable_type(type)
10
+ joins{taggings}.where{taggings.taggable_type == type.to_s}
11
+ end
12
+
13
+
8
14
  end
9
15
  end
@@ -1,4 +1,40 @@
1
1
  require 'squeel'
2
+ module Squeel
3
+ module Adapters
4
+ module ActiveRecord
5
+ module RelationExtensions
6
+
7
+ # The purpose of this call is to close a query and make
8
+ # it behave as a simple table or view. If a query has
9
+ # aggregate functions applied then downstream active
10
+ # relation chaining causes unpredictable behaviour.
11
+ #
12
+ # This call isolates the group by behaviours.
13
+ def isolate_group_by_as(type)
14
+ type.from("(#{to_sql}) #{type.table_name}")
15
+ end
16
+
17
+ def select_all
18
+ select('*')
19
+ end
20
+
21
+ # We really only want to group on id for practical
22
+ # purposes but POSTGRES requires that a group by outputs
23
+ # all the column names not under an aggregate function.
24
+ #
25
+ # This little helper generates such a group by
26
+ def group_by_all_columns
27
+ cn = self.column_names
28
+ group { cn.map { |col| __send__(col) } }
29
+ end
30
+
31
+ def exists
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
2
38
 
3
39
  module RocketTag
4
40
  module Taggable
@@ -81,6 +117,7 @@ module RocketTag
81
117
  @contexts[context.to_sym] || []
82
118
  end
83
119
 
120
+
84
121
  def tagged_similar options = {}
85
122
  context = options.delete :on
86
123
  if context
@@ -111,14 +148,13 @@ module RocketTag
111
148
  s | t
112
149
  end
113
150
 
114
- inner = self.class.select{count(~id).as(tags_count)}.
115
- select("#{self.class.table_name}.*").
116
- joins{tags}.where{condition}.
117
- group(self.class.column_names.map{|col| "#{self.class.table_name}.#{col}"}).
118
- where{~id != my{id}}.
119
- order("tags_count DESC")
151
+ r = self.class.
152
+ joins{tags}.
153
+ where{condition}.
154
+ where{~id != my{id}}
155
+
156
+ self.class.count_tags(r)
120
157
 
121
- r = self.class.from("(#{inner.to_sql}) #{self.class.table_name}")
122
158
  end
123
159
  end
124
160
 
@@ -128,9 +164,44 @@ module RocketTag
128
164
  @rocket_tag ||= RocketTag::Taggable::Manager.new(self)
129
165
  end
130
166
 
131
- def _with_tag_context context
167
+ # Provides the tag counting functionality by adding an
168
+ # aggregate count on id. Assumes valid a join has been
169
+ # made.
170
+ def count_tags(rel)
171
+ rel.select_all.
172
+ select{count(~id).as(tags_count)}.
173
+ group_by_all_columns.
174
+ order("tags_count DESC").
175
+ isolate_group_by_as(self)
176
+ end
177
+
178
+ # Filters tags according to
179
+ # context. context param can
180
+ # be either a single context
181
+ # id or an array of context ids
182
+ def with_tag_context context
132
183
  if context
133
- where{taggings.context == context.to_s}
184
+ if context
185
+ if context.class == Array
186
+ contexts = context
187
+ else
188
+ contexts = [context]
189
+ end
190
+ else
191
+ contexts = []
192
+ end
193
+
194
+ conditions = contexts.map do |context|
195
+ squeel do
196
+ (taggings.context == context.to_s)
197
+ end
198
+ end
199
+
200
+ condition = conditions.inject do |s, t|
201
+ s | t
202
+ end
203
+
204
+ where{condition}
134
205
  else
135
206
  where{ }
136
207
  end
@@ -151,25 +222,17 @@ module RocketTag
151
222
  # Generates a query that provides the matches
152
223
  # along with an extra column :tags_count.
153
224
  def tagged_with tags_list, options = {}
154
- on = options.delete :on
155
225
 
156
- inner = select{count(~id).as(tags_count)}.
157
- select("#{self.table_name}.*").
158
- joins{tags}.
226
+ r = joins{tags}.
159
227
  where{tags.name.in(tags_list)}.
160
- _with_tag_context(on).
161
- group(self.column_names.map{|col| "#{self.table_name}.#{col}"})
228
+ with_tag_context(options.delete :on)
162
229
 
163
- # Wrap the inner query with an outer query to shield
164
- # the group and aggregate clauses from downstream
165
- # queries
166
- r = from("(#{inner.to_sql}) #{table_name}")
230
+
231
+ r = count_tags(r)
167
232
 
168
233
  if options.delete :all
169
234
  r = r.where{tags_count==tags_list.length}
170
- end
171
-
172
- if min = options.delete(:min)
235
+ elsif min = options.delete(:min)
173
236
  r = r.where{tags_count>=min}
174
237
  end
175
238
 
@@ -178,7 +241,7 @@ module RocketTag
178
241
  id.in(r.select{"id"})
179
242
  end
180
243
  else
181
- r.select("*")
244
+ r
182
245
  end
183
246
 
184
247
  end
@@ -186,45 +249,20 @@ module RocketTag
186
249
  # Generates a query that returns list of popular tags
187
250
  # for given model with an extra column :tags_count.
188
251
  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
252
 
200
- conditions = contexts.map do |context|
201
- squeel do
202
- (taggings.context == context.to_s)
203
- end
204
- end
253
+ r =
254
+ RocketTag::Tag.
255
+ joins{taggings}.
256
+ with_tag_context(options.delete :on).
257
+ by_taggable_type(self)
205
258
 
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}")
259
+ r = count_tags(r)
216
260
 
217
261
  if min = options.delete(:min)
218
262
  r = r.where{tags_count>=min}
219
263
  end
220
264
 
221
- if options.delete :sifter
222
- squeel do
223
- id.in(r.select{"id"})
224
- end
225
- else
226
- r.select("*")
227
- end
265
+ r
228
266
  end
229
267
 
230
268
  def setup_for_rocket_tag
@@ -261,7 +299,10 @@ module RocketTag
261
299
  tags_to_assign = exisiting_tags + created_tags
262
300
 
263
301
  tags_to_assign.each do |tag|
264
- tagging = Tagging.new :tag => tag, :taggable => self, :context => context, :tagger => nil
302
+ tagging = Tagging.new :tag => tag,
303
+ :taggable => self,
304
+ :context => context,
305
+ :tagger => nil
265
306
  self.taggings << tagging
266
307
  end
267
308
  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.5.0"
8
+ s.version = "0.5.1"
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-06-26"
12
+ s.date = "2012-06-29"
13
13
  s.description = ""
14
14
  s.email = "bradphelan@xtargets.com"
15
15
  s.extra_rdoc_files = [
@@ -306,18 +306,16 @@ describe TaggableModel do
306
306
  end
307
307
  end
308
308
 
309
- describe "#tagged_with_sifter" do
310
- it "should be the work horse of #tagged_with but returns a sifter that can be composed into other queries" do
309
+ describe "Using in subqueries" do
310
+ it "should be possible to select the 'id' of the relation to use in a subquery" do
311
+
311
312
  TaggableModel.where do
312
- TaggableModel.tagged_with_sifter(["a", "b"]) & TaggableModel.tagged_with_sifter(["c"])
313
+ id.in(TaggableModel.tagged_with(["a", "b"]).select{id}) &
314
+ id.in(TaggableModel.tagged_with(["c"]).select{id})
313
315
  end.count.should == 2
314
316
 
315
- x = TaggableModel.where do
316
- TaggableModel.tagged_with_sifter(["a", "b"]) & TaggableModel.tagged_with_sifter(["c"])
317
- end.to_sql
318
-
319
317
  TaggableModel.where do
320
- TaggableModel.tagged_with_sifter(["a", "b"])
318
+ id.in(TaggableModel.tagged_with(["a", "b"]).select{id})
321
319
  end.count.should == 4
322
320
  end
323
321
 
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.0
5
+ version: 0.5.1
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-06-26 00:00:00 Z
13
+ date: 2012-06-29 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -144,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
- hash: -285597818413014276
147
+ hash: 88231840862745454
148
148
  segments:
149
149
  - 0
150
150
  version: "0"