closure_tree 4.5.0 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +11 -3
  4. data/CHANGELOG.md +13 -0
  5. data/Gemfile +13 -0
  6. data/README.md +61 -12
  7. data/closure_tree.gemspec +4 -5
  8. data/gemfiles/activerecord_3.2.gemfile +12 -0
  9. data/gemfiles/activerecord_4.0.gemfile +12 -0
  10. data/gemfiles/activerecord_4.1.gemfile +12 -0
  11. data/gemfiles/activerecord_edge.gemfile +12 -0
  12. data/lib/closure_tree/acts_as_tree.rb +2 -9
  13. data/lib/closure_tree/deterministic_ordering.rb +4 -0
  14. data/lib/closure_tree/finders.rb +4 -4
  15. data/lib/closure_tree/hash_tree.rb +1 -1
  16. data/lib/closure_tree/hierarchy_maintenance.rb +19 -6
  17. data/lib/closure_tree/model.rb +2 -10
  18. data/lib/closure_tree/numeric_deterministic_ordering.rb +53 -36
  19. data/lib/closure_tree/numeric_order_support.rb +20 -13
  20. data/lib/closure_tree/support.rb +8 -0
  21. data/lib/closure_tree/support_attributes.rb +10 -0
  22. data/lib/closure_tree/test/matcher.rb +85 -0
  23. data/lib/closure_tree/version.rb +1 -1
  24. data/lib/closure_tree.rb +14 -1
  25. data/spec/cache_invalidation_spec.rb +39 -0
  26. data/spec/{support → db}/models.rb +6 -0
  27. data/spec/db/schema.rb +18 -0
  28. data/spec/label_spec.rb +171 -46
  29. data/spec/matcher_spec.rb +32 -0
  30. data/spec/parallel_spec.rb +85 -49
  31. data/spec/spec_helper.rb +6 -96
  32. data/spec/support/database.rb +49 -0
  33. data/spec/support/database_cleaner.rb +14 -0
  34. data/spec/support/deprecated/attr_accessible.rb +5 -0
  35. data/spec/support/hash_monkey_patch.rb +13 -0
  36. data/spec/support/helpers.rb +8 -0
  37. data/spec/support/sqlite3_with_advisory_lock.rb +10 -0
  38. data/tests.sh +7 -2
  39. metadata +31 -43
  40. data/spec/parallel_prepend_sibling_spec.rb +0 -42
@@ -1,85 +1,99 @@
1
1
  require 'spec_helper'
2
+ require 'securerandom'
2
3
 
3
-
4
- class DbThread
5
- def initialize(&block)
4
+ class WorkerBase
5
+ def initialize(target, run_at, name)
6
+ @target = target
6
7
  @thread = Thread.new do
7
- ActiveRecord::Base.connection_pool.with_connection(&block)
8
+ ActiveRecord::Base.connection_pool.with_connection do
9
+ before_work
10
+ sleep((run_at - Time.now).to_f)
11
+ do_work(name)
12
+ end
8
13
  end
9
14
  end
10
15
 
16
+ def before_work
17
+ end
18
+
19
+ def work(name)
20
+ raise
21
+ end
22
+
11
23
  def join
12
24
  @thread.join
13
25
  end
14
26
  end
15
27
 
16
- describe "threadhot", concurrency: true do
17
-
18
- before :each do
19
- @parent = nil
20
- @iterations = 3
21
- @workers = 10
22
- @min_sleep_time = 0.3
23
- @lock = Mutex.new
24
- @wake_times = []
25
- DatabaseCleaner.clean
28
+ class FindOrCreateWorker < WorkerBase
29
+ def do_work(name)
30
+ (@target || Tag).find_or_create_by_path([name.to_s, :a, :b, :c])
26
31
  end
32
+ end
27
33
 
28
- after :each do
29
- DatabaseCleaner.clean
30
- end
34
+ describe 'Concurrent creation', if: support_concurrency do
31
35
 
32
- def find_or_create_at_same_time(name)
33
- @lock.synchronize { @wake_times << Time.now.to_f + @min_sleep_time }
34
- while @wake_times.size < @workers
35
- sleep(0.1)
36
- end
37
- max_wait_time = @lock.synchronize { @wake_times.max }
38
- sleep_time = max_wait_time - Time.now.to_f
39
- sleep(sleep_time)
40
- (@parent || Tag).find_or_create_by_path([name.to_s, :a, :b, :c])
36
+ before :each do
37
+ @target = nil
38
+ @iterations = 5
39
+ @threads = 10
41
40
  end
