rocket_tag 0.0.4 → 0.1.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/.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+