rocket_tag 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm ruby-1.9.2@rocket_tag
2
+
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source "http://rubygems.org"
2
2
  # Add dependencies required to use your gem here.
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
- gem "activerecord", ">= 3.1.0"
5
+ gem "activerecord", ">= 3.2.0"
6
6
  gem "squeel", :require => false
7
7
 
8
8
  # Add dependencies to develop your gem here.
@@ -12,6 +12,7 @@ group :development do
12
12
  gem "yard", "~> 0.6.0"
13
13
  gem "bundler", "~> 1.0.0"
14
14
  gem "jeweler", "~> 1.6.4"
15
- gem "rcov", ">= 0"
15
+ #gem "rcov", ">= 0"
16
16
  gem 'sqlite3'
17
+ gem "rake"
17
18
  end
data/Gemfile.lock CHANGED
@@ -1,20 +1,18 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- activemodel (3.1.0)
5
- activesupport (= 3.1.0)
6
- bcrypt-ruby (~> 3.0.0)
4
+ activemodel (3.2.0)
5
+ activesupport (= 3.2.0)
7
6
  builder (~> 3.0.0)
8
- i18n (~> 0.6)
9
- activerecord (3.1.0)
10
- activemodel (= 3.1.0)
11
- activesupport (= 3.1.0)
12
- arel (~> 2.2.1)
7
+ activerecord (3.2.0)
8
+ activemodel (= 3.2.0)
9
+ activesupport (= 3.2.0)
10
+ arel (~> 3.0.0)
13
11
  tzinfo (~> 0.3.29)
14
- activesupport (3.1.0)
12
+ activesupport (3.2.0)
13
+ i18n (~> 0.6)
15
14
  multi_json (~> 1.0)
16
- arel (2.2.1)
17
- bcrypt-ruby (3.0.1)
15
+ arel (3.0.0)
18
16
  builder (3.0.0)
19
17
  diff-lcs (1.1.3)
20
18
  git (1.2.5)
@@ -23,11 +21,10 @@ GEM
23
21
  bundler (~> 1.0)
24
22
  git (>= 1.2.5)
25
23
  rake
26
- multi_json (1.0.3)
24
+ multi_json (1.0.4)
27
25
  polyamorous (0.5.0)
28
26
  activerecord (~> 3.0)
29
- rake (0.9.2)
30
- rcov (0.9.10)
27
+ rake (0.9.2.2)
31
28
  rspec (2.3.0)
32
29
  rspec-core (~> 2.3.0)
33
30
  rspec-expectations (~> 2.3.0)
@@ -36,22 +33,22 @@ GEM
36
33
  rspec-expectations (2.3.0)
37
34
  diff-lcs (~> 1.1.2)
38
35
  rspec-mocks (2.3.0)
39
- sqlite3 (1.3.4)
40
- squeel (0.9.0)
36
+ sqlite3 (1.3.5)
37
+ squeel (0.9.5)
41
38
  activerecord (~> 3.0)
42
39
  activesupport (~> 3.0)
43
40
  polyamorous (~> 0.5.0)
44
- tzinfo (0.3.29)
41
+ tzinfo (0.3.31)
45
42
  yard (0.6.8)
46
43
 
47
44
  PLATFORMS
48
45
  ruby
49
46
 
50
47
  DEPENDENCIES
51
- activerecord (>= 3.1.0)
48
+ activerecord (>= 3.2.0)
52
49
  bundler (~> 1.0.0)
53
50
  jeweler (~> 1.6.4)
54
- rcov
51
+ rake
55
52
  rspec (~> 2.3.0)
56
53
  sqlite3
57
54
  squeel
data/README.md CHANGED
@@ -44,6 +44,10 @@ Usage
44
44
 
45
45
  # Match all tags on a specific context
46
46
  TaggableModel.tagged_with ["math", "kiting"], :all => true, :on => "skills"
47
+
48
+ # Match a miniumum number of tags
49
+ TaggableModel.tagged_with ["math", "kiting", "coding", "sleeping"], :min => 2, :on => "skills"
50
+
47
51
 
48
52
  # Mix with active relation
49
53
  TaggableModel.tagged_with(["forking", "kiting"]).where( ["created_at > ?", Time.zone.now.ago(5.hours)])
data/Rakefile CHANGED
@@ -38,5 +38,5 @@ end
38
38
 
39
39
  task :default => :spec
40
40
 
41
- require 'yard'
42
- YARD::Rake::YardocTask.new
41
+ # require 'yard'
42
+ # YARD::Rake::YardocTask.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.1.0
@@ -4,6 +4,7 @@ module RocketTag
4
4
  module Taggable
5
5
  def self.included(base)
6
6
  base.extend ClassMethods
7
+ base.send :include, InstanceMethods
7
8
  end
8
9
 