42
41
 
43
- def run_workers
44
- @names = []
45
- @iterations.times.each do |iter|
46
- name = "iteration ##{iter}"
47
- @names << name
48
- threads = @workers.times.map do
49
- DbThread.new { find_or_create_at_same_time(name) }
42
+ def run_workers(worker_class = FindOrCreateWorker)
43
+ all_workers = []
44
+ @names = @iterations.times.map { |iter| "iteration ##{iter}" }
45
+ @names.each do |name|
46
+ wake_time = 1.second.from_now
47
+ workers = @threads.times.map do
48
+ worker_class.new(@target, wake_time, name)
50
49
  end
51
- threads.each { |ea| ea.join }
52
- @wake_times.clear
50
+ workers.each(&:join)
51
+ all_workers += workers
52
+ puts name
53
53
  end
54
+ # Ensure we're still connected:
55
+ ActiveRecord::Base.connection_pool.connection
56
+ all_workers
54
57
  end
55
58
 
56
- it "class method will not create dupes" do
59
+ it 'will not create dupes from class methods' do
57
60
  run_workers
58
61
  Tag.roots.collect { |ea| ea.name }.should =~ @names
59
62
  # No dupe children:
60
63
  %w(a b c).each do |ea|
61
- Tag.where(:name => ea).size.should == @iterations
64
+ Tag.where(name: ea).size.should == @iterations
62
65
  end
63
66
  end
64
67
 
65
- it "instance method will not create dupes" do
66
- @parent = Tag.create!(:name => "root")
68
+ it 'will not create dupes from instance methods' do
69
+ @target = Tag.create!(name: 'root')
67
70
  run_workers
68
- @parent.reload.children.collect { |ea| ea.name }.should =~ @names
69
- Tag.where(:name => @names).size.should == @iterations
71
+ @target.reload.children.collect { |ea| ea.name }.should =~ @names
72
+ Tag.where(name: @names).size.should == @iterations
70
73
  %w(a b c).each do |ea|
71
- Tag.where(:name => ea).size.should == @iterations
74
+ Tag.where(name: ea).size.should == @iterations
72
75
  end
73
76
  end
74
77
 
75
- it "creates dupe roots without advisory locks" do
78
+ it 'creates dupe roots without advisory locks' do
76
79
  # disable with_advisory_lock:
77
- Tag.stub(:with_advisory_lock).and_return { |lock_name, &block| block.call }
80
+ Tag.stub(:with_advisory_lock).and_return { |_lock_name, &block| block.call }
78
81
  run_workers
79
- Tag.where(:name => @names).size.should > @iterations
82
+ Tag.where(name: @names).size.should > @iterations
80
83
  end
81
84
 
82
- it 'fails to deadlock from parallel sibling churn' do
85
+ class SiblingPrependerWorker < WorkerBase
86
+ def before_work
87
+ @target.reload
88
+ @sibling = Label.new(name: SecureRandom.hex(10))
89
+ end
90
+
91
+ def do_work(name)
92
+ @target.prepend_sibling @sibling
93
+ end
94
+ end
95
+
96
+ xit 'fails to deadlock from parallel sibling churn' do
83
97
  # target should be non-trivially long to maximize time spent in hierarchy maintenance
84
98
  target = Tag.find_or_create_by_path(('a'..'z').to_a + ('A'..'Z').to_a)
85
99
  expected_children = (1..100).to_a.map { |ea| "root ##{ea}" }
@@ -106,7 +120,7 @@ describe "threadhot", concurrency: true do
106
120
  victim_name = children_to_delete.shift
107
121
  if victim_name
108
122
  Tag.transaction do
109
- victim = target.children.where(:name => victim_name).first
123
+ victim = target.children.where(name: victim_name).first
110
124
  victim.destroy
111
125
  deleted_children << victim_name
112
126
  end
@@ -123,14 +137,14 @@ describe "threadhot", concurrency: true do
123
137
  deleted_children.should =~ expected_children
124
138
  end
125
139
 
