rocket_tag 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -43,10 +43,6 @@ module RocketTag
43
43
  end
44
44
  end
45
45
 
46
- def tags_for_context context
47
- tags.where{taggings.context==my{context}}
48
- end
49
-
50
46
  def taggings_for_context context
51
47
  taggings.where{taggings.context==my{context}}
52
48
  end
@@ -86,6 +82,20 @@ module RocketTag
86
82
  end
87
83
  end
88
84
 
85
+ module InstanceMethods
86
+ def tagged_similar options = {}
87
+ context = options.delete :on
88
+ raise Exception.new("#{context} is not a valid tag context for #{self.class}") unless self.class.rocket_tag.contexts.include? context
89
+ if context
90
+ contexts = [context]
91
+ else
92
+ contexts = self.class.rocket_tag.contexts
93
+ end
94
+ tags = send context.to_sym
95
+ self.class.tagged_with(tags, options).where{id != my{id}}
96
+ end
97
+ end
98
+
89
99
  module ClassMethods
90
100
 
91
101
  def rocket_tag
@@ -100,14 +110,6 @@ module RocketTag
100
110
  end
101
111
  end
102
112
 
103
- def _with_min min, tags_list
104
- if min and min > 1
105
- group{~id}.
106
- having{count(~id)>=my{min}}
107
- else
108
- where{}
109
- end
110
- end
111
113
 
112
114
  # Generates a sifter or a where clause depending on options.
113
115
  # The sifter generates a subselect with the body of the
@@ -116,41 +118,43 @@ module RocketTag
116
118
  #
117
119
  # Query optimization is left up to the SQL engine.
118
120
  def tagged_with_sifter tags_list, options = {}
121
+ options[:sifter] = true
122
+ tagged_with tags_list, options
123
+ end
124
+
125
+ # Generates a query that provides the matches
126
+ # along with an extra column :tags_count.
127
+ def tagged_with tags_list, options = {}
119
128
  on = options.delete :on
120
- all = options.delete :all
121
- min = options.delete(:min)
122
- if all
123
- min = tags_list.length
124
- end
125
129
 
126
- lambda do |&block|
127
- if options.delete :where
128
- where &block
129
- else
130
- squeel &block
131
- end
132
- end.call do
133
- id.in(
134
- my{self}.
135
- select{id}.
130
+ inner = select{count(~id).as(tags_count)}
131
+ .select("#{self.table_name}.*").
136
132
  joins{tags}.
137
133
  where{tags.name.in(my{tags_list})}.
138
134
  _with_tag_context(on).
139
- _with_min(min, tags_list)
140
- )
141
- end
135
+ group{~id}
142
136
 
143
- end
137
+ # Wrap the inner query with an outer query to shield
138
+ # the group and aggregate clauses from downstream
139
+ # queries
140
+ r = from("(#{inner.to_sql}) #{table_name}")
144
141
 
145
- # We need to overide the default count
146
- # and do a sub select as we are using gr
147
- def count
148
- end
142
+ if options.delete :all
143
+ r = r.where{tags_count==my{tags_list.length}}
144
+ end
149
145
 
146
+ if min = options.delete(:min)
147
+ r = r.where{tags_count>=my{min}}
148
+ end
149
+
150
+ if options.delete :sifter
151
+ squeel do
152
+ id.in(r.select{"id"})
153
+ end
154
+ else
155
+ r.select("*")
156
+ end
150
157
 
151
- def tagged_with tags_list, options = {}
152
- options[:where] = true
153
- tagged_with_sifter(tags_list, options)
154
158
  end
155
159
 
156
160
  def setup_for_rocket_tag
data/rocket_tag.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "rocket_tag"
8
- s.version = "0.1.0"
8
+ s.version = "0.2.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"]
@@ -121,6 +121,55 @@ describe TaggableModel do
121
121
  pending "Need to figure out how to verify eager loading other than manually inspect the log file"
122
122
  end
123
123
 
