From 722eaa0bcf4b47ab983f97d38fae7974dc24f1f8 Mon Sep 17 00:00:00 2001 From: sanemat Date: Wed, 12 Jun 2013 17:50:53 +0900 Subject: [PATCH 01/13] Test against ruby2.0 in travis-ci --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 576396e71..18b8d446c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ rvm: - 1.8.7 - 1.9.2 - 1.9.3 + - 2.0.0 env: - DB=sqlite3 - DB=mysql - - DB=postgresql \ No newline at end of file + - DB=postgresql From 763bd368420a53b7b511b35b7ca266c9a25ab49e Mon Sep 17 00:00:00 2001 From: RSL Date: Fri, 7 Jun 2013 14:30:24 -0400 Subject: [PATCH 02/13] fix bug with unprepared statements to sql in Rails 4 --- .../acts_as_taggable_on/collection.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb b/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb index 60eb4bd6e..c48cfba7a 100644 --- a/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb +++ b/lib/acts_as_taggable_on/acts_as_taggable_on/collection.rb @@ -87,9 +87,9 @@ def all_tags(options = {}) # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore: scoped_select = "#{table_name}.#{primary_key}" - tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})").group(group_columns) + tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(select(scoped_select))})").group(group_columns) - tag_scope = tag_scope.joins("JOIN (#{tagging_scope.to_sql}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id") + tag_scope = tag_scope.joins("JOIN (#{safe_to_sql(tagging_scope)}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id") tag_scope end @@ -164,14 +164,18 @@ def all_tag_counts(options = {}) unless options[:id] # Append the current scope to the scope, because we can't use scope(:find) in RoR 3.0 anymore: scoped_select = "#{table_name}.#{primary_key}" - tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{select(scoped_select).to_sql})") + tagging_scope = tagging_scope.where("#{ActsAsTaggableOn::Tagging.table_name}.taggable_id IN(#{safe_to_sql(select(scoped_select))})") end tagging_scope = tagging_scope.group(group_columns).having(having) - tag_scope = tag_scope.joins("JOIN (#{tagging_scope.to_sql}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id") + tag_scope = tag_scope.joins("JOIN (#{safe_to_sql(tagging_scope)}) AS #{ActsAsTaggableOn::Tagging.table_name} ON #{ActsAsTaggableOn::Tagging.table_name}.tag_id = #{ActsAsTaggableOn::Tag.table_name}.id") tag_scope end + + def safe_to_sql(relation) + connection.respond_to?(:unprepared_statement) ? connection.unprepared_statement{relation.to_sql} : relation.to_sql + end end module InstanceMethods From fec2dd17609dda7f9da540152570041f33b65ddc Mon Sep 17 00:00:00 2001 From: Ken Dreyer Date: Mon, 29 Jul 2013 19:51:34 -0600 Subject: [PATCH 03/13] fix a spelling error in spec_helper --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e317bc319..02b1f1dbc 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,7 +14,7 @@ Bundler.setup rescue Bundler::GemNotFound raise RuntimeError, "Bundler couldn't find some gems." + - "Did you run \`bundlee install\`?" + "Did you run \`bundle install\`?" end Bundler.require From ca6768aec91e00dd3bd75d54c460d3e8a0dfb8d3 Mon Sep 17 00:00:00 2001 From: Michael Bernstein Date: Thu, 10 Oct 2013 14:20:37 -0400 Subject: [PATCH 04/13] Add Code Climate badge to repo README Add Code Climate badge to repo README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a67d267ce..efe825962 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # ActsAsTaggableOn [![Build Status](https://secure.travis-ci.org/mbleigh/acts-as-taggable-on.png)](http://travis-ci.org/mbleigh/acts-as-taggable-on) +[![Code Climate](https://codeclimate.com/github/mbleigh/acts-as-taggable-on.png)](https://codeclimate.com/github/mbleigh/acts-as-taggable-on) This plugin was originally based on Acts as Taggable on Steroids by Jonathan Viney. It has evolved substantially since that point, but all credit goes to him for the From 66955c9bc5dd8cf9140db2e5adc59794d0dd4c8d Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 8 Dec 2013 21:53:58 -0600 Subject: [PATCH 05/13] Remove 1.8, 1.9.2 support, per README --- .travis.yml | 2 -- Rakefile | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 18b8d446c..00d3fe8ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ script: "cp spec/database.yml.sample spec/database.yml && bundle install && bundle exec rake" rvm: - - 1.8.7 - - 1.9.2 - 1.9.3 - 2.0.0 env: diff --git a/Rakefile b/Rakefile index a81fa40d8..5d0808e21 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ require 'bundler/setup' require 'appraisal' desc 'Default: run specs' -task :default => :spec +task :default => :spec require 'rspec/core/rake_task' RSpec::Core::RakeTask.new do |t| From fc71c3636ddf3e037bc89bd79678943ada777026 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 5 Aug 2013 09:15:28 -0500 Subject: [PATCH 06/13] Rails needs to be loaded to meet all gem dependencies --- lib/acts-as-taggable-on.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/acts-as-taggable-on.rb b/lib/acts-as-taggable-on.rb index 7a6e116c2..7deaafc45 100644 --- a/lib/acts-as-taggable-on.rb +++ b/lib/acts-as-taggable-on.rb @@ -1,6 +1,7 @@ require "active_record" require "active_record/version" require "action_view" +require 'rails' require "digest/sha1" From 2f7cde3b4490dddc0f1b4ad6b309069f9c5842a5 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 5 Aug 2013 09:19:09 -0500 Subject: [PATCH 07/13] No need to monkey with load path in gem, add to tests --- lib/acts-as-taggable-on.rb | 4 ---- spec/spec_helper.rb | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/acts-as-taggable-on.rb b/lib/acts-as-taggable-on.rb index 7deaafc45..6f722b3c4 100644 --- a/lib/acts-as-taggable-on.rb +++ b/lib/acts-as-taggable-on.rb @@ -5,8 +5,6 @@ require "digest/sha1" -$LOAD_PATH.unshift(File.dirname(__FILE__)) - module ActsAsTaggableOn mattr_accessor :delimiter @@delimiter = ',' @@ -51,8 +49,6 @@ def self.setup require "acts_as_taggable_on/tags_helper" require "acts_as_taggable_on/tagging" -$LOAD_PATH.shift - if defined?(ActiveRecord::Base) ActiveRecord::Base.extend ActsAsTaggableOn::Compatibility diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 02b1f1dbc..493538bb1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ $LOAD_PATH << "." unless $LOAD_PATH.include?(".") +$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) require 'logger' begin From f1f3d09356137d3de260088ed2057afd0bb25023 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 5 Aug 2013 09:20:27 -0500 Subject: [PATCH 08/13] Use ActiveSupport hooks to lazy-extend/include rails classes --- lib/acts-as-taggable-on.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/acts-as-taggable-on.rb b/lib/acts-as-taggable-on.rb index 6f722b3c4..0e22128d1 100644 --- a/lib/acts-as-taggable-on.rb +++ b/lib/acts-as-taggable-on.rb @@ -49,14 +49,12 @@ def self.setup require "acts_as_taggable_on/tags_helper" require "acts_as_taggable_on/tagging" - -if defined?(ActiveRecord::Base) - ActiveRecord::Base.extend ActsAsTaggableOn::Compatibility - ActiveRecord::Base.extend ActsAsTaggableOn::Taggable - ActiveRecord::Base.send :include, ActsAsTaggableOn::Tagger +ActiveSupport.on_load(:active_record) do + extend ActsAsTaggableOn::Compatibility + extend ActsAsTaggableOn::Taggable + include ActsAsTaggableOn::Tagger end - -if defined?(ActionView::Base) - ActionView::Base.send :include, ActsAsTaggableOn::TagsHelper +ActiveSupport.on_load(:action_view) do + include ActsAsTaggableOn::TagsHelper end From bd04da4141d34528c4d31809db7445132dbb774f Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 9 Dec 2013 17:15:21 -0600 Subject: [PATCH 09/13] Only ActiveSupport actually required, not Rails gem --- lib/acts-as-taggable-on.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/acts-as-taggable-on.rb b/lib/acts-as-taggable-on.rb index 0e22128d1..25eb9dd5b 100644 --- a/lib/acts-as-taggable-on.rb +++ b/lib/acts-as-taggable-on.rb @@ -1,7 +1,7 @@ require "active_record" require "active_record/version" require "action_view" -require 'rails' +require 'active_support/all' require "digest/sha1" From 3a0b6a93ad085c549b7c65442e7734a2615a3897 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 9 Dec 2013 17:38:04 -0600 Subject: [PATCH 10/13] Update test schema.rb to match current migration However, set force: true and didn't set indices Fixes test failures for postgresql:: No integer type has byte size 11. Use a numeric with precision 0 instead. https://travis-ci.org/mbleigh/acts-as-taggable-on/jobs/15196579 see https://github.com/rails/rails/issues/6272 --- spec/schema.rb | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/spec/schema.rb b/spec/schema.rb index 4949f0107..2d2e27453 100644 --- a/spec/schema.rb +++ b/spec/schema.rb @@ -1,59 +1,63 @@ ActiveRecord::Schema.define :version => 0 do - create_table "taggings", :force => true do |t| - t.integer "tag_id", :limit => 11 - t.integer "taggable_id", :limit => 11 - t.string "taggable_type" - t.string "context" - t.datetime "created_at" - t.integer "tagger_id", :limit => 11 - t.string "tagger_type" + create_table :tags, :force => true do |t| + t.string :name end - add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id" - add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context" + create_table :taggings, :force => true do |t| + t.references :tag - create_table "tags", :force => true do |t| - t.string "name" + # You should make sure that the column created is + # long enough to store the required class names. + t.references :taggable, :polymorphic => true + t.references :tagger, :polymorphic => true + + # Limit is created to prevent MySQL error on index + # length for MyISAM table type: http://bit.ly/vgW2Ql + t.string :context, :limit => 128 + + t.datetime :created_at end - + # above copied from + # generators/acts_as_taggable_on/migration/migration_generator + create_table :taggable_models, :force => true do |t| t.column :name, :string t.column :type, :string end - + create_table :non_standard_id_taggable_models, :primary_key => "an_id", :force => true do |t| t.column :name, :string t.column :type, :string end - + create_table :untaggable_models, :force => true do |t| t.column :taggable_model_id, :integer t.column :name, :string end - + create_table :cached_models, :force => true do |t| t.column :name, :string t.column :type, :string t.column :cached_tag_list, :string end - + create_table :other_cached_models, :force => true do |t| t.column :name, :string t.column :type, :string - t.column :cached_language_list, :string + t.column :cached_language_list, :string t.column :cached_status_list, :string t.column :cached_glass_list, :string end - + create_table :users, :force => true do |t| t.column :name, :string end - + create_table :other_taggable_models, :force => true do |t| t.column :name, :string t.column :type, :string end - + create_table :ordered_taggable_models, :force => true do |t| t.column :name, :string t.column :type, :string From aebd28aa66c720716bb09f22c5efc377f35d64d8 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 9 Dec 2013 17:43:47 -0600 Subject: [PATCH 11/13] Add .travis.yml bundle cache --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 00d3fe8ae..958087b36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,3 +6,4 @@ env: - DB=sqlite3 - DB=mysql - DB=postgresql +cache: bundler From f6d99bab819c39181ec74e6c8ce43a111cd34224 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 9 Dec 2013 22:16:10 -0600 Subject: [PATCH 12/13] Use #detect in Rails to avoid conflicts with AR.find Fixes https://travis-ci.org/mbleigh/acts-as-taggable-on/jobs/15206376 --- lib/acts_as_taggable_on/tag.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/acts_as_taggable_on/tag.rb b/lib/acts_as_taggable_on/tag.rb index 07bbb6ca7..eb3949542 100644 --- a/lib/acts_as_taggable_on/tag.rb +++ b/lib/acts_as_taggable_on/tag.rb @@ -64,7 +64,7 @@ def self.find_or_create_all_with_like_by_name(*list) list.map do |tag_name| comparable_tag_name = comparable_name(tag_name) - existing_tag = existing_tags.find { |tag| comparable_name(tag.name) == comparable_tag_name } + existing_tag = existing_tags.detect { |tag| comparable_name(tag.name) == comparable_tag_name } existing_tag || Tag.create(:name => tag_name) end From 11c31614152a69bff129822597dd223003d8074b Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Mon, 9 Dec 2013 22:40:10 -0600 Subject: [PATCH 13/13] Refactoring; Ensure queried tag is 8bit ASCII Fixes SQLITE3 failures --- lib/acts_as_taggable_on/tag.rb | 32 +++++++++++++--- .../acts_as_taggable_on_spec.rb | 7 ++-- .../acts_as_tagger_spec.rb | 38 +++++++++---------- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/lib/acts_as_taggable_on/tag.rb b/lib/acts_as_taggable_on/tag.rb index eb3949542..336f8ba9f 100644 --- a/lib/acts_as_taggable_on/tag.rb +++ b/lib/acts_as_taggable_on/tag.rb @@ -1,3 +1,4 @@ +# coding: utf-8 module ActsAsTaggableOn class Tag < ::ActiveRecord::Base include ActsAsTaggableOn::Utils @@ -31,18 +32,29 @@ def self.named(name) def self.named_any(list) if ActsAsTaggableOn.strict_case_match - where(list.map { |tag| sanitize_sql(["name = #{binary}?", tag.to_s.mb_chars]) }.join(" OR ")) + clause = list.map { |tag| + sanitize_sql(["name = #{binary}?", as_8bit_ascii(tag)]) + }.join(" OR ") + where(clause) else - where(list.map { |tag| sanitize_sql(["lower(name) = ?", tag.to_s.mb_chars.downcase]) }.join(" OR ")) + clause = list.map { |tag| + lowercase_ascii_tag = as_8bit_ascii(tag).downcase + sanitize_sql(["lower(name) = ?", lowercase_ascii_tag]) + }.join(" OR ") + where(clause) end end def self.named_like(name) - where(["name #{like_operator} ? ESCAPE '!'", "%#{escape_like(name)}%"]) + clause = ["name #{like_operator} ? ESCAPE '!'", "%#{escape_like(name)}%"] + where(clause) end def self.named_like_any(list) - where(list.map { |tag| sanitize_sql(["name #{like_operator} ? ESCAPE '!'", "%#{escape_like(tag.to_s)}%"]) }.join(" OR ")) + clause = list.map { |tag| + sanitize_sql(["name #{like_operator} ? ESCAPE '!'", "%#{escape_like(tag.to_s)}%"]) + }.join(" OR ") + where(clause) end ### CLASS METHODS: @@ -56,7 +68,7 @@ def self.find_or_create_with_like_by_name(name) end def self.find_or_create_all_with_like_by_name(*list) - list = [list].flatten + list = Array(list).flatten return [] if list.empty? @@ -88,12 +100,20 @@ class << self private def comparable_name(str) - str.mb_chars.downcase.to_s + as_8bit_ascii(str).downcase end def binary /mysql/ === ActiveRecord::Base.connection_config[:adapter] ? "BINARY " : nil end + + def as_8bit_ascii(string) + if defined?(Encoding) + string.to_s.force_encoding('BINARY') + else + string.to_s.mb_chars + end + end end end end diff --git a/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb b/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb index 3c9dc2495..3c1ab3205 100644 --- a/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb +++ b/spec/acts_as_taggable_on/acts_as_taggable_on_spec.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require 'spec_helper' describe "Acts As Taggable On" do @@ -8,7 +9,7 @@ it "should provide a class method 'taggable?' that is false for untaggable models" do UntaggableModel.should_not be_taggable end - + describe "Taggable Method Generation To Preserve Order" do before(:each) do clean_database! @@ -46,7 +47,7 @@ it "should have all tag types" do @taggable.tag_types.should == [:tags, :languages, :skills, :needs, :offerings] end - + it "should create a class attribute for preserve tag order" do @taggable.class.should respond_to(:preserve_tag_order?) end @@ -54,7 +55,7 @@ it "should create an instance attribute for preserve tag order" do @taggable.should respond_to(:preserve_tag_order?) end - + it "should respond 'false' to preserve_tag_order?" do @taggable.class.preserve_tag_order?.should be_false end diff --git a/spec/acts_as_taggable_on/acts_as_tagger_spec.rb b/spec/acts_as_taggable_on/acts_as_tagger_spec.rb index 9591fcba3..22e93067a 100644 --- a/spec/acts_as_taggable_on/acts_as_tagger_spec.rb +++ b/spec/acts_as_taggable_on/acts_as_tagger_spec.rb @@ -4,7 +4,7 @@ before(:each) do clean_database! end - + describe "Tagger Method Generation" do before(:each) do @tagger = User.new @@ -13,55 +13,55 @@ it "should add #is_tagger? query method to the class-side" do User.should respond_to(:is_tagger?) end - + it "should return true from the class-side #is_tagger?" do User.is_tagger?.should be_true end - + it "should return false from the base #is_tagger?" do ActiveRecord::Base.is_tagger?.should be_false end - + it "should add #is_tagger? query method to the singleton" do @tagger.should respond_to(:is_tagger?) end - + it "should add #tag method on the instance-side" do @tagger.should respond_to(:tag) end - + it "should generate an association for #owned_taggings and #owned_tags" do @tagger.should respond_to(:owned_taggings, :owned_tags) end end - + describe "#tag" do context 'when called with a non-existent tag context' do before(:each) do @tagger = User.new @taggable = TaggableModel.new(:name=>"Richard Prior") end - + it "should by default not throw an exception " do @taggable.tag_list_on(:foo).should be_empty lambda { @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo) }.should_not raise_error end - + it 'should by default create the tag context on-the-fly' do @taggable.tag_list_on(:here_ond_now).should be_empty @tagger.tag(@taggable, :with=>'that', :on => :here_ond_now) @taggable.tag_list_on(:here_ond_now).should_not include('that') @taggable.all_tags_list_on(:here_ond_now).should include('that') end - + it "should show all the tag list when both public and owned tags exist" do @taggable.tag_list = 'ruby, python' @tagger.tag(@taggable, :with => 'java, lisp', :on => :tags) @taggable.all_tags_on(:tags).map(&:name).sort.should == %w(ruby python java lisp).sort end - + it "should not add owned tags to the common list" do @taggable.tag_list = 'ruby, python' @tagger.tag(@taggable, :with => 'java, lisp', :on => :tags) @@ -69,12 +69,12 @@ @tagger.tag(@taggable, :with => '', :on => :tags) @taggable.tag_list.should == %w(ruby python) end - + it "should throw an exception when the default is over-ridden" do @taggable.tag_list_on(:foo_boo).should be_empty lambda { @tagger.tag(@taggable, :with=>'this, and, that', :on=>:foo_boo, :force=>false) - }.should raise_error + }.should raise_error end it "should not create the tag context on-the-fly when the default is over-ridden" do @@ -83,28 +83,28 @@ @taggable.tag_list_on(:foo_boo).should be_empty end end - + describe "when called by multiple tagger's" do before(:each) do @user_x = User.create(:name => "User X") @user_y = User.create(:name => "User Y") @taggable = TaggableModel.create(:name => 'acts_as_taggable_on', :tag_list => 'plugin') - + @user_x.tag(@taggable, :with => 'ruby, rails', :on => :tags) @user_y.tag(@taggable, :with => 'ruby, plugin', :on => :tags) @user_y.tag(@taggable, :with => '', :on => :tags) @user_y.tag(@taggable, :with => '', :on => :tags) end - - it "should delete owned tags" do + + it "should delete owned tags" do @user_y.owned_tags.should == [] end - + it "should not delete other taggers tags" do @user_x.owned_tags.should have(2).items end - + it "should not delete original tags" do @taggable.all_tags_list_on(:tags).should include('plugin') end