closure_tree 6.5.0 → 7.4.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 (63) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +98 -0
  3. data/.gitignore +2 -0
  4. data/.rspec +1 -1
  5. data/Appraisals +90 -7
  6. data/CHANGELOG.md +100 -42
  7. data/Gemfile +3 -11
  8. data/README.md +68 -24
  9. data/Rakefile +16 -10
  10. data/_config.yml +1 -0
  11. data/bin/appraisal +29 -0
  12. data/bin/rake +29 -0
  13. data/bin/rspec +29 -0
  14. data/closure_tree.gemspec +16 -9
  15. data/lib/closure_tree/finders.rb +32 -9
  16. data/lib/closure_tree/has_closure_tree.rb +4 -0
  17. data/lib/closure_tree/has_closure_tree_root.rb +5 -7
  18. data/lib/closure_tree/hash_tree_support.rb +4 -4
  19. data/lib/closure_tree/hierarchy_maintenance.rb +28 -8
  20. data/lib/closure_tree/model.rb +42 -16
  21. data/lib/closure_tree/numeric_deterministic_ordering.rb +20 -6
  22. data/lib/closure_tree/numeric_order_support.rb +7 -3
  23. data/lib/closure_tree/support.rb +18 -12
  24. data/lib/closure_tree/support_attributes.rb +10 -1
  25. data/lib/closure_tree/support_flags.rb +1 -4
  26. data/lib/closure_tree/version.rb +1 -1
  27. data/lib/generators/closure_tree/migration_generator.rb +8 -0
  28. data/lib/generators/closure_tree/templates/create_hierarchies_table.rb.erb +1 -1
  29. metadata +78 -79
  30. data/.travis.yml +0 -29
  31. data/gemfiles/activerecord_4.2.gemfile +0 -19
  32. data/gemfiles/activerecord_5.0.gemfile +0 -19
  33. data/gemfiles/activerecord_5.0_foreigner.gemfile +0 -20
  34. data/gemfiles/activerecord_edge.gemfile +0 -20
  35. data/img/example.png +0 -0
  36. data/img/preorder.png +0 -0
  37. data/spec/cache_invalidation_spec.rb +0 -39
  38. data/spec/cuisine_type_spec.rb +0 -38
  39. data/spec/db/database.yml +0 -21
  40. data/spec/db/models.rb +0 -128
  41. data/spec/db/schema.rb +0 -166
  42. data/spec/fixtures/tags.yml +0 -98
  43. data/spec/generators/migration_generator_spec.rb +0 -48
  44. data/spec/has_closure_tree_root_spec.rb +0 -154
  45. data/spec/hierarchy_maintenance_spec.rb +0 -16
  46. data/spec/label_spec.rb +0 -554
  47. data/spec/matcher_spec.rb +0 -34
  48. data/spec/metal_spec.rb +0 -55
  49. data/spec/model_spec.rb +0 -9
  50. data/spec/namespace_type_spec.rb +0 -13
  51. data/spec/parallel_spec.rb +0 -159
  52. data/spec/spec_helper.rb +0 -24
  53. data/spec/support/database.rb +0 -52
  54. data/spec/support/database_cleaner.rb +0 -14
  55. data/spec/support/exceed_query_limit.rb +0 -18
  56. data/spec/support/hash_monkey_patch.rb +0 -13
  57. data/spec/support/query_counter.rb +0 -18
  58. data/spec/support/sqlite3_with_advisory_lock.rb +0 -10
  59. data/spec/support_spec.rb +0 -14
  60. data/spec/tag_examples.rb +0 -665
  61. data/spec/tag_spec.rb +0 -6
  62. data/spec/user_spec.rb +0 -174
  63. data/spec/uuid_tag_spec.rb +0 -6
