acts-as-taggable-on 2.0.0.pre5 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/{spec/spec.opts → .rspec} +0 -0
  4. data/.travis.yml +40 -0
  5. data/Appraisals +16 -0
  6. data/CHANGELOG.md +208 -0
  7. data/CONTRIBUTING.md +44 -0
  8. data/Gemfile +10 -5
  9. data/Guardfile +5 -0
  10. data/{MIT-LICENSE → LICENSE.md} +1 -1
  11. data/README.md +477 -0
  12. data/Rakefile +14 -52
  13. data/UPGRADING.md +8 -0
  14. data/acts-as-taggable-on.gemspec +36 -0
  15. data/{lib/generators/acts_as_taggable_on/migration/templates/active_record/migration.rb → db/migrate/1_acts_as_taggable_on_migration.rb} +5 -3
  16. data/db/migrate/2_add_missing_unique_indices.rb +19 -0
  17. data/db/migrate/3_add_taggings_counter_cache_to_tags.rb +14 -0
  18. data/db/migrate/4_add_missing_taggable_index.rb +9 -0
  19. data/db/migrate/5_change_collation_for_tag_names.rb +9 -0
  20. data/gemfiles/activerecord_3.2.gemfile +15 -0
  21. data/gemfiles/activerecord_4.0.gemfile +15 -0
  22. data/gemfiles/activerecord_4.1.gemfile +15 -0
  23. data/gemfiles/activerecord_4.2.gemfile +16 -0
  24. data/lib/acts-as-taggable-on.rb +117 -22
  25. data/lib/acts_as_taggable_on/compatibility.rb +35 -0
  26. data/lib/acts_as_taggable_on/default_parser.rb +79 -0
  27. data/lib/acts_as_taggable_on/engine.rb +5 -0
  28. data/lib/acts_as_taggable_on/generic_parser.rb +19 -0
  29. data/lib/acts_as_taggable_on/tag.rb +137 -61
  30. data/lib/acts_as_taggable_on/tag_list.rb +96 -75
  31. data/lib/acts_as_taggable_on/tag_list_parser.rb +21 -0
  32. data/lib/acts_as_taggable_on/taggable/cache.rb +86 -0
  33. data/lib/acts_as_taggable_on/taggable/collection.rb +178 -0
  34. data/lib/acts_as_taggable_on/taggable/core.rb +459 -0
  35. data/lib/acts_as_taggable_on/taggable/dirty.rb +36 -0
  36. data/lib/acts_as_taggable_on/taggable/ownership.rb +125 -0
  37. data/lib/acts_as_taggable_on/taggable/related.rb +71 -0
  38. data/lib/acts_as_taggable_on/taggable.rb +102 -0
  39. data/lib/acts_as_taggable_on/tagger.rb +88 -0
  40. data/lib/acts_as_taggable_on/tagging.rb +38 -18
  41. data/lib/acts_as_taggable_on/tags_helper.rb +12 -14
  42. data/lib/acts_as_taggable_on/utils.rb +38 -0
  43. data/lib/acts_as_taggable_on/version.rb +4 -0
  44. data/lib/acts_as_taggable_on.rb +6 -0
  45. data/lib/tasks/tags_collate_utf8.rake +21 -0
  46. data/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +205 -195
  47. data/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +79 -81
  48. data/spec/acts_as_taggable_on/caching_spec.rb +83 -0
  49. data/spec/acts_as_taggable_on/default_parser_spec.rb +47 -0
  50. data/spec/acts_as_taggable_on/generic_parser_spec.rb +14 -0
  51. data/spec/acts_as_taggable_on/related_spec.rb +99 -0
  52. data/spec/acts_as_taggable_on/single_table_inheritance_spec.rb +211 -0
  53. data/spec/acts_as_taggable_on/tag_list_parser_spec.rb +46 -0
  54. data/spec/acts_as_taggable_on/tag_list_spec.rb +142 -62
  55. data/spec/acts_as_taggable_on/tag_spec.rb +274 -64
  56. data/spec/acts_as_taggable_on/taggable/dirty_spec.rb +127 -0
  57. data/spec/acts_as_taggable_on/taggable_spec.rb +704 -181
  58. data/spec/acts_as_taggable_on/tagger_spec.rb +134 -56
  59. data/spec/acts_as_taggable_on/tagging_spec.rb +54 -22
  60. data/spec/acts_as_taggable_on/tags_helper_spec.rb +39 -22
  61. data/spec/acts_as_taggable_on/utils_spec.rb +23 -0
  62. data/spec/internal/app/models/altered_inheriting_taggable_model.rb +3 -0
  63. data/spec/internal/app/models/cached_model.rb +3 -0
  64. data/spec/internal/app/models/cached_model_with_array.rb +5 -0
  65. data/spec/internal/app/models/company.rb +15 -0
  66. data/spec/internal/app/models/inheriting_taggable_model.rb +2 -0
  67. data/spec/internal/app/models/market.rb +2 -0
  68. data/spec/internal/app/models/models.rb +90 -0
  69. data/spec/internal/app/models/non_standard_id_taggable_model.rb +8 -0
  70. data/spec/internal/app/models/ordered_taggable_model.rb +4 -0
  71. data/spec/internal/app/models/other_cached_model.rb +3 -0
  72. data/spec/internal/app/models/other_taggable_model.rb +4 -0
  73. data/spec/internal/app/models/student.rb +2 -0
  74. data/spec/internal/app/models/taggable_model.rb +13 -0
  75. data/spec/internal/app/models/untaggable_model.rb +3 -0
  76. data/spec/internal/app/models/user.rb +3 -0
  77. data/spec/internal/config/database.yml.sample +19 -0
  78. data/spec/internal/db/schema.rb +97 -0
  79. data/spec/spec_helper.rb +12 -38
  80. data/spec/support/0-helpers.rb +32 -0
  81. data/spec/support/array.rb +9 -0
  82. data/spec/support/database.rb +42 -0
  83. data/spec/support/database_cleaner.rb +21 -0
  84. metadata +268 -73
  85. data/CHANGELOG +0 -25
  86. data/README.rdoc +0 -212
  87. data/VERSION +0 -1
  88. data/lib/acts_as_taggable_on/acts_as_taggable_on/cache.rb +0 -56
  89. data/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +0 -97
  90. data/lib/acts_as_taggable_on/acts_as_taggable_on/core.rb +0 -220
  91. data/lib/acts_as_taggable_on/acts_as_taggable_on/dirty.rb +0 -29
  92. data/lib/acts_as_taggable_on/acts_as_taggable_on/ownership.rb +0 -101
  93. data/lib/acts_as_taggable_on/acts_as_taggable_on/related.rb +0 -64
  94. data/lib/acts_as_taggable_on/acts_as_taggable_on.rb +0 -41
  95. data/lib/acts_as_taggable_on/acts_as_tagger.rb +0 -47
  96. data/lib/acts_as_taggable_on/compatibility/Gemfile +0 -6
  97. data/lib/acts_as_taggable_on/compatibility/active_record_backports.rb +0 -17
  98. data/lib/generators/acts_as_taggable_on/migration/migration_generator.rb +0 -31
  99. data/rails/init.rb +0 -1
  100. data/spec/bm.rb +0 -52
  101. data/spec/models.rb +0 -36
  102. data/spec/schema.rb +0 -42
