closure_tree 6.1.0 → 6.2.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -0
  3. data/.rspec +0 -0
  4. data/.travis.yml +0 -0
  5. data/.yardopts +0 -0
  6. data/Appraisals +0 -0
  7. data/CHANGELOG.md +7 -0
  8. data/Gemfile +0 -0
  9. data/MIT-LICENSE +0 -0
  10. data/README.md +6 -1
  11. data/Rakefile +0 -0
  12. data/closure_tree.gemspec +0 -0
  13. data/gemfiles/activerecord_4.1.gemfile +0 -0
  14. data/gemfiles/activerecord_4.2.gemfile +0 -0
  15. data/gemfiles/activerecord_5.0.gemfile +0 -0
  16. data/gemfiles/activerecord_5.0_foreigner.gemfile +0 -0
  17. data/gemfiles/activerecord_edge.gemfile +0 -0
  18. data/img/example.png +0 -0
  19. data/img/preorder.png +0 -0
  20. data/lib/closure_tree.rb +2 -0
  21. data/lib/closure_tree/active_record_support.rb +0 -0
  22. data/lib/closure_tree/configuration.rb +0 -0
  23. data/lib/closure_tree/deterministic_ordering.rb +0 -0
  24. data/lib/closure_tree/digraphs.rb +0 -0
  25. data/lib/closure_tree/finders.rb +3 -3
  26. data/lib/closure_tree/has_closure_tree.rb +0 -0
  27. data/lib/closure_tree/has_closure_tree_root.rb +88 -0
  28. data/lib/closure_tree/hash_tree.rb +0 -0
  29. data/lib/closure_tree/hash_tree_support.rb +0 -0
  30. data/lib/closure_tree/hierarchy_maintenance.rb +0 -0
  31. data/lib/closure_tree/model.rb +0 -0
  32. data/lib/closure_tree/numeric_deterministic_ordering.rb +1 -1
  33. data/lib/closure_tree/numeric_order_support.rb +0 -0
  34. data/lib/closure_tree/support.rb +0 -0
  35. data/lib/closure_tree/support_attributes.rb +1 -2
  36. data/lib/closure_tree/support_flags.rb +0 -0
  37. data/lib/closure_tree/test/matcher.rb +0 -0
  38. data/lib/closure_tree/version.rb +1 -1
  39. data/lib/generators/closure_tree/config_generator.rb +0 -0
  40. data/lib/generators/closure_tree/migration_generator.rb +0 -0
  41. data/lib/generators/closure_tree/templates/config.rb +0 -0
  42. data/lib/generators/closure_tree/templates/create_hierarchies_table.rb.erb +0 -0
  43. data/spec/cache_invalidation_spec.rb +0 -0
  44. data/spec/cuisine_type_spec.rb +0 -0
  45. data/spec/db/database.yml +0 -0
  46. data/spec/db/models.rb +24 -2
  47. data/spec/db/schema.rb +22 -0
  48. data/spec/fixtures/tags.yml +0 -0
  49. data/spec/generators/migration_generator_spec.rb +0 -0
  50. data/spec/has_closure_tree_root_spec.rb +132 -0
  51. data/spec/hierarchy_maintenance_spec.rb +0 -0
  52. data/spec/label_spec.rb +0 -0
  53. data/spec/matcher_spec.rb +0 -0
  54. data/spec/metal_spec.rb +0 -0
  55. data/spec/model_spec.rb +0 -0
  56. data/spec/namespace_type_spec.rb +0 -0
  57. data/spec/parallel_spec.rb +0 -0
  58. data/spec/spec_helper.rb +0 -0
  59. data/spec/support/database.rb +0 -0
  60. data/spec/support/database_cleaner.rb +0 -0
  61. data/spec/support/exceed_query_limit.rb +18 -0
  62. data/spec/support/hash_monkey_patch.rb +0 -0
  63. data/spec/support/query_counter.rb +18 -0
  64. data/spec/support/sqlite3_with_advisory_lock.rb +0 -0
  65. data/spec/support_spec.rb +0 -0
  66. data/spec/tag_examples.rb +0 -0
  67. data/spec/tag_spec.rb +0 -0
  68. data/spec/user_spec.rb +0 -0
  69. data/spec/uuid_tag_spec.rb +0 -0
  70. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be5442160917053dfca50f710fa8276b2d2d937c