data/spec/db/schema.rb DELETED
@@ -1,166 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- ActiveRecord::Schema.define(:version => 0) do
4
-
5
- create_table "tags" do |t|
6
- t.string "name"
7
- t.string "title"
8
- t.integer "parent_id"
9
- t.integer "sort_order"
10
- t.timestamps null: false
11
- end
12
-
13
- add_foreign_key(:tags, :tags, :column => 'parent_id')
14
-
15
- create_table "tag_hierarchies", :id => false do |t|
16
- t.integer "ancestor_id", :null => false
17
- t.integer "descendant_id", :null => false
18
- t.integer "generations", :null => false
19
- end
20
-
21
- add_foreign_key(:tag_hierarchies, :tags, :column => 'ancestor_id')
22
- add_foreign_key(:tag_hierarchies, :tags, :column => 'descendant_id')
23
-
24
- create_table "uuid_tags", :id => false do |t|
25
- t.string "uuid", :unique => true
26
- t.string "name"
27
- t.string "title"
28
- t.string "parent_uuid"
29
- t.integer "sort_order"
30
- t.timestamps null: false
31
- end
32
-
33
- create_table "uuid_tag_hierarchies", :id => false do |t|
34
- t.string "ancestor_id", :null => false
35
- t.string "descendant_id", :null => false
36
- t.integer "generations", :null => false
37
- end
38
-
39
- create_table "destroyed_tags" do |t|
40
- t.string "name"
41
- end
42
-
43
- add_index "tag_hierarchies", [:ancestor_id, :descendant_id, :generations], :unique => true, :name => "tag_anc_desc_idx"
44
- add_index "tag_hierarchies", [:descendant_id], :name => "tag_desc_idx"
45
-
46
- create_table "groups" do |t|
47
- t.string "name", null: false
48
- end
49
-
50
- create_table "groupings" do |t|
51
- t.string "name", null: false
52
- end
53
-
54
- create_table "user_sets" do |t|
55
- t.string "name", null: false
56
- end
57
-
58
- create_table "teams" do |t|
59
- t.string "name", null: false
60
- end
61
-
62
- create_table "users" do |t|
63
- t.string "email"
64
- t.integer "referrer_id"
65
- t.integer "group_id"
66
- t.timestamps null: false
67
- end
68
-
69
- add_foreign_key(:users, :users, :column => 'referrer_id')
70
-
71
- create_table "contracts" do |t|
72
- t.integer "user_id", :null => false
73
- t.integer "contract_type_id"
74
- t.string "title"
75
- end
76
-
77
- create_table "contract_types" do |t|
78
- t.string "name", :null => false
79
- end
80
-
81
- create_table "referral_hierarchies", :id => false do |t|
82
- t.integer "ancestor_id", :null => false
83
- t.integer "descendant_id", :null => false
84
- t.integer "generations", :null => false
85
- end
86
-
87
- add_index "referral_hierarchies", [:ancestor_id, :descendant_id, :generations], :unique => true, :name => "ref_anc_desc_idx"
88
- add_index "referral_hierarchies", [:descendant_id], :name => "ref_desc_idx"
89
-
90
- create_table "labels" do |t|
91
- t.string "name"
92
- t.string "type"
93
- t.integer "column_whereby_ordering_is_inferred"
94
- t.integer "mother_id"
95
- end
96
-
97
- add_foreign_key(:labels, :labels, :column => 'mother_id')
98
-
99
- create_table "label_hierarchies", :id => false do |t|
100
- t.integer "ancestor_id", :null => false
101
- t.integer "descendant_id", :null => false
102
- t.integer "generations", :null => false
103
- end
104
-
105
- add_index "label_hierarchies", [:ancestor_id, :descendant_id, :generations], :unique => true, :name => "lh_anc_desc_idx"
106
- add_index "label_hierarchies", [:descendant_id], :name => "lh_desc_idx"
107
-
108
- create_table "cuisine_types" do |t|
109
- t.string "name"
110
- t.integer "parent_id"
111
- end
112
-
113
- create_table "cuisine_type_hierarchies", :id => false do |t|
114
- t.integer "ancestor_id", :null => false
115
- t.integer "descendant_id", :null => false
116
- t.integer "generations", :null => false
117
- end
118
-
119
- create_table "namespace_types" do |t|
120
- t.string "name"
121
- t.integer "parent_id"
122
- end
123
-
124
- create_table "namespace_type_hierarchies", :id => false do |t|
125
- t.integer "ancestor_id", :null => false
126
- t.integer "descendant_id", :null => false
127
- t.integer "generations", :null => false
128
- end
129
-
130
- create_table "metal" do |t|
131
- t.integer "parent_id"
132
- t.string "metal_type"
133
- t.string "value"
134
- t.string "description"
135
- t.integer "sort_order"
136
- end
137
-
138
- add_foreign_key(:metal, :metal, :column => 'parent_id')
139
-
140
- create_table "metal_hierarchies", :id => false do |t|
141
- t.integer "ancestor_id", :null => false
142
- t.integer "descendant_id", :null => false
143
- t.integer "generations", :null => false
144
- end
145
-
146
- add_foreign_key(:metal_hierarchies, :metal, :column => 'ancestor_id')
147
- add_foreign_key(:metal_hierarchies, :metal, :column => 'descendant_id')
148
-
149
- create_table 'menu_items' do |t|
150
- t.string 'name'
151
- t.integer 'parent_id'
152
- t.timestamps null: false
153
- end
154
-
155
- add_foreign_key(:menu_items, :menu_items, :column => 'parent_id')
156
-
157
- create_table 'menu_item_hierarchies', :id => false do |t|
158
- t.integer 'ancestor_id', :null => false
159
- t.integer 'descendant_id', :null => false
160
- t.integer 'generations', :null => false
161
- end
162
-
163
- add_foreign_key(:menu_item_hierarchies, :menu_items, :column => 'ancestor_id')
164
- add_foreign_key(:menu_item_hierarchies, :menu_items, :column => 'descendant_id')
165
-
166
- end
@@ -1,98 +0,0 @@
1
- # Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
2
-
3
- grandparent:
4
- name: grandparent
5
- title: Nonnie
6
-
7
- parent:
8
- name: parent
9
- parent: grandparent
10
- title: Mom
11
-
12
- child:
13
- name: child
14
- parent: parent
15
- title: Kid
16
-
17
- people:
18
- name: people
19
-
20
- # people has no children
21
-
22
- events:
23
- name: events
24
-
25
- # events has only one child
26
-
27
- birthday:
28
- name: birthday
29
- parent: events
30
-
31
- places:
32
- name: places
33
-
34
- # places has many children, with many depths
35
-
36
- home:
37
- name: home
38
- parent: places
39
-
40
- indoor:
41
- name: indoor
42
- parent: places
43
-
44
- outdoor:
45
- name: outdoor
46
- parent: places
47
-
48
- museum:
49
- name: museum
50
- parent: places
51
-
52
- united_states:
53
- name: united_states
54
- parent: places
55
-
56
- california:
57
- name: california
58
- parent: united_states
59
-
60
- san_francisco:
61
- name: san_francisco
62
- parent: california
63
-
64
-
65
- # Move and deletion test tree
66
-
67
- a1:
68
- name: a1
69
-
70
- b1:
71
- name: b1
72
- parent: a1
73
-
74
- b2:
75
- name: b2
76
- parent: a1
77
-
78
- c1a:
79
- name: c1a
80
- parent: b1
81
- sort_order: 2
82
-
83
- c1b:
84
- name: c1b
85
- parent: b1
86
- sort_order: 1
87
-
88
- c2:
89
- name: c2
90
- parent: b2
91
-
92
- d2:
93
- name: d2
94
- parent: c2
95
-
96
- e2:
97
- name: e2
98
- parent: d2
@@ -1,48 +0,0 @@
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
@@ -1,154 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe "has_closure_tree_root" do
4
- let!(:ct1) { ContractType.create!(name: "Type1") }
5
- let!(:ct2) { ContractType.create!(name: "Type2") }
6
- let!(:user1) { User.create!(email: "1@example.com", group_id: group.id) }
7
- let!(:user2) { User.create!(email: "2@example.com", group_id: group.id) }
8
- let!(:user3) { User.create!(email: "3@example.com", group_id: group.id) }
9
- let!(:user4) { User.create!(email: "4@example.com", group_id: group.id) }
10
- let!(:user5) { User.create!(email: "5@example.com", group_id: group.id) }
11
- let!(:user6) { User.create!(email: "6@example.com", group_id: group.id) }
12
- let!(:group_reloaded) { group.class.find(group.id) } # Ensures were starting fresh.
13
-
14
- before do
15
- # The tree (contract types in parens)
16
- #
17
- # U1(1)
18
- # / \
19
- # U2(1) U3(1&2)
20
- # / / \
21
- # U4(2) U5(1) U6(2)
22
-
23
- user1.children << user2
24
- user1.children << user3
25
- user2.children << user4
26
- user3.children << user5
27
- user3.children << user6
28
-
29
- user1.contracts.create!(title: "Contract 1", contract_type: ct1)
30
- user2.contracts.create!(title: "Contract 2", contract_type: ct1)
31
- user3.contracts.create!(title: "Contract 3", contract_type: ct1)
32
- user3.contracts.create!(title: "Contract 4", contract_type: ct2)
33
- user4.contracts.create!(title: "Contract 5", contract_type: ct2)
34
- user5.contracts.create!(title: "Contract 6", contract_type: ct1)
35
- user6.contracts.create!(title: "Contract 7", contract_type: ct2)
36
- end
37
-
38
- context "with basic config" do
39
- let!(:group) { Group.create!(name: "TheGroup") }
40
-
41
- it "loads all nodes in a constant number of queries" do
42
- expect do
43
- root = group_reloaded.root_user_including_tree
44
- expect(root.children[0].email).to eq "2@example.com"
45
- expect(root.children[0].parent.children[1].email).to eq "3@example.com"
46
- end.to_not exceed_query_limit(2)
47
- end
48
-
49
- it "loads all nodes plus single association in a constant number of queries" do
50
- expect do
51
- root = group_reloaded.root_user_including_tree(:contracts)
52
- expect(root.children[0].email).to eq "2@example.com"
53
- expect(root.children[0].parent.children[1].email).to eq "3@example.com"
54
- expect(root.children[0].children[0].contracts[0].user.
55
- parent.parent.children[1].children[1].contracts[0].title).to eq "Contract 7"
56
- end.to_not exceed_query_limit(3)
57
- end
58
-
59
- it "loads all nodes and associations in a constant number of queries" do
60
- expect do
61
- root = group_reloaded.root_user_including_tree(contracts: :contract_type)
62
- expect(root.children[0].email).to eq "2@example.com"
63
- expect(root.children[0].parent.children[1].email).to eq "3@example.com"
64
- expect(root.children[1].contracts.map(&:contract_type).map(&:name)).to eq %w(Type1 Type2)
65
- expect(root.children[1].children[0].contracts[0].contract_type.name).to eq "Type1"
66
- expect(root.children[0].children[0].contracts[0].user.
67
- parent.parent.children[1].children[1].contracts[0].contract_type.name).to eq "Type2"
68
- end.to_not exceed_query_limit(4) # Without this feature, this is 15, and scales with number of nodes.
69
- end
70
-
71
- it "memoizes by assoc_map" do
72
- group_reloaded.root_user_including_tree.email = "x"
73
- expect(group_reloaded.root_user_including_tree.email).to eq "x"
74
- group_reloaded.root_user_including_tree(contracts: :contract_type).email = "y"
75
- expect(group_reloaded.root_user_including_tree(contracts: :contract_type).email).to eq "y"
76
- expect(group_reloaded.root_user_including_tree.email).to eq "x"
77
- end
78
-
79
- it "doesn't memoize if true argument passed" do
80
- group_reloaded.root_user_including_tree.email = "x"
81
- expect(group_reloaded.root_user_including_tree(true).email).to eq "1@example.com"
82
- group_reloaded.root_user_including_tree(contracts: :contract_type).email = "y"
83
- expect(group_reloaded.root_user_including_tree(true, contracts: :contract_type).email).
84
- to eq "1@example.com"
85
- end
86
-
87
- it "works if true passed on first call" do
88
- expect(group_reloaded.root_user_including_tree(true).email).to eq "1@example.com"
89
- end
90
-
91
- it "eager loads inverse association to group" do
92
- expect do
93
- root = group_reloaded.root_user_including_tree
94
- expect(root.group).to eq group
95
- expect(root.children[0].group).to eq group
96
- end.to_not exceed_query_limit(2)
97
- end
98
-
99
- it "works if eager load association map is not given" do
100
- expect do
101
- root = group_reloaded.root_user_including_tree
102
- expect(root.children[0].email).to eq "2@example.com"
103
- expect(root.children[0].parent.children[1].children[0].email).to eq "5@example.com"
104
- end.to_not exceed_query_limit(2)
105
- end
106
-
107
- context "with no tree root" do
108
- let(:group2) { Group.create!(name: "OtherGroup") }
109
-
110
- it "should return nil" do
111
- expect(group2.root_user_including_tree(contracts: :contract_type)).to be_nil
112
- end
113
- end
114
-
115
- context "with multiple tree roots" do
116
- let!(:other_root) { User.create!(email: "10@example.com", group_id: group.id) }
117
-
118
- it "should error" do
119
- expect do
120
- root = group_reloaded.root_user_including_tree(contracts: :contract_type)
121
- end.to raise_error(ClosureTree::MultipleRootError)
122
- end
123
- end
124
- end
125
-
126
- context "with explicit class_name and foreign_key" do
127
- let(:group) { Grouping.create!(name: "TheGrouping") }
128
-
129
- it "should still work" do
130
- root = group_reloaded.root_person_including_tree(contracts: :contract_type)
131
- expect(root.children[0].email).to eq "2@example.com"
132
- end
133
- end
134
-
135
- context "with bad class_name" do
136
- let(:group) { UserSet.create!(name: "TheUserSet") }
137
-
138
- it "should error" do
139
- expect do
140
- root = group_reloaded.root_user_including_tree(contracts: :contract_type)
141
- end.to raise_error(NameError)
142
- end
143
- end
144
-
145
- context "with bad foreign_key" do
146
- let(:group) { Team.create!(name: "TheTeam") }
147
-
148
- it "should error" do
149
- expect do
150
- root = group_reloaded.root_user_including_tree(contracts: :contract_type)
151
- end.to raise_error(ActiveRecord::StatementInvalid)
152
- end
153
- end
154
- end
@@ -1,16 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ClosureTree::HierarchyMaintenance do
4
- describe '.rebuild!' do
5
- it 'rebuild tree' do
6
- 20.times do |counter|
7
- Metal.create(:value => "Nitro-#{counter}", parent: Metal.all.sample)
8
- end
9
- hierarchy_count = MetalHierarchy.count
10
- expect(hierarchy_count).to be > (20*2)-1 # shallowest-possible case, where all children use the first root
11
- MetalHierarchy.delete_all
12
- Metal.rebuild!
13
- expect(MetalHierarchy.count).to eq(hierarchy_count)
14
- end
15
- end
16
- end