@@ -1,101 +0,0 @@
1
- module ActsAsTaggableOn::Taggable
2
- module Ownership
3
- def self.included(base)
4
- base.send :include, ActsAsTaggableOn::Taggable::Ownership::InstanceMethods
5
- base.extend ActsAsTaggableOn::Taggable::Ownership::ClassMethods
6
-
7
- base.class_eval do
8
- after_save :save_owned_tags
9
- end
10
-
11
- base.initialize_acts_as_taggable_on_ownership
12
- end
13
-
14
- module ClassMethods
15
- def acts_as_taggable_on(*args)
16
- initialize_acts_as_taggable_on_ownership
17
- super(*args)
18
- end
19
-
20
- def initialize_acts_as_taggable_on_ownership
21
- tag_types.map(&:to_s).each do |tag_type|
22
- class_eval %(
23
- def #{tag_type}_from(owner)
24
- owner_tag_list_on(owner, '#{tag_type}')
25
- end
26
- )
27
- end
28
- end
29
- end
30
-
31
- module InstanceMethods
32
- def owner_tags_on(owner, context)
33
- base_tags.where([%(#{Tagging.table_name}.context = ? AND
34
- #{Tagging.table_name}.tagger_id = ? AND
35
- #{Tagging.table_name}.tagger_type = ?), context.to_s, owner.id, owner.class.to_s]).all
36
- end
37
-
38
- def cached_owned_tag_list_on(context)
39
- variable_name = "@owned_#{context}_list"
40
- cache = instance_variable_get(variable_name) || instance_variable_set(variable_name, {})
41
- end
42
-
43
- def owner_tag_list_on(owner, context)
44
- add_custom_context(context)
45
-
46
- cache = cached_owned_tag_list_on(context)
47
- cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
48
-
49
- cache[owner] ||= TagList.new(*owner_tags_on(owner, context).map(&:name))
50
- end
51
-
52
- def set_owner_tag_list_on(owner, context, new_list)
53
- add_custom_context(context)
54
-
55
- cache = cached_owned_tag_list_on(context)
56
- cache.delete_if { |key, value| key.id == owner.id && key.class == owner.class }
57
-
58
- cache[owner] = TagList.from(new_list)
59
- end
60
-
61
- def reload
62
- self.class.tag_types.each do |context|
63
- instance_variable_set("@owned_#{context}_list", nil)
64
- end
65
-
66
- super
67
- end
68
-
69
- def save_owned_tags
70
- transaction do
71
- tagging_contexts.each do |context|
72
- cached_owned_tag_list_on(context).each do |owner, tag_list|
73
- # Find existing tags or create non-existing tags:
74
- tag_list = Tag.find_or_create_all_with_like_by_name(tag_list.uniq)
75
-
76
- owned_tags = owner_tags_on(owner, context)
77
- old_tags = owned_tags - tag_list
78
- new_tags = tag_list - owned_tags
79
-
80
- # Find all taggings that belong to the taggable (self), are owned by the owner,
81
- # have the correct context, and are removed from the list.
82
- old_taggings = Tagging.where(:taggable_id => id, :taggable_type => self.class.base_class.to_s,
83
- :tagger_type => owner.class.to_s, :tagger_id => owner.id,
84
- :tag_id => old_tags, :context => context).all
85
-
86
- if old_taggings.present?
87
- # Destroy old taggings:
88
- Tagging.destroy_all(:id => old_taggings.map(&:id))
89
- end
90
-
91
- # Create new taggings:
92
- new_tags.each do |tag|
93
- Tagging.create!(:tag_id => tag.id, :context => context.to_s, :tagger => owner, :taggable => self)
94
- end
95
- end
96
- end
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,64 +0,0 @@
1
- module ActsAsTaggableOn::Taggable
2
- module Related
3
- def self.included(base)
4
- base.send :include, ActsAsTaggableOn::Taggable::Related::InstanceMethods
5
- base.extend ActsAsTaggableOn::Taggable::Related::ClassMethods
6
- end
7
-
8
- module ClassMethods
9
- def initialize_acts_as_taggable_on_related
10
- tag_types.map(&:to_s).each do |tag_type|
11
- class_eval %(
12
- def find_related_#{tag_type}(options = {})
13
- related_tags_for('#{tag_type}', self.class, options)
14
- end
15
- alias_method :find_related_on_#{tag_type}, :find_related_#{tag_type}
16
-
17
- def find_related_#{tag_type}_for(klass, options = {})
18
- related_tags_for('#{tag_type}', klass, options)
19
- end
20
-
21
- def find_matching_contexts(search_context, result_context, options = {})
22
- matching_contexts_for(search_context.to_s, result_context.to_s, self.class, options)
23
- end
24
-
25
- def find_matching_contexts_for(klass, search_context, result_context, options = {})
26
- matching_contexts_for(search_context.to_s, result_context.to_s, klass, options)
27
- end
28
- )
29
- end
30
- end
31
-
32
- def acts_as_taggable_on(*args)
33
- super(*args)
34
- initialize_acts_as_taggable_on_related
35
- end
36
- end
37
-
38
- module InstanceMethods
39
- def matching_contexts_for(search_context, result_context, klass, options = {})
40
- tags_to_find = tags_on(search_context).collect { |t| t.name }
41
-
42
- exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
43
-
44
- klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
45
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
46
- :conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?) AND #{Tagging.table_name}.context = ?", tags_to_find, result_context],
47
- :group => grouped_column_names_for(klass),
48
- :order => "count DESC" }.update(options))
49
- end
50
-
51
- def related_tags_for(context, klass, options = {})
52
- tags_to_find = tags_on(context).collect { |t| t.name }
53
-
54
- exclude_self = "#{klass.table_name}.id != #{id} AND" if self.class == klass
55
-
56
- klass.scoped({ :select => "#{klass.table_name}.*, COUNT(#{Tag.table_name}.id) AS count",
57
- :from => "#{klass.table_name}, #{Tag.table_name}, #{Tagging.table_name}",
58
- :conditions => ["#{exclude_self} #{klass.table_name}.id = #{Tagging.table_name}.taggable_id AND #{Tagging.table_name}.taggable_type = '#{klass.to_s}' AND #{Tagging.table_name}.tag_id = #{Tag.table_name}.id AND #{Tag.table_name}.name IN (?)", tags_to_find],
59
- :group => grouped_column_names_for(klass),
60
- :order => "count DESC" }.update(options))
61
- end
62
- end
63
- end
64
- end
@@ -1,41 +0,0 @@
1
- module ActsAsTaggableOn
2
- module Taggable
3
- def taggable?
4
- false
5
- end
6
-
7
- def acts_as_taggable
8
- acts_as_taggable_on :tags
9
- end
10
-
11
- def acts_as_taggable_on(*tag_types)
12
- tag_types = tag_types.to_a.flatten.compact.map(&:to_sym)
13
-
14
- if taggable?
15
- write_inheritable_attribute(:tag_types, (self.tag_types + tag_types).uniq)
16
- else
17
- if ::ActiveRecord::VERSION::MAJOR < 3
18
- include ActsAsTaggableOn::ActiveRecord::Backports
19
- end
20
-
21
- write_inheritable_attribute(:tag_types, tag_types)
22
- class_inheritable_reader(:tag_types)
23
-
24
- class_eval do
25
- has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
26
- has_many :base_tags, :class_name => "Tag", :through => :taggings, :source => :tag
27
-
28
- def self.taggable?
29
- true
30
- end
31
-
32
- include ActsAsTaggableOn::Taggable::Core
33
- include ActsAsTaggableOn::Taggable::Collection
34
- include ActsAsTaggableOn::Taggable::Cache
35
- include ActsAsTaggableOn::Taggable::Ownership
36
- include ActsAsTaggableOn::Taggable::Related
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,47 +0,0 @@
1
- module ActsAsTaggableOn
2
- module Tagger
3
- def self.included(base)
4
- base.extend ClassMethods
5
- end
6
-
7
- module ClassMethods
8
- def acts_as_tagger(opts={})
9
- has_many :owned_taggings, opts.merge(:as => :tagger, :dependent => :destroy,
10
- :include => :tag, :class_name => "Tagging")
11
- has_many :owned_tags, :through => :owned_taggings, :source => :tag, :uniq => true
12
-
13
- include ActsAsTaggableOn::Tagger::InstanceMethods
14
- extend ActsAsTaggableOn::Tagger::SingletonMethods
15
- end
16
-
17
- def is_tagger?
18
- false
19
- end
20
- end
21
-
22
- module InstanceMethods
23
- def tag(taggable, opts={})
24
- opts.reverse_merge!(:force => true)
25
-
26
- return false unless taggable.respond_to?(:is_taggable?) && taggable.is_taggable?
27
-
28
- raise "You need to specify a tag context using :on" unless opts.has_key?(:on)
29
- raise "You need to specify some tags using :with" unless opts.has_key?(:with)
30
- raise "No context :#{opts[:on]} defined in #{taggable.class.to_s}" unless (opts[:force] || taggable.tag_types.include?(opts[:on]))
31
-
32
- taggable.set_owner_tag_list_on(self, opts[:on].to_s, opts[:with])
33
- taggable.save
34
- end
35
-
36
- def is_tagger?
37
- self.class.is_tagger?
38
- end
39
- end
40
-
41
- module SingletonMethods
42
- def is_tagger?
43
- true
44
- end
45
- end
46
- end
47
- end
@@ -1,6 +0,0 @@
1
- source :gemcutter
2
-
3
- # Rails 2.3
4
- gem 'rails', '2.3.5'
5
- gem 'rspec', '1.3.0', :require => 'spec'
6
- gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
@@ -1,17 +0,0 @@
1
- module ActsAsTaggableOn
2
- module ActiveRecord
3
- module Backports
4
- def self.included(base)
5
- base.class_eval do
6
- named_scope :where, lambda { |conditions| { :conditions => conditions } }
7
- named_scope :joins, lambda { |joins| { :joins => joins } }
8
- named_scope :group, lambda { |group| { :group => group } }
9
- named_scope :order, lambda { |order| { :order => order } }
10
- named_scope :select, lambda { |select| { :select => select } }
11
- named_scope :limit, lambda { |limit| { :limit => limit } }
12
- named_scope :readonly, lambda { |readonly| { :readonly => readonly } }
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,31 +0,0 @@
1
- require 'rails/generators/migration'
2
-
3
- module ActsAsTaggableOn
4
- class MigrationGenerator < Rails::Generators::Base
5
- include Rails::Generators::Migration
6
-
7
- desc "Generates migration for Tag and Tagging models"
8
-
9
- def self.orm
10
- Rails::Generators.options[:rails][:orm]
11
- end
12
-
13
- def self.source_root
14
- File.join(File.dirname(__FILE__), 'templates', orm)
15
- end
16
-
17
- def self.orm_has_migration?
18
- [:active_record].include? orm
19
- end
20
-
21
- def self.next_migration_number(path)
22
- Time.now.utc.strftime("%Y%m%d%H%M%S")
23
- end
24
-
25
- def create_migration_file
26
- if self.class.orm_has_migration?
27
- migration_template 'migration.rb', 'db/migrate/acts_as_taggable_on_migration'
28
- end
29
- end
30
- end
31
- end
data/rails/init.rb DELETED
@@ -1 +0,0 @@
1
- require 'acts-as-taggable-on'
data/spec/bm.rb DELETED
@@ -1,52 +0,0 @@
1
- require 'active_record'
2
- require 'action_view'
3
- require File.expand_path('../../lib/acts-as-taggable-on', __FILE__)
4
-
5
- if defined?(ActiveRecord::Acts::TaggableOn)
6
- ActiveRecord::Base.send :include, ActiveRecord::Acts::TaggableOn
7
- ActiveRecord::Base.send :include, ActiveRecord::Acts::Tagger
8
- ActionView::Base.send :include, TagsHelper if defined?(ActionView::Base)
9
- end
10
-
11
- TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), '..', 'test.sqlite3')
12
- File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
13
- ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => TEST_DATABASE_FILE
14
-
15
- ActiveRecord::Base.silence do
16
- ActiveRecord::Migration.verbose = false
17
- ActiveRecord::Schema.define :version => 0 do
18
- create_table "taggings", :force => true do |t|
19
- t.integer "tag_id", :limit => 11
20
- t.integer "taggable_id", :limit => 11
21
- t.string "taggable_type"
22
- t.string "context"
23
- t.datetime "created_at"
24
- t.integer "tagger_id", :limit => 11
25
- t.string "tagger_type"
26
- end
27
-
28
- add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
29
- add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
30
-
31
- create_table "tags", :force => true do |t|
32
- t.string "name"
33
- end
34
-
35
- create_table :taggable_models, :force => true do |t|
36
- t.column :name, :string
37
- t.column :type, :string
38
- t.column :cached_tag_list, :string
39
- end
40
- end
41
-
42
- class TaggableModel < ActiveRecord::Base
43
- acts_as_taggable
44
- acts_as_taggable_on :languages
45
- acts_as_taggable_on :skills
46
- acts_as_taggable_on :needs, :offerings
47
- end
48
- end
49
-
50
- puts Benchmark.measure {
51
- 1000.times { TaggableModel.create :tag_list => "awesome, epic, neat" }
52
- }
data/spec/models.rb DELETED
@@ -1,36 +0,0 @@
1
- class TaggableModel < ActiveRecord::Base
2
- acts_as_taggable
3
- acts_as_taggable_on :languages
4
- acts_as_taggable_on :skills
5
- acts_as_taggable_on :needs, :offerings
6
- end
7
-
8
- class CachedModel < ActiveRecord::Base
9
- acts_as_taggable
10
- end
11
-
12
- class OtherTaggableModel < ActiveRecord::Base
13
- acts_as_taggable_on :tags, :languages
14
- acts_as_taggable_on :needs, :offerings
15
- end
16
-
17
- class InheritingTaggableModel < TaggableModel
18
- end
19
-
20
- class AlteredInheritingTaggableModel < TaggableModel
21
- acts_as_taggable_on :parts
22
- end
23
-
24
- class TaggableUser < ActiveRecord::Base
25
- acts_as_tagger
26
- end
27
-
28
- class UntaggableModel < ActiveRecord::Base
29
- end
30
-
31
- # if ActiveRecord::VERSION::MAJOR < 3
32
- # [TaggableModel, CachedModel, OtherTaggableModel, InheritingTaggableModel,
33
- # AlteredInheritingTaggableModel, TaggableUser, UntaggableModel].each do |klass|
34
- # klass.send(:include, ActsAsTaggableOn::ActiveRecord::Backports)
35
- # end
36
- # end
data/spec/schema.rb DELETED
@@ -1,42 +0,0 @@
1
- ActiveRecord::Schema.define :version => 0 do
2
- create_table "taggings", :force => true do |t|
3
- t.integer "tag_id", :limit => 11
4
- t.integer "taggable_id", :limit => 11
5
- t.string "taggable_type"
6
- t.string "context"
7
- t.datetime "created_at"
8
- t.integer "tagger_id", :limit => 11
9
- t.string "tagger_type"
10
- end
11
-
12
- add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
13
- add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
14
-
15
- create_table "tags", :force => true do |t|
16
- t.string "name"
17
- end
18
-
19
- create_table :taggable_models, :force => true do |t|
20
- t.column :name, :string
21
- t.column :type, :string
22
- end
23
-
24
- create_table :cached_models, :force => true do |t|
25
- t.column :name, :string
26
- t.column :type, :string
27
- t.column :cached_tag_list, :string
28
- end
29
-
30
- create_table :taggable_users, :force => true do |t|
31
- t.column :name, :string
32
- end
33
-
34
- create_table :other_taggable_models, :force => true do |t|
35
- t.column :name, :string
36
- t.column :type, :string
37
- end
38
-
39
- create_table :untaggable_models, :force => true do |t|
40
- t.column :name, :string
41
- end
42
- end