activerecord-hierarchical_query 0.0.2 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +59 -30
  3. data/lib/active_record/hierarchical_query.rb +11 -11
  4. data/lib/active_record/hierarchical_query/cte/columns.rb +2 -15
  5. data/lib/active_record/hierarchical_query/cte/cycle_detector.rb +54 -0
  6. data/lib/active_record/hierarchical_query/cte/non_recursive_term.rb +32 -11
  7. data/lib/active_record/hierarchical_query/cte/query_builder.rb +82 -0
  8. data/lib/active_record/hierarchical_query/cte/recursive_term.rb +27 -13
  9. data/lib/active_record/hierarchical_query/cte/union_term.rb +10 -6
  10. data/lib/active_record/hierarchical_query/join_builder.rb +31 -10
  11. data/lib/active_record/hierarchical_query/orderings.rb +113 -0
  12. data/lib/active_record/hierarchical_query/{builder.rb → query.rb} +66 -36
  13. data/lib/active_record/hierarchical_query/version.rb +1 -1
  14. data/lib/arel/nodes/postgresql.rb +26 -6
  15. data/spec/active_record/hierarchical_query_spec.rb +56 -32
  16. data/spec/database.yml +1 -9
  17. data/spec/schema.rb +2 -2
  18. data/spec/spec_helper.rb +2 -4
  19. data/spec/support/models.rb +4 -4
  20. metadata +28 -33
  21. data/lib/active_record/hierarchical_query/adapters.rb +0 -34
  22. data/lib/active_record/hierarchical_query/adapters/abstract.rb +0 -41
  23. data/lib/active_record/hierarchical_query/adapters/postgresql.rb +0 -19
  24. data/lib/active_record/hierarchical_query/cte/query.rb +0 -65
  25. data/lib/active_record/hierarchical_query/visitors/orderings.rb +0 -87
  26. data/lib/active_record/hierarchical_query/visitors/postgresql/cycle_detector.rb +0 -49
  27. data/lib/active_record/hierarchical_query/visitors/postgresql/orderings.rb +0 -70
  28. data/lib/active_record/hierarchical_query/visitors/visitor.rb +0 -17
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module HierarchicalQuery
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.5'
4
4
  end
5
5
  end
@@ -1,3 +1,5 @@
1
+ require 'arel/visitors/to_sql'
2
+
1
3
  module Arel
2
4
  module Nodes
3
5
  class PostgresArray < Node
@@ -14,14 +16,32 @@ module Arel
14
16
  end
15
17
 
16
18
  module Visitors
17
- class ToSql < Arel::Visitors::Visitor
19
+ class ToSql < ToSql.superclass
18
20
  private
19
- def visit_Arel_Nodes_PostgresArray o, *a
20
- "ARRAY[#{visit o.values, *a}]"
21
- end
21
+ ARRAY_OPENING = 'ARRAY['.freeze
22
+ ARRAY_CLOSING = ']'.freeze
23
+ ARRAY_CONCAT = '||'.freeze
24
+
25
+ if Arel::VERSION < '6.0.0'
26
+ def visit_Arel_Nodes_PostgresArray o, *a
27
+ "#{ARRAY_OPENING}#{visit o.values, *a}#{ARRAY_CLOSING}"
28
+ end
29
+
30
+ def visit_Arel_Nodes_ArrayConcat o, *a
31
+ "#{visit o.left, *a} #{ARRAY_CONCAT} #{visit o.right, *a}"
32
+ end
33
+ else
34
+ def visit_Arel_Nodes_PostgresArray o, collector
35
+ collector << ARRAY_OPENING
36
+ visit o.values, collector
37
+ collector << ARRAY_CLOSING
38
+ end
22
39
 
23
- def visit_Arel_Nodes_ArrayConcat o, *a
24
- "#{visit o.left, *a} || #{visit o.right, *a}"
40
+ def visit_Arel_Nodes_ArrayConcat o, collector
41
+ visit o.left, collector
42
+ collector << ARRAY_CONCAT
43
+ visit o.right, collector
44
+ end
25
45
  end
26
46
  end
