yetanothernguyen-acts-as-taggable-on 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.0.5
@@ -11,11 +11,11 @@ module ActsAsTaggableOn::Taggable
11
11
  before_save :save_cached_tag_list
12
12
  end
13
13
 
14
- base.initialize_acts_as_taggable_on_cache
14
+ base.intialize_acts_as_taggable_on_cache
15
15
  end
16
16
 
17
17
  module ClassMethods
18
- def initialize_acts_as_taggable_on_cache
18
+ def intialize_acts_as_taggable_on_cache
19
19
  tag_types.map(&:to_s).each do |tag_type|
20
20
  class_eval %(
21
21
  def self.caching_#{tag_type.singularize}_list?
@@ -27,7 +27,7 @@ module ActsAsTaggableOn::Taggable
27
27
 
28
28
  def acts_as_taggable_on(*args)
29
29
  super(*args)
30
- initialize_acts_as_taggable_on_cache
30
+ intialize_acts_as_taggable_on_cache
31
31
  end
32
32
 
33
33
  def caching_tag_list_on?(context)
@@ -61,72 +61,65 @@ module ActsAsTaggableOn::Taggable
61
61
 
62
62
  ## Generate conditions:
63
63
  options[:conditions] = sanitize_sql(options[:conditions]) if options[:conditions]
64
-
64
+
65
65
  start_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
66
66
  end_at_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
67
-
67
+
68
68
  taggable_conditions = sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.taggable_type = ?", base_class.name])
69
- taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
70
- taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
71
-
72
- tagging_conditions = [
69
+ taggable_conditions << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = ?", options.delete(:id)]) if options[:id]
70
+
71
+ conditions = [
73
72
  taggable_conditions,
73
+ options[:conditions],
74
74
  scope[:conditions],
75
75
  start_at_conditions,
76
76
  end_at_conditions
77
77
  ].compact.reverse
78
78
 
79
- tag_conditions = [
80
- options[:conditions]
81
- ].compact.reverse
82
-
83
79
  ## Generate joins:
80
+ tagging_join = "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tag.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.tag_id"
81
+ tagging_join << sanitize_sql([" AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", options.delete(:on).to_s]) if options[:on]
82
+
84
83
  taggable_join = "INNER JOIN #{table_name} ON #{table_name}.#{primary_key} = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id"
85
84
  taggable_join << " AND #{table_name}.#{inheritance_column} = '#{name}'" unless descends_from_active_record? # Current model is STI descendant, so add type checking to the join condition
86
85
 
87
- tagging_joins = [
86
+ joins = [
87
+ tagging_join,
88
88
  taggable_join,
89
89
  scope[:joins]
90
90
  ].compact
91
91
 
92
- tag_joins = [
93
- ].compact
92
+ joins = joins.reverse if ActiveRecord::VERSION::MAJOR < 3
94
93
 
95
- [tagging_joins, tag_joins].each(&:reverse!) if ActiveRecord::VERSION::MAJOR < 3
96
94
 
97
95
  ## Generate scope:
98
- tagging_scope = ActsAsTaggableOn::Tagging.select("#{ActsAsTaggableOn::Tagging.table_name}.tag_id, COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) AS tags_count")
99
- tag_scope = ActsAsTaggableOn::Tag.select("#{ActsAsTaggableOn::Tag.table_name}.*, #{ActsAsTaggableOn::Tagging.table_name}.tags_count AS count").order(options[:order]).limit(options[:limit])
100
-
96
+ scope = ActsAsTaggableOn::Tag.scoped(:select => "#{ActsAsTaggableOn::Tag.table_name}.*, COUNT(*) AS count").order(options[:order]).limit(options[:limit])
97
+
101
98
  # Joins and conditions
102
- tagging_joins.each { |join| tagging_scope = tagging_scope.joins(join) }
103
- tagging_conditions.each { |condition| tagging_scope = tagging_scope.where(condition) }
104
-
105
- tag_joins.each { |join| tag_scope = tag_scope.joins(join) }
106
- tag_conditions.each { |condition| tag_scope = tag_scope.where(condition) }
107
-
99
+ joins.each { |join| scope = scope.joins(join) }
100
+ conditions.each { |condition| scope = scope.where(condition) }
101
+
108
102
  # GROUP BY and HAVING clauses:
109
- at_least = sanitize_sql(['tags_count >= ?', options.delete(:at_least)]) if options[:at_least]
110
- at_most = sanitize_sql(['tags_count <= ?', options.delete(:at_most)]) if options[:at_most]
111
- having = ["COUNT(#{ActsAsTaggableOn::Tagging.table_name}.tag_id) > 0", at_least, at_most].compact.join(' AND ')
112
-
113
- group_columns = "#{ActsAsTaggableOn::Tagging.table_name}.tag_id"
103
+ at_least = sanitize_sql(['COUNT(*) >= ?', options.delete(:at_least)]) if options[:at_least]
104
+ at_most = sanitize_sql(['COUNT(*) <= ?', options.delete(:at_most)]) if options[:at_most]
105
+ having = [at_least, at_most].compact.join(' AND ')
114
106
 
115
107
  if ActiveRecord::VERSION::MAJOR >= 3
116
108
  # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore:
117
109
  scoped_select = "#{table_name}.#{primary_key}"
118
- tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})").
119
- group(group_columns).
120
- having(having)
110
+ scope = scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})")
111
+
112
+ # We have having() in RoR 3.0 so use it:
113
+ having = having.blank? ? "COUNT(*) > 0" : "COUNT(*) > 0 AND #{having}"
114
+ scope = scope.group(grouped_column_names_for(ActsAsTaggableOn::Tag)).having(having)
121
115
  else