4
- data.tar.gz: e6012dd4b630ace5fa36835de936bd395e41df20
3
+ metadata.gz: e3b96ba78aea59298da3138316379a458d28c713
4
+ data.tar.gz: f1c786fb41309ea4460a06ae296b7393ea058b63
5
5
  SHA512:
6
- metadata.gz: 43316c4fd1030f11ca90d59cf477f9a7d5300a16771152e3b81a779d5a25f665b9f9a288c42bc0c54183717d537cc95d704fc180166064079fbe6a9ee20d2bc5
7
- data.tar.gz: 7f65c0281b3d878b2c333087f3e63a8e09c6aecbcabc98d798e2e1651853f50f7aefe50ece31b611bf12b17718fa3b023adc67cb39c747034a79aafae152dd93
6
+ metadata.gz: b5850fadca83ecdc88e53f4fac48a19faaafda82c7b6bd93f8051e264b632a94b56edbc3b22d9d060d658cf76dca02268fda625835c74e9e6cfb6ef75e9430d6
7
+ data.tar.gz: cae0ca262a5f9f76d1a01b24aac119a5307a49637a77e9287305630d2e97c2b3265a767180b5e76eedff9a4a9ada44ff07aebe648d0b109949c7ed2e012b9b93
data/.gitignore CHANGED
File without changes
data/.rspec CHANGED
File without changes
data/.travis.yml CHANGED
File without changes
data/.yardopts CHANGED
File without changes
data/Appraisals CHANGED
File without changes
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ### 6.2.0
4
+
5
+ * Fix for [MySQL lock lengths](https://github.com/mceachen/closure_tree/issues/231).
6
+ Thanks to [Liam](https://github.com/hut8)!
7
+ * [Tom Smyth](https://github.com/hooverlunch) added [eager tree loading](https://github.com/mceachen/closure_tree/pull/232)
8
+ * Merged [PR 200](https://github.com/mceachen/closure_tree/pull/200) which may or may not add support to SQLServer 2008 (but this is not a supported RDBMS).
9
+
3
10
  ### 6.1.0
4
11
 
5
12
  * Added official support for ActiveRecord 5.0! Thanks to [Abdelkader Boudih](https://github.com/seuros),
data/Gemfile CHANGED
File without changes
data/MIT-LICENSE CHANGED
File without changes
data/README.md CHANGED
@@ -468,7 +468,12 @@ Yup! [Ilya Bodrov](https://github.com/bodrovis) wrote [Nested Comments with Rail
468
468
 
469
469
  ### Does this work well with ```#default_scope```?
470
470
 
471
- No. Please see [issue 86](https://github.com/mceachen/closure_tree/issues/86) for details.
471
+ **No.** Please see [issue 86](https://github.com/mceachen/closure_tree/issues/86) for details.
472
+
473
+ ### Can I update parentage with `update_attribute`?
474
+
475
+ **No.** `update_attribute` skips the validation hook that is required for maintaining the
476
+ hierarchy table.
472
477
 
473
478
  ### Does this gem support multiple parents?
474
479
 
data/Rakefile CHANGED
File without changes
data/closure_tree.gemspec CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
data/img/example.png CHANGED
File without changes
data/img/preorder.png CHANGED
File without changes
data/lib/closure_tree.rb CHANGED
@@ -4,6 +4,7 @@ module ClosureTree
4
4
  extend ActiveSupport::Autoload
5
5
 
6
6
  autoload :HasClosureTree
7
+ autoload :HasClosureTreeRoot
7
8
  autoload :Support
8
9
  autoload :HierarchyMaintenance
9
10
  autoload :Model
@@ -25,4 +26,5 @@ end
25
26
 
26
27
  ActiveSupport.on_load :active_record do
27
28
  ActiveRecord::Base.send :extend, ClosureTree::HasClosureTree
29
+ ActiveRecord::Base.send :extend, ClosureTree::HasClosureTreeRoot
28
30
  end
File without changes
File without changes
File without changes
File without changes
@@ -39,7 +39,7 @@ module ClosureTree
39
39
  SELECT descendant_id
40
40
  FROM #{_ct.quoted_hierarchy_table_name}
41
41
  WHERE ancestor_id = #{_ct.quote(self.id)}
42
- GROUP BY 1
42
+ GROUP BY descendant_id
43
43
  HAVING MAX(#{_ct.quoted_hierarchy_table_name}.generations) = #{generation_level.to_i}
44
44
  ) AS descendants ON (#{_ct.quoted_table_name}.#{_ct.base_class.primary_key} = descendants.descendant_id)
45
45
  SQL
@@ -74,7 +74,7 @@ module ClosureTree
74
74
  INNER JOIN (
75
75
  SELECT ancestor_id
76
76
  FROM #{_ct.quoted_hierarchy_table_name}
77
- GROUP BY 1
77
+ GROUP BY ancestor_id
78
78
  HAVING MAX(#{_ct.quoted_hierarchy_table_name}.generations) = 0
79
79
  ) AS leaves ON (#{_ct.quoted_table_name}.#{primary_key} = leaves.ancestor_id)
80
80
  SQL
@@ -100,7 +100,7 @@ module ClosureTree
100
100
  INNER JOIN (
101
101
  SELECT ancestor_id, descendant_id
102
102
  FROM #{_ct.quoted_hierarchy_table_name}
103
- GROUP BY 1, 2
103
+ GROUP BY ancestor_id, descendant_id
104
104
  HAVING MAX(generations) = #{generation_level.to_i}
105
105
  ) AS descendants ON (
106
106
  #{_ct.quoted_table_name}.#{primary_key} = descendants.descendant_id
File without changes
@@ -0,0 +1,88 @@
1
+ module ClosureTree
2
+ class MultipleRootError < StandardError; end
3
+
4
+ module HasClosureTreeRoot
5
+
6
+ def has_closure_tree_root(assoc_name, options = {})
7
+ options.assert_valid_keys(
8
+ :class_name,
9
+ :foreign_key
10
+ )
11
+
12
+ options[:class_name] ||= assoc_name.to_s.sub(/\Aroot_/, "").classify
13
+ options[:foreign_key] ||= self.name.underscore << "_id"
14
+
15
+ has_one assoc_name, -> { where(parent: nil) }, options
16
+
17
+ # Fetches the association, eager loading all children and given associations
18
+ define_method("#{assoc_name}_including_tree") do |assoc_map_or_reload = nil, assoc_map = nil|
19
+ reload = false
20
+ if assoc_map_or_reload.is_a?(::Hash)
21
+ assoc_map = assoc_map_or_reload
22
+ else
23
+ reload = assoc_map_or_reload
24
+ end
25
+
26
+ unless reload
27
+ # Memoize
28
+ @closure_tree_roots ||= {}
29
+ @closure_tree_roots[assoc_name] ||= {}
30
+ if @closure_tree_roots[assoc_name].has_key?(assoc_map)
31
+ return @closure_tree_roots[assoc_name][assoc_map]
32
+ end
33
+ end
34
+
35
+ roots = options[:class_name].constantize.where(parent: nil, options[:foreign_key] => id).to_a
36
+
37
+ return nil if roots.empty?
38
+
39
+ if roots.size > 1
40
+ raise MultipleRootError.new("#{self.class.name}: has_closure_tree_root requires a single root")
41
+ end
42
+
43
+ temp_root = roots.first
44
+ root = nil
45
+ id_hash = {}
46
+ parent_col_id = temp_root.class._ct.options[:parent_column_name]
47
+
48
+ # Lookup inverse belongs_to association reflection on target class.
49
+ inverse = temp_root.class.reflections.values.detect do |r|
50
+ r.macro == :belongs_to && r.klass == self.class
51
+ end
52
+
53
+ # Fetch all descendants in constant number of queries.
54
+ # This is the last query-triggering statement in the method.
55
+ temp_root.self_and_descendants.includes(assoc_map).each do |node|
56
+ id_hash[node.id] = node
57
+ parent_node = id_hash[node[parent_col_id]]
58
+
59
+ # Pre-assign parent association
60
+ parent_assoc = node.association(:parent)
61
+ parent_assoc.loaded!
62
+ parent_assoc.target = parent_node
63
+
64
+ # Pre-assign children association as empty for now,
65
+ # children will be added in subsequent loop iterations
66
+ children_assoc = node.association(:children)
67
+ children_assoc.loaded!
68
+
69
+ if parent_node
70
+ parent_node.association(:children).target << node
71
+ else
72
+ # Capture the root we're going to use
73
+ root = node
74
+ end
75
+
76
+ # Pre-assign inverse association back to this class, if it exists on target class.
77
+ if inverse
78
+ inverse_assoc = node.association(inverse.name)
79
+ inverse_assoc.loaded!
80
+ inverse_assoc.target = self
81
+ end
82
+ end
83
+
84
+ @closure_tree_roots[assoc_name][assoc_map] = root
85
+ end
86
+ end
87
+ end
88
+ end
File without changes
File without changes
File without changes
File without changes
@@ -72,7 +72,7 @@ module ClosureTree
72
72
  JOIN (
73
73
  SELECT descendant_id, max(generations) AS max_depth
74
74
  FROM #{_ct.quoted_hierarchy_table_name}
75
- GROUP BY 1
75
+ GROUP BY descendant_id
76
76
  ) AS depths ON depths.descendant_id = anc.#{_ct.quoted_id_column_name}
77
77
  SQL
78
78
  joins(join_sql)
File without changes
File without changes
@@ -1,12 +1,11 @@
1
1
  require 'forwardable'
2
2
  module ClosureTree
3
3
  module SupportAttributes
4
-
5
4
  extend Forwardable
6
5
  def_delegators :model_class, :connection, :transaction, :table_name, :base_class, :inheritance_column, :column_names
7
6
 
8
7
  def advisory_lock_name
9
- "ClosureTree::#{base_class.name}"
8
+ Digest::SHA1.hexdigest("ClosureTree::#{base_class.name}")[0..32]
10
9
  end
11
10
 
12
11
  def quoted_table_name
File without changes
File without changes
@@ -1,3 +1,3 @@
1
1
  module ClosureTree
2
- VERSION = Gem::Version.new('6.1.0')
2
+ VERSION = Gem::Version.new('6.2.0')
3
3
  end
File without changes
File without changes
File without changes
File without changes
File without changes
data/spec/db/database.yml CHANGED
File without changes
data/spec/db/models.rb CHANGED
@@ -35,13 +35,30 @@ end
35
35
  class DestroyedTag < ActiveRecord::Base
36
36
  end
37
37
 
38
+ class Group < ActiveRecord::Base
39
+ has_closure_tree_root :root_user
40
+ end
41
+
42
+ class Grouping < ActiveRecord::Base
43
+ has_closure_tree_root :root_person, class_name: "User", foreign_key: :group_id
44
+ end
45
+
46
+ class UserSet < ActiveRecord::Base
47
+ has_closure_tree_root :root_user, class_name: "Useur"
48
+ end
49
+
50
+ class Team < ActiveRecord::Base
51
+ has_closure_tree_root :root_user, class_name: "User", foreign_key: :grp_id
52
+ end
53
+
38
54
  class User < ActiveRecord::Base
39
55
  acts_as_tree :parent_column_name => "referrer_id",
40
56
  :name_column => 'email',
41
57
  :hierarchy_class_name => 'ReferralHierarchy',
42
58
  :hierarchy_table_name => 'referral_hierarchies'
43
59
 
44
- has_many :contracts
60
+ has_many :contracts, inverse_of: :user
61
+ belongs_to :group # Can't use and don't need inverse_of here when using has_closure_tree_root.
45
62
 
46
63
  def indirect_contracts
47
64
  Contract.where(:user_id => descendant_ids)
@@ -53,7 +70,12 @@ class User < ActiveRecord::Base
53
70
  end
54
71
 
55
72
  class Contract < ActiveRecord::Base
56
- belongs_to :user
73
+ belongs_to :user, inverse_of: :contracts
74
+ belongs_to :contract_type, inverse_of: :contracts
75
+ end
76
+
77
+ class ContractType < ActiveRecord::Base
78
+ has_many :contracts, inverse_of: :contract_type
57
79
  end
58
80
 
59
81
  class Label < ActiveRecord::Base
data/spec/db/schema.rb CHANGED
@@ -43,9 +43,26 @@ ActiveRecord::Schema.define(:version => 0) do
43
43
  add_index "tag_hierarchies", [:ancestor_id, :descendant_id, :generations], :unique => true, :name => "tag_anc_desc_idx"
44
44
  add_index "tag_hierarchies", [:descendant_id], :name => "tag_desc_idx"
45
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
+
46
62
  create_table "users" do |t|
47
63
  t.string "email"
48
64
  t.integer "referrer_id"
65
+ t.integer "group_id"
49
66
  t.timestamps null: false
50
67
  end
51
68
 
@@ -53,6 +70,11 @@ ActiveRecord::Schema.define(:version => 0) do
53
70
 
54
71
  create_table "contracts" do |t|
55
72
  t.integer "user_id", :null => false
73
+ t.integer "contract_type_id"
74
+ end
75
+
76
+ create_table "contract_types" do |t|
77
+ t.string "name", :null => false
56
78
  end
57
79
 
58
80
  create_table "referral_hierarchies", :id => false do |t|
File without changes
File without changes
@@ -0,0 +1,132 @@
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!(contract_type: ct1)
30
+ user2.contracts.create!(contract_type: ct1)
31
+ user3.contracts.create!(contract_type: ct1)
32
+ user3.contracts.create!(contract_type: ct2)
33
+ user4.contracts.create!(contract_type: ct2)
34
+ user5.contracts.create!(contract_type: ct1)
35
+ user6.contracts.create!(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 and associations in a constant number of queries" do
42
+ expect do
43
+ root = group_reloaded.root_user_including_tree(contracts: :contract_type)
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
+ expect(root.children[1].contracts.map(&:contract_type).map(&:name)).to eq %w(Type1 Type2)
47
+ expect(root.children[1].children[0].contracts[0].contract_type.name).to eq "Type1"
48
+ expect(root.children[0].children[0].contracts[0].user.
49
+ parent.parent.children[1].children[1].contracts[0].contract_type.name).to eq "Type2"
50
+ end.to_not exceed_query_limit(4) # Without this feature, this is 15, and scales with number of nodes.
51
+ end
52
+
53
+ it "memoizes by assoc_map" do
54
+ group_reloaded.root_user_including_tree.email = "x"
55
+ expect(group_reloaded.root_user_including_tree.email).to eq "x"
56
+ group_reloaded.root_user_including_tree(contracts: :contract_type).email = "y"
57
+ expect(group_reloaded.root_user_including_tree(contracts: :contract_type).email).to eq "y"
58
+ expect(group_reloaded.root_user_including_tree.email).to eq "x"
59
+ end
60
+
61
+ it "doesn't memoize if true argument passed" do
62
+ group_reloaded.root_user_including_tree.email = "x"
63
+ expect(group_reloaded.root_user_including_tree(true).email).to eq "1@example.com"
64
+ group_reloaded.root_user_including_tree(contracts: :contract_type).email = "y"
65
+ expect(group_reloaded.root_user_including_tree(true, contracts: :contract_type).email).
66
+ to eq "1@example.com"
67
+ end
68
+
69
+ it "eager loads inverse association to group" do
70
+ expect do
71
+ root = group_reloaded.root_user_including_tree
72
+ expect(root.group).to eq group
73
+ expect(root.children[0].group).to eq group
74
+ end.to_not exceed_query_limit(2)
75
+ end
76
+
77
+ it "works if eager load association map is not given" do
78
+ expect do
79
+ root = group_reloaded.root_user_including_tree
80
+ expect(root.children[0].email).to eq "2@example.com"
81
+ expect(root.children[0].parent.children[1].children[0].email).to eq "5@example.com"
82
+ end.to_not exceed_query_limit(2)
83
+ end
84
+
85
+ context "with no tree root" do
86
+ let(:group2) { Group.create!(name: "OtherGroup") }
87
+
88
+ it "should return nil" do
89
+ expect(group2.root_user_including_tree(contracts: :contract_type)).to be_nil
90
+ end
91
+ end
92
+
93
+ context "with multiple tree roots" do
94
+ let!(:other_root) { User.create!(email: "10@example.com", group_id: group.id) }
95
+
96
+ it "should error" do
97
+ expect do
98
+ root = group_reloaded.root_user_including_tree(contracts: :contract_type)
99
+ end.to raise_error(ClosureTree::MultipleRootError)
100
+ end
101
+ end
102
+ end
103
+
104
+ context "with explicit class_name and foreign_key" do
105
+ let(:group) { Grouping.create!(name: "TheGrouping") }
106
+
107
+ it "should still work" do
108
+ root = group_reloaded.root_person_including_tree(contracts: :contract_type)
109
+ expect(root.children[0].email).to eq "2@example.com"
110
+ end
111
+ end
112
+
113
+ context "with bad class_name" do
114
+ let(:group) { UserSet.create!(name: "TheUserSet") }
115
+
116
+ it "should error" do
117
+ expect do
118
+ root = group_reloaded.root_user_including_tree(contracts: :contract_type)
119
+ end.to raise_error(NameError)
120
+ end
121
+ end
122
+
123
+ context "with bad foreign_key" do
124
+ let(:group) { Team.create!(name: "TheTeam") }
125
+
126
+ it "should error" do
127
+ expect do
128
+ root = group_reloaded.root_user_including_tree(contracts: :contract_type)
129
+ end.to raise_error(ActiveRecord::StatementInvalid)
130
+ end
131
+ end
132
+ end
File without changes
data/spec/label_spec.rb CHANGED
File without changes
data/spec/matcher_spec.rb CHANGED
File without changes
data/spec/metal_spec.rb CHANGED
File without changes
data/spec/model_spec.rb CHANGED
File without changes
File without changes
File without changes
data/spec/spec_helper.rb CHANGED
File without changes
File without changes
File without changes
@@ -0,0 +1,18 @@
1
+ # Derived from http://stackoverflow.com/a/13423584/153896. Updated for RSpec 3.
2
+ RSpec::Matchers.define :exceed_query_limit do |expected|
3
+ supports_block_expectations
4
+
5
+ match do |block|
6
+ query_count(&block) > expected
7
+ end
8
+
9
+ failure_message_when_negated do |actual|
10
+ "Expected to run maximum #{expected} queries, got #{@counter.query_count}"
11
+ end
12
+
13
+ def query_count(&block)
14
+ @counter = ActiveRecord::QueryCounter.new
15
+ ActiveSupport::Notifications.subscribed(@counter.to_proc, 'sql.active_record', &block)
16
+ @counter.query_count
17
+ end
18
+ end
File without changes
@@ -0,0 +1,18 @@
1
+ # From http://stackoverflow.com/a/13423584/153896
2
+ module ActiveRecord
3
+ class QueryCounter
4
+ attr_reader :query_count
5
+
6
+ def initialize
7
+ @query_count = 0
8
+ end
9
+
10
+ def to_proc
11
+ lambda(&method(:callback))
12
+ end
13
+
14
+ def callback(name, start, finish, message_id, values)
15
+ @query_count += 1 unless %w(CACHE SCHEMA).include?(values[:name])
16
+ end
17
+ end
18
+ end
File without changes
data/spec/support_spec.rb CHANGED
File without changes
data/spec/tag_examples.rb CHANGED
File without changes
data/spec/tag_spec.rb CHANGED
File without changes
data/spec/user_spec.rb CHANGED
File without changes
File without changes
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: 6.1.0
4
+ version: 6.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: 2016-07-22 00:00:00.000000000 Z
11
+ date: 2016-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -154,6 +154,7 @@ files:
154
154
  - lib/closure_tree/digraphs.rb
155
155
  - lib/closure_tree/finders.rb
156
156
  - lib/closure_tree/has_closure_tree.rb
157
+ - lib/closure_tree/has_closure_tree_root.rb
157
158
  - lib/closure_tree/hash_tree.rb
158
159
  - lib/closure_tree/hash_tree_support.rb
159
160
  - lib/closure_tree/hierarchy_maintenance.rb
@@ -177,6 +178,7 @@ files:
177
178
  - spec/db/schema.rb
178
179
  - spec/fixtures/tags.yml
179
180
  - spec/generators/migration_generator_spec.rb
181
+ - spec/has_closure_tree_root_spec.rb
180
182
  - spec/hierarchy_maintenance_spec.rb
181
183
  - spec/label_spec.rb
182
184
  - spec/matcher_spec.rb
@@ -187,7 +189,9 @@ files:
187
189
  - spec/spec_helper.rb
188
190
  - spec/support/database.rb
189
191
  - spec/support/database_cleaner.rb
192
+ - spec/support/exceed_query_limit.rb
190
193
  - spec/support/hash_monkey_patch.rb
194
+ - spec/support/query_counter.rb
191
195
  - spec/support/sqlite3_with_advisory_lock.rb
192
196
  - spec/support_spec.rb
193
197
  - spec/tag_examples.rb
@@ -227,6 +231,7 @@ test_files:
227
231
  - spec/db/schema.rb
228
232
  - spec/fixtures/tags.yml
229
233
  - spec/generators/migration_generator_spec.rb
234
+ - spec/has_closure_tree_root_spec.rb
230
235
  - spec/hierarchy_maintenance_spec.rb
231
236
  - spec/label_spec.rb
232
237
  - spec/matcher_spec.rb
@@ -237,7 +242,9 @@ test_files:
237
242
  - spec/spec_helper.rb
238
243
  - spec/support/database.rb
239
244
  - spec/support/database_cleaner.rb
245
+ - spec/support/exceed_query_limit.rb
240
246
  - spec/support/hash_monkey_patch.rb
247
+ - spec/support/query_counter.rb
241
248
  - spec/support/sqlite3_with_advisory_lock.rb
242
249
  - spec/support_spec.rb
243
250
  - spec/tag_examples.rb