27
47
  end
@@ -4,11 +4,11 @@ describe ActiveRecord::HierarchicalQuery do
4
4
  let(:klass) { Category }
5
5
 
6
6
  let!(:root) { klass.create }
7
- let!(:child_1) { klass.create(:parent => root) }
8
- let!(:child_2) { klass.create(:parent => child_1) }
9
- let!(:child_3) { klass.create(:parent => child_1) }
10
- let!(:child_4) { klass.create(:parent => root) }
11
- let!(:child_5) { klass.create(:parent => child_4) }
7
+ let!(:child_1) { klass.create(parent: root) }
8
+ let!(:child_2) { klass.create(parent: child_1) }
9
+ let!(:child_3) { klass.create(parent: child_1) }
10
+ let!(:child_4) { klass.create(parent: root) }
11
+ let!(:child_5) { klass.create(parent: child_4) }
12
12
 
13
13
  describe '#join_recursive' do
14
14
  describe 'CONNECT BY clause' do
@@ -20,7 +20,7 @@ describe ActiveRecord::HierarchicalQuery do
20
20
 
21
21
  it 'joins parent and child rows by hash map' do
22
22
  expect(
23
- klass.join_recursive { connect_by(:id => :parent_id) }
23
+ klass.join_recursive { connect_by(id: :parent_id) }
24
24
  ).to include root, child_1, child_2, child_3, child_4, child_5
25
25
  end
26
26
 
@@ -37,25 +37,25 @@ describe ActiveRecord::HierarchicalQuery do
37
37
  def assert_start_with(&block)
38
38
  expect(
39
39
  klass.join_recursive do |b|
40
- b.connect_by(:id => :parent_id).instance_eval(&block)
40
+ b.connect_by(id: :parent_id).instance_eval(&block)
41
41
  end
42
42
  ).to match_array [root, child_1, child_2, child_3, child_4, child_5]
43
43
  end
44
44
 
45
45
  it 'filters rows in non-recursive term by hash' do
46
- assert_start_with { start_with(:parent_id => nil) }
46
+ assert_start_with { start_with(parent_id: nil) }
47
47
  end
48
48
 
49
49
  it 'filters rows in non-recursive term by block with arity > 0' do
50
- assert_start_with { start_with { |root| root.where(:parent_id => nil) } }
50
+ assert_start_with { start_with { |root| root.where(parent_id: nil) } }
51
51
  end
52
52
 
53
53
  it 'filters rows in non-recursive term by block with arity = 0' do
54
- assert_start_with { start_with { where(:parent_id => nil) } }
54
+ assert_start_with { start_with { where(parent_id: nil) } }
55
55
  end
56
56
 
57
57
  it 'filters rows in non-recursive term by scope' do
58
- assert_start_with { start_with(klass.where(:parent_id => nil)) }
58
+ assert_start_with { start_with(klass.where(parent_id: nil)) }
59
59
  end
60
60
  end
61
61
 
@@ -63,7 +63,7 @@ describe ActiveRecord::HierarchicalQuery do
63
63
  def assert_ordered_by_name_desc(&block)
64
64
  expect(
65
65
  klass.join_recursive do |b|
66
- b.connect_by(:id => :parent_id).start_with(:parent_id => nil).instance_eval(&block)
66
+ b.connect_by(id: :parent_id).start_with(parent_id: nil).instance_eval(&block)
67
67
  end
68
68
  ).to eq [root, child_4, child_5, child_1, child_3, child_2]
69
69
  end
@@ -71,13 +71,13 @@ describe ActiveRecord::HierarchicalQuery do
71
71
  def assert_ordered_by_name_asc(&block)
72
72
  expect(
73
73
  klass.join_recursive do |b|
74
- b.connect_by(:id => :parent_id).start_with(:parent_id => nil).instance_eval(&block)
74
+ b.connect_by(id: :parent_id).start_with(parent_id: nil).instance_eval(&block)
75
75
  end
76
76
  ).to eq [root, child_1, child_2, child_3, child_4, child_5]
77
77
  end
78
78
 
