Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ When you include ```has_closure_tree``` in your model, you can provide a hash to
* ```:order``` used to set up [deterministic ordering](#deterministic-ordering)
* ```:scope``` restricts root nodes and sibling ordering to specific columns. Can be a single symbol or an array of symbols. Example: ```scope: :user_id``` or ```scope: [:user_id, :group_id]```. This ensures that root nodes and siblings are scoped correctly when reordering. See [Ordering Roots](#ordering-roots) for more details.
* ```:touch``` delegates to the `belongs_to` annotation for the parent, so `touch`ing cascades to all children (the performance of this for deep trees isn't currently optimal).
* ```:advisory_lock_timeout_seconds``` Raises an error when the advisory lock cannot be acquired within the timeout period

## Accessing Data

Expand Down
1 change: 1 addition & 0 deletions lib/closure_tree/has_closure_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def has_closure_tree(options = {})
:touch,
:with_advisory_lock,
:advisory_lock_name,
:advisory_lock_timeout_seconds,
:scope
)

Expand Down
11 changes: 9 additions & 2 deletions lib/closure_tree/support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def initialize(model_class, options)
dependent: :nullify, # or :destroy, :delete_all, or :adopt -- see the README
name_column: 'name',
with_advisory_lock: true, # This will be overridden by adapter support
advisory_lock_timeout_seconds: nil,
numeric_order: false
}.merge(options)
raise ArgumentError, "name_column can't be 'path'" if options[:name_column] == 'path'
Expand All @@ -30,6 +31,10 @@ def initialize(model_class, options)
end
end

if !options[:with_advisory_lock] && options[:advisory_lock_timeout_seconds].present?
raise ArgumentError, "advisory_lock_timeout_seconds can't be specified when advisory_lock is disabled"
end

return unless order_is_numeric?

extend NumericOrderSupport.adapter_for_connection(connection)
Expand Down Expand Up @@ -153,8 +158,10 @@ def build_scope_where_clause(scope_conditions)
end

def with_advisory_lock(&block)
if options[:with_advisory_lock] && connection.supports_advisory_locks? && model_class.respond_to?(:with_advisory_lock)
model_class.with_advisory_lock(advisory_lock_name) do
if options[:with_advisory_lock] && connection.supports_advisory_locks? && model_class.respond_to?(:with_advisory_lock!)
lock_method = options[:advisory_lock_timeout_seconds].present? ? :with_advisory_lock! : :with_advisory_lock

model_class.public_send(lock_method, advisory_lock_name, advisory_lock_options) do
transaction(&block)
end
else
Expand Down
4 changes: 4 additions & 0 deletions lib/closure_tree/support_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def advisory_lock_name
end
end

def advisory_lock_options
{ timeout_seconds: options[:advisory_lock_timeout_seconds] }.compact
end

def quoted_table_name
connection.quote_table_name(table_name)
end
Expand Down
2 changes: 1 addition & 1 deletion test/closure_tree/parallel_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def run_workers(worker_class = FindOrCreateWorker)
skip('unsupported') unless run_parallel_tests?

# disable with_advisory_lock:
Tag.stub(:with_advisory_lock, ->(_lock_name, &block) { block.call }) do
Tag.stub(:with_advisory_lock, ->(_lock_name, _lock_options, &block) { block.call }) do
run_workers
# duplication from at least one iteration:
assert Tag.where(name: @names).size > @iterations
Expand Down
39 changes: 38 additions & 1 deletion test/closure_tree/support_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
require 'test_helper'

describe ClosureTree::Support do
let(:sut) { Tag._ct }
let(:model) { Tag }
let(:sut) { model._ct }

it 'passes through table names without prefix and suffix' do
expected = 'some_random_table_name'
Expand All @@ -15,4 +16,40 @@
tn = ActiveRecord::Base.table_name_prefix + expected + ActiveRecord::Base.table_name_suffix
assert_equal expected, sut.remove_prefix_and_suffix(tn)
end

it 'initializes without error when with_advisory_lock is false' do
assert ClosureTree::Support.new(model, { with_advisory_lock: false })
end

it 'initializes without error when with_advisory_lock is true and advisory_lock_timeout_seconds is set' do
assert ClosureTree::Support.new(model, { with_advisory_lock: true, advisory_lock_timeout_seconds: 10 })
end

it 'calls :with_advisory_lock! when with_advisory_lock is true and timeout is 10' do
options = sut.options.merge(with_advisory_lock: true, advisory_lock_timeout_seconds: 10)
called = false
sut.stub(:options, options) do
model.stub(:with_advisory_lock!, ->(_lock_name, _options, &block) { block.call }) do
sut.with_advisory_lock { called = true }
end
end
assert called, 'block should have been called'
end

it 'calls :with_advisory_lock when with_advisory_lock is true and timeout is nil' do
called = false
model.stub(:with_advisory_lock, ->(_lock_name, _options, &block) { block.call }) do
sut.with_advisory_lock { called = true }
end
assert called, 'block should have been called'
end

it 'does not call advisory lock methods when with_advisory_lock is false' do
options = sut.options.merge(with_advisory_lock: false, advisory_lock_timeout_seconds: nil)
called = false
sut.stub(:options, options) do
sut.with_advisory_lock { called = true }
end
assert called, 'block should have been called'
end
end
Loading