closure_tree 6.6.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +7 -6
  3. data/Appraisals +59 -1
  4. data/CHANGELOG.md +21 -0
  5. data/Gemfile +0 -12
  6. data/README.md +40 -3
  7. data/closure_tree.gemspec +10 -7
  8. data/lib/closure_tree/finders.rb +15 -5
  9. data/lib/closure_tree/has_closure_tree.rb +2 -0
  10. data/lib/closure_tree/has_closure_tree_root.rb +2 -6
  11. data/lib/closure_tree/hash_tree_support.rb +1 -1
  12. data/lib/closure_tree/hierarchy_maintenance.rb +3 -3
  13. data/lib/closure_tree/model.rb +31 -1
  14. data/lib/closure_tree/numeric_deterministic_ordering.rb +11 -2
  15. data/lib/closure_tree/numeric_order_support.rb +3 -0
  16. data/lib/closure_tree/support.rb +7 -3
  17. data/lib/closure_tree/support_attributes.rb +9 -0
  18. data/lib/closure_tree/support_flags.rb +1 -4
  19. data/lib/closure_tree/version.rb +1 -1
  20. data/lib/generators/closure_tree/migration_generator.rb +8 -0
  21. data/lib/generators/closure_tree/templates/create_hierarchies_table.rb.erb +1 -1
  22. metadata +28 -76
  23. data/gemfiles/activerecord_4.2.gemfile +0 -19
  24. data/gemfiles/activerecord_5.0.gemfile +0 -19
  25. data/gemfiles/activerecord_5.1.gemfile +0 -19
  26. data/gemfiles/activerecord_edge.gemfile +0 -20
  27. data/img/example.png +0 -0
  28. data/img/preorder.png +0 -0
  29. data/spec/cache_invalidation_spec.rb +0 -39
  30. data/spec/cuisine_type_spec.rb +0 -38
  31. data/spec/db/database.yml +0 -21
  32. data/spec/db/models.rb +0 -128
  33. data/spec/db/schema.rb +0 -166
  34. data/spec/fixtures/tags.yml +0 -98
  35. data/spec/generators/migration_generator_spec.rb +0 -48
  36. data/spec/has_closure_tree_root_spec.rb +0 -154
  37. data/spec/hierarchy_maintenance_spec.rb +0 -16
  38. data/spec/label_spec.rb +0 -554
  39. data/spec/matcher_spec.rb +0 -34
  40. data/spec/metal_spec.rb +0 -55
  41. data/spec/model_spec.rb +0 -9
  42. data/spec/namespace_type_spec.rb +0 -13
  43. data/spec/parallel_spec.rb +0 -159
  44. data/spec/pool_spec.rb +0 -27
  45. data/spec/spec_helper.rb +0 -24
  46. data/spec/support/database.rb +0 -52
  47. data/spec/support/database_cleaner.rb +0 -14
  48. data/spec/support/exceed_query_limit.rb +0 -18
  49. data/spec/support/hash_monkey_patch.rb +0 -13
  50. data/spec/support/query_counter.rb +0 -18
  51. data/spec/support/sqlite3_with_advisory_lock.rb +0 -10
  52. data/spec/support_spec.rb +0 -14
  53. data/spec/tag_examples.rb +0 -665
  54. data/spec/tag_spec.rb +0 -6
  55. data/spec/user_spec.rb +0 -174
  56. data/spec/uuid_tag_spec.rb +0 -6
@@ -65,10 +65,15 @@ module ClosureTree
65
65
  node_score = "(1 + anc.#{_ct.quoted_order_column(false)}) * " +
66
66
  "power(#{h['total_descendants']}, #{h['max_depth'].to_i + 1} - #{depth_column})"
67
67
 
68
- "sum(#{node_score})"
68
+ # We want the NULLs to be first in case we are not ordering roots and they have NULL order.
69
+ Arel.sql("SUM(#{node_score}) IS NULL DESC, SUM(#{node_score})")
69
70
  end
70
71
 
71
72
  def roots_and_descendants_preordered
73
+ if _ct.dont_order_roots
74
+ raise ClosureTree::RootOrderingDisabledError.new("Root ordering is disabled on this model")
75
+ end
76
+
72
77
  join_sql = <<-SQL.strip_heredoc
73
78
  JOIN #{_ct.quoted_hierarchy_table_name} anc_hier
74
79
  ON anc_hier.descendant_id = #{_ct.quoted_table_name}.#{_ct.quoted_id_column_name}