124
+ describe "#tagged_with" do
125
+ it "should count the number of matched tags" do
126
+
127
+ #<TaggableModel id: 2, name: "00", type: nil, foo: "A"> - 3 - german, french, a, b, x
128
+ #<TaggableModel id: 3, name: "01", type: nil, foo: "B"> - 3 - german, italian, a, b, y
129
+ #<TaggableModel id: 4, name: "10", type: nil, foo: "A"> - 1 - a, c
130
+ #<TaggableModel id: 5, name: "11", type: nil, foo: "B"> - 1 - a, c
131
+ #<TaggableModel id: 7, name: "21", type: nil, foo: "B"> - 1 - german, jinglish, c, d
132
+
133
+ # r = TaggableModel.tagged_with(["a", "b", "german"]).all.each do |m|
134
+ # puts "#{m.inspect} - #{m.tags_count} - #{m.tags.map(&:name).join ', '}"
135
+ # end
136
+
137
+ r = TaggableModel.tagged_with(["a", "b", "german"]).all
138
+ r.find{|i|i.name == "00"}.tags_count.should == 3
139
+ r.find{|i|i.name == "01"}.tags_count.should == 3
140
+ r.find{|i|i.name == "10"}.tags_count.should == 1
141
+ r.find{|i|i.name == "11"}.tags_count.should == 1
142
+ r.find{|i|i.name == "21"}.tags_count.should == 1
143
+
144
+ # The 'group by' operation to generate the count tags should
145
+ # be opaque to downstream operations. Thus count should
146
+ # return the correct number of records
147
+ r = TaggableModel.tagged_with(["a", "b", "german"]).count.should == 5
148
+
149
+ # It should be possible to cascade active relation queries on
150
+ # the
151
+ r = TaggableModel.tagged_with(["a", "b", "german"]).
152
+ where{tags_count>2}.count.should == 2
153
+
154
+ # The min option is a shortcut for a query on tags_count
155
+ r = TaggableModel.tagged_with(["a", "b", "german"], :min => 2).count.should == 2
156
+
157
+ r = TaggableModel.tagged_with(["a", "b", "german"], :on => :skills).all
158
+ r.find{|i|i.name == "00"}.tags_count.should == 2
159
+ r.find{|i|i.name == "01"}.tags_count.should == 2
160
+ r.find{|i|i.name == "10"}.tags_count.should == 1
161
+ r.find{|i|i.name == "11"}.tags_count.should == 1
162
+ r.find{|i|i.name == "21"}.should be_nil
163
+
164
+ end
165
+ end
166
+
167
+ describe "#tagged_similar" do
168
+ it "should work" do
169
+ @t00.tagged_similar(:on => :skills).count.should == 3
170
+ end
171
+ end
172
+
124
173
  describe "#tagged_with" do
125
174
  describe ":all => true" do
126
175
  it "should return records where *all* tags match on any context" do
@@ -176,34 +225,60 @@ describe TaggableModel do
176
225
  end
177
226
  end
178
227
 
228
+ describe "Experiments with AREL" do
229
+ it "foo" do
230
+ u_t = Arel::Table::new :users
231
+ l_t = Arel::Table::new :logs
232
+
233
+ counts = l_t.
234
+ group(l_t[:user_id]).
235
+ project(
236
+ l_t[:user_id].as("user_id"),
237
+ l_t[:user_id].count.as("count_all")
238
+ ).as "foo"
239
+
240
+ puts TaggableModel.joins("JOIN " + counts.to_sql ).to_sql
241
+ end
242
+ it "should" do
243
+ u_t = Arel::Table::new :users
244
+ l_t = Arel::Table::new :logs
245
+
246
+ counts = l_t.
247
+ group(l_t[:user_id]).
248
+ project(
249
+ l_t[:user_id].as("user_id"),
250
+ l_t[:user_id].count.as("count_all")
251
+ ).as "foo"
252
+
253
+ users = u_t.
254
+ join(counts).
255
+ on(u_t[:id].
256
+ eq(counts[:user_id])).
257
+ project("*").project(counts[:count_all])
258
+
259
+ puts users.to_sql
260
+
261
+ end
262
+ end
263
+
179
264
  describe "#tagged_with_sifter" do
180
265
  it "should be the work horse of #tagged_with but returns a sifter that can be composed into other queries" do
181
266
  TaggableModel.where do
182
267
  TaggableModel.tagged_with_sifter(["a", "b"]) & TaggableModel.tagged_with_sifter(["c"])
183
268
  end.count.should == 2
184
269
 
270
+ x = TaggableModel.where do
271
+ TaggableModel.tagged_with_sifter(["a", "b"]) & TaggableModel.tagged_with_sifter(["c"])
272
+ end.to_sql
273
+ puts x
274
+
185
275
  TaggableModel.where do
186
276
  TaggableModel.tagged_with_sifter(["a", "b"])
187
277
  end.count.should == 4
188
278
  end
189
279
 
190
- it "should have the options from #tagged_with passed through" do
191
- tags_list = ["a", "b"]
192
- options = {:x=>10, :y=>20}
193
- TaggableModel.should_receive(:tagged_with_sifter).with(tags_list, options)
194
- TaggableModel.tagged_with(tags_list, options)
195
- end
196
280
  end
197
281
 
198
- describe "option :min" do
199
- it "should return records where the number of matching tags >= :min" do
200
- TaggableModel.tagged_with(["a", "b", "x"], :on => :skills).count.should == 4
201
-
202
- TaggableModel.tagged_with(["a", "b", "x"], :on => :skills, :min => 1).count.should == 4
203
- TaggableModel.tagged_with(["a", "b", "x"], :on => :skills, :min => 2).count.should == 2
204
- TaggableModel.tagged_with(["a", "b", "x"], :on => :skills, :min => 3).count.should == 1
205
- end
206
- end
207
282
  end
208
283
  end
209
284
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rocket_tag
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.2.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Brad Phelan
@@ -144,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
144
  requirements:
145
145
  - - ">="
146
146
  - !ruby/object:Gem::Version
147
- hash: -3706512005983090004
147
+ hash: -1107478710277863404
148
148
  segments:
149
149
  - 0
150
150
  version: "0"