79
79
  it 'orders rows by Hash' do
80
- assert_ordered_by_name_desc { order_siblings(:name => :desc) }
80
+ assert_ordered_by_name_desc { order_siblings(name: :desc) }
81
81
  end
82
82
 
83
83
  it 'orders rows by String' do
@@ -95,15 +95,15 @@ describe ActiveRecord::HierarchicalQuery do
95
95
 
96
96
  it 'throws error when something weird given' do
97
97
  expect {
98
- klass.join_recursive { connect_by(:id => :parent_id).order_siblings(1) }
98
+ klass.join_recursive { connect_by(id: :parent_id).order_siblings(1) }
99
99
  }.to raise_error /ORDER BY SIBLINGS/
100
100
  end
101
101
 
102
102
  context 'when one attribute given and this attribute support natural sorting' do
103
103
  let(:relation) do
104
104
  klass.join_recursive do
105
- connect_by(:id => :parent_id).
106
- start_with(:parent_id => nil).
105
+ connect_by(id: :parent_id).
106
+ start_with(parent_id: nil).
107
107
  order_siblings(:position)
108
108
  end
109
109
  end
@@ -121,12 +121,23 @@ describe ActiveRecord::HierarchicalQuery do
121
121
  it 'limits all rows' do
122
122
  expect(
123
123
  klass.join_recursive do
124
- connect_by(:id => :parent_id).
125
- start_with(:parent_id => nil).
126
- order_siblings(:name).
124
+ connect_by(id: :parent_id).
125
+ start_with(parent_id: nil).
127
126
  limit(2).
128
127
  offset(2)
129
- end
128
+ end.size
129
+ ).to eq 2
130
+ end
131
+
132
+ it 'limits all rows if ordering given' do
133
+ expect(
134
+ klass.join_recursive do
135
+ connect_by(id: :parent_id).
136
+ start_with(parent_id: nil).
137
+ order_siblings(:name).
138
+ limit(2).
139
+ offset(2)
140
+ end
130
141
  ).to eq ordered_nodes[2...4]
131
142
  end
132
143
  end
@@ -135,8 +146,8 @@ describe ActiveRecord::HierarchicalQuery do
135
146
  it 'filters child rows' do
136
147
  expect(
137
148
  klass.join_recursive do
138
- connect_by(:id => :parent_id).
139
- start_with(:parent_id => nil).
149
+ connect_by(id: :parent_id).
150
+ start_with(parent_id: nil).
140
151
  where('depth < ?', 2)
141
152
  end
142
153
  ).to match_array [root, child_1, child_4]
@@ -145,8 +156,8 @@ describe ActiveRecord::HierarchicalQuery do
145
156
  it 'allows to use PRIOR relation' do