@@ -78,7 +83,7 @@ module ClosureTree
78
83
  SELECT descendant_id, max(generations) AS max_depth
79
84
  FROM #{_ct.quoted_hierarchy_table_name}
80
85
  GROUP BY descendant_id
81
- ) AS depths ON depths.descendant_id = anc.#{_ct.quoted_id_column_name}
86
+ ) #{ _ct.t_alias_keyword } depths ON depths.descendant_id = anc.#{_ct.quoted_id_column_name}
82
87
  SQL
83
88
  joins(join_sql)
84
89
  .group("#{_ct.quoted_table_name}.#{_ct.quoted_id_column_name}")
@@ -113,6 +118,10 @@ module ClosureTree
113
118
  def add_sibling(sibling, add_after = true)
114
119
  fail "can't add self as sibling" if self == sibling
115
120
 
121
+ if _ct.dont_order_roots && parent.nil?
122
+ raise ClosureTree::RootOrderingDisabledError.new("Root ordering is disabled on this model")
123
+ end
124
+
116
125
  # Make sure self isn't dirty, because we're going to call reload:
117
126
  save
118
127
 
@@ -14,6 +14,7 @@ module ClosureTree
14
14
 
15
15
  module MysqlAdapter
16
16
  def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil)
17
+ return if parent_id.nil? && dont_order_roots
17
18
  min_where = if minimum_sort_order_value
18
19
  "AND #{quoted_order_column} >= #{minimum_sort_order_value}"
19
20
  else
@@ -31,6 +32,7 @@ module ClosureTree
31
32
 
32
33
  module PostgreSQLAdapter
33
34
  def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil)
35
+ return if parent_id.nil? && dont_order_roots
34
36
  min_where = if minimum_sort_order_value
35
37
  "AND #{quoted_order_column} >= #{minimum_sort_order_value}"
36
38
  else
@@ -56,6 +58,7 @@ module ClosureTree
56
58
 
57
59
  module GenericAdapter
58
60
  def reorder_with_parent_id(parent_id, minimum_sort_order_value = nil)
61
+ return if parent_id.nil? && dont_order_roots
59
62
  scope = model_class.
60
63
  where(parent_column_sym => parent_id).
61
64
  order(nulls_last_order_by)
@@ -22,7 +22,8 @@ module ClosureTree
22
22
  :parent_column_name => 'parent_id',
23
23
  :dependent => :nullify, # or :destroy or :delete_all -- see the README
24
24
  :name_column => 'name',
25
- :with_advisory_lock => true
25
+ :with_advisory_lock => true,
26
+ :numeric_order => false
26
27
  }.merge(options)
27
28
  raise ArgumentError, "name_column can't be 'path'" if options[:name_column] == 'path'
28
29
  if order_is_numeric?
@@ -82,12 +83,15 @@ module ClosureTree
82
83
 
83
84
  # lambda-ize the order, but don't apply the default order_option
84
85
  def has_many_without_order_option(opts)
85
- [lambda { order(opts[:order]) }, opts.except(:order)]
86
+ [lambda { order(opts[:order].call) }, opts.except(:order)]
86
87
  end
87
88
 
88
89
  def has_many_with_order_option(opts)
89
90
  order_options = [opts[:order], order_by].compact
90
- [lambda { order(order_options) }, opts.except(:order)]
91
+ [lambda {
92
+ order_options = order_options.map { |o| o.is_a?(Proc) ? o.call : o }
93
+ order(order_options)
94
+ }, opts.except(:order)]
91
95
  end
92
96
 
93
97
  def ids_from(scope)
@@ -75,6 +75,10 @@ module ClosureTree
75
75
  options[:order]
76
76
  end
77
77
 
78
+ def dont_order_roots
79
+ options[:dont_order_roots] || false
80
+ end
81
+
78
82
  def nulls_last_order_by
79
83
  "-#{quoted_order_column} #{order_by_order(reverse = true)}"
80
84
  end
@@ -110,5 +114,10 @@ module ClosureTree
110
114
  prefix = include_table_name ? "#{quoted_table_name}." : ""
111
115
  "#{prefix}#{connection.quote_column_name(order_column)}"
112
116
  end
117
+
118
+ # table_name alias keyword , like "AS". When used on table name alias, Oracle Database don't support used 'AS'
119
+ def t_alias_keyword
120
+ (ActiveRecord::Base.connection.adapter_name.to_sym == :OracleEnhanced) ? "" : "AS"
121
+ end
113
122
  end
114
123
  end
@@ -17,10 +17,7 @@ module ClosureTree
17
17
  end
