Skip to content

Commit

Permalink
Merge branch 'sjke-compatibility-rails-5'
Browse files Browse the repository at this point in the history
  • Loading branch information
Zachary Scott committed Jul 8, 2016
2 parents 3150a6b + 7da8712 commit 11ea17d
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 47 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,18 @@ gemfile:
- gemfiles/active_record_40.gemfile
- gemfiles/active_record_41.gemfile
- gemfiles/active_record_42.gemfile
- gemfiles/active_record_50.gemfile
matrix:
allow_failures:
- rvm: ruby-head
- rvm: rbx
- rvm: jruby
- rvm: jruby-9.1.0.0
fast_finish: true
exclude:
- rvm: 1.9.3
gemfile: gemfiles/active_record_50.gemfile
- rvm: 2.0.0
gemfile: gemfiles/active_record_50.gemfile
- rvm: 2.1.9
gemfile: gemfiles/active_record_50.gemfile
4 changes: 2 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ desc 'Default: run unit tests.'
task :default => "test:all"

namespace :test do
%w(active_record_40 active_record_41 active_record_42).each do |version|
%w(active_record_40 active_record_41 active_record_42 active_record_50).each do |version|
desc "Test acts_as_paranoid against #{version}"
task version do
sh "BUNDLE_GEMFILE='gemfiles/#{version}.gemfile' bundle install --quiet"
Expand All @@ -19,7 +19,7 @@ namespace :test do

desc "Run all tests for acts_as_paranoid"
task :all do
%w(active_record_40 active_record_41 active_record_42).each do |version|
%w(active_record_40 active_record_41 active_record_42 active_record_50).each do |version|
sh "BUNDLE_GEMFILE='gemfiles/#{version}.gemfile' bundle install --quiet"
sh "BUNDLE_GEMFILE='gemfiles/#{version}.gemfile' bundle exec rake -t test"
end
Expand Down
4 changes: 2 additions & 2 deletions acts_as_paranoid.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ Gem::Specification.new do |spec|

spec.required_rubygems_version = ">= 1.3.6"

spec.add_dependency "activerecord", "~> 4.0"
spec.add_dependency "activesupport", "~> 4.0"
spec.add_dependency "activerecord", ">= 4.0", "< 5.1"
spec.add_dependency "activesupport", ">= 4.0", "< 5.1"

spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake"
Expand Down
25 changes: 25 additions & 0 deletions gemfiles/active_record_50.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
source 'https://rubygems.org'

gem 'activerecord', '>= 5.0.0.beta1', '< 5.1', require: 'active_record'
gem 'activesupport', '>= 5.0.0.beta1', '< 5.1', require: 'active_support'

platforms :ruby do
if RUBY_VERSION > "2.1.0"
gem 'sqlite3'
else
gem 'sqlite3', '1.3.8'
end
end

platforms :jruby do
gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.0'
end

platforms :rbx do
gem 'rubysl', '~> 2.0'
gem 'racc'
gem 'minitest'
gem 'rubinius-developer_tools'
end

gemspec :path => '../'
2 changes: 1 addition & 1 deletion lib/acts_as_paranoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def acts_as_paranoid(options = {})
include ActsAsParanoid::Core

# Magic!
default_scope { where(paranoid_default_scope_sql) }
default_scope { where(paranoid_default_scope) }