9
10
  class Manager
@@ -40,8 +41,6 @@ module RocketTag
40
41
  klass.has_many :taggings , :dependent => :destroy , :as => :taggable, :class_name => "RocketTag::Tagging"
41
42
  klass.has_many :tags , :source => :tag, :through => :taggings, :class_name => "RocketTag::Tag"
42
43
  end
43
-
44
-
45
44
  end
46
45
 
47
46
  def tags_for_context context
@@ -56,6 +55,37 @@ module RocketTag
56
55
  taggings_for_context(context).delete_all
57
56
  end
58
57
 
58
+ module InstanceMethods
59
+ def reload
60
+ super
61
+ self.class.rocket_tag.contexts.each do |context|
62
+ write_context context, []
63
+ end
64
+ @tags_cached = false
65
+ cache_tags
66
+ end
67
+
68
+ def cache_tags
69
+ unless @tags_cached
70
+ tags_by_context ||= send("taggings").group_by{|f| f.context }
71
+ tags_by_context.each do |context,v|
72
+ write_context context, v.map{|t| t.tag.name}
73
+ end
74
+ @tags_cached = true
75
+ end
76
+ end
77
+
78
+ def write_context context, list
79
+ @contexts ||= {}
80
+ @contexts[context.to_sym] = list
81
+ end
82
+
83
+ def read_context context
84
+ @contexts ||= {}
85
+ @contexts[context.to_sym] || []
86
+ end
87
+ end
88
+
59
89
  module ClassMethods
60
90
 
61
91
  def rocket_tag
@@ -70,13 +100,13 @@ module RocketTag
70
100
  end
71
101
  end
72
102
 
73
- def _with_all condition, tags_list
74
- if condition
75
- group{~id}.
76
- having{count(~id)==my{tags_list.length}}
77
- else
78
- where{}
79
- end
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
80
110
  end
81
111
 
82
112
  # Generates a sifter or a where clause depending on options.
@@ -88,62 +118,49 @@ module RocketTag
88
118
  def tagged_with_sifter tags_list, options = {}
89
119
  on = options.delete :on
90
120
  all = options.delete :all
121
+ min = options.delete(:min)
122
+ if all
123
+ min = tags_list.length
124
+ end
91
125
 
92
- proc do |&block|
93
- if options.delete :where
94
- where &block
95
- else
96
- squeel &block
97
- end
98
- end.call {
99
- id.in(
100
- my{self}.
101
- select{distinct(id)}.
102
- joins{tags}.
103
- where{ tags.name.in(my{tags_list})}.
104
- _with_tag_context(my{on}).
105
- _with_all(my{all}, my{tags_list})
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}.
136
+ joins{tags}.
137
+ where{tags.name.in(my{tags_list})}.
138
+ _with_tag_context(on).
139
+ _with_min(min, tags_list)
106
140
  )
107
- }
141
+ end
108
142
 
109
143
  end
110
144
 
145
+ # We need to overide the default count
146
+ # and do a sub select as we are using gr
147
+ def count
148
+ end
149
+
150
+
111
151
  def tagged_with tags_list, options = {}
112
152
  options[:where] = true
113
153
  tagged_with_sifter(tags_list, options)
114
154
  end
115
155
 
116
- def attr_taggable *contexts
117
-
118
- if contexts.blank?
119
- contexts = [:tag]
120
- end
121
-
122
- rocket_tag.contexts += contexts
123
-
124
- contexts.each do |context|
156
+ def setup_for_rocket_tag
157
+ unless @setup_for_rocket_tag
158
+ @setup_for_rocket_tag = true
125
159
  class_eval do
126
-
127
160
  default_scope do
128
161
  preload{taggings}.preload{tags}
129
162
  end
130
163
 
131
- has_many "#{context}_taggings".to_sym,
132
- :source => :taggable,
133
- :as => :taggable,
134
- :conditions => { :context => context }
135
-
136
- has_many "#{context}_tags".to_sym,
137
- :source => :tag,
138
- :through => :taggings,
139
- :conditions => [ "taggings.context = ?", context ]
140
-
141
-
142
- validate context do
143
- if not send(context).kind_of? Enumerable
144
- errors.add context, :invalid
145
- end
146
- end
147
164
  before_save do
148
165
  @tag_dirty ||= Set.new
149
166
 
@@ -176,30 +193,45 @@ module RocketTag
176
193
  end
177
194
  @tag_dirty = Set.new
178
195
  end
196
+ end
197
+ end
198
+ end
179
199
 
180
- def reload
181
- super
182
- self.class.rocket_tag.contexts.each do |context|
183
- write_attribute context, []
184
- end
185
- @tags_cached = false
186
- cache_tags
187
- end
200
+ def attr_taggable *contexts
188
201
 