126
- it "fails to deadlock while simultaneously deleting items from the same hierarchy" do
140
+ xit 'fails to deadlock while simultaneously deleting items from the same hierarchy' do
127
141
  target = User.find_or_create_by_path((1..200).to_a.map { |ea| ea.to_s })
128
142
  to_delete = target.self_and_ancestors.to_a.shuffle.map(&:email)
129
143
  destroyer_threads = @workers.times.map do
130
144
  DbThread.new do
131
145
  until to_delete.empty?
132
146
  email = to_delete.shift
133
- User.transaction { User.where(:email => email).first.destroy } if email
147
+ User.transaction { User.where(email: email).first.destroy } if email
134
148
  end
135
149
  end
136
150
  end
@@ -138,4 +152,26 @@ describe "threadhot", concurrency: true do
138
152
  User.all.should be_empty
139
153
  end
140
154
 
155
+ class SiblingPrependerWorker < WorkerBase
156
+ def before_work
157
+ @target.reload
158
+ @sibling = Label.new(name: SecureRandom.hex(10))
159
+ end
160
+
161
+ def do_work(name)
162
+ @target.prepend_sibling @sibling
163
+ end
164
+ end
165
+
166
+ it 'fails to deadlock from prepending siblings' do
167
+ @target = Label.find_or_create_by_path %w(root parent)
168
+ run_workers(SiblingPrependerWorker)
169
+ children = Label.roots
170
+ uniq_sort_orders = children.collect { |ea| ea.sort_order }.uniq
171
+ children.size.should == uniq_sort_orders.size
172
+
173
+ # The only non-root node should be "root":
174
+ Label.all.select { |ea| ea.root? }.should == [@target.parent]
175
+ end
176
+
141
177
  end
data/spec/spec_helper.rb CHANGED
@@ -1,110 +1,20 @@
1
- $:.unshift(File.dirname(__FILE__) + '/../lib')
2
- plugin_test_dir = File.dirname(__FILE__)
3
-
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
4
2
 
5
3
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
6
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
4
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
7
5
  require 'rspec'
8
6
  require 'active_record'
9
7
  require 'foreigner'
10
8
  require 'database_cleaner'
11
9
  require 'closure_tree'
10
+ require 'closure_tree/test/matcher'
12
11
  require 'tmpdir'
13
-
14
- if ENV['STDOUT_LOGGING']
15
- log = Logger.new(STDOUT)
16
- log.sev_threshold = Logger::DEBUG
17
- ActiveRecord::Base.logger = log
18
- end
19
-
20
-
21
- ENV["DB"] ||= "mysql"
22
- ActiveRecord::Base.table_name_prefix = ENV['DB_PREFIX'].to_s
23
- ActiveRecord::Base.table_name_suffix = ENV['DB_SUFFIX'].to_s
24
-
25
- if ENV['ATTR_ACCESSIBLE'] == '1'
26
- # turn on whitelisted attributes:
27
- ActiveRecord::Base.send(:include, ActiveModel::MassAssignmentSecurity)
28
- end
29
-
30
- ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(plugin_test_dir + "/db/database.yml")).result)
31
-
32
- def recreate_db
33
- db_name = ActiveRecord::Base.configurations[ENV["DB"]]["database"]
34
- case ENV['DB']
35
- when 'sqlite'
36
- when 'postgresql'
37
- `psql -c 'DROP DATABASE #{db_name}' -U postgres`
38
- `psql -c 'CREATE DATABASE #{db_name}' -U postgres`
39
- else
40
- `mysql -e 'DROP DATABASE IF EXISTS #{db_name}'`
41
- `mysql -e 'CREATE DATABASE #{db_name}'`
42
- end
43
- ActiveRecord::Base.connection.reconnect!
44
- end
45
-
46
- ActiveRecord::Base.establish_connection(ENV["DB"].to_sym)
47
-
48
- ActiveRecord::Migration.verbose = false
49
- if ENV['NONUKES']
50
- puts 'skipping database creation'
51
- else
52
- Foreigner.load
53
- recreate_db
54
- require 'db/schema'
55
- end
56
- require 'support/models'
57
-
58
- class Hash
59
- def render_from_yield(&block)
60
- inject({}) do |h, entry|
61
- k, v = entry
62
- h[block.call(k)] = if v.is_a?(Hash) then
63
- v.render_from_yield(&block)
64
- else
65
- block.call(v)
66
- end
67
- h
68
- end
69
- end
70
- end
71
-
72
- DB_QUERIES = []
73
-
74
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
75
- def log_with_query_append(query, *args, &block)
76
- DB_QUERIES << query
77
- log_without_query_append(query, *args, &block)
78
- end
79
-
80
- alias_method_chain :log, :query_append
81
- end
12
+ require 'timecop'
82
13
 
