closure_tree 1.0.0 → 2.0.0.beta1
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.
- data/README.md +34 -10
- data/Rakefile +5 -13
- data/lib/closure_tree/acts_as_tree.rb +155 -107
- data/lib/closure_tree/version.rb +1 -1
- data/spec/db/database.yml +18 -0
- data/spec/db/schema.rb +40 -0
- data/{test/dummy/test → spec}/fixtures/tags.yml +34 -1
- data/spec/spec_helper.rb +34 -0
- data/spec/support/models.rb +26 -0
- data/spec/tag_spec.rb +278 -0
- data/spec/user_spec.rb +75 -0
- metadata +73 -75
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/app/models/tag.rb +0 -3
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -53
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -45
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -25
- data/test/dummy/config/environments/production.rb +0 -52
- data/test/dummy/config/environments/test.rb +0 -39
- data/test/dummy/config/routes.rb +0 -3
- data/test/dummy/db/migrate/20110522004834_create_tags.rb +0 -24
- data/test/dummy/db/schema.rb +0 -32
- data/test/dummy/log/.gitkeep +0 -0
- data/test/dummy/script/rails +0 -6
- data/test/dummy/test/unit/tag_test.rb +0 -118
- data/test/test_helper.rb +0 -10
data/README.md
CHANGED
@@ -16,7 +16,7 @@ Note that closure_tree is being developed for Rails 3.1.0.rc1
|
|
16
16
|
|
17
17
|
2. Run ```bundle install```
|
18
18
|
|
19
|
-
3. Add ```acts_as_tree``` to your hierarchical model(s).
|
19
|
+
3. Add ```acts_as_tree``` to your hierarchical model(s) (see the <a href="#options">available options</a>).
|
20
20
|
|
21
21
|
4. Add a migration to add a ```parent_id``` column to the model you want to act_as_tree.
|
22
22
|
|
@@ -35,19 +35,19 @@ Note that closure_tree is being developed for Rails 3.1.0.rc1
|
|
35
35
|
"_hierarchy". Note that by calling ```acts_as_tree```, a "virtual model" (in this case, ```TagsHierarchy```) will be added automatically, so you don't need to create it.
|
36
36
|
|
37
37
|
```ruby
|
38
|
-
class
|
38
|
+
class CreateTagHierarchies < ActiveRecord::Migration
|
39
39
|
def change
|
40
|
-
create_table :
|
40
|
+
create_table :tag_hierarchies, :id => false do |t|
|
41
41
|
t.integer :ancestor_id, :null => false # ID of the parent/grandparent/great-grandparent/... tag
|
42
42
|
t.integer :descendant_id, :null => false # ID of the target tag
|
43
43
|
t.integer :generations, :null => false # Number of generations between the ancestor and the descendant. Parent/child = 1, for example.
|
44
44
|
end
|
45
45
|
|
46
46
|
# For "all progeny of..." selects:
|
47
|
-
add_index :
|
47
|
+
add_index :tag_hierarchies, [:ancestor_id, :descendant_id], :unique => true
|
48
48
|
|
49
49
|
# For "all ancestors of..." selects
|
50
|
-
add_index :
|
50
|
+
add_index :tag_hierarchies, [:descendant_id]
|
51
51
|
end
|
52
52
|
end
|
53
53
|
```
|
@@ -106,6 +106,19 @@ You can ```find``` as well as ```find_or_create``` by "ancestry paths". Ancestry
|
|
106
106
|
|
107
107
|
Note that the other columns will be null if nodes are created, other than auto-generated columns like ID and created_at timestamp. Only the specified column will receive the path element value.
|
108
108
|
|
109
|
+
### Available options
|
110
|
+
<a id="options" />
|
111
|
+
|
112
|
+
When you include ```acts_as_tree``` in your model, you can provide a hash to override the following defaults:
|
113
|
+
|
114
|
+
* ```:parent_column_name``` to override the column name of the parent foreign key in the model's table
|
115
|
+
* ```:hierarchy_table_name``` to override the hierarchy table name. This defaults to the singular name of the model + "_hierarchies".
|
116
|
+
* ```:name_column``` used by #```find_or_create_by_path```, #```find_by_path```, and ```ancestry_path``` instance methods. This is primarily useful if the model only has one required field (like a "tag").
|
117
|
+
* ```:dependent``` determines what happens when a node is destroyed. Defaults to ```nil```.
|
118
|
+
* ```nil``` will simply set the parent column to null. Each child node will be considered a "root" node
|
119
|
+
* ```:delete_all``` will delete all descendant nodes (which circumvents the destroy hooks)
|
120
|
+
* ```:destroy``` will destroy all descendant nodes (which runs the destroy hooks on each child node)
|
121
|
+
|
109
122
|
## Accessing Data
|
110
123
|
|
111
124
|
### Class methods
|
@@ -121,15 +134,26 @@ Note that the other columns will be null if nodes are created, other than auto-g
|
|
121
134
|
* ``` tag.child?``` returns true if this is a child node. It has a parent.
|
122
135
|
* ``` tag.leaf?``` returns true if this is a leaf node. It has no children.
|
123
136
|
* ``` tag.leaves``` returns an array of all the nodes in self_and_descendants that are leaves.
|
124
|
-
* ``` tag.level``` returns the level, or "generation", for this node in the tree. A root node
|
125
|
-
* ``` tag.parent``` returns the node's immediate parent
|
126
|
-
* ``` tag.children``` returns an array of immediate children (just those
|
127
|
-
* ``` tag.ancestors``` returns an array of
|
128
|
-
* ``` tag.self_and_ancestors``` returns an array of
|
137
|
+
* ``` tag.level``` returns the level, or "generation", for this node in the tree. A root node == 0.
|
138
|
+
* ``` tag.parent``` returns the node's immediate parent. Root nodes will return nil.
|
139
|
+
* ``` tag.children``` returns an array of immediate children (just those nodes whose parent is the current node).
|
140
|
+
* ``` tag.ancestors``` returns an array of [ parent, grandparent, great grandparent, ... ]. Note that the size of this array will always equal ```tag.level```.
|
141
|
+
* ``` tag.self_and_ancestors``` returns an array of self, parent, grandparent, great grandparent, etc.
|
129
142
|
* ``` tag.siblings``` returns an array of brothers and sisters (all at that level), excluding self.
|
130
143
|
* ``` tag.self_and_siblings``` returns an array of brothers and sisters (all at that level), including self.
|
131
144
|
* ``` tag.descendants``` returns an array of all children, childrens' children, etc., excluding self.
|
132
145
|
* ``` tag.self_and_descendants``` returns an array of all children, childrens' children, etc., including self.
|
146
|
+
* ``` tag.reparent``` lets you move a node (and all it's children) to a new parent.
|
147
|
+
* ``` tag.destroy``` will destroy a node as well as possibly all of its children. See the ```:dependent``` option passed to ```acts_as_tree```.
|
148
|
+
|
149
|
+
## Changelog
|
150
|
+
|
151
|
+
### 2.0.0.beta1
|
152
|
+
|
153
|
+
* Had to increment the major version, as rebuild! will need to be called by prior consumers to support the new ```leaves``` class and instance methods.
|
154
|
+
* Tag deletion is supported now along with ```:dependent => :destroy``` and ```:dependent => :delete_all```
|
155
|
+
* Added new instance method ```reparent```
|
156
|
+
* Switched from default rails plugin directory structure to rspec
|
133
157
|
|
134
158
|
## Thanks to
|
135
159
|
|
data/Rakefile
CHANGED
@@ -4,22 +4,14 @@ rescue LoadError
|
|
4
4
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
5
|
end
|
6
6
|
|
7
|
-
Bundler::GemHelper.install_tasks
|
8
|
-
|
9
7
|
require 'yard'
|
10
|
-
|
11
8
|
YARD::Rake::YardocTask.new do |t|
|
12
|
-
t.files = ['lib/**/*.rb', 'README.md']
|
9
|
+
t.files = ['lib/**/*.rb', 'README.md']
|
13
10
|
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
Rake::TestTask.new(:test) do |t|
|
18
|
-
t.libs << 'lib'
|
19
|
-
t.libs << 'test'
|
20
|
-
t.pattern = 'test/**/*_test.rb'
|
21
|
-
t.verbose = false
|
22
|
-
end
|
12
|
+
Bundler::GemHelper.install_tasks
|
23
13
|
|
14
|
+
require "rspec/core/rake_task"
|
15
|
+
RSpec::Core::RakeTask.new(:spec)
|
24
16
|
|
25
|
-
task :default => :
|
17
|
+
task :default => :spec
|
@@ -1,12 +1,12 @@
|
|
1
|
-
module ClosureTree
|
2
|
-
module ActsAsTree
|
3
|
-
def acts_as_tree
|
1
|
+
module ClosureTree
|
2
|
+
module ActsAsTree
|
3
|
+
def acts_as_tree(options = {})
|
4
4
|
|
5
5
|
class_attribute :closure_tree_options
|
6
|
+
|
6
7
|
self.closure_tree_options = {
|
7
8
|
:parent_column_name => 'parent_id',
|
8
|
-
:dependent => :
|
9
|
-
:hierarchy_table_suffix => '_hierarchies',
|
9
|
+
:dependent => :nullify, # or :destroy or :delete_all -- see the README
|
10
10
|
:name_column => 'name'
|
11
11
|
}.merge(options)
|
12
12
|
|
@@ -25,53 +25,55 @@ module ClosureTree #:nodoc:
|
|
25
25
|
|
26
26
|
include ClosureTree::Model
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
before_destroy :acts_as_tree_before_destroy
|
29
|
+
before_save :acts_as_tree_before_save
|
30
|
+
after_save :acts_as_tree_after_save
|
31
|
+
|
32
|
+
belongs_to :parent,
|
33
|
+
:class_name => base_class.to_s,
|
34
|
+
:foreign_key => parent_column_name
|
30
35
|
|
31
36
|
has_many :children,
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
has_and_belongs_to_many :
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
has_and_belongs_to_many :
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
37
|
+
:class_name => base_class.to_s,
|
38
|
+
:foreign_key => parent_column_name,
|
39
|
+
:dependent => closure_tree_options[:dependent]
|
40
|
+
|
41
|
+
has_and_belongs_to_many :self_and_ancestors,
|
42
|
+
:class_name => base_class.to_s,
|
43
|
+
:join_table => hierarchy_table_name,
|
44
|
+
:foreign_key => "descendant_id",
|
45
|
+
:association_foreign_key => "ancestor_id",
|
46
|
+
:order => "generations asc"
|
47
|
+
|
48
|
+
has_and_belongs_to_many :self_and_descendants,
|
49
|
+
:class_name => base_class.to_s,
|
50
|
+
:join_table => hierarchy_table_name,
|
51
|
+
:foreign_key => "ancestor_id",
|
52
|
+
:association_foreign_key => "descendant_id",
|
53
|
+
:order => "generations asc"
|
49
54
|
|
50
55
|
scope :roots, where(parent_column_name => nil)
|
51
56
|
|
52
|
-
scope :leaves,
|
57
|
+
scope :leaves, where(" #{quoted_table_name}.#{primary_key} IN
|
58
|
+
(SELECT ancestor_id
|
59
|
+
FROM #{quoted_hierarchy_table_name}
|
60
|
+
GROUP BY 1
|
61
|
+
HAVING MAX(generations) = 0)")
|
53
62
|
end
|
54
63
|
end
|
55
64
|
|
56
65
|
module Model
|
57
66
|
extend ActiveSupport::Concern
|
58
67
|
module InstanceMethods
|
59
|
-
def parent_id
|
60
|
-
self[parent_column_name]
|
61
|
-
end
|
62
|
-
|
63
|
-
def parent_id= new_parent_id
|
64
|
-
self[parent_column_name] = new_parent_id
|
65
|
-
end
|
66
68
|
|
67
69
|
# Returns true if this node has no parents.
|
68
70
|
def root?
|
69
|
-
|
71
|
+
parent.nil?
|
70
72
|
end
|
71
73
|
|
72
|
-
# Returns
|
73
|
-
def
|
74
|
-
|
74
|
+
# Returns true if this node has a parent, and is not a root.
|
75
|
+
def child?
|
76
|
+
!parent.nil?
|
75
77
|
end
|
76
78
|
|
77
79
|
# Returns true if this node has no children.
|
@@ -79,81 +81,126 @@ module ClosureTree #:nodoc:
|
|
79
81
|
children.empty?
|
80
82
|
end
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
-
|
84
|
+
# Returns the farthest ancestor, or self if +root?+
|
85
|
+
def root
|
86
|
+
root? ? self : ancestors.last
|
85
87
|
end
|
86
88
|
|
87
|
-
|
88
|
-
|
89
|
-
|
89
|
+
def leaves
|
90
|
+
return [self] if leaf?
|
91
|
+
self.class.leaves.where(<<-SQL
|
92
|
+
#{quoted_table_name}.#{self.class.primary_key} IN (
|
93
|
+
SELECT descendant_id
|
94
|
+
FROM #{quoted_hierarchy_table_name}
|
95
|
+
WHERE ancestor_id = #{id})
|
96
|
+
SQL
|
97
|
+
)
|
90
98
|
end
|
91
99
|
|
92
100
|
def level
|
93
101
|
ancestors.size
|
94
102
|
end
|
95
103
|
|
96
|
-
def
|
97
|
-
|
104
|
+
def ancestors
|
105
|
+
without_self(self_and_ancestors)
|
98
106
|
end
|
99
107
|
|
100
108
|
# Returns an array, root first, of self_and_ancestors' values of the +to_s_column+, which defaults
|
101
109
|
# to the +name_column+.
|
102
110
|
# (so child.ancestry_path == +%w{grandparent parent child}+
|
103
|
-
def ancestry_path
|
111
|
+
def ancestry_path(to_s_column = name_column)
|
104
112
|
self_and_ancestors.reverse.collect { |n| n.send to_s_column.to_sym }
|
105
113
|
end
|
106
114
|
|
107
|
-
def
|
108
|
-
|
115
|
+
def descendants
|
116
|
+
without_self(self_and_descendants)
|
109
117
|
end
|
110
118
|
|
111
119
|
def self_and_siblings
|
112
|
-
self.class.scoped.where(:
|
120
|
+
self.class.scoped.where(:parent => parent)
|
113
121
|
end
|
114
122
|
|
115
123
|
def siblings
|
116
124
|
without_self(self_and_siblings)
|
117
125
|
end
|
118
126
|
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
child_node
|
123
|
-
self_and_ancestors.inject(1) do |gen, ancestor|
|
124
|
-
hierarchy_class.create!(:ancestor => ancestor, :descendant => child_node, :generations => gen)
|
125
|
-
gen + 1
|
126
|
-
end
|
127
|
-
nil
|
128
|
-
end
|
129
|
-
|
130
|
-
def move_to_child_of new_parent
|
131
|
-
connection.execute <<-SQL
|
132
|
-
DELETE FROM #{quoted_hierarchy_table_name}
|
133
|
-
WHERE descendant_id = #{child_node.id}
|
134
|
-
SQL
|
135
|
-
new_parent.add_child self
|
127
|
+
# alias for appending to the children collect
|
128
|
+
def add_child(child_node)
|
129
|
+
children << child_node
|
130
|
+
child_node
|
136
131
|
end
|
137
132
|
|
138
133
|
# Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+.
|
139
134
|
# If the first argument is a symbol, it will be used as the column to search by
|
140
|
-
def find_by_path
|
141
|
-
|
135
|
+
def find_by_path(*path)
|
136
|
+
foc_by_path("find", *path)
|
142
137
|
end
|
143
138
|
|
144
139
|
# Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+
|
145
|
-
def find_or_create_by_path
|
146
|
-
|
140
|
+
def find_or_create_by_path(*path)
|
141
|
+
foc_by_path("find_or_create", *path)
|
147
142
|
end
|
148
143
|
|
149
144
|
protected
|
150
145
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
146
|
+
def acts_as_tree_before_save
|
147
|
+
@was_new_record = new_record?
|
148
|
+
if changes[parent_column_name] &&
|
149
|
+
parent.present? &&
|
150
|
+
parent.self_and_ancestors.include?(self)
|
151
|
+
# TODO: raise Ouroboros or Philip J. Fry error:
|
152
|
+
raise ActiveRecord::ActiveRecordError "You cannot add an ancestor as a descendant"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def acts_as_tree_after_save
|
157
|
+
rebuild! if changes[parent_column_name] || @was_new_record
|
158
|
+
end
|
159
|
+
|
160
|
+
def rebuild!
|
161
|
+
delete_hierarchy_references unless @was_new_record
|
162
|
+
hierarchy_class.create!(:ancestor => self, :descendant => self, :generations => 0)
|
163
|
+
unless root?
|
164
|
+
connection.execute <<-SQL
|
165
|
+
INSERT INTO #{quoted_hierarchy_table_name}
|
166
|
+
(ancestor_id, descendant_id, generations)
|
167
|
+
SELECT x.ancestor_id, #{id}, x.generations + 1
|
168
|
+
FROM #{quoted_hierarchy_table_name} x
|
169
|
+
WHERE x.descendant_id = #{self._parent_id}
|
170
|
+
SQL
|
171
|
+
end
|
172
|
+
children.each { |c| c.rebuild! }
|
173
|
+
end
|
174
|
+
|
175
|
+
def acts_as_tree_before_destroy
|
176
|
+
delete_hierarchy_references
|
177
|
+
if closure_tree_options[:dependent] == :nullify
|
178
|
+
children.each { |c| c.rebuild! }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def delete_hierarchy_references
|
183
|
+
# The crazy double-wrapped sub-subselect works around MySQL's limitation of subselects on the same table that is being mutated.
|
184
|
+
# It shouldn't affect performance of postgresql.
|
185
|
+
# See http://dev.mysql.com/doc/refman/5.0/en/subquery-errors.html
|
186
|
+
connection.execute <<-SQL
|
187
|
+
DELETE FROM #{quoted_hierarchy_table_name}
|
188
|
+
WHERE descendant_id IN (
|
189
|
+
SELECT DISTINCT descendant_id
|
190
|
+
FROM ( SELECT descendant_id
|
191
|
+
FROM #{quoted_hierarchy_table_name}
|
192
|
+
WHERE ancestor_id = #{id}
|
193
|
+
) AS x )
|
194
|
+
OR descendant_id = #{id}
|
195
|
+
SQL
|
196
|
+
end
|
197
|
+
|
198
|
+
def foc_by_path(method_prefix, *path)
|
199
|
+
path = path.flatten
|
200
|
+
return self if path.empty?
|
154
201
|
node = self
|
155
|
-
while (
|
156
|
-
node = node.children.send("#{method_prefix}_by_#{
|
202
|
+
while (!path.empty? && node)
|
203
|
+
node = node.children.send("#{method_prefix}_by_#{name_column}", path.shift)
|
157
204
|
end
|
158
205
|
node
|
159
206
|
end
|
@@ -162,9 +209,13 @@ module ClosureTree #:nodoc:
|
|
162
209
|
scope.where(["#{quoted_table_name}.#{self.class.primary_key} != ?", self])
|
163
210
|
end
|
164
211
|
|
212
|
+
def _parent_id
|
213
|
+
send(parent_column_name)
|
214
|
+
end
|
165
215
|
end
|
166
216
|
|
167
217
|
module ClassMethods
|
218
|
+
|
168
219
|
# Returns an arbitrary node that has no parents.
|
169
220
|
def root
|
170
221
|
roots.first
|
@@ -173,42 +224,39 @@ module ClosureTree #:nodoc:
|
|
173
224
|
# Rebuilds the hierarchy table based on the parent_id column in the database.
|
174
225
|
# Note that the hierarchy table will be truncated.
|
175
226
|
def rebuild!
|
176
|
-
|
177
|
-
|
178
|
-
SQL
|
179
|
-
roots.each { |n| rebuild_node_and_children n }
|
227
|
+
hierarchy_class.delete_all # not destroy_all -- we just want a simple truncate.
|
228
|
+
roots.each { |n| n.send(:rebuild!) } # roots just uses the parent_id column, so this is safe.
|
180
229
|
nil
|
181
230
|
end
|
182
231
|
|
183
232
|
# Find the node whose +ancestry_path+ is +path+
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
path
|
188
|
-
self.where(to_s_column => path.last).each do |n|
|
189
|
-
return n if path == n.ancestry_path(to_s_column)
|
190
|
-
end
|
191
|
-
nil
|
233
|
+
def find_by_path(*path)
|
234
|
+
path = path.flatten
|
235
|
+
r = roots.send("find_by_#{name_column}", path.shift)
|
236
|
+
r.nil? ? nil : r.find_by_path(*path)
|
192
237
|
end
|
193
238
|
|
194
239
|
# Find or create nodes such that the +ancestry_path+ is +path+
|
195
|
-
def find_or_create_by_path
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
column_sym = path.first.is_a?(Symbol) ? path.shift : name_sym
|
201
|
-
path.flatten!
|
202
|
-
s = path.shift
|
203
|
-
node = roots.where(column_sym => s).first
|
204
|
-
node = create!(column_sym => s) unless node
|
205
|
-
node.find_or_create_by_path column_sym, path
|
240
|
+
def find_or_create_by_path(*path)
|
241
|
+
path = path.flatten
|
242
|
+
root = roots.send("find_or_create_by_#{name_column}", path.shift)
|
243
|
+
root.find_or_create_by_path(*path)
|
206
244
|
end
|
207
245
|
|
208
|
-
|
209
|
-
def
|
210
|
-
|
211
|
-
|
246
|
+
# From https://github.com/collectiveidea/awesome_nested_set:
|
247
|
+
def in_tenacious_transaction(&block)
|
248
|
+
retry_count = 0
|
249
|
+
begin
|
250
|
+
transaction(&block)
|
251
|
+
rescue ActiveRecord::StatementInvalid => error
|
252
|
+
raise unless connection.open_transactions.zero?
|
253
|
+
raise unless error.message =~ /Deadlock found when trying to get lock|Lock wait timeout exceeded/
|
254
|
+
raise unless retry_count < 10
|
255
|
+
retry_count += 1
|
256
|
+
logger.info "Deadlock detected on retry #{retry_count}, restarting transaction"
|
257
|
+
sleep(rand(retry_count)*0.2) # Aloha protocol
|
258
|
+
retry
|
259
|
+
end
|
212
260
|
end
|
213
261
|
end
|
214
262
|
end
|
@@ -220,6 +268,10 @@ module ClosureTree #:nodoc:
|
|
220
268
|
closure_tree_options[:parent_column_name]
|
221
269
|
end
|
222
270
|
|
271
|
+
def parent_column_sym
|
272
|
+
parent_column_name.to_sym
|
273
|
+
end
|
274
|
+
|
223
275
|
def has_name?
|
224
276
|
ct_class.new.attributes.include? closure_tree_options[:name_column]
|
225
277
|
end
|
@@ -233,7 +285,8 @@ module ClosureTree #:nodoc:
|
|
233
285
|
end
|
234
286
|
|
235
287
|
def hierarchy_table_name
|
236
|
-
|
288
|
+
# We need to use the table_name, not ct_class.to_s.demodulize, because they may have overridden the table name
|
289
|
+
closure_tree_options[:hierarchy_table_name] || ct_table_name.singularize + "_hierarchies"
|
237
290
|
end
|
238
291
|
|
239
292
|
def hierarchy_class_name
|
@@ -244,10 +297,6 @@ module ClosureTree #:nodoc:
|
|
244
297
|
connection.quote_column_name hierarchy_table_name
|
245
298
|
end
|
246
299
|
|
247
|
-
def scope_column_names
|
248
|
-
Array closure_tree_options[:scope]
|
249
|
-
end
|
250
|
-
|
251
300
|
def quoted_parent_column_name
|
252
301
|
connection.quote_column_name parent_column_name
|
253
302
|
end
|
@@ -263,6 +312,5 @@ module ClosureTree #:nodoc:
|
|
263
312
|
def quoted_table_name
|
264
313
|
connection.quote_column_name ct_table_name
|
265
314
|
end
|
266
|
-
|
267
315
|
end
|
268
316
|
end
|