146
157
  expect(
147
158
  klass.join_recursive do |b|
148
- b.connect_by(:id => :parent_id)
149
- .start_with(:parent_id => nil)
159
+ b.connect_by(id: :parent_id)
160
+ .start_with(parent_id: nil)
150
161
  .select(:depth)
151
162
  .where(b.prior[:depth].lt(1))
152
163
  end
@@ -157,9 +168,9 @@ describe ActiveRecord::HierarchicalQuery do
157
168
  describe 'SELECT clause' do
158
169
  it 'adds column to both recursive and non-recursive term' do
159
170
  expect(
160
- klass.join_recursive(:as => 'categories_r') do
161
- connect_by(:id => :parent_id).
162
- start_with(:parent_id => nil).
171
+ klass.join_recursive(as: 'categories_r') do
172
+ connect_by(id: :parent_id).
173
+ start_with(parent_id: nil).
163
174
  select(:depth)
164
175
  end.where('categories_r.depth = 0')
165
176
  ).to eq [root]
@@ -167,25 +178,38 @@ describe ActiveRecord::HierarchicalQuery do
167
178
  end
168
179
 
169
180
  describe 'NOCYCLE clause' do
170
- before { klass.where(:id => child_4.id).update_all(:parent_id => child_5.id) }
181
+ before { klass.where(id: child_4.id).update_all(parent_id: child_5.id) }
171
182
 
172
183
  it 'prevents recursive query from endless loops' do
173
184
  expect(
174
185
  klass.join_recursive do |query|
175
- query.start_with(:id => child_4.id)
176
- .connect_by(:id => :parent_id)
186
+ query.start_with(id: child_4.id)
187
+ .connect_by(id: :parent_id)
177
188
  .nocycle
178
189
  end
179
190
  ).to match_array [child_4, child_5]
180
191
  end
181
192
  end
193
+
194
+ describe 'binding values' do
195
+ it 'binds values' do
196
+ expect(
197
+ klass.join_recursive do |query|
198
+ query.start_with('id = $1')
199
+ .connect_by(id: :parent_id)
200
+ .where(nil)
201
+ .bind([nil, child_4.id])
202
+ end
203
+ ).to match_array([child_4, child_5])
204
+ end
205
+ end
182
206
  end
183
207
 
184
208
  describe '#join_recursive options' do
185
209
  describe ':as option' do
186
210
  it 'builds a join with specified alias' do
187
211
  expect(
188
- klass.join_recursive(:as => 'my_table') { connect_by(:id => :parent_id) }.to_sql
212
+ klass.join_recursive(as: 'my_table') { connect_by(id: :parent_id) }.to_sql
189
213
  ).to match /my_table/
190
214
  end
191
215
  end
data/spec/database.yml CHANGED
@@ -2,12 +2,4 @@ pg:
2
2
  adapter: postgresql
3
3
  database: hierarchical_query_test
4
4
  username: vagrant
5
- min_messages: ERROR
6
-
7
- oracle:
8
- adapter: oracle_enhanced
9
- host: localhost
10
- port: 1521
11
- database: xe
12
- username: system
13
- password: manager
5
+ min_messages: ERROR
data/spec/schema.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  ActiveRecord::Migration.verbose = false
2
2
 
3
- ActiveRecord::Schema.define(:version => 0) do
4
- create_table :categories, :force => true do |t|
3
+ ActiveRecord::Schema.define(version: 0) do
4
+ create_table :categories, force: true do |t|
5
5
  t.column :parent_id, :integer
6
6
  t.column :name, :string
7
7
  t.column :depth, :integer
data/spec/spec_helper.rb CHANGED
@@ -3,19 +3,18 @@ require 'pathname'
3
3
  require 'logger'
4
4
 
5
5
  ENV['TZ'] = 'UTC'
6
- ENV['DB'] ||= 'pg'
7
6
 
8
7
  SPEC_ROOT = Pathname.new(File.dirname(__FILE__))
9
8
 
10
9
  require 'bundler'
11
- Bundler.setup(:default, ENV['TRAVIS'] ? :travis : :local, ENV['DB'].to_sym)
10
+ Bundler.setup(:default, ENV['TRAVIS'] ? :travis : :local)
12
11
 
13
12
  require 'rspec'
14
13
  require 'database_cleaner'
15
14
  require 'active_record'
16
15
 
17
16
  ActiveRecord::Base.configurations = YAML.load(SPEC_ROOT.join('database.yml').read)
18
- ActiveRecord::Base.establish_connection(ENV['DB'].to_sym)
17
+ ActiveRecord::Base.establish_connection(:pg)
19
18
  ActiveRecord::Base.logger = Logger.new(ENV['DEBUG'] ? $stderr : '/dev/null')
20
19
  ActiveRecord::Base.logger.formatter = proc do |severity, datetime, progname, msg|
21
20
  "#{datetime.strftime('%H:%M:%S.%L')}: #{msg}\n"
@@ -28,7 +27,6 @@ DatabaseCleaner.strategy = :transaction
28
27
 
29
28
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
30
29
  RSpec.configure do |config|
31
- config.treat_symbols_as_metadata_keys_with_true_values = true
32
30
  config.run_all_when_everything_filtered = true
33
31
  config.filter_run :focus
34
32
 
@@ -14,10 +14,10 @@ class Category < ActiveRecord::Base
14
14
  end
15
15
  end
16
16
 
17
- belongs_to :parent, :class_name => 'Category'
18
- has_many :children, :class_name => 'Category'
17
+ belongs_to :parent, class_name: 'Category'
18
+ has_many :children, class_name: 'Category'
19
19
 
20
- before_save :generate_name, :unless => :name?
20
+ before_save :generate_name, unless: :name?
21
21
  before_save :count_depth
22
22
  before_save :count_position
23
23
 
@@ -30,7 +30,7 @@ class Category < ActiveRecord::Base
30
30
  end
31
31
 
32
32
  def count_position
33
- self.position = (self.class.where(:parent_id => parent_id).maximum(:position) || 0) + 1
33
+ self.position = (self.class.where(parent_id: parent_id).maximum(:position) || 0) + 1
34
34
  end
35
35
 
36
36
  def ancestors
metadata CHANGED
@@ -1,99 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-hierarchical_query
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexei Mikhailov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-04 00:00:00.000000000 Z
11
+ date: 2014-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: 3.1.0
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
26
  version: 3.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.5'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.5'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: 10.1.1
47
+ version: 10.4.2
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 10.1.1
54
+ version: 10.4.2
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 2.14.1
61
+ version: 3.1.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: 2.14.1
68
+ version: 3.1.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: database_cleaner
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: 1.2.0
75
+ version: 1.3.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ~>
81
81
  - !ruby/object:Gem::Version
82
- version: 1.2.0
82
+ version: 1.3.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ~>
88
88
  - !ruby/object:Gem::Version
89
- version: 0.8.2
89
+ version: 0.9.1
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ~>
95
95
  - !ruby/object:Gem::Version
96
- version: 0.8.2
96
+ version: 0.9.1
97
97
  description:
98
98
  email:
99
99
  - amikhailov83@gmail.com
@@ -104,21 +104,16 @@ files:
104
104
  - LICENSE.txt
105
105
  - README.md
106
106
  - lib/active_record/hierarchical_query.rb
107
- - lib/active_record/hierarchical_query/adapters.rb
108
- - lib/active_record/hierarchical_query/adapters/abstract.rb
109
- - lib/active_record/hierarchical_query/adapters/postgresql.rb
110
- - lib/active_record/hierarchical_query/builder.rb
111
107
  - lib/active_record/hierarchical_query/cte/columns.rb
108
+ - lib/active_record/hierarchical_query/cte/cycle_detector.rb
112
109
  - lib/active_record/hierarchical_query/cte/non_recursive_term.rb
113
- - lib/active_record/hierarchical_query/cte/query.rb
110
+ - lib/active_record/hierarchical_query/cte/query_builder.rb
114
111
  - lib/active_record/hierarchical_query/cte/recursive_term.rb
115
112
  - lib/active_record/hierarchical_query/cte/union_term.rb
116
113
  - lib/active_record/hierarchical_query/join_builder.rb
114
+ - lib/active_record/hierarchical_query/orderings.rb
115
+ - lib/active_record/hierarchical_query/query.rb
117
116
  - lib/active_record/hierarchical_query/version.rb
118
- - lib/active_record/hierarchical_query/visitors/orderings.rb
119
- - lib/active_record/hierarchical_query/visitors/postgresql/cycle_detector.rb
120
- - lib/active_record/hierarchical_query/visitors/postgresql/orderings.rb
121
- - lib/active_record/hierarchical_query/visitors/visitor.rb
122
117
  - lib/arel/nodes/postgresql.rb
123
118
  - spec/active_record/hierarchical_query_spec.rb
124
119
  - spec/database.travis.yml
@@ -136,12 +131,12 @@ require_paths:
136
131
  - lib
137
132
  required_ruby_version: !ruby/object:Gem::Requirement
138
133
  requirements:
139
- - - ">="
134
+ - - '>='
140
135
  - !ruby/object:Gem::Version
141
136
  version: '0'
142
137
  required_rubygems_version: !ruby/object:Gem::Requirement
143
138
  requirements:
144
- - - ">="
139
+ - - '>='
145
140
  - !ruby/object:Gem::Version
146
141
  version: '0'
147
142
  requirements: []