closure_tree 5.1.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed9e9ee1d96298188863154501be9befbc2529d1
4
- data.tar.gz: 9eab4fbf0b8b40dbaead055c5d18d4367d801489
3
+ metadata.gz: d4e2702f6eb10fd2f2b7c9c9347e1bfbff2b97ea
4
+ data.tar.gz: 472bf0e636274ee25de4c88b64a1684ea627fb0b
5
5
  SHA512:
6
- metadata.gz: f0e90ba79f6900faa44bead080114a2c83b51fbc5c97ef5aea18dd8a563352e89ed2d49a9e8645a5957d6a5bd95cf219ebd5fd886400ecfc1ecde2a51b1e80d1
7
- data.tar.gz: 66406674bbe8f32d8ce0c545dcc412d8bd2e50a2f61e4e8428cf0bbedbdb367e0faf0bb783a6e51e6634cd438344c0bae29e8bb9f39fb54c8443cb6e0845993e
6
+ metadata.gz: ec12064f1459d2f72e4d0b04af9392426dcf463b09abcf5053eaf598c54b917e0ed6e29509c04a9dbc04a574ace5ede33199527cbdbcde028f44a611d5d7a7c8
7
+ data.tar.gz: 1553a1b5bbf0bf4ffce3d3acae823d3e319b5ac9eab2edfff3e0555cec89de8d80927bbb51ab497f83856e81504437a76cd9c8e8d693a7cb6f8b80c660542d75
data/.gitignore CHANGED
@@ -10,3 +10,4 @@ tmp/
10
10
  .yardoc/
11
11
  .rvmrc
12
12
  *.lock
13
+ tmp/
@@ -1,7 +1,9 @@
1
+ cache: bundler
2
+ sudo: false
1
3
  language: ruby
2
4
  rvm:
3
5
  - 1.9.3
4
- - 2.1.2
6
+ - 2.1.5
5
7
  - rbx-2
6
8
  - jruby-19mode
7
9
  # - ruby-head
@@ -12,6 +14,7 @@ gemfile:
12
14
  - gemfiles/activerecord_3.2.gemfile
13
15
  - gemfiles/activerecord_4.0.gemfile
14
16
  - gemfiles/activerecord_4.1.gemfile
17
+ - gemfiles/activerecord_4.2.gemfile
15
18
  - gemfiles/activerecord_edge.gemfile
16
19
 
17
20
  env:
@@ -22,7 +25,7 @@ env:
22
25
  addons:
23
26
  postgresql: "9.3"
24
27
 
25
- script: WITH_ADVISORY_LOCK_PREFIX=$TRAVIS_JOB_ID bundle exec rake --trace all_spec_flavors
28
+ script: WITH_ADVISORY_LOCK_PREFIX=$TRAVIS_JOB_ID bundle exec rake --trace spec:all
26
29
 
27
30
  matrix:
28
31
  allow_failures:
data/Appraisals CHANGED
@@ -11,6 +11,10 @@ appraise 'activerecord-4.1' do
11
11
  gem 'activerecord', '~> 4.1.0'
12
12
  end
13
13
 
14
+ appraise 'activerecord-4.2' do
15
+ gem 'activerecord', '~> 4.2.0'
16
+ end
17
+
14
18
  appraise 'activerecord-edge' do
15
19
  gem 'activerecord', github: 'rails/rails'