83
14
  Thread.abort_on_exception = true
84
15
 
85
- DatabaseCleaner.strategy = :truncation
86
-
87
-
88
- def support_concurrency
89
- # SQLite doesn't support parallel writes
90
- !(ENV['DB'] =~ /sqlite/)
91
- end
16
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
92
17
 
93
18
  RSpec.configure do |config|
94
- config.before(:each) do
95
- DatabaseCleaner.start
96
- end
97
- config.after(:each) do
98
- DatabaseCleaner.clean
99
- DB_QUERIES.clear
100
- end
101
- config.before(:all) do
102
- ENV['FLOCK_DIR'] = Dir.mktmpdir
103
- end
104
- config.after(:all) do
105
- FileUtils.remove_entry_secure ENV['FLOCK_DIR']
106
- end
107
- config.filter_run_excluding :concurrency => !support_concurrency
19
+ config.include ClosureTree::Test::Matcher
108
20
  end
109
-
110
-
@@ -0,0 +1,49 @@
1
+ database_folder = "#{File.dirname(__FILE__)}/../db"
2
+ database_adapter = ENV['DB'] ||= 'mysql'
3
+
4
+ if ENV['STDOUT_LOGGING']
5
+ log = Logger.new(STDOUT)
6
+ log.sev_threshold = Logger::DEBUG
7
+ ActiveRecord::Base.logger = log
8
+ end
9
+
10
+ ActiveRecord::Migration.verbose = false
11
+ ActiveRecord::Base.table_name_prefix = ENV['DB_PREFIX'].to_s
12
+ ActiveRecord::Base.table_name_suffix = ENV['DB_SUFFIX'].to_s
13
+ ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read("#{database_folder}/database.yml")).result)
14
+
15
+ config = ActiveRecord::Base.configurations[database_adapter]
16
+
17
+ unless config['database'] == ':memory:'
18
+ # Postgresql or Mysql
19
+ config['database'].concat ENV['TRAVIS_JOB_NUMBER'].to_s.gsub(/\W/, '_')
20
+ end
21
+
22
+ begin
23
+ case database_adapter
24
+ when 'sqlite'
25
+ ActiveRecord::Base.establish_connection(database_adapter.to_sym)
26
+ when 'mysql'
27
+ ActiveRecord::Base.establish_connection(config.merge('database' => nil))
28
+ ActiveRecord::Base.connection.recreate_database(config['database'], {charset: 'utf8', collation: 'utf8_unicode_ci'})
29
+ when 'postgresql'
30
+ ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
31
+ ActiveRecord::Base.connection.recreate_database(config['database'], config.merge('encoding' => 'utf8'))
32
+ end
33
+ end unless ENV['NONUKES']
34
+
35
+ ActiveRecord::Base.establish_connection(config)
36
+ Foreigner.load
37
+
38
+ require "#{database_folder}/schema"
39
+ require "#{database_folder}/models"
40
+
41
+ # See http://stackoverflow.com/a/22388177/1268016
42
+ def count_queries(&block)
43
+ count = 0
44
+ counter_fn = ->(name, started, finished, unique_id, payload) do
45
+ count += 1 unless payload[:name].in? %w[ CACHE SCHEMA ]
46
+ end
47
+ ActiveSupport::Notifications.subscribed(counter_fn, "sql.active_record", &block)
48
+ count
49
+ end
@@ -0,0 +1,14 @@
1
+ RSpec.configure do |config|
2
+
3
+ DatabaseCleaner.strategy = :truncation
4
+
5
+ config.before(:each) do
6
+ ActiveRecord::Base.connection_pool.connection
7
+ DatabaseCleaner.start
8
+ end
9
+
10
+ config.after(:each) do
11
+ ActiveRecord::Base.connection_pool.connection
12
+ DatabaseCleaner.clean
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ # Delete this file when support for 3.2 is dropped
2
+ if ENV['ATTR_ACCESSIBLE'] == '1'
3
+ # turn on whitelisted attributes:
4
+ ActiveRecord::Base.send(:include, ActiveModel::MassAssignmentSecurity)
5
+ end
@@ -0,0 +1,13 @@
1
+ class Hash
2
+ def render_from_yield(&block)
3
+ reduce({}) do |h, entry|
4
+ k, v = entry
5
+ h[block.call(k)] = if v.is_a?(Hash) then
6
+ v.render_from_yield(&block)
7
+ else
8
+ block.call(v)
9
+ end
10
+ h
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ def sqlite?
2
+ ENV['DB'] =~ /sqlite/
3
+ end
4
+
5
+ def support_concurrency
6
+ # SQLite doesn't support parallel writes
7
+ !sqlite?
8
+ end
@@ -0,0 +1,10 @@
1
+ if sqlite?
2
+ RSpec.configure do |config|
3
+ config.before(:suite) do
4
+ ENV['FLOCK_DIR'] = Dir.mktmpdir
5
+ end
6
+ config.after(:suite) do
7
+ FileUtils.remove_entry_secure ENV['FLOCK_DIR']
8
+ end
9
+ end
10
+ end
data/tests.sh CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  appraisal install
4
4
 