189
- define_method "cache_tags" do
190
- unless @tags_cached
191
- tags_by_context ||= send("taggings").group_by{|f| f.context }
192
- tags_by_context.each do |context,v|
193
- write_attribute context, v.map{|t| t.tag.name}
194
- end
195
- @tags_cached = true
202
+ if contexts.blank?
203
+ contexts = [:tag]
204
+ end
205
+
206
+ rocket_tag.contexts += contexts
207
+
208
+ setup_for_rocket_tag
209
+
210
+ contexts.each do |context|
211
+ class_eval do
212
+
213
+ has_many "#{context}_taggings".to_sym,
214
+ :source => :taggable,
215
+ :as => :taggable,
216
+ :conditions => { :context => context }
217
+
218
+ has_many "#{context}_tags".to_sym,
219
+ :source => :tag,
220
+ :through => :taggings,
221
+ :conditions => [ "taggings.context = ?", context ]
222
+
223
+
224
+ validate context do
225
+ if not send(context).kind_of? Enumerable
226
+ errors.add context, :invalid
196
227
  end
197
228
  end
198
229
 
230
+
199
231
  # Return an array of RocketTag::Tags for the context
200
232
  define_method "#{context}" do
201
233
  cache_tags
202
- r = read_attribute(context) || []
234
+ read_context(context)
203
235
  end
204
236
 
205
237
 
@@ -208,11 +240,11 @@ module RocketTag
208
240
 
209
241
  # Ensure the tags are loaded
210
242
  cache_tags
211
- write_attribute(context, list)
243
+ write_context(context, list)
212
244
 
213
245
  (@tag_dirty ||= Set.new) << context
214
246
 
215
-
247
+
216
248
  end
217
249
  end
218
250
  end
data/rocket_tag.gemspec CHANGED
@@ -4,14 +4,14 @@
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = %q{rocket_tag}
8
- s.version = "0.0.4"
7
+ s.name = "rocket_tag"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = [%q{Brad Phelan}]
12
- s.date = %q{2011-09-19}
13
- s.description = %q{}
14
- s.email = %q{bradphelan@xtargets.com}
11
+ s.authors = ["Brad Phelan"]
12
+ s.date = "2012-01-24"
13
+ s.description = ""
14
+ s.email = "bradphelan@xtargets.com"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE.txt",
17
17
  "README.md"
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".rspec",
22
+ ".rvmrc",
22
23
  "Gemfile",
23
24
  "Gemfile.lock",
24
25
  "LICENSE.txt",
@@ -38,43 +39,43 @@ Gem::Specification.new do |s|
38
39
  "spec/schema.rb",
39
40
  "spec/spec_helper.rb"
40
41
  ]