16
20
  gem 'arel', github: 'rails/arel'
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ### 5.2.0
4
+
5
+ * [Eduardo Turiño](https://github.com/eturino) renamed `acts_as_tree` to `has_closure_tree`. We'll
6
+ keep both annotations around for the forseeable future, but I think not name-colliding by default is
7
+ strictly better. (Thanks for both the suggestion and PR!)
8
+ * [Ryan Selk](https://github.com/rselk) made several enhancements to the migration generation (thanks!).
9
+ * [ruok5](https://github.com/ruok5) updated the README to clarify a heirarchy maintenance usecase. Thanks!
10
+ * Made migrations error with a helpful message if the target didn't have the `has_closure_tree` or
11
+ `acts_as_tree` annotation. This addresses [issue 131](https://github.com/mceachen/closure_tree/issues/131).
12
+
3
13
  ### 5.1.1
4
14
 
5
15
  * Fixed bug in `rails g closure_tree:migration` (introduced by me, not by seuros!)
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
- gem 'foreigner', :git => 'https://github.com/mceachen/foreigner.git'
2
+ gem 'foreigner', :git => 'https://github.com/matthuhiggins/foreigner.git'
3
3
 
4
4
  platforms :ruby, :rbx do
5
5
  gem 'mysql2'
data/README.md CHANGED
@@ -43,6 +43,7 @@ for a description of different tree storage algorithms.
43
43
  ## Table of Contents
44
44
 
45
45
  - [Installation](#installation)
46
+ - [Warning](#warning)
46
47
  - [Usage](#usage)
47
48
  - [Accessing Data](#accessing-data)
48
49
  - [Polymorphic hierarchies with STI](#polymorphic-hierarchies-with-sti)
@@ -60,19 +61,26 @@ Note that closure_tree only supports Rails 3.2 and later, and has test coverage
60
61
 
61
62
  2. Run `bundle install`
62
63
 
63
- 3. Add `acts_as_tree` to your hierarchical model:
64
+ 3. Add `has_closure_tree` (or `acts_as_tree`, which is an alias of the same method) to your hierarchical model:
64
65
 
65
66
  ```ruby
66
67
  class Tag < ActiveRecord::Base
68
+ has_closure_tree
69
+ end
70
+
71
+ class AnotherTag < ActiveRecord::Base
67
72
  acts_as_tree
68
73
  end
69
74
  ```
70
75
 
71
- Make sure you check out the [large number options](#available-options) that `acts_as_tree` accepts.
76
+ Make sure you check out the [large number options](#available-options) that `has_closure_tree` accepts.
72
77
 
73
- Make sure you add `acts_as_tree` **after** `attr_accessible` and
78
+ Make sure you add `has_closure_tree` **after** `attr_accessible` and
74
79
  `self.table_name =` lines in your model.
75
80
 
81
+ If you're already using other hierarchical gems, like `ancestry` or `acts_as_tree`, please refer
82
+ to the [warning section](#warning)!
83
+
76
84
  4. Add a migration to add a `parent_id` column to the hierarchical model.
77
85
  You may want to also [add a column for deterministic ordering of children](#sort_order), but that's optional.
78
86
 
@@ -90,7 +98,7 @@ Note that closure_tree only supports Rails 3.2 and later, and has test coverage
90
98
  to create the closure tree table for your model.
91
99
 
92
100
  By default the table name will be the model's table name, followed by
93
- "_hierarchies". Note that by calling ```acts_as_tree```, a "virtual model" (in this case, ```TagHierarchy```)
101
+ "_hierarchies". Note that by calling ```has_closure_tree```, a "virtual model" (in this case, ```TagHierarchy```)
94
102
  will be created dynamically. You don't need to create it.
95
103
 
96
104
  6. Run `rake db:migrate`
@@ -101,6 +109,12 @@ Note that closure_tree only supports Rails 3.2 and later, and has test coverage
101
109
 
102
110
  If you're starting from scratch you don't need to call `rebuild!`.
103
111
 
112
+ ## Warning
113
+
114
+ As stated above, using multiple hierarchy gems (like `ancestry` or `nested set`) on the same model
115
+ will most likely result in pain, suffering, hair loss, tooth decay, heel-related ailments, and gingivitis.
116
+ Assume things will break.
117
+
104
118
  ## Usage
105
119
 
106
120
  ### Creation
@@ -146,7 +160,7 @@ child1.ancestry_path
146
160
  You can `find` as well as `find_or_create` by "ancestry paths".
147
161
 
148
162
  If you provide an array of strings to these methods, they reference the `name` column in your
149
- model, which can be overridden with the `:name_column` option provided to `acts_as_tree`.
163
+ model, which can be overridden with the `:name_column` option provided to `has_closure_tree`.
150
164
 
151
165
  ```ruby
152
166
  child = Tag.find_or_create_by_path(%w[grandparent parent child])
@@ -187,6 +201,17 @@ h.ancestry_path
187
201
  => ["a", "b", "c", "d", "e", "f", "g", "h"]
188
202
  ```
189
203
 
204
+ When it is more convenient to simply change the `parent_id` of a node directly (for example, when dealing with a form `<select>`), closure_tree will handle the necessary changes automatically when the record is saved:
205
+
206
+ ```ruby
207
+ j = Tag.find 102
208
+ j.self_and_ancestor_ids
209
+ => [102, 87, 77]
210
+ j.update parent_id: 96
211
+ j.self_and_ancestor_ids
212
+ => [102, 96, 95, 78]
213
+ ```
214
+
190
215
  ### Nested hashes
191
216
 
192
217
  ```hash_tree``` provides a method for rendering a subtree as an
@@ -241,7 +266,7 @@ Just for kicks, this is the test tree I used for proving that preordered tree tr
241
266
 
242
267
  ### Available options
243
268
 
244
- When you include ```acts_as_tree``` in your model, you can provide a hash to override the following defaults:
269
+ When you include ```has_closure_tree``` in your model, you can provide a hash to override the following defaults:
245
270
 
246
271
  * ```:parent_column_name``` to override the column name of the parent foreign key in the model's table. This defaults to "parent_id".
247
272
  * ```:hierarchy_class_name``` to override the hierarchy class name. This defaults to the singular name of the model + "Hierarchy", like ```TagHierarchy```.
@@ -296,18 +321,18 @@ When you include ```acts_as_tree``` in your model, you can provide a hash to ove
296
321
  * ```tag.find_all_by_generation(0).to_a``` == ```[tag]```
297
322
  * ```tag.find_all_by_generation(1)``` == ```tag.children```
298
323
  * ```tag.find_all_by_generation(2)``` will return the tag's grandchildren, and so on.
299
- * ```tag.destroy``` will destroy a node and do <em>something</em> to its children, which is determined by the ```:dependent``` option passed to ```acts_as_tree```.
324
+ * ```tag.destroy``` will destroy a node and do <em>something</em> to its children, which is determined by the ```:dependent``` option passed to ```has_closure_tree```.
300
325
 
301
326
  ## Polymorphic hierarchies with STI
302
327
 
303
328
  Polymorphic models using single table inheritance (STI) are supported:
304
329
 
305
330
  1. Create a db migration that adds a String ```type``` column to your model
306
- 2. Subclass the model class. You only need to add ```acts_as_tree``` to your base class:
331
+ 2. Subclass the model class. You only need to add ```has_closure_tree``` to your base class:
307
332
 
308
333
  ```ruby
309
334
  class Tag < ActiveRecord::Base
310
- acts_as_tree
335
+ has_closure_tree
311
336
  end
312
337
  class WhenTag < Tag ; end
313
338
  class WhereTag < Tag ; end
@@ -319,17 +344,17 @@ you use the ```:type``` attribute, so **this doesn't work**:
319
344
 
320
345
  ```ruby
321
346
  # BAD: ActiveRecord ignores the :type attribute:
322
- root.children.create(:name => "child", :type => "WhenTag")
347
+ root.children.create(name: "child", type: "WhenTag")
323
348
  ```
324
349
 
325
350
  Instead, use either ```.add_child``` or ```children <<```:
326
351
 
327
352
  ```ruby
328
353
  # GOOD!
329
- a = Tag.create!(:name => "a")
330
- b = WhenTag.new(:name => "b")
354
+ a = Tag.create!(name: "a")
355
+ b = WhenTag.new(name: "b")
331
356
  a.children << b
332
- c = WhatTag.new(:name => "c")
357
+ c = WhatTag.new(name: "c")
333
358
  b.add_child(c)
334
359
  ```
335
360
 
@@ -343,7 +368,7 @@ If you want to order children alphabetically, and your model has a ```name``` co
343
368
 
344
369
  ```ruby
345
370
  class Tag < ActiveRecord::Base
346
- acts_as_tree :order => 'name'
371
+ has_closure_tree order: 'name'
347
372
  end
348
373
  ```
349
374
 
@@ -357,7 +382,7 @@ and in your model:
357
382
 
358
383
  ```ruby
359
384
  class OrderedTag < ActiveRecord::Base
360
- acts_as_tree :order => 'sort_order'
385
+ has_closure_tree order: 'sort_order'
361
386
  end
362
387
  ```
363
388
 
@@ -434,11 +459,11 @@ for both MySQL and PostgreSQL, [with_advisory_lock](https://github.com/mceachen/
434
459
  is used automatically to ensure correctness.
435
460
 
436
461
  If you are already managing concurrency elsewhere in your application, and want to disable the use
437
- of with_advisory_lock, pass ```:with_advisory_lock => false``` in the options hash:
462
+ of with_advisory_lock, pass ```with_advisory_lock: false``` in the options hash:
438
463
 
439
464
  ```ruby
440
465
  class Tag
441
- acts_as_tree :with_advisory_lock => false
466
+ has_closure_tree with_advisory_lock: false
442
467
  end
443
468
  ```
444
469
 
data/Rakefile CHANGED
@@ -12,19 +12,29 @@ YARD::Rake::YardocTask.new do |t|
12
12
  end
13
13
 
14
14
  require "rspec/core/rake_task"
15
- RSpec::Core::RakeTask.new(:spec)
15
+ RSpec::Core::RakeTask.new(:spec) do |task|
16
+ task.pattern = 'spec/*_spec.rb'
17
+ end
16
18
 
17
19
  task :default => :spec
18
20
 
19
- task :all_spec_flavors do
20
- [["", ""], ["db_prefix_", ""], ["", "_db_suffix"], ["abc_", "_123"]].each do |prefix, suffix|
21
- fail unless system("bundle exec rake spec DB_PREFIX=#{prefix} DB_SUFFIX=#{suffix}")
21
+ namespace :spec do
22
+ desc 'Run all spec variants'
23
+ task :all do
24
+ rake = 'bundle exec rake'
25
+ fail unless system("#{rake} spec:generators")
26
+ [['', ''], ['db_prefix_', ''], ['', '_db_suffix'], ['abc_', '_123']].each do |prefix, suffix|
27
+ env = "DB_PREFIX=#{prefix} DB_SUFFIX=#{suffix}"
28
+ fail unless system("#{rake} spec #{env}")
29
+ end
30
+ require 'active_record/version'
31
+ if ActiveRecord::VERSION::MAJOR == 3
32
+ fail unless system("#{rake} spec ATTR_ACCESSIBLE=1")
33
+ end
22
34
  end
23
- require 'active_record/version'
24
- if ActiveRecord::VERSION::MAJOR == 3
25
- fail unless system("rake spec ATTR_ACCESSIBLE=1")
35
+
36
+ desc 'Run generator specs'
37
+ RSpec::Core::RakeTask.new(:generators) do |task|
38
+ task.pattern = 'spec/generators/*_spec.rb'
26
39
  end
27
40
  end
28
-
29
- # Run the specs using all the different database engines:
30
- # for DB in sqlite3 mysql postgresql ; do rake ; done
@@ -20,14 +20,14 @@ Gem::Specification.new do |gem|
20
20
  gem.add_runtime_dependency 'with_advisory_lock', '>= 3.0.0'
21
21
 
22
22
  gem.add_development_dependency 'yard'
23
- gem.add_development_dependency 'rspec', '>= 3.0'
24
23
  gem.add_development_dependency 'rspec-instafail'
25
- # TODO: delete rspec-rails.
26
- gem.add_development_dependency 'rspec-rails' # FIXME: for rspec-rails and rspec fixture support
24
+ gem.add_development_dependency 'rspec-rails', '>= 3.1'
27
25
  gem.add_development_dependency 'uuidtools'
28
26
  gem.add_development_dependency 'database_cleaner'
29
27
  gem.add_development_dependency 'appraisal'
30
28
  gem.add_development_dependency 'timecop'
31
29
  gem.add_development_dependency 'parallel'
30
+ gem.add_development_dependency 'ammeter', '~> 1.1.2'
31
+ # gem.add_development_dependency 'byebug'
32
32
  # gem.add_development_dependency 'ruby-prof' # <- don't need this normally.
33
33
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "foreigner", :git => "https://github.com/mceachen/foreigner.git"
5
+ gem "foreigner", :git => "https://github.com/matthuhiggins/foreigner.git"
6
6
  gem "activerecord", "~> 3.2.0"
7
7
  gem "strong_parameters"
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "foreigner", :git => "https://github.com/mceachen/foreigner.git"
5
+ gem "foreigner", :git => "https://github.com/matthuhiggins/foreigner.git"
6
6
  gem "activerecord", "~> 4.0.0"
7
7
 
8
8
  platforms :ruby, :rbx do
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "foreigner", :git => "https://github.com/mceachen/foreigner.git"
5
+ gem "foreigner", :git => "https://github.com/matthuhiggins/foreigner.git"
6
6
  gem "activerecord", "~> 4.1.0"
7
7
 
8
8
  platforms :ruby, :rbx do
@@ -0,0 +1,20 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "foreigner", :git => "https://github.com/matthuhiggins/foreigner.git"
6
+ gem "activerecord", "~> 4.2.0"
7
+
8
+ platforms :ruby, :rbx do
9
+ gem "mysql2"
10
+ gem "pg"
11
+ gem "sqlite3"
12
+ end
13
+
14
+ platforms :jruby do
15
+ gem "activerecord-jdbcmysql-adapter"
16
+ gem "activerecord-jdbcpostgresql-adapter"
17
+ gem "activerecord-jdbcsqlite3-adapter"
18
+ end
19
+
20
+ gemspec :path => "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "foreigner", :git => "https://github.com/mceachen/foreigner.git"
5
+ gem "foreigner", :git => "https://github.com/matthuhiggins/foreigner.git"
6
6
  gem "activerecord", :github => "rails/rails"
7
7
  gem "arel", :github => "rails/arel"
8
8
 
@@ -3,7 +3,7 @@ require 'active_support'
3
3
  module ClosureTree
4
4
  extend ActiveSupport::Autoload
5
5
 
6
- autoload :ActsAsTree
6
+ autoload :HasClosureTree
7
7
  autoload :Support
8
8
  autoload :HierarchyMaintenance
9
9
  autoload :Model
@@ -15,5 +15,5 @@ module ClosureTree
15
15
  end
16
16
 
17
17
  ActiveSupport.on_load :active_record do
18
- ActiveRecord::Base.send :extend, ClosureTree::ActsAsTree
18
+ ActiveRecord::Base.send :extend, ClosureTree::HasClosureTree
19
19
  end
@@ -0,0 +1,20 @@
1
+ module ClosureTree
2
+ module ActiveRecordSupport
3
+ def ensure_fixed_table_name(table_name)
4
+ [
5
+ ActiveRecord::Base.table_name_prefix,
6
+ remove_prefix_and_suffix(table_name),
7
+ ActiveRecord::Base.table_name_suffix
8
+ ].compact.join
9
+ end
10
+
11
+ def remove_prefix_and_suffix(table_name)
12
+ pre, suff = ActiveRecord::Base.table_name_prefix, ActiveRecord::Base.table_name_suffix
13
+ if table_name.start_with?(pre) && table_name.end_with?(suff)
14
+ table_name[pre.size..-(suff.size + 1)]
15
+ else
16
+ table_name
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,8 +1,6 @@
1
- require 'with_advisory_lock'
2
-
3
1
  module ClosureTree
4
- module ActsAsTree
5
- def acts_as_tree(options = {})
2
+ module HasClosureTree
3
+ def has_closure_tree(options = {})
6
4
  options.assert_valid_keys(
7
5
  :parent_column_name,
8
6
  :dependent,
@@ -35,5 +33,7 @@ module ClosureTree
35
33
  # Support Heroku's database-less assets:precompile pre-deploy step:
36
34
  raise e unless ENV['DATABASE_URL'].to_s.include?('//user:pass@127.0.0.1/') && ENV['RAILS_GROUPS'] == 'assets'
37
35
  end
36
+
37
+ alias_method :acts_as_tree, :has_closure_tree
38
38
  end
39
39
  end
@@ -1,12 +1,15 @@
1
1
  require 'closure_tree/support_flags'
2
2
  require 'closure_tree/support_attributes'
3
3
  require 'closure_tree/numeric_order_support'
4
+ require 'closure_tree/active_record_support'
5
+ require 'with_advisory_lock'
4
6
 
5
7
  # This class and mixins are an effort to reduce the namespace pollution to models that act_as_tree.
6
8
  module ClosureTree
7
9
  class Support
8
10
  include ClosureTree::SupportFlags
9
11
  include ClosureTree::SupportAttributes
12
+ include ClosureTree::ActiveRecordSupport
10
13
 
11
14
  attr_reader :model_class
12
15
  attr_reader :options
@@ -93,15 +96,6 @@ module ClosureTree
93
96
  end
94
97
  end
95
98
 
96
- def remove_prefix_and_suffix(table_name)
97
- pre, suff = ActiveRecord::Base.table_name_prefix, ActiveRecord::Base.table_name_suffix
98
- if table_name.start_with?(pre) && table_name.end_with?(suff)
99
- table_name[pre.size..-(suff.size + 1)]
100
- else
101
- table_name
102
- end
103
- end
104
-
105
99
  def ids_from(scope)
106
100
  scope.pluck(model_class.primary_key)
107
101
  end
@@ -1,3 +1,3 @@
1
1
  module ClosureTree
2
- VERSION = Gem::Version.new('5.1.1') unless defined?(::ClosureTree::VERSION)
2
+ VERSION = Gem::Version.new('5.2.0') unless defined?(::ClosureTree::VERSION)
3
3
  end
@@ -1,12 +1,13 @@
1
- require 'rails/generators/named_base'
2
- require 'rails/generators/active_record/migration'
1
+ require 'closure_tree/active_record_support'
3
2
  require 'forwardable'
3
+ require 'rails/generators/active_record'
4
+ require 'rails/generators/named_base'
4
5
 
5
6
  module ClosureTree
6
7
  module Generators # :nodoc:
7
- class MigrationGenerator < ::Rails::Generators::NamedBase # :nodoc:
8
- include ActiveRecord::Generators::Migration
9
-
8
+ class MigrationGenerator < Rails::Generators::NamedBase # :nodoc:
9
+ include Rails::Generators::Migration
10
+ include ClosureTree::ActiveRecordSupport
10
11
  extend Forwardable
11
12
  def_delegators :ct, :hierarchy_table_name, :primary_key_type
12
13
 
@@ -15,15 +16,33 @@ module ClosureTree
15
16
  end
16
17
 
17
18
  def create_migration_file
18
- migration_template 'create_hierarchies_table.rb.erb', "db/migrate/create_#{ct.hierarchy_table_name}.rb"
19
+ migration_template 'create_hierarchies_table.rb.erb', "db/migrate/create_#{migration_name}.rb"
20
+ end
21
+
22
+ private
23
+
24
+ def migration_name
25
+ remove_prefix_and_suffix(ct.hierarchy_table_name)
19
26
  end
20
27
 
21
28
  def migration_class_name
22
- "Create#{ct.hierarchy_table_name.camelize}"
29
+ "Create#{migration_name.camelize}"
30
+ end
31
+
32
+ def target_class
33
+ @target_class ||= class_name.constantize
23
34
  end
24
35
 
25
36
  def ct
26
- @ct ||= class_name.constantize._ct
37
+ @ct ||= if target_class.respond_to?(:_ct)
38
+ target_class._ct
39
+ else
40
+ fail "Please RTFM and add the `has_closure_tree` (or `acts_as_tree`) annotation to #{class_name} before creating the migration."
41
+ end
42
+ end
43
+
44
+ def self.next_migration_number(dirname)
45
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
27
46
  end
28
47
  end
29
48
  end
@@ -1,16 +1,16 @@
1
1
  class <%= migration_class_name %> < ActiveRecord::Migration
2
2
  def change
3
- create_table :<%= hierarchy_table_name %>, id: false do |t|
3
+ create_table :<%= migration_name %>, id: false do |t|
4
4
  t.<%= primary_key_type %> :ancestor_id, null: false
5
5
  t.<%= primary_key_type %> :descendant_id, null: false
6
6
  t.integer :generations, null: false
7
7
  end
8
8
 
9
- add_index :<%= hierarchy_table_name %>, [:ancestor_id, :descendant_id, :generations],
9
+ add_index :<%= migration_name %>, [:ancestor_id, :descendant_id, :generations],
10
10
  unique: true,
11
- name: "anc_desc_idx"
11
+ name: "<%= file_name %>_anc_desc_idx"
12
12
 
13
- add_index :<%= hierarchy_table_name -%>, [:descendant_id],
14
- name: "desc_idx"
13
+ add_index :<%= migration_name -%>, [:descendant_id],
14
+ name: "<%= file_name %>_desc_idx"
15
15
  end
16
16
  end
@@ -1,7 +1,7 @@
1
1
  require 'uuidtools'
2
2
 
3
3
  class Tag < ActiveRecord::Base
4
- acts_as_tree :dependent => :destroy, :order => :name
4
+ has_closure_tree :dependent => :destroy, :order => :name
5
5
  before_destroy :add_destroyed_tag
6
6
  attr_accessible :name, :title if _ct.use_attr_accessible?
7
7
 
@@ -18,7 +18,7 @@ end
18
18
  class UUIDTag < ActiveRecord::Base
19
19
  self.primary_key = :uuid
20
20
  before_create :set_uuid
21
- acts_as_tree :dependent => :destroy, :order => 'name', :parent_column_name => 'parent_uuid'
21
+ has_closure_tree dependent: :destroy, order: 'name', parent_column_name: 'parent_uuid'
22
22
  before_destroy :add_destroyed_tag
23
23
  attr_accessible :name, :title if _ct.use_attr_accessible?
24
24
 
@@ -90,15 +90,18 @@ class CuisineType < ActiveRecord::Base
90
90
  end
91
91
 
92
92
  module Namespace
93
+ def self.table_name_prefix
94
+ 'namespace_'
95
+ end
93
96
  class Type < ActiveRecord::Base
94
- acts_as_tree :dependent => :destroy
97
+ has_closure_tree dependent: :destroy
95
98
  attr_accessible :name if _ct.use_attr_accessible?
96
99
  end
97
100
  end
98
101
 
99
102
  class Metal < ActiveRecord::Base
100
103
  self.table_name = "#{table_name_prefix}metal#{table_name_suffix}"
101
- acts_as_tree order: 'sort_order', name_column: 'value'
104
+ has_closure_tree order: 'sort_order', name_column: 'value'
102
105
  self.inheritance_column = 'metal_type'
103
106
  end
104
107
 
@@ -109,5 +112,5 @@ class Unobtanium < Metal
109
112
  end
110
113
 
111
114
  class MenuItem < ActiveRecord::Base
112
- acts_as_tree(touch: true, with_advisory_lock: false)
115
+ has_closure_tree touch: true, with_advisory_lock: false
113
116
  end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'ammeter/init'
3
+
4
+ # Generators are not automatically loaded by Rails
5
+ require 'generators/closure_tree/migration_generator'
6
+
7
+ RSpec.describe ClosureTree::Generators::MigrationGenerator, type: :generator do
8
+ TMPDIR = Dir.mktmpdir
9
+ # Tell generator where to put its output
10
+ destination TMPDIR
11
+ before { prepare_destination }
12
+
13
+ describe 'generator output' do
14
+ before { run_generator %w(tag) }
15
+ subject { migration_file('db/migrate/create_tag_hierarchies.rb') }
16
+ it { is_expected.to be_a_migration }
17
+ it { is_expected.to contain(/t.integer :ancestor_id, null: false/) }
18
+ it { is_expected.to contain(/t.integer :descendant_id, null: false/) }
19
+ it { is_expected.to contain(/t.integer :generations, null: false/) }
20
+ it { is_expected.to contain(/add_index :tag_hierarchies/) }
21
+ end
22
+
23
+ describe 'generator output with namespaced model' do
24
+ before { run_generator %w(Namespace::Type) }
25
+ subject { migration_file('db/migrate/create_namespace_type_hierarchies.rb') }
26
+ it { is_expected.to be_a_migration }
27
+ it { is_expected.to contain(/t.integer :ancestor_id, null: false/) }
28
+ it { is_expected.to contain(/t.integer :descendant_id, null: false/) }
29
+ it { is_expected.to contain(/t.integer :generations, null: false/) }
30
+ it { is_expected.to contain(/add_index :namespace_type_hierarchies/) }
31
+ end
32
+
33
+ describe 'generator output with namespaced model with /' do
34
+ before { run_generator %w(namespace/type) }
35
+ subject { migration_file('db/migrate/create_namespace_type_hierarchies.rb') }
36
+ it { is_expected.to be_a_migration }
37
+ it { is_expected.to contain(/t.integer :ancestor_id, null: false/) }
38
+ it { is_expected.to contain(/t.integer :descendant_id, null: false/) }
39
+ it { is_expected.to contain(/t.integer :generations, null: false/) }
40
+ it { is_expected.to contain(/add_index :namespace_type_hierarchies/) }
41
+ end
42
+
43
+ it 'should run all tasks in generator without errors' do
44
+ gen = generator %w(tag)
45
+ expect(gen).to receive :create_migration_file
46
+ capture(:stdout) { gen.invoke_all }
47
+ end
48
+ end
@@ -11,21 +11,26 @@ def max_threads
11
11
  5
12
12
  end
13
13
 
14
+
14
15
  class WorkerBase
15
16
  extend Forwardable
16
17
  attr_reader :name
17
18
  def_delegators :@thread, :join, :wakeup, :status, :to_s
18
19
 
20
+ def log(msg)
21
+ puts("#{Thread.current}: #{msg}") if ENV['VERBOSE']
22
+ end
23
+
19
24
  def initialize(target, name)
20
25
  @target = target
21
26
  @name = name
22
27
  @thread = Thread.new do
23
28
  ActiveRecord::Base.connection_pool.with_connection { before_work } if respond_to? :before_work
24
- puts "#{Thread.current} going to sleep..."
29
+ log 'going to sleep...'
25
30
  sleep
26
- puts "#{Thread.current} woke up..."
31
+ log 'woke up...'
27
32
  ActiveRecord::Base.connection_pool.with_connection { work }
28
- puts "#{Thread.current} done..."
33
+ log 'done.'
29
34
  end
30
35
  end
31
36
  end
@@ -33,9 +38,9 @@ end
33
38
  class FindOrCreateWorker < WorkerBase
34
39
  def work
35
40
  path = [name, :a, :b, :c]
36
- puts "#{Thread.current} making #{path}..."
41
+ log "making #{path}..."
37
42
  t = (@target || Tag).find_or_create_by_path(path)
38
- puts "#{Thread.current} made #{t.id}, #{t.ancestry_path}"
43
+ log "made #{t.id}, #{t.ancestry_path}"
39
44
  end
40
45
  end
41
46
 
@@ -45,6 +50,10 @@ describe 'Concurrent creation' do
45
50
  @iterations = 5
46
51
  end
47
52
 
53
+ def log(msg)
54
+ puts(msg) if ENV['VERBOSE']
55
+ end
56
+
48
57
  def run_workers(worker_class = FindOrCreateWorker)
49
58
  @names = @iterations.times.map { |iter| "iteration ##{iter}" }
50
59
  @names.each do |name|
@@ -55,17 +64,17 @@ describe 'Concurrent creation' do
55
64
  if unready_workers.empty?
56
65
  break
57
66
  else
58
- puts "Not ready to wakeup: #{unready_workers.map { |ea| [ea.to_s, ea.status] }}"
67
+ log "Not ready to wakeup: #{unready_workers.map { |ea| [ea.to_s, ea.status] }}"
59
68
  sleep(0.1)
60
69
  end
61
70
  end
62
71
  sleep(0.25)
63
72
  # OK, GO!
64
- puts 'Calling .wakeup on all workers...'
73
+ log 'Calling .wakeup on all workers...'
65
74
  workers.each(&:wakeup)
66
75
  sleep(0.25)
67
76
  # Then wait for them to finish:
68
- puts 'Calling .join on all workers...'
77
+ log 'Calling .join on all workers...'
69
78
  workers.each(&:join)
70
79
  end
71
80
  # Ensure we're still connected:
@@ -160,7 +169,7 @@ describe 'Concurrent creation' do
160
169
  Parallel.map(emails, :in_threads => max_threads) do |email|
161
170
  ActiveRecord::Base.connection_pool.with_connection do
162
171
  User.transaction do
163
- puts "Destroying #{email}..."
172
+ log "Destroying #{email}..."
164
173
  User.where(email: email).destroy_all
165
174
  end
166
175
  end
data/tests.sh CHANGED
@@ -1,11 +1,10 @@
1
1
  #!/bin/sh -ex
2
2
 
3
- for RMI in 2.1.2 jruby-1.6.13
3
+ for RMI in 2.1.5 #jruby-1.6.13 :P
4
4
  do
5
5
  rbenv local $RMI
6
- for db in postgresql mysql sqlite
6
+ for DB in postgresql mysql sqlite
7
7
  do
8
- appraisal bundle update
9
- DB=$db WITH_ADVISORY_LOCK_PREFIX=$(date +%s) appraisal rake all_spec_flavors
8
+ appraisal rake spec:all WITH_ADVISORY_LOCK_PREFIX=$(date +%s) DB=$DB
10
9
  done
11
10
  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: 5.1.1
4
+ version: 5.2.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-10-13 00:00:00.000000000 Z
11
+ date: 2014-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '3.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '3.0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rspec-instafail
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +72,14 @@ dependencies:
86
72
  requirements:
87
73
  - - ">="
88
74
  - !ruby/object:Gem::Version
89
- version: '0'
75
+ version: '3.1'
90
76
  type: :development
91
77
  prerelease: false
92
78
  version_requirements: !ruby/object:Gem::Requirement
93
79
  requirements:
94
80
  - - ">="
95
81
  - !ruby/object:Gem::Version
96
- version: '0'
82
+ version: '3.1'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: uuidtools
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +150,20 @@ dependencies:
164
150
  - - ">="
165
151
  - !ruby/object:Gem::Version
166
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: ammeter
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 1.1.2
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 1.1.2
167
167
  description: Easily and efficiently make your ActiveRecord model support hierarchies
168
168
  email:
169
169
  - matthew-github@mceachen.org
@@ -185,14 +185,16 @@ files:
185
185
  - gemfiles/activerecord_3.2.gemfile
186
186
  - gemfiles/activerecord_4.0.gemfile
187
187
  - gemfiles/activerecord_4.1.gemfile
188
+ - gemfiles/activerecord_4.2.gemfile
188
189
  - gemfiles/activerecord_edge.gemfile
189
190
  - img/example.png
190
191
  - img/preorder.png
191
192
  - lib/closure_tree.rb
192
- - lib/closure_tree/acts_as_tree.rb
193
+ - lib/closure_tree/active_record_support.rb
193
194
  - lib/closure_tree/deterministic_ordering.rb
194
195
  - lib/closure_tree/digraphs.rb
195
196
  - lib/closure_tree/finders.rb
197
+ - lib/closure_tree/has_closure_tree.rb
196
198
  - lib/closure_tree/hash_tree.rb
197
199
  - lib/closure_tree/hierarchy_maintenance.rb
198
200
  - lib/closure_tree/model.rb
@@ -212,6 +214,7 @@ files:
212
214
  - spec/db/models.rb
213
215
  - spec/db/schema.rb
214
216
  - spec/fixtures/tags.yml
217
+ - spec/generators/migration_generator_spec.rb
215
218
  - spec/hierarchy_maintenance_spec.rb
216
219
  - spec/label_spec.rb
217
220
  - spec/matcher_spec.rb
@@ -251,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
254
  version: '0'
252
255
  requirements: []
253
256
  rubyforge_project:
254
- rubygems_version: 2.4.1
257
+ rubygems_version: 2.2.2
255
258
  signing_key:
256
259
  specification_version: 4
257
260
  summary: Easily and efficiently make your ActiveRecord model support hierarchies
@@ -262,6 +265,7 @@ test_files:
262
265
  - spec/db/models.rb
263
266
  - spec/db/schema.rb
264
267
  - spec/fixtures/tags.yml
268
+ - spec/generators/migration_generator_spec.rb
265
269
  - spec/hierarchy_maintenance_spec.rb
266
270
  - spec/label_spec.rb
267
271
  - spec/matcher_spec.rb