122
116
  # Having is not available in 2.3.x:
123
- group_by = "#{group_columns} HAVING COUNT(*) > 0"
117
+ group_by = "#{grouped_column_names_for(ActsAsTaggableOn::Tag)} HAVING COUNT(*) > 0"
124
118
  group_by << " AND #{having}" unless having.blank?
125
- tagging_scope = tagging_scope.group(group_by)
119
+ scope = scope.group(group_by)
126
120
  end
127
121
 
128
- tag_scope = tag_scope.joins("JOIN (#{tagging_scope.to_sql}) AS taggings ON taggings.tag_id = tags.id")
129
- tag_scope
122
+ scope
130
123
  end
131
124
  end
132
125
 
@@ -21,7 +21,7 @@ module ActsAsTaggableOn::Taggable
21
21
 
22
22
  class_eval do
23
23
  has_many context_taggings, :as => :taggable, :dependent => :destroy, :include => :tag, :class_name => "ActsAsTaggableOn::Tagging",
24
- :conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_type]
24
+ :conditions => ["#{ActsAsTaggableOn::Tagging.table_name}.tagger_id IS NULL AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_type]
25
25
  has_many context_tags, :through => context_taggings, :source => :tag, :class_name => "ActsAsTaggableOn::Tag"
26
26
  end
27
27
 
@@ -80,18 +80,8 @@ module ActsAsTaggableOn::Taggable
80
80
  conditions << "#{table_name}.#{primary_key} NOT IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
81
81
 
82
82
  elsif options.delete(:any)
83
- conditions << tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
84
-
85
- tagging_join = " JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
86
- " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{table_name}.#{primary_key}" +
87
- " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)}" +
88
- " JOIN #{ActsAsTaggableOn::Tag.table_name}" +
89
- " ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id"
90
-
91
- tagging_join << " AND " + sanitize_sql(["#{ActsAsTaggableOn::Tagging.table_name}.context = ?", context.to_s]) if context
92
- select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?
93
-
94
- joins << tagging_join
83
+ tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", t]) }.join(" OR ")
84
+ conditions << "#{table_name}.#{primary_key} IN (SELECT #{ActsAsTaggableOn::Tagging.table_name}.taggable_id FROM #{ActsAsTaggableOn::Tagging.table_name} JOIN #{ActsAsTaggableOn::Tag.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name)})"
95
85
 
96
86
  else
97
87
  tags = ActsAsTaggableOn::Tag.named_any(tag_list)
@@ -101,7 +91,7 @@ module ActsAsTaggableOn::Taggable
101
91
  safe_tag = tag.name.gsub(/[^a-zA-Z0-9]/, '')
102
92
  prefix = "#{safe_tag}_#{rand(1024)}"
103
93
 
104
- taggings_alias = "#{undecorated_table_name}_taggings_#{prefix}"
94
+ taggings_alias = "#{table_name}_taggings_#{prefix}"
105
95
 
106
96
  tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
107
97
  " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
@@ -113,20 +103,18 @@ module ActsAsTaggableOn::Taggable
113
103
  end
114
104
  end
115
105
 
116
- taggings_alias, tags_alias = "#{undecorated_table_name}_taggings_group", "#{undecorated_table_name}_tags_group"
106
+ taggings_alias, tags_alias = "#{table_name}_taggings_group", "#{table_name}_tags_group"
117
107
 
118
108
  if options.delete(:match_all)
119
109
  joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
120
110
  " ON #{taggings_alias}.taggable_id = #{table_name}.#{primary_key}" +
121
111
  " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name)}"
122
112
 
123
-
124
- group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
125
- group = "#{group_columns} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
113
+ group = "#{grouped_column_names_for(self)} HAVING COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
126
114
  end
127
115
 
128
- scoped(:select => select_clause,
129
- :joins => joins.join(" "),
116
+
117
+ scoped(:joins => joins.join(" "),
130
118
  :group => group,
131
119
  :conditions => conditions.join(" AND "),
132
120
  :order => options[:order],
@@ -188,17 +176,8 @@ module ActsAsTaggableOn::Taggable
188
176
  tag_table_name = ActsAsTaggableOn::Tag.table_name
189
177
  tagging_table_name = ActsAsTaggableOn::Tagging.table_name
190
178
 
191
- opts = ["#{tagging_table_name}.context = ?", context.to_s]
192
- scope = base_tags.where(opts)
193
-
194
- if ActsAsTaggableOn::Tag.using_postgresql?
195
- group_columns = grouped_column_names_for(ActsAsTaggableOn::Tag)
196
- scope = scope.order("max(#{tagging_table_name}.created_at)").group(group_columns)
197
- else
198
- scope = scope.group("#{ActsAsTaggableOn::Tag.table_name}.#{ActsAsTaggableOn::Tag.primary_key}")
199
- end
200
-
201
- scope.all
179
+ opts = ["#{tagging_table_name}.context = ?", context.to_s]
180
+ base_tags.where(opts).order("max(#{tagging_table_name}.created_at)").group("#{tag_table_name}.id, #{tag_table_name}.name").all
202
181
  end
203
182
 
204
183
  ##
@@ -30,13 +30,9 @@ module ActsAsTaggableOn::Taggable
30
30
 
31
31
  module InstanceMethods
32
32
  def owner_tags_on(owner, context)
33
- if owner.nil?
34
- base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ?), context.to_s]).all
35
- else
36
- base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND
37
- #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = ? AND
38
- #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
39
- end
33
+ base_tags.where([%(#{ActsAsTaggableOn::Tagging.table_name}.context = ? AND
34
+ #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = ? AND
35
+ #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
40
36
  end
41
37
 
42
38
  def cached_owned_tag_list_on(context)
@@ -42,12 +42,10 @@ module ActsAsTaggableOn::Taggable
42
42
 
43
43
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
44
44
 
45
- group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(klass) : "#{klass.table_name}.#{klass.primary_key}"
46
-
47
45
  klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
48
46
  :from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
49
47
  :conditions => ["#{exclude_self} #{klass.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?) AND #{ActsAsTaggableOn::Tagging.table_name}.context = ?", tags_to_find, result_context],
50
- :group => group_columns,
48
+ :group => grouped_column_names_for(klass),
51
49
  :order => "count DESC" }.update(options))
52
50
  end
53
51
 
@@ -56,12 +54,10 @@ module ActsAsTaggableOn::Taggable
56
54
 
57
55
  exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
58
56
 
59
- group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(klass) : "#{klass.table_name}.#{klass.primary_key}"
60
-
61
57
  klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{ActsAsTaggableOn::Tag.table_name}.id) AS count",
