Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix User-Defined Column Cache Override #911

Merged
merged 2 commits into from
Aug 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ Each change should fall into categories that would affect whether the release is
As such, a _Feature_ would map to either major or minor. A _bug fix_ to a patch. And _misc_ is either minor or patch, the difference being kind of fuzzy for the purposes of history. Adding tests would be patch level.

### [Master / Unreleased](https://github.com/mbleigh/acts-as-taggable-on/compare/v6.0.1...master)
* Fixes
* [@tonyta Avoid overriding user-defined columns cache methods](https://github.com/mbleigh/acts-as-taggable-on/pull/911)
* Misc
* [@gssbzn Remove legacy code for an empty query and replace it with ` ActiveRecord::none`](https://github.com/mbleigh/acts-as-taggable-on/pull/906)

Expand Down
72 changes: 38 additions & 34 deletions lib/acts_as_taggable_on/taggable/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,51 @@ module Cache
def self.included(base)
# When included, conditionally adds tag caching methods when the model
# has any "cached_#{tag_type}_list" column
base.instance_eval do
# @private
def _has_tags_cache_columns?(db_columns)
db_column_names = db_columns.map(&:name)
tag_types.any? do |context|
db_column_names.include?("cached_#{context.to_s.singularize}_list")
end
base.extend Columns
end

module Columns
# ActiveRecord::Base.columns makes a database connection and caches the
# calculated columns hash for the record as @columns. Since we don't
# want to add caching methods until we confirm the presence of a
# caching column, and we don't want to force opening a database
# connection when the class is loaded, here we intercept and cache
# the call to :columns as @acts_as_taggable_on_cache_columns
# to mimic the underlying behavior. While processing this first
# call to columns, we do the caching column check and dynamically add
# the class and instance methods
# FIXME: this method cannot compile in rubinius
def columns
@acts_as_taggable_on_cache_columns ||= begin
db_columns = super
_add_tags_caching_methods if _has_tags_cache_columns?(db_columns)
db_columns
end
end

# @private
def _add_tags_caching_methods
send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
extend ActsAsTaggableOn::Taggable::Cache::ClassMethods
def reset_column_information
super
@acts_as_taggable_on_cache_columns = nil
end

before_save :save_cached_tag_list
private

initialize_tags_cache
# @private
def _has_tags_cache_columns?(db_columns)
db_column_names = db_columns.map(&:name)
tag_types.any? do |context|
db_column_names.include?("cached_#{context.to_s.singularize}_list")
end
end

# ActiveRecord::Base.columns makes a database connection and caches the
# calculated columns hash for the record as @columns. Since we don't
# want to add caching methods until we confirm the presence of a
# caching column, and we don't want to force opening a database
# connection when the class is loaded, here we intercept and cache
# the call to :columns as @acts_as_taggable_on_cache_columns
# to mimic the underlying behavior. While processing this first
# call to columns, we do the caching column check and dynamically add
# the class and instance methods
# FIXME: this method cannot compile in rubinius
def columns
@acts_as_taggable_on_cache_columns ||= begin
db_columns = super
_add_tags_caching_methods if _has_tags_cache_columns?(db_columns)
db_columns
end
end
# @private
def _add_tags_caching_methods
send :include, ActsAsTaggableOn::Taggable::Cache::InstanceMethods
extend ActsAsTaggableOn::Taggable::Cache::ClassMethods

def reset_column_information
super
@acts_as_taggable_on_cache_columns = nil
end
before_save :save_cached_tag_list

initialize_tags_cache
end
end

Expand Down
6 changes: 6 additions & 0 deletions spec/acts_as_taggable_on/caching_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@
CachedModel.reset_column_information
expect(CachedModel.instance_variable_get(:@acts_as_taggable_on_cache_columns)).to eql(nil)
end

it 'should not override a user-defined columns method' do
expect(ColumnsOverrideModel.columns.map(&:name)).not_to include('ignored_column')
ColumnsOverrideModel.acts_as_taggable
expect(ColumnsOverrideModel.columns.map(&:name)).not_to include('ignored_column')
end
end

describe 'with a custom delimiter' do
Expand Down
2 changes: 2 additions & 0 deletions spec/internal/app/models/altered_inheriting_taggable_model.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative 'taggable_model'

class AlteredInheritingTaggableModel < TaggableModel
acts_as_taggable_on :parts
end
6 changes: 6 additions & 0 deletions spec/internal/app/models/cached_model_with_array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
class CachedModelWithArray < ActiveRecord::Base
acts_as_taggable
end
if postgresql_support_json?
class TaggableModelWithJson < ActiveRecord::Base
acts_as_taggable
acts_as_taggable_on :skills
end
end
end
5 changes: 5 additions & 0 deletions spec/internal/app/models/columns_override_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ColumnsOverrideModel < ActiveRecord::Base
def self.columns
super.reject { |c| c.name == 'ignored_column' }
end
end
2 changes: 1 addition & 1 deletion spec/internal/app/models/company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ def find_or_create_tags_from_list_with_context(tag_list, context)
super
end
end
end
end
2 changes: 2 additions & 0 deletions spec/internal/app/models/inheriting_taggable_model.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
require_relative 'taggable_model'

class InheritingTaggableModel < TaggableModel
end
2 changes: 1 addition & 1 deletion spec/internal/app/models/market.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
class Market < ActsAsTaggableOn::Tag
end
end
90 changes: 0 additions & 90 deletions spec/internal/app/models/models.rb

This file was deleted.

2 changes: 1 addition & 1 deletion spec/internal/app/models/non_standard_id_taggable_model.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class NonStandardIdTaggableModel < ActiveRecord::Base
self.primary_key = 'an_id'
self.primary_key = :an_id
acts_as_taggable
acts_as_taggable_on :languages
acts_as_taggable_on :skills
Expand Down
2 changes: 2 additions & 0 deletions spec/internal/app/models/student.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
require_relative 'user'

class Student < User
end
1 change: 1 addition & 0 deletions spec/internal/app/models/taggable_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class TaggableModel < ActiveRecord::Base
has_many :untaggable_models

attr_reader :tag_list_submethod_called

def tag_list=(v)
@tag_list_submethod_called = true
super
Expand Down
2 changes: 1 addition & 1 deletion spec/internal/app/models/user.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
class User < ActiveRecord::Base
acts_as_tagger
end
end
6 changes: 6 additions & 0 deletions spec/internal/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
t.column :type, :string
end

create_table :columns_override_models, force: true do |t|
t.column :name, :string
t.column :type, :string
t.column :ignored_column, :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
Expand Down
1 change: 0 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,3 @@
RSpec.configure do |config|
config.raise_errors_for_deprecations!
end

6 changes: 3 additions & 3 deletions spec/support/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
ActiveRecord::Base.establish_connection(config)
end

load(File.dirname(__FILE__) + '/../internal/db/schema.rb')
load(File.dirname(__FILE__) + '/../internal/app/models/models.rb')
require File.dirname(__FILE__) + '/../internal/db/schema.rb'
Dir[File.dirname(__dir__) + '/internal/app/models/*.rb'].each { |f| require f }

else
fail "Please create #{database_yml} first to configure your database. Take a look at: #{database_yml}.sample"
end
end