18
18
 
19
19
  def order_is_numeric?
20
- # The table might not exist yet (in the case of ActiveRecord::Observer use, see issue 32)
21
- return false if !order_option? || !model_class.table_exists?
22
- c = model_class.columns_hash[order_column]
23
- c && c.type == :integer
20
+ options[:numeric_order]
24
21
  end
25
22
 
26
23
  def subclass?
@@ -1,3 +1,3 @@
1
1
  module ClosureTree
2
- VERSION = Gem::Version.new('6.6.0')
2
+ VERSION = Gem::Version.new('7.0.0')
3
3
  end
@@ -1,5 +1,6 @@
1
1
  require 'closure_tree/active_record_support'
2
2
  require 'forwardable'
3
+ require 'rails/generators'
3
4
  require 'rails/generators/active_record'
4
5
  require 'rails/generators/named_base'
5
6
 
@@ -41,6 +42,13 @@ module ClosureTree
41
42
  end
42
43
  end
43
44
 
45
+ def migration_version
46
+ major = ActiveRecord::VERSION::MAJOR
47
+ if major >= 5
48
+ "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
49
+ end
50
+ end
51
+
44
52
  def self.next_migration_number(dirname)
45
53
  ActiveRecord::Generators::Base.next_migration_number(dirname)
46
54
  end
@@ -1,4 +1,4 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration
1
+ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table :<%= migration_name %>, id: false do |t|
4
4
  t.<%= primary_key_type %> :ancestor_id, null: false
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.6.0
4
+ version: 7.0.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: 2017-07-12 00:00:00.000000000 Z
11
+ date: 2018-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,30 +16,30 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.1.0
19
+ version: 4.2.10
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.1.0
26
+ version: 4.2.10
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: with_advisory_lock
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 3.0.0
33
+ version: 4.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 3.0.0
40
+ version: 4.0.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec-instafail
42
+ name: appraisal
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec-rails
56
+ name: database_cleaner
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: database_cleaner
70
+ name: generator_spec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: appraisal
84
+ name: parallel
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: timecop
98
+ name: rspec-instafail
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,21 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: parallel
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: timecop
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -140,12 +154,6 @@ files:
140
154
  - README.md
141
155
  - Rakefile
142
156
  - closure_tree.gemspec
143
- - gemfiles/activerecord_4.2.gemfile
144
- - gemfiles/activerecord_5.0.gemfile
145
- - gemfiles/activerecord_5.1.gemfile
146
- - gemfiles/activerecord_edge.gemfile
147
- - img/example.png
148
- - img/preorder.png
149
157
  - lib/closure_tree.rb
150
158
  - lib/closure_tree/active_record_support.rb
151
159
  - lib/closure_tree/configuration.rb
@@ -170,34 +178,6 @@ files:
170
178
  - lib/generators/closure_tree/templates/config.rb
171
179
  - lib/generators/closure_tree/templates/create_hierarchies_table.rb.erb
172
180
  - mktree.rb
173
- - spec/cache_invalidation_spec.rb
174
- - spec/cuisine_type_spec.rb
175
- - spec/db/database.yml
176
- - spec/db/models.rb
177
- - spec/db/schema.rb
178
- - spec/fixtures/tags.yml
179
- - spec/generators/migration_generator_spec.rb
180
- - spec/has_closure_tree_root_spec.rb
181
- - spec/hierarchy_maintenance_spec.rb
182
- - spec/label_spec.rb
183
- - spec/matcher_spec.rb
184
- - spec/metal_spec.rb
185
- - spec/model_spec.rb
186
- - spec/namespace_type_spec.rb
187
- - spec/parallel_spec.rb
188
- - spec/pool_spec.rb
189
- - spec/spec_helper.rb
190
- - spec/support/database.rb
191
- - spec/support/database_cleaner.rb
192
- - spec/support/exceed_query_limit.rb
193
- - spec/support/hash_monkey_patch.rb
194
- - spec/support/query_counter.rb
195
- - spec/support/sqlite3_with_advisory_lock.rb
196
- - spec/support_spec.rb
197
- - spec/tag_examples.rb
198
- - spec/tag_spec.rb
199
- - spec/user_spec.rb
200
- - spec/uuid_tag_spec.rb
201
181
  - tests.sh
202
182
  homepage: http://mceachen.github.io/closure_tree/
203
183
  licenses:
@@ -219,36 +199,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
199
  version: '0'
220
200
  requirements: []
221
201
  rubyforge_project:
