Commit ecfd33c12f0d008d4b09b9dab486e3d0100239c3
1 parent
e18d5be5
Exists in
spb-stable
and in
2 other branches
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
| ... | ... | @@ -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 | ... | ... |