41
- s.homepage = %q{http://github.com/bradphelan/rocket_tag}
42
- s.licenses = [%q{MIT}]
43
- s.require_paths = [%q{lib}]
44
- s.rubygems_version = %q{1.8.8}
45
- s.summary = %q{A modern fast tagging framework for Rails 3.1+}
42
+ s.homepage = "http://github.com/bradphelan/rocket_tag"
43
+ s.licenses = ["MIT"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = "1.8.15"
46
+ s.summary = "A modern fast tagging framework for Rails 3.1+"
46
47
 
47
48
  if s.respond_to? :specification_version then
48
49
  s.specification_version = 3
49
50
 
50
51
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
- s.add_runtime_dependency(%q<activerecord>, [">= 3.1.0"])
52
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.2.0"])
52
53
  s.add_runtime_dependency(%q<squeel>, [">= 0"])
53
54
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
54
55
  s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
55
56
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
56
57
  s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
57
- s.add_development_dependency(%q<rcov>, [">= 0"])
58
58
  s.add_development_dependency(%q<sqlite3>, [">= 0"])
59
+ s.add_development_dependency(%q<rake>, [">= 0"])
59
60
  else
60
- s.add_dependency(%q<activerecord>, [">= 3.1.0"])
61
+ s.add_dependency(%q<activerecord>, [">= 3.2.0"])
61
62
  s.add_dependency(%q<squeel>, [">= 0"])
62
63
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
63
64
  s.add_dependency(%q<yard>, ["~> 0.6.0"])
64
65
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
65
66
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
66
- s.add_dependency(%q<rcov>, [">= 0"])
67
67
  s.add_dependency(%q<sqlite3>, [">= 0"])
68
+ s.add_dependency(%q<rake>, [">= 0"])
68
69
  end
69
70
  else
70
- s.add_dependency(%q<activerecord>, [">= 3.1.0"])
71
+ s.add_dependency(%q<activerecord>, [">= 3.2.0"])
71
72
  s.add_dependency(%q<squeel>, [">= 0"])
72
73
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
73
74
  s.add_dependency(%q<yard>, ["~> 0.6.0"])
74
75
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
75
76
  s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
76
- s.add_dependency(%q<rcov>, [">= 0"])
77
77
  s.add_dependency(%q<sqlite3>, [">= 0"])
78
+ s.add_dependency(%q<rake>, [">= 0"])
78
79
  end
79
80
  end
80
81
 
@@ -7,13 +7,13 @@ describe TaggableModel do
7
7
  end
8
8
 
9
9
  describe "parsing" do
10
- it "converts strings to arrays using ruby core lib CSV" do
11
- m = TaggableModel.new :skills => %q%hello, is it me, you are looking for, cat%
12
- m.skills.should == ["hello", "is it me", "you are looking for", "cat"]
10
+ it "converts strings to arrays using ruby core lib CSV" do
11
+ m = TaggableModel.new :skills => %q%hello, is it me, you are looking for, cat%
12
+ m.skills.should == ["hello", "is it me", "you are looking for", "cat"]
13
13
 
14
- m = TaggableModel.new :skills => %q%hello, "is it me, you are looking for", cat%
15
- m.skills.should == ["hello", "is it me, you are looking for", "cat"]
16
- end
14
+ m = TaggableModel.new :skills => %q%hello, "is it me, you are looking for", cat%
15
+ m.skills.should == ["hello", "is it me, you are looking for", "cat"]
16
+ end
17
17
  end
18
18
 
19
19
  describe "#save" do
@@ -93,10 +93,10 @@ describe TaggableModel do
93
93
  @t20 = TaggableModel.create :name => "20", :foo => "A"
94
94
  @t21 = TaggableModel.create :name => "21", :foo => "B"
95
95
 
96
- @t00.skills = [ "a" , "b"]
96
+ @t00.skills = [ "a" , "b", "x"]
97
97
  @t00.languages = [ "german" , "french"]
98
98
 
99
- @t01.skills = [ "a" , "b"]
99
+ @t01.skills = [ "a" , "b", "y"]
100
100
  @t01.languages = [ "german" , "italian"]
101
101
 
102
102
  @t10.skills = [ "a" , "c"]
@@ -141,7 +141,7 @@ describe TaggableModel do
141
141
  q0.should include @t21
142
142
 
143
143
  q0.should_not include @t20 # as it has neither "a" nor "german" tagged
144
- # on any context
144
+ # on any context
145
145
  end
146
146
  end
147
147
 
@@ -175,6 +175,36 @@ describe TaggableModel do
175
175
  q0.should_not include @t01
176
176
  end
177
177
  end
178
+
179
+ describe "#tagged_with_sifter" do
180
+ it "should be the work horse of #tagged_with but returns a sifter that can be composed into other queries" do
181
+ TaggableModel.where do
182
+ TaggableModel.tagged_with_sifter(["a", "b"]) & TaggableModel.tagged_with_sifter(["c"])
183
+ end.count.should == 2
184
+
185
+ TaggableModel.where do
186
+ TaggableModel.tagged_with_sifter(["a", "b"])
187
+ end.count.should == 4
188
+ end
189
+
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
+ end
197
+
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
178
207
  end
179
208
  end
180
209
  end
210
+
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rocket_tag
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.4
5
+ version: 0.1.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: 2011-09-19 00:00:00 Z
13
+ date: 2012-01-24 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -19,7 +19,7 @@ dependencies:
19
19
  requirements:
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 3.1.0
22
+ version: 3.2.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: *id001
@@ -79,7 +79,7 @@ dependencies:
79
79
  prerelease: false
80
80
  version_requirements: *id006
81
81
  - !ruby/object:Gem::Dependency
82
- name: rcov
82
+ name: sqlite3
83
83
  requirement: &id007 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
@@ -90,7 +90,7 @@ dependencies:
90
90
  prerelease: false
91
91
  version_requirements: *id007
92
92
  - !ruby/object:Gem::Dependency
93
- name: sqlite3
93
+ name: rake
94
94
  requirement: &id008 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
@@ -112,6 +112,7 @@ extra_rdoc_files:
112
112
  files:
113
113
  - .document
114
114
  - .rspec
115
+ - .rvmrc
115
116
  - Gemfile
116
117
  - Gemfile.lock
117
118
  - LICENSE.txt
@@ -143,7 +144,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
144
  requirements:
144
145
  - - ">="
145
146
  - !ruby/object:Gem::Version
146
- hash: -1104045043849403616
147
+ hash: -3706512005983090004
147
148
  segments:
148
149
  - 0
149
150
  version: "0"
@@ -156,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
157
  requirements: []
157
158
 
158
159
  rubyforge_project:
159
- rubygems_version: 1.8.8
160
+ rubygems_version: 1.8.15
160
161
  signing_key:
161
162
  specification_version: 3
162
163
  summary: A modern fast tagging framework for Rails 3.1+