5
- for db in sqlite mysql postgresql
5
+ for RMI in 1.9.3-p429 2.1.2
6
6
  do
7
- DB=$db WITH_ADVISORY_LOCK_PREFIX=$(date +%s) appraisal rake all_spec_flavors
7
+ rbenv local $RMI
8
+ for db in postgresql mysql sqlite
9
+ do
10
+ bundle install --quiet
11
+ DB=$db WITH_ADVISORY_LOCK_PREFIX=$(date +%s) appraisal rake all_spec_flavors
12
+ done
8
13
  done
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: closure_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.5.0
4
+ version: 4.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew McEachen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-18 00:00:00.000000000 Z
11
+ date: 2014-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 2.14.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 2.14.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec-instafail
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -109,35 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: mysql2
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: pg
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: sqlite3
112
+ name: uuidtools
141
113
  requirement: !ruby/object:Gem::Requirement
142
114
  requirements:
143
115
  - - ">="
@@ -151,7 +123,7 @@ dependencies:
151
123
  - !ruby/object:Gem::Version
152
124
  version: '0'
153
125
  - !ruby/object:Gem::Dependency
154
- name: uuidtools
126
+ name: database_cleaner
155
127
  requirement: !ruby/object:Gem::Requirement
156
128
  requirements:
157
129
  - - ">="
@@ -165,7 +137,7 @@ dependencies:
165
137
  - !ruby/object:Gem::Version
166
138
  version: '0'
167
139
  - !ruby/object:Gem::Dependency
168
- name: database_cleaner
140
+ name: appraisal
169
141
  requirement: !ruby/object:Gem::Requirement
170
142
  requirements:
171
143
  - - ">="
@@ -179,7 +151,7 @@ dependencies:
179
151
  - !ruby/object:Gem::Version
180
152
  version: '0'
181
153
  - !ruby/object:Gem::Dependency
182
- name: appraisal
154
+ name: timecop
183
155
  requirement: !ruby/object:Gem::Requirement
184
156
  requirements:
185
157
  - - ">="
@@ -200,6 +172,7 @@ extensions: []
200
172
  extra_rdoc_files: []
201
173
  files:
202
174
  - ".gitignore"
175
+ - ".rspec"
203
176
  - ".travis.yml"
204
177
  - ".yardopts"
205
178
  - Appraisals
@@ -228,21 +201,29 @@ files:
228
201
  - lib/closure_tree/support.rb
229
202
  - lib/closure_tree/support_attributes.rb
230
203
  - lib/closure_tree/support_flags.rb
204
+ - lib/closure_tree/test/matcher.rb
231
205
  - lib/closure_tree/version.rb
232
206
  - mktree.rb
207
+ - spec/cache_invalidation_spec.rb
233
208
  - spec/cuisine_type_spec.rb
234
209
  - spec/db/database.yml
210
+ - spec/db/models.rb
235
211
  - spec/db/schema.rb