62
58
  :from => "#{klass.table_name}, #{ActsAsTaggableOn::Tag.table_name}, #{ActsAsTaggableOn::Tagging.table_name}",
63
59
  :conditions => ["#{exclude_self} #{klass.table_name}.id = #{ActsAsTaggableOn::Tagging.table_name}.taggable_id AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id AND #{ActsAsTaggableOn::Tag.table_name}.name IN (?)", tags_to_find],
64
- :group => group_columns,
60
+ :group => grouped_column_names_for(klass),
65
61
  :order => "count DESC" }.update(options))
66
62
  end
67
63
  end
@@ -9,11 +9,7 @@ module ActsAsTaggableOn
9
9
  named_scope :order, lambda { |order| { :order => order } }
10
10
  named_scope :select, lambda { |select| { :select => select } }
11
11
  named_scope :limit, lambda { |limit| { :limit => limit } }
12
- named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
13
-
14
- def self.to_sql
15
- construct_finder_sql({})
16
- end
12
+ named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
17
13
  end
18
14
  end
19
15
  end
@@ -14,10 +14,6 @@ module ActsAsTaggableOn
14
14
  validates_uniqueness_of :name
15
15
 
16
16
  ### SCOPES:
17
-
18
- def self.using_postgresql?
19
- connection.adapter_name == 'PostgreSQL'
20
- end
21
17
 
