Commit ecfd33c12f0d008d4b09b9dab486e3d0100239c3

Authored by Marin Jankovski
1 parent e18d5be5

Add acts-as-taggable-on-patch for gem version 2.4.1 and Rails 4.1.

Showing 1 changed file with 130 additions and 0 deletions   Show diff stats
config/initializers/acts_as_taggable_on_patch.rb 0 → 100644
... ... @@ -0,0 +1,130 @@
  1 +# This is a patch to address the issue in https://github.com/mbleigh/acts-as-taggable-on/issues/427 caused by
  2 +# https://github.com/rails/rails/commit/31a43ebc107fbd50e7e62567e5208a05909ec76c
  3 +# gem 'acts-as-taggable-on' has the fix included https://github.com/mbleigh/acts-as-taggable-on/commit/89bbed3864a9252276fb8dd7d535fce280454b90
  4 +# but not in the currently used version of gem ('2.4.1')
  5 +# With replacement of 'acts-as-taggable-on' gem this file will become obsolete
  6 +
  7 +module ActsAsTaggableOn::Taggable
  8 + module Core
  9 + module ClassMethods
  10 + def tagged_with(tags, options = {})
  11 + tag_list = ActsAsTaggableOn::TagList.from(tags)
  12 + empty_result = where("1 = 0")
  13 +
  14 + return empty_result if tag_list.empty?
  15 +
  16 + joins = []
  17 + conditions = []
  18 + having = []
  19 + select_clause = []
  20 +
  21 + context = options.delete(:on)
  22 + owned_by = options.delete(:owned_by)
  23 + alias_base_name = undecorated_table_name.gsub('.','_')
  24 + quote = ActsAsTaggableOn::Tag.using_postgresql? ? '"' : ''
  25 +
  26 + if options.delete(:exclude)
  27 + if options.delete(:wild)
  28 + tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ? ESCAPE '!'", "%#{escape_like(t)}%"]) }.join(" OR ")
  29 + else
  30 + tags_conditions = tag_list.map { |t| sanitize_sql(["#{ActsAsTaggableOn::Tag.table_name}.name #{like_operator} ?", t]) }.join(" OR ")
  31 + end
  32 +
  33 + 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}.#{ActsAsTaggableOn::Tag.primary_key} AND (#{tags_conditions}) WHERE #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)})"
  34 +
  35 + if owned_by
  36 + joins << "JOIN #{ActsAsTaggableOn::Tagging.table_name}" +
  37 + " ON #{ActsAsTaggableOn::Tagging.table_name}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
  38 + " AND #{ActsAsTaggableOn::Tagging.table_name}.taggable_type = #{quote_value(base_class.name, nil)}" +
  39 + " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_id = #{owned_by.id}" +
  40 + " AND #{ActsAsTaggableOn::Tagging.table_name}.tagger_type = #{quote_value(owned_by.class.base_class.to_s, nil)}"
  41 + end
  42 +
  43 + elsif options.delete(:any)
  44 + # get tags, drop out if nothing returned (we need at least one)
  45 + tags = if options.delete(:wild)
  46 + ActsAsTaggableOn::Tag.named_like_any(tag_list)
  47 + else
  48 + ActsAsTaggableOn::Tag.named_any(tag_list)
  49 + end
  50 +
  51 + return empty_result unless tags.length > 0
  52 +
  53 + # setup taggings alias so we can chain, ex: items_locations_taggings_awesome_cool_123
  54 + # avoid ambiguous column name
  55 + taggings_context = context ? "_#{context}" : ''
  56 +
  57 + taggings_alias = adjust_taggings_alias(
  58 + "#{alias_base_name[0..4]}#{taggings_context[0..6]}_taggings_#{sha_prefix(tags.map(&:name).join('_'))}"
  59 + )
  60 +
  61 + tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
  62 + " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
  63 + " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
  64 + tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
  65 +
  66 + # don't need to sanitize sql, map all ids and join with OR logic
  67 + conditions << tags.map { |t| "#{taggings_alias}.tag_id = #{t.id}" }.join(" OR ")
  68 + select_clause = "DISTINCT #{table_name}.*" unless context and tag_types.one?
  69 +
  70 + if owned_by
  71 + tagging_join << " AND " +
  72 + sanitize_sql([
  73 + "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
  74 + owned_by.id,
  75 + owned_by.class.base_class.to_s
  76 + ])
  77 + end
  78 +
  79 + joins << tagging_join
  80 + else
  81 + tags = ActsAsTaggableOn::Tag.named_any(tag_list)
  82 +
  83 + return empty_result unless tags.length == tag_list.length
  84 +
  85 + tags.each do |tag|
  86 + taggings_alias = adjust_taggings_alias("#{alias_base_name[0..11]}_taggings_#{sha_prefix(tag.name)}")
  87 + tagging_join = "JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
  88 + " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
  89 + " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}" +
  90 + " AND #{taggings_alias}.tag_id = #{tag.id}"
  91 +
  92 + tagging_join << " AND " + sanitize_sql(["#{taggings_alias}.context = ?", context.to_s]) if context
  93 +
  94 + if owned_by
  95 + tagging_join << " AND " +
  96 + sanitize_sql([
  97 + "#{taggings_alias}.tagger_id = ? AND #{taggings_alias}.tagger_type = ?",
  98 + owned_by.id,
  99 + owned_by.class.base_class.to_s
  100 + ])
  101 + end
  102 +
  103 + joins << tagging_join
  104 + end
  105 + end
  106 +
  107 + taggings_alias, tags_alias = adjust_taggings_alias("#{alias_base_name}_taggings_group"), "#{alias_base_name}_tags_group"
  108 +
  109 + if options.delete(:match_all)
  110 + joins << "LEFT OUTER JOIN #{ActsAsTaggableOn::Tagging.table_name} #{taggings_alias}" +
  111 + " ON #{taggings_alias}.taggable_id = #{quote}#{table_name}#{quote}.#{primary_key}" +
  112 + " AND #{taggings_alias}.taggable_type = #{quote_value(base_class.name, nil)}"
  113 +
  114 +
  115 + group_columns = ActsAsTaggableOn::Tag.using_postgresql? ? grouped_column_names_for(self) : "#{table_name}.#{primary_key}"
  116 + group = group_columns
  117 + having = "COUNT(#{taggings_alias}.taggable_id) = #{tags.size}"
  118 + end
  119 +
  120 + select(select_clause) \
  121 + .joins(joins.join(" ")) \
  122 + .where(conditions.join(" AND ")) \
  123 + .group(group) \
  124 + .having(having) \
  125 + .order(options[:order]) \
  126 + .readonly(false)
  127 + end
  128 + end
  129 + end
  130 +end
... ...