236
212
  - spec/fixtures/tags.yml
237
213
  - spec/hierarchy_maintenance_spec.rb
238
214
  - spec/label_spec.rb
215
+ - spec/matcher_spec.rb
239
216
  - spec/metal_spec.rb
240
217
  - spec/model_spec.rb
241
218
  - spec/namespace_type_spec.rb
242
- - spec/parallel_prepend_sibling_spec.rb
243
219
  - spec/parallel_spec.rb
244
220
  - spec/spec_helper.rb
245
- - spec/support/models.rb
221
+ - spec/support/database.rb
222
+ - spec/support/database_cleaner.rb
223
+ - spec/support/deprecated/attr_accessible.rb
224
+ - spec/support/hash_monkey_patch.rb
225
+ - spec/support/helpers.rb
226
+ - spec/support/sqlite3_with_advisory_lock.rb
246
227
  - spec/support_spec.rb
247
228
  - spec/tag_examples.rb
248
229
  - spec/tag_spec.rb
@@ -261,7 +242,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
261
242
  requirements:
262
243
  - - ">="
263
244
  - !ruby/object:Gem::Version
264
- version: '0'
245
+ version: 1.9.3
265
246
  required_rubygems_version: !ruby/object:Gem::Requirement
266
247
  requirements:
267
248
  - - ">="
@@ -274,19 +255,26 @@ signing_key:
274
255
  specification_version: 4
275
256
  summary: Easily and efficiently make your ActiveRecord model support hierarchies
276
257
  test_files:
258
+ - spec/cache_invalidation_spec.rb
277
259
  - spec/cuisine_type_spec.rb
278
260
  - spec/db/database.yml
261
+ - spec/db/models.rb
279
262
  - spec/db/schema.rb
280
263
  - spec/fixtures/tags.yml
281
264
  - spec/hierarchy_maintenance_spec.rb
282
265
  - spec/label_spec.rb
266
+ - spec/matcher_spec.rb
283
267
  - spec/metal_spec.rb
284
268
  - spec/model_spec.rb
285
269
  - spec/namespace_type_spec.rb
286
- - spec/parallel_prepend_sibling_spec.rb
287
270
  - spec/parallel_spec.rb
288
271
  - spec/spec_helper.rb
289
- - spec/support/models.rb
272
+ - spec/support/database.rb
273
+ - spec/support/database_cleaner.rb
274
+ - spec/support/deprecated/attr_accessible.rb
275
+ - spec/support/hash_monkey_patch.rb
276
+ - spec/support/helpers.rb
277
+ - spec/support/sqlite3_with_advisory_lock.rb
290
278
  - spec/support_spec.rb
291
279
  - spec/tag_examples.rb
292
280
  - spec/tag_spec.rb
@@ -1,42 +0,0 @@
1
- require 'spec_helper'
2
- require 'securerandom'
3
-
4
- describe "threadhot", concurrency: true do
5
-
6
- before :each do
7
- @iterations = 5
8
- @workers = 8
9
- end
10
-
11
- def prepend_sibling_at_even_second(run_at)
12
- ActiveRecord::Base.connection.reconnect!
13
- sibling = Label.new(:name => SecureRandom.hex(10))
14
- target = Label.find(@target.id)
15
- sleep(run_at - Time.now.to_f)
16
- target.prepend_sibling sibling
17
- end
18
-
19
- def run_workers
20
- start_time = Time.now.to_i + 2
21
- @times = @iterations.times.collect { |ea| start_time + (ea * 2) }
22
- @names = @times.collect { |ea| ea.to_s }
23
- @threads = @workers.times.collect do
24
- Thread.new do
25
- @times.each { |ea| prepend_sibling_at_even_second(ea) }
26
- end
27
- end
28
- @threads.each { |ea| ea.join }
29
- end
30
-
31
- it "prepend_sibling on a non-root node doesn't cause deadlocks" do
32
- @target = Label.find_or_create_by_path %w(root parent)
33
- run_workers
34
- children = Label.roots
35
- uniq_sort_orders = children.collect { |ea| ea.sort_order }.uniq
36
- children.size.should == uniq_sort_orders.size
37
-
38
- # The only non-root node should be "root":
39
- Label.all.select { |ea| ea.root? }.should == [@target.parent]
40
- end
41
-
42
- end