if paranoid_configuration[:column_type] == 'time'
scope :deleted_inside_time_window, lambda {|time, window|
Expand Down
28 changes: 15 additions & 13 deletions lib/acts_as_paranoid/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ def with_deleted

def only_deleted
if string_type_with_deleted_value?
without_paranoid_default_scope.where("#{paranoid_column_reference} IS ?", paranoid_configuration[:deleted_value])
without_paranoid_default_scope.where(paranoid_column_reference => paranoid_configuration[:deleted_value])
elsif boolean_type_not_nullable?
without_paranoid_default_scope.where("#{paranoid_column_reference} = ?", true)
without_paranoid_default_scope.where(paranoid_column_reference => true)
else
without_paranoid_default_scope.where("#{paranoid_column_reference} IS NOT ?", nil)
without_paranoid_default_scope.where.not(paranoid_column_reference => nil)
end
end

Expand All @@ -39,15 +39,14 @@ def delete_all(conditions = nil)
where(conditions).update_all(["#{paranoid_configuration[:column]} = ?", delete_now_value])
end

def paranoid_default_scope_sql
def paranoid_default_scope
if string_type_with_deleted_value?
self.all.table[paranoid_column].eq(nil).
or(self.all.table[paranoid_column].not_eq(paranoid_configuration[:deleted_value])).
to_sql(self)
or(self.all.table[paranoid_column].not_eq(paranoid_configuration[:deleted_value]))
elsif boolean_type_not_nullable?
self.all.table[paranoid_column].eq(false).to_sql(self)
self.all.table[paranoid_column].eq(false)
else
self.all.table[paranoid_column].eq(nil).to_sql(self)
self.all.table[paranoid_column].eq(nil)
end
end

Expand Down Expand Up @@ -83,12 +82,15 @@ def delete_now_value

def without_paranoid_default_scope
scope = self.all
if scope.where_values.include? paranoid_default_scope_sql
# ActiveRecord 4.1
scope.where_values.delete(paranoid_default_scope_sql)

if ActiveRecord::VERSION::MAJOR < 5
# ActiveRecord 4.0.*
scope = scope.with_default_scope if ActiveRecord::VERSION::MINOR < 1
scope.where_values.delete(paranoid_default_scope)
else
scope = scope.with_default_scope
scope.where_values.delete(paranoid_default_scope_sql)
scope = scope.unscope(where: paranoid_default_scope)
# Fix problems with unscope group chain
scope = scope.unscoped if scope.to_sql.include? paranoid_default_scope.to_sql
end

scope
Expand Down
84 changes: 59 additions & 25 deletions lib/acts_as_paranoid/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,76 @@ def self.included(base)
base.extend ClassMethods
end

class UniquenessWithoutDeletedValidator < ActiveRecord::Validations::UniquenessValidator
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
table = finder_class.arel_table

# TODO: Use record.class.column_types[attribute.to_s].coder ?
coder = record.class.column_types[attribute.to_s]

if value && coder
value = if coder.respond_to? :type_cast_for_database
coder.type_cast_for_database value
else
coder.type_cast_for_write value
end
class UniquenessWithoutDeletedValidator
def self.[](version)
version = version.to_s
name = "V#{version.tr('.', '_')}"
unless constants.include? name.to_sym
raise "Unknown validator version #{version.inspect}; expected one of #{constants.sort.join(', ')}"
end
const_get name
end

class V5 < ActiveRecord::Validations::UniquenessValidator
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
table = finder_class.arel_table

coder = record.class.attribute_types[attribute.to_s]
value = coder.type_cast_for_schema value if value && coder

relation = build_relation(finder_class, table, attribute, value)
[Array(finder_class.primary_key), Array(record.send(:id))].transpose.each do |pk_key, pk_value|
relation = relation.and(table[pk_key.to_sym].not_eq(pk_value))
end if record.persisted?
relation = build_relation(finder_class, table, attribute, value)
[Array(finder_class.primary_key), Array(record.send(:id))].transpose.each do |pk_key, pk_value|
relation = relation.where(table[pk_key.to_sym].not_eq(pk_value))
end if record.persisted?

Array.wrap(options[:scope]).each do |scope_item|
scope_value = record.send(scope_item)
relation = relation.and(table[scope_item].eq(scope_value))
Array.wrap(options[:scope]).each do |scope_item|
relation = relation.where(table[scope_item].eq(record.public_send(scope_item)))
end

if relation.where(finder_class.paranoid_default_scope).where(relation).exists?
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
end
end
end

class V4 < ActiveRecord::Validations::UniquenessValidator
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
table = finder_class.arel_table

# TODO: Use record.class.column_types[attribute.to_s].coder ?
coder = record.class.column_types[attribute.to_s]

if value && coder
value = if coder.respond_to? :type_cast_for_database
coder.type_cast_for_database value
else
coder.type_cast_for_write value
end
end

relation = build_relation(finder_class, table, attribute, value)
[Array(finder_class.primary_key), Array(record.send(:id))].transpose.each do |pk_key, pk_value|
relation = relation.and(table[pk_key.to_sym].not_eq(pk_value))
end if record.persisted?

Array.wrap(options[:scope]).each do |scope_item|
scope_value = record.send(scope_item)
relation = relation.and(table[scope_item].eq(scope_value))
end

# Re-add ActsAsParanoid default scope conditions manually.
if finder_class.unscoped.where(finder_class.paranoid_default_scope_sql).where(relation).exists?
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
# Re-add ActsAsParanoid default scope conditions manually.
if finder_class.unscoped.where(finder_class.paranoid_default_scope).where(relation).exists?
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
end
end
end
end

module ClassMethods
def validates_uniqueness_of_without_deleted(*attr_names)
validates_with UniquenessWithoutDeletedValidator, _merge_attributes(attr_names)
validates_with UniquenessWithoutDeletedValidator[ActiveRecord::VERSION::MAJOR], _merge_attributes(attr_names)
end
end
end
Expand Down
11 changes: 7 additions & 4 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ def setup_db

create_table :super_paranoids do |t|
t.string :type
t.references :has_many_inherited_super_paranoidz
t.references :has_many_inherited_super_paranoidz, index: { name: 'index__sp_id_on_has_many_isp' }
t.datetime :deleted_at

timestamps t
end

create_table :has_many_inherited_super_paranoidzs do |t|
t.references :super_paranoidz
t.references :super_paranoidz, index: { name: 'index_has_many_isp_on_sp_id' }
t.datetime :deleted_at

timestamps t
Expand Down Expand Up @@ -226,9 +226,12 @@ def timestamps(table)
end

def teardown_db
ActiveRecord::Base.connection.tables.each do |table|
ActiveRecord::Base.connection.drop_table(table)
tables = if ActiveRecord::VERSION::MAJOR < 5
ActiveRecord::Base.connection.tables
else
ActiveRecord::Base.connection.data_sources
end
tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
end

class ParanoidTime < ActiveRecord::Base
Expand Down

0 comments on commit 11ea17d

Please sign in to comment.