222
- rubygems_version: 2.6.8
202
+ rubygems_version: 2.7.6
223
203
  signing_key:
224
204
  specification_version: 4
225
205
  summary: Easily and efficiently make your ActiveRecord model support hierarchies
226
- test_files:
227
- - spec/cache_invalidation_spec.rb
228
- - spec/cuisine_type_spec.rb
229
- - spec/db/database.yml
230
- - spec/db/models.rb
231
- - spec/db/schema.rb
232
- - spec/fixtures/tags.yml
233
- - spec/generators/migration_generator_spec.rb
234
- - spec/has_closure_tree_root_spec.rb
235
- - spec/hierarchy_maintenance_spec.rb
236
- - spec/label_spec.rb
237
- - spec/matcher_spec.rb
238
- - spec/metal_spec.rb
239
- - spec/model_spec.rb
240
- - spec/namespace_type_spec.rb
241
- - spec/parallel_spec.rb
242
- - spec/pool_spec.rb
243
- - spec/spec_helper.rb
244
- - spec/support/database.rb
245
- - spec/support/database_cleaner.rb
246
- - spec/support/exceed_query_limit.rb
247
- - spec/support/hash_monkey_patch.rb
248
- - spec/support/query_counter.rb
249
- - spec/support/sqlite3_with_advisory_lock.rb
250
- - spec/support_spec.rb
251
- - spec/tag_examples.rb
252
- - spec/tag_spec.rb
253
- - spec/user_spec.rb
254
- - spec/uuid_tag_spec.rb
206
+ test_files: []
@@ -1,19 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", "~> 4.2.0"
6
-
7
- platforms :ruby, :rbx do
8
- gem "mysql2"
9
- gem "pg"
10
- gem "sqlite3"
11
- end
12
-
13
- platforms :jruby do
14
- gem "activerecord-jdbcmysql-adapter"
15
- gem "activerecord-jdbcpostgresql-adapter"
16
- gem "activerecord-jdbcsqlite3-adapter"
17
- end
18
-
19
- gemspec :path => "../"
@@ -1,19 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", "~> 5.0.0"
6
-
7
- platforms :ruby, :rbx do
8
- gem "mysql2"
9
- gem "pg"
10
- gem "sqlite3"
11
- end
12
-
13
- platforms :jruby do
14
- gem "activerecord-jdbcmysql-adapter"
15
- gem "activerecord-jdbcpostgresql-adapter"
16
- gem "activerecord-jdbcsqlite3-adapter"
17
- end
18
-
19
- gemspec :path => "../"
@@ -1,19 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", "~> 5.1.0"
6
-
7
- platforms :ruby, :rbx do
8
- gem "mysql2"
9
- gem "pg"
10
- gem "sqlite3"
11
- end
12
-
13
- platforms :jruby do
14
- gem "activerecord-jdbcmysql-adapter"
15
- gem "activerecord-jdbcpostgresql-adapter"
16
- gem "activerecord-jdbcsqlite3-adapter"
17
- end
18
-
19
- gemspec :path => "../"
@@ -1,20 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", :github => "rails/rails"
6
- gem "arel", :github => "rails/arel"
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 => "../"
Binary file
Binary file
@@ -1,39 +0,0 @@
1
- require 'spec_helper'
2
-
3
-
4
- describe 'cache invalidation', cache: true do
5
- before do
6
- Timecop.travel(10.seconds.ago) do
7
- #create a long tree with 2 branch
8
- @root = MenuItem.create(
9
- name: SecureRandom.hex(10)
10
- )
11
- 2.times do
12
- parent = @root
13
- 10.times do
14
- parent = parent.children.create(
15
- name: SecureRandom.hex(10)
16
- )
17
- end
18
- end
19
- @first_leaf = MenuItem.leaves.first
20
- @second_leaf = MenuItem.leaves.last
21
- end
22
- end
23
-
24
- describe 'touch option' do
25
- it 'should invalidate cache for all it ancestors' do
26
- old_time_stamp = @first_leaf.ancestors.pluck(:updated_at)
27
- @first_leaf.touch
28
- new_time_stamp = @first_leaf.ancestors.pluck(:updated_at)
29
- expect(old_time_stamp).to_not eq(new_time_stamp)
30
- end
31
-
32
- it 'should not invalidate cache for another branch' do
33
- old_time_stamp = @second_leaf.updated_at
34
- @first_leaf.touch
35
- new_time_stamp = @second_leaf.updated_at
36
- expect(old_time_stamp).to eq(new_time_stamp)
37
- end
38
- end
39
- end