closure_tree 3.5.2 → 3.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.
- data/README.md +30 -2
- data/Rakefile +7 -1
- data/lib/closure_tree/acts_as_tree.rb +16 -3
- data/lib/closure_tree/version.rb +1 -1
- data/spec/cuisine_type_spec.rb +1 -1
- data/spec/db/schema.rb +38 -28
- data/spec/spec_helper.rb +6 -8
- data/spec/support/models.rb +1 -0
- data/spec/tag_spec.rb +1 -1
- metadata +4 -4
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Closure Tree
|
1
|
+
# Closure Tree
|
2
2
|
|
3
3
|
Closure Tree is a mostly-API-compatible replacement for the
|
4
4
|
[ancestry](https://github.com/stefankroes/ancestry),
|
@@ -18,6 +18,17 @@ See [Bill Karwin](http://karwin.blogspot.com/)'s excellent
|
|
18
18
|
[Models for hierarchical data presentation](http://www.slideshare.net/billkarwin/models-for-hierarchical-data)
|
19
19
|
for a description of different tree storage algorithms.
|
20
20
|
|
21
|
+
## Table of Contents
|
22
|
+
|
23
|
+
- [Installation](#installation)
|
24
|
+
- [Usage](#usage)
|
25
|
+
- [Accessing Data](#accessing-data)
|
26
|
+
- [Polymorphic hierarchies with STI](#sti)
|
27
|
+
- [Deterministic ordering](#deterministic-ordering)
|
28
|
+
- [FAQ](#faq)
|
29
|
+
- [Testing](#testing)
|
30
|
+
- [Change log](#change-log)
|
31
|
+
|
21
32
|
## Installation
|
22
33
|
|
23
34
|
Note that closure_tree only supports Rails 3.0 and later, and has test coverage for MySQL, PostgreSQL, and SQLite.
|
@@ -187,7 +198,8 @@ HT: [ancestry](https://github.com/stefankroes/ancestry#arrangement) and [elhoyos
|
|
187
198
|
When you include ```acts_as_tree``` in your model, you can provide a hash to override the following defaults:
|
188
199
|
|
189
200
|
* ```:parent_column_name``` to override the column name of the parent foreign key in the model's table. This defaults to "parent_id".
|
190
|
-
* ```:hierarchy_table_name``` to override the hierarchy
|
201
|
+
* ```:hierarchy_table_name``` to override the hierarchy class name. This defaults to the singular name of the model + "Hierarchy", like ```TagHierarchy```.
|
202
|
+
* ```:hierarchy_table_name``` to override the hierarchy table name. This defaults to the singular name of the model + "_hierarchies", like ```tag_hierarchies```.
|
191
203
|
* ```:dependent``` determines what happens when a node is destroyed. Defaults to ```nullify```.
|
192
204
|
* ```:nullify``` will simply set the parent column to null. Each child node will be considered a "root" node. This is the default.
|
193
205
|
* ```:delete_all``` will delete all descendant nodes (which circumvents the destroy hooks)
|
@@ -321,6 +333,15 @@ root.children.collect(&:name)
|
|
321
333
|
=> ["a", "b", "c"]
|
322
334
|
```
|
323
335
|
|
336
|
+
## FAQ
|
337
|
+
|
338
|
+
### Does this gem support multiple parents?
|
339
|
+
|
340
|
+
No. This gem's API is based on the assumption that each node has either 0 or 1 parent.
|
341
|
+
|
342
|
+
The underlying closure tree structure will support multiple parents, but there would be many
|
343
|
+
breaking-API changes to support it. I'm open to suggestions and pull requests.
|
344
|
+
|
324
345
|
## Testing
|
325
346
|
|
326
347
|
Closure tree is [tested under every combination](http://travis-ci.org/#!/mceachen/closure_tree) of
|
@@ -331,6 +352,13 @@ Closure tree is [tested under every combination](http://travis-ci.org/#!/mceache
|
|
331
352
|
|
332
353
|
## Change log
|
333
354
|
|
355
|
+
### 3.6.0
|
356
|
+
|
357
|
+
* Added support for:
|
358
|
+
* ```:hierarchy_class_name``` as an option
|
359
|
+
* ActiveRecord::Base.table_name_prefix
|
360
|
+
* ActiveRecord::Base.table_name_suffix
|
361
|
+
|
334
362
|
### 3.5.2
|
335
363
|
|
336
364
|
* Added ```find_all_by_generation```
|
data/Rakefile
CHANGED
@@ -16,5 +16,11 @@ RSpec::Core::RakeTask.new(:spec)
|
|
16
16
|
|
17
17
|
task :default => :spec
|
18
18
|
|
19
|
+
task :specs_with_db_ixes do
|
20
|
+
[["", ""], ["db_prefix_", ""], ["", "_db_suffix"], ["abc_", "_123"]].each do |prefix, suffix|
|
21
|
+
fail unless system("rake spec DB_PREFIX=#{prefix} DB_SUFFIX=#{suffix}")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
19
25
|
# Run the specs using all the different database engines:
|
20
|
-
# for DB in sqlite3 mysql postgresql ; do rake ; done
|
26
|
+
# for DB in sqlite3 mysql postgresql ; do rake ; done
|
@@ -31,6 +31,8 @@ module ClosureTree
|
|
31
31
|
alias :eql? :==
|
32
32
|
RUBY
|
33
33
|
|
34
|
+
self.hierarchy_class.table_name = hierarchy_table_name
|
35
|
+
|
34
36
|
unless order_option.nil?
|
35
37
|
include ClosureTree::DeterministicOrdering
|
36
38
|
include ClosureTree::DeterministicNumericOrdering if order_is_numeric
|
@@ -388,12 +390,17 @@ module ClosureTree
|
|
388
390
|
end
|
389
391
|
|
390
392
|
def hierarchy_table_name
|
391
|
-
# We need to use the table_name, not ct_class.to_s.demodulize
|
392
|
-
|
393
|
+
# We need to use the table_name, not something like ct_class.to_s.demodulize + "_hierarchies",
|
394
|
+
# because they may have overridden the table name, which is what we want to be consistent with
|
395
|
+
# in order for the schema to make sense.
|
396
|
+
tablename = closure_tree_options[:hierarchy_table_name] ||
|
397
|
+
remove_prefix_and_suffix(ct_table_name).singularize + "_hierarchies"
|
398
|
+
|
399
|
+
ActiveRecord::Base.table_name_prefix + tablename + ActiveRecord::Base.table_name_suffix
|
393
400
|
end
|
394
401
|
|
395
402
|
def hierarchy_class_name
|
396
|
-
|
403
|
+
closure_tree_options[:hierarchy_class_name] || ct_class.to_s + "Hierarchy"
|
397
404
|
end
|
398
405
|
|
399
406
|
def quoted_hierarchy_table_name
|
@@ -445,6 +452,12 @@ module ClosureTree
|
|
445
452
|
def quoted_table_name
|
446
453
|
connection.quote_column_name ct_table_name
|
447
454
|
end
|
455
|
+
|
456
|
+
def remove_prefix_and_suffix(table_name)
|
457
|
+
prefix = Regexp.escape(ActiveRecord::Base.table_name_prefix)
|
458
|
+
suffix = Regexp.escape(ActiveRecord::Base.table_name_suffix)
|
459
|
+
table_name.gsub(/^#{prefix}(.+)#{suffix}$/, "\\1")
|
460
|
+
end
|
448
461
|
end
|
449
462
|
|
450
463
|
module DeterministicOrdering
|
data/lib/closure_tree/version.rb
CHANGED
data/spec/cuisine_type_spec.rb
CHANGED
@@ -25,6 +25,6 @@ describe CuisineType do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it "sets the table_name of the hierarchy class properly" do
|
28
|
-
CuisineTypeHierarchy.table_name.should == "cuisine_type_hierarchies"
|
28
|
+
CuisineTypeHierarchy.table_name.should == ActiveRecord::Base.table_name_prefix + "cuisine_type_hierarchies" + ActiveRecord::Base.table_name_suffix
|
29
29
|
end
|
30
30
|
end
|
data/spec/db/schema.rb
CHANGED
@@ -1,72 +1,82 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
class ActiveRecord::ConnectionAdapters::AbstractAdapter
|
3
|
+
def force_add_index(table_name, columns, options = {})
|
4
|
+
begin
|
5
|
+
remove_index!(table_name, options[:name])
|
6
|
+
rescue ActiveRecord::StatementInvalid, ArgumentError
|
7
|
+
end
|
8
|
+
add_index table_name, columns, options
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
2
12
|
ActiveRecord::Schema.define(:version => 0) do
|
3
13
|
|
4
14
|
create_table "tags", :force => true do |t|
|
5
|
-
t.string
|
6
|
-
t.string
|
7
|
-
t.integer
|
8
|
-
t.integer
|
15
|
+
t.string "name"
|
16
|
+
t.string "title"
|
17
|
+
t.integer "parent_id"
|
18
|
+
t.integer "sort_order"
|
9
19
|
t.datetime "created_at"
|
10
20
|
t.datetime "updated_at"
|
11
21
|
end
|
12
22
|
|
13
23
|
create_table "tag_hierarchies", :id => false, :force => true do |t|
|
14
|
-
t.integer "ancestor_id",
|
24
|
+
t.integer "ancestor_id", :null => false
|
15
25
|
t.integer "descendant_id", :null => false
|
16
|
-
t.integer "generations",
|
26
|
+
t.integer "generations", :null => false
|
17
27
|
end
|
18
28
|
|
19
29
|
create_table "destroyed_tags", :force => true do |t|
|
20
|
-
t.string
|
30
|
+
t.string "name"
|
21
31
|
end
|
22
32
|
|
23
|
-
|
24
|
-
|
33
|
+
force_add_index "tag_hierarchies", [:ancestor_id, :descendant_id], :unique => true, :name => "tag_anc_desc_idx"
|
34
|
+
force_add_index "tag_hierarchies", [:descendant_id], :name => "tag_desc_idx"
|
25
35
|
|
26
36
|
create_table "users", :force => true do |t|
|
27
|
-
t.string
|
28
|
-
t.integer
|
37
|
+
t.string "email"
|
38
|
+
t.integer "referrer_id"
|
29
39
|
t.datetime "created_at"
|
30
40
|
t.datetime "updated_at"
|
31
41
|
end
|
32
42
|
|
33
43
|
create_table "contracts", :force => true do |t|
|
34
|
-
t.integer
|
44
|
+
t.integer "user_id", :null => false
|
35
45
|
end
|
36
46
|
|
37
47
|
create_table "referral_hierarchies", :id => false, :force => true do |t|
|
38
|
-
t.integer "ancestor_id",
|
48
|
+
t.integer "ancestor_id", :null => false
|
39
49
|
t.integer "descendant_id", :null => false
|
40
|
-
t.integer "generations",
|
50
|
+
t.integer "generations", :null => false
|
41
51
|
end
|
42
52
|
|
43
|
-
|
44
|
-
|
53
|
+
force_add_index "referral_hierarchies", [:ancestor_id, :descendant_id], :unique => true, :name => "ref_anc_desc_idx"
|
54
|
+
force_add_index "referral_hierarchies", [:descendant_id], :name => "ref_desc_idx"
|
45
55
|
|
46
56
|
create_table "labels", :force => true do |t|
|
47
|
-
t.string
|
48
|
-
t.string
|
49
|
-
t.integer
|
50
|
-
t.integer
|
57
|
+
t.string "name"
|
58
|
+
t.string "type"
|
59
|
+
t.integer "sort_order"
|
60
|
+
t.integer "mother_id"
|
51
61
|
end
|
52
62
|
|
53
63
|
create_table "label_hierarchies", :id => false, :force => true do |t|
|
54
|
-
t.integer "ancestor_id",
|
64
|
+
t.integer "ancestor_id", :null => false
|
55
65
|
t.integer "descendant_id", :null => false
|
56
|
-
t.integer "generations",
|
66
|
+
t.integer "generations", :null => false
|
57
67
|
end
|
58
68
|
|
59
|
-
|
60
|
-
|
69
|
+
force_add_index "label_hierarchies", [:ancestor_id, :descendant_id], :unique => true, :name => "lh_anc_desc_idx"
|
70
|
+
force_add_index "label_hierarchies", [:descendant_id], :name => "lh_desc_idx"
|
61
71
|
|
62
72
|
create_table "cuisine_types", :force => true do |t|
|
63
|
-
t.string
|
64
|
-
t.integer
|
73
|
+
t.string "name"
|
74
|
+
t.integer "parent_id"
|
65
75
|
end
|
66
76
|
|
67
77
|
create_table "cuisine_type_hierarchies", :id => false, :force => true do |t|
|
68
|
-
t.integer "ancestor_id",
|
78
|
+
t.integer "ancestor_id", :null => false
|
69
79
|
t.integer "descendant_id", :null => false
|
70
|
-
t.integer "generations",
|
80
|
+
t.integer "generations", :null => false
|
71
81
|
end
|
72
82
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -14,21 +14,19 @@ require 'action_controller' # rspec-rails needs this :(
|
|
14
14
|
|
15
15
|
require 'closure_tree'
|
16
16
|
|
17
|
-
log = Logger.new(
|
18
|
-
log.sev_threshold = Logger::DEBUG
|
19
|
-
|
20
|
-
log.formatter = Logger::Formatter.new
|
21
|
-
|
22
|
-
ActiveRecord::Base.logger = log
|
17
|
+
#log = Logger.new(STDOUT)
|
18
|
+
#log.sev_threshold = Logger::DEBUG
|
19
|
+
#ActiveRecord::Base.logger = log
|
23
20
|
|
24
21
|
require 'yaml'
|
25
22
|
require 'erb'
|
26
23
|
ENV["DB"] ||= "sqlite3mem"
|
24
|
+
ActiveRecord::Base.table_name_prefix = ENV['DB_PREFIX'].to_s
|
25
|
+
ActiveRecord::Base.table_name_suffix = ENV['DB_SUFFIX'].to_s
|
27
26
|
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(plugin_test_dir + "/db/database.yml")).result)
|
28
27
|
ActiveRecord::Base.establish_connection(ENV["DB"])
|
29
28
|
ActiveRecord::Migration.verbose = false
|
30
|
-
|
31
|
-
|
29
|
+
require 'db/schema'
|
32
30
|
require 'support/models'
|
33
31
|
require 'rspec/rails' # TODO: clean this up-- I don't want to pull the elephant through the mouse hole just for fixture support
|
34
32
|
|
data/spec/support/models.rb
CHANGED
data/spec/tag_spec.rb
CHANGED
@@ -390,7 +390,7 @@ end
|
|
390
390
|
describe "Tag with AR whitelisted attributes enabled" do
|
391
391
|
before(:all) do
|
392
392
|
ActiveRecord::Base.attr_accessible(nil) # turn on whitelisted attributes
|
393
|
-
ActiveRecord::Base.
|
393
|
+
ActiveRecord::Base.descendants.each{|ea|ea.reset_column_information}
|
394
394
|
end
|
395
395
|
it_behaves_like Tag
|
396
396
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: closure_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -192,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
192
|
version: '0'
|
193
193
|
segments:
|
194
194
|
- 0
|
195
|
-
hash:
|
195
|
+
hash: 1756781626803345033
|
196
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
197
|
none: false
|
198
198
|
requirements:
|
@@ -201,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
201
|
version: '0'
|
202
202
|
segments:
|
203
203
|
- 0
|
204
|
-
hash:
|
204
|
+
hash: 1756781626803345033
|
205
205
|
requirements: []
|
206
206
|
rubyforge_project:
|
207
207
|
rubygems_version: 1.8.23
|