22
18
  def self.named(name)
23
19
  where(["name #{like_operator} ?", name])
@@ -47,10 +43,7 @@ module ActsAsTaggableOn
47
43
  return [] if list.empty?
48
44
 
49
45
  existing_tags = Tag.named_any(list).all
50
- new_tag_names = list.reject do |name|
51
- name = comparable_name(name)
52
- existing_tags.any? { |tag| comparable_name(tag.name) == name }
53
- end
46
+ new_tag_names = list.reject { |name| existing_tags.any? { |tag| tag.name.mb_chars.downcase == name.mb_chars.downcase } }
54
47
  created_tags = new_tag_names.map { |name| Tag.create(:name => name) }
55
48
 
56
49
  existing_tags + created_tags
@@ -73,12 +66,9 @@ module ActsAsTaggableOn
73
66
  class << self
74
67
  private
75
68
  def like_operator
76
- using_postgresql? ? 'ILIKE' : 'LIKE'
77
- end
78
-
79
- def comparable_name(str)
80
- RUBY_VERSION >= "1.9" ? str.downcase : str.mb_chars.downcase
69
+ connection.adapter_name == 'PostgreSQL' ? 'ILIKE' : 'LIKE'
81
70
  end
82
71
  end
72
+
83
73
  end
84
74
  end
@@ -4,7 +4,6 @@ describe "Taggable" do
4
4
  before(:each) do
5
5
  clean_database!
6
6
  @taggable = TaggableModel.new(:name => "Bob Jones")
7
- @taggables = [@taggable, TaggableModel.new(:name => "John Doe")]
8
7
  end
9
8
 
10
9
  it "should have tag types" do
@@ -69,22 +68,6 @@ describe "Taggable" do
69
68
  @taggable.should have(2).skills
70
69
  end
71
70
 
72
- it "should be able to select taggables by subset of tags using ActiveRelation methods" do
73
- @taggables[0].tag_list = "bob"
74
- @taggables[1].tag_list = "charlie"
75
- @taggables[0].skill_list = "ruby"
76
- @taggables[1].skill_list = "css"
77
- @taggables.each{|taggable| taggable.save}
78
-
79
- @found_taggables_by_tag = TaggableModel.joins(:tags).where(:tags => {:name => ["bob"]})
80
- @found_taggables_by_skill = TaggableModel.joins(:skills).where(:tags => {:name => ["ruby"]})
81
-
82
- @found_taggables_by_tag.should include @taggables[0]
83
- @found_taggables_by_tag.should_not include @taggables[1]
84
- @found_taggables_by_skill.should include @taggables[0]
85
- @found_taggables_by_skill.should_not include @taggables[1]
86
- end
87
-
88
71
  it "should be able to find by tag" do
89
72
  @taggable.skill_list = "ruby, rails, css"
90
73
  @taggable.save
data/spec/spec_helper.rb CHANGED
@@ -13,7 +13,7 @@ begin
13
13
  Bundler.setup
14
14
  rescue Bundler::GemNotFound
15
15
  raise RuntimeError, "Bundler couldn't find some gems." +
16
- "Did you run \`bundlee install\`?"
16
+ "Did you run `bundle install`?"
17
17
  end
18
18
 
19
19
  Bundler.require
@@ -57,4 +57,4 @@ def clean_database!
57
57
  end
58
58
  end
59
59
 
60
- clean_database!
60
+ clean_database!
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yetanothernguyen-acts-as-taggable-on
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
9
+ - 5
10
+ version: 0.0.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Michael Bleigh