activerecord-hierarchical_query 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +266 -0
- data/lib/active_record/hierarchical_query.rb +53 -0
- data/lib/active_record/hierarchical_query/adapters.rb +20 -0
- data/lib/active_record/hierarchical_query/adapters/postgresql.rb +29 -0
- data/lib/active_record/hierarchical_query/builder.rb +289 -0
- data/lib/active_record/hierarchical_query/cte/columns.rb +39 -0
- data/lib/active_record/hierarchical_query/cte/cycle_detector.rb +63 -0
- data/lib/active_record/hierarchical_query/cte/join_builder.rb +55 -0
- data/lib/active_record/hierarchical_query/cte/non_recursive_term.rb +44 -0
- data/lib/active_record/hierarchical_query/cte/orderings.rb +108 -0
- data/lib/active_record/hierarchical_query/cte/query.rb +94 -0
- data/lib/active_record/hierarchical_query/cte/recursive_term.rb +47 -0
- data/lib/active_record/hierarchical_query/cte/union_term.rb +28 -0
- data/lib/active_record/hierarchical_query/version.rb +5 -0
- data/lib/arel/nodes/postgresql.rb +28 -0
- data/spec/active_record/hierarchical_query_spec.rb +193 -0
- data/spec/database.travis.yml +4 -0
- data/spec/database.yml +4 -0
- data/spec/schema.rb +10 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/support/models.rb +39 -0
- metadata +171 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_record/hierarchical_query/cte/non_recursive_term'
|
2
|
+
require 'active_record/hierarchical_query/cte/recursive_term'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module HierarchicalQuery
|
6
|
+
module CTE
|
7
|
+
class UnionTerm
|
8
|
+
# @param [ActiveRecord::HierarchicalQuery::CTE::Query] query
|
9
|
+
def initialize(query)
|
10
|
+
@query = query
|
11
|
+
end
|
12
|
+
|
13
|
+
def arel
|
14
|
+
non_recursive_term.union(:all, recursive_term)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def recursive_term
|
19
|
+
RecursiveTerm.new(@query).arel
|
20
|
+
end
|
21
|
+
|
22
|
+
def non_recursive_term
|
23
|
+
NonRecursiveTerm.new(@query).arel
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Arel
|
2
|
+
module Nodes
|
3
|
+
class PostgresArray < Node
|
4
|
+
include AliasPredication
|
5
|
+
attr_accessor :values
|
6
|
+
|
7
|
+
def initialize(values)
|
8
|
+
self.values = values
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ArrayConcat < Binary
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Visitors
|
17
|
+
class ToSql < Arel::Visitors::Visitor
|
18
|
+
private
|
19
|
+
def visit_Arel_Nodes_PostgresArray o, *a
|
20
|
+
"ARRAY[#{visit o.values, *a}]"
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit_Arel_Nodes_ArrayConcat o, *a
|
24
|
+
"#{visit o.left, *a} || #{visit o.right, *a}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::HierarchicalQuery do
|
4
|
+
let(:klass) { Category }
|
5
|
+
|
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) }
|
12
|
+
|
13
|
+
describe '#join_recursive' do
|
14
|
+
describe 'CONNECT BY clause' do
|
15
|
+
it 'throws error if CONNECT BY clause not specified' do
|
16
|
+
expect {
|
17
|
+
klass.join_recursive {}
|
18
|
+
}.to raise_error /CONNECT BY clause/
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'joins parent and child rows by hash map' do
|
22
|
+
expect(
|
23
|
+
klass.join_recursive { connect_by(:id => :parent_id) }
|
24
|
+
).to include root, child_1, child_2, child_3, child_4, child_5
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'joins parent and child rows by block' do
|
28
|
+
expect(
|
29
|
+
klass.join_recursive do
|
30
|
+
connect_by { |parent, child| parent[:id].eq child[:parent_id] }
|
31
|
+
end
|
32
|
+
).to include root, child_1, child_2, child_3, child_4, child_5
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'START WITH clause' do
|
37
|
+
def assert_start_with(&block)
|
38
|
+
expect(
|
39
|
+
klass.join_recursive do |b|
|
40
|
+
b.connect_by(:id => :parent_id).instance_eval(&block)
|
41
|
+
end
|
42
|
+
).to match_array [root, child_1, child_2, child_3, child_4, child_5]
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'filters rows in non-recursive term by hash' do
|
46
|
+
assert_start_with { start_with(:parent_id => nil) }
|
47
|
+
end
|
48
|
+
|
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) } }
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'filters rows in non-recursive term by block with arity = 0' do
|
54
|
+
assert_start_with { start_with { where(:parent_id => nil) } }
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'filters rows in non-recursive term by scope' do
|
58
|
+
assert_start_with { start_with(klass.where(:parent_id => nil)) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'ORDER SIBLINGS BY clause' do
|
63
|
+
def assert_ordered_by_name_desc(&block)
|
64
|
+
expect(
|
65
|
+
klass.join_recursive do |b|
|
66
|
+
b.connect_by(:id => :parent_id).start_with(:parent_id => nil).instance_eval(&block)
|
67
|
+
end
|
68
|
+
).to eq [root, child_4, child_5, child_1, child_3, child_2]
|
69
|
+
end
|
70
|
+
|
71
|
+
def assert_ordered_by_name_asc(&block)
|
72
|
+
expect(
|
73
|
+
klass.join_recursive do |b|
|
74
|
+
b.connect_by(:id => :parent_id).start_with(:parent_id => nil).instance_eval(&block)
|
75
|
+
end
|
76
|
+
).to eq [root, child_1, child_2, child_3, child_4, child_5]
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'orders rows by Hash' do
|
80
|
+
assert_ordered_by_name_desc { order_siblings(:name => :desc) }
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'orders rows by String' do
|
84
|
+
assert_ordered_by_name_desc { order_siblings('name desc') }
|
85
|
+
assert_ordered_by_name_asc { order_siblings('name asc') }
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'orders rows by Arel::Nodes::Ordering' do
|
89
|
+
assert_ordered_by_name_desc { order_siblings(table[:name].desc) }
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'orders rows by Arel::Nodes::Node' do
|
93
|
+
assert_ordered_by_name_asc { order_siblings(table[:name]) }
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'throws error when something weird given' do
|
97
|
+
expect {
|
98
|
+
klass.join_recursive { connect_by(:id => :parent_id).order_siblings(1) }
|
99
|
+
}.to raise_error /ORDER BY SIBLINGS/
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when one attribute given and this attribute support natural sorting' do
|
103
|
+
let(:relation) do
|
104
|
+
klass.join_recursive do
|
105
|
+
connect_by(:id => :parent_id).
|
106
|
+
start_with(:parent_id => nil).
|
107
|
+
order_siblings(:position)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'orders rows by given attribute' do
|
112
|
+
expect(relation).to eq [root, child_1, child_2, child_3, child_4, child_5]
|
113
|
+
expect(relation.to_sql).not_to match /row_number/i
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'LIMIT and OFFSET clauses' do
|
119
|
+
let(:ordered_nodes) { [root, child_1, child_2, child_3, child_4, child_5] }
|
120
|
+
|
121
|
+
it 'limits all rows' do
|
122
|
+
expect(
|
123
|
+
klass.join_recursive do
|
124
|
+
connect_by(:id => :parent_id).
|
125
|
+
start_with(:parent_id => nil).
|
126
|
+
order_siblings(:name).
|
127
|
+
limit(2).
|
128
|
+
offset(2)
|
129
|
+
end
|
130
|
+
).to eq ordered_nodes[2...4]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'WHERE clause' do
|
135
|
+
it 'filters child rows' do
|
136
|
+
expect(
|
137
|
+
klass.join_recursive do
|
138
|
+
connect_by(:id => :parent_id).
|
139
|
+
start_with(:parent_id => nil).
|
140
|
+
where('depth < ?', 2)
|
141
|
+
end
|
142
|
+
).to match_array [root, child_1, child_4]
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'allows to use PRIOR relation' do
|
146
|
+
expect(
|
147
|
+
klass.join_recursive do |b|
|
148
|
+
b.connect_by(:id => :parent_id)
|
149
|
+
.start_with(:parent_id => nil)
|
150
|
+
.select(:depth)
|
151
|
+
.where(b.prior[:depth].lt(1))
|
152
|
+
end
|
153
|
+
).to match_array [root, child_1, child_4]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'SELECT clause' do
|
158
|
+
it 'adds column to both recursive and non-recursive term' do
|
159
|
+
expect(
|
160
|
+
klass.join_recursive(:as => 'categories_r') do
|
161
|
+
connect_by(:id => :parent_id).
|
162
|
+
start_with(:parent_id => nil).
|
163
|
+
select(:depth)
|
164
|
+
end.where('categories_r.depth = 0')
|
165
|
+
).to eq [root]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe 'NOCYCLE clause' do
|
170
|
+
before { klass.where(:id => child_4.id).update_all(:parent_id => child_5.id) }
|
171
|
+
|
172
|
+
it 'prevents recursive query from endless loops' do
|
173
|
+
expect(
|
174
|
+
klass.join_recursive do |query|
|
175
|
+
query.start_with(:id => child_4.id)
|
176
|
+
.connect_by(:id => :parent_id)
|
177
|
+
.nocycle
|
178
|
+
end
|
179
|
+
).to match_array [child_4, child_5]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#join_recursive options' do
|
185
|
+
describe ':as option' do
|
186
|
+
it 'builds a join with specified alias' do
|
187
|
+
expect(
|
188
|
+
klass.join_recursive(:as => 'my_table') { connect_by(:id => :parent_id) }.to_sql
|
189
|
+
).to match /my_table/
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
data/spec/database.yml
ADDED
data/spec/schema.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
ActiveRecord::Migration.verbose = false
|
2
|
+
|
3
|
+
ActiveRecord::Schema.define(:version => 0) do
|
4
|
+
create_table :categories, :force => true do |t|
|
5
|
+
t.column :parent_id, :integer
|
6
|
+
t.column :name, :string
|
7
|
+
t.column :depth, :integer
|
8
|
+
t.column :position, :integer
|
9
|
+
end
|
10
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'pathname'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
SPEC_ROOT = Pathname.new(File.dirname(__FILE__))
|
6
|
+
|
7
|
+
require 'bundler/setup'
|
8
|
+
|
9
|
+
Bundler.setup(:default, ENV['TRAVIS'] ? :travis : :local)
|
10
|
+
|
11
|
+
require 'rspec'
|
12
|
+
require 'database_cleaner'
|
13
|
+
require 'active_record'
|
14
|
+
|
15
|
+
ActiveRecord::Base.establish_connection(YAML.load(SPEC_ROOT.join('database.yml').read))
|
16
|
+
ActiveRecord::Base.logger = Logger.new(ENV['DEBUG'] ? $stderr : '/dev/null')
|
17
|
+
ActiveRecord::Base.logger.formatter = proc do |severity, datetime, progname, msg|
|
18
|
+
"#{datetime.strftime('%H:%M:%S.%L')}: #{msg}\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
load SPEC_ROOT.join('schema.rb')
|
22
|
+
require SPEC_ROOT.join('support', 'models').to_s
|
23
|
+
|
24
|
+
DatabaseCleaner.strategy = :transaction
|
25
|
+
|
26
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
29
|
+
config.run_all_when_everything_filtered = true
|
30
|
+
config.filter_run :focus
|
31
|
+
|
32
|
+
# Run specs in random order to surface order dependencies. If you find an
|
33
|
+
# order dependency and want to debug it, you can fix the order by providing
|
34
|
+
# the seed, which is printed after each run.
|
35
|
+
# --seed 1234
|
36
|
+
config.order = 'random'
|
37
|
+
|
38
|
+
config.before(:suite) do
|
39
|
+
DatabaseCleaner.strategy = :transaction
|
40
|
+
DatabaseCleaner.clean_with(:truncation)
|
41
|
+
end
|
42
|
+
|
43
|
+
config.around(:each) do |example|
|
44
|
+
DatabaseCleaner.start
|
45
|
+
example.run
|
46
|
+
DatabaseCleaner.clean
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if ENV['TRAVIS']
|
51
|
+
require 'coveralls'
|
52
|
+
Coveralls.wear!
|
53
|
+
else
|
54
|
+
require 'simplecov'
|
55
|
+
SimpleCov.start
|
56
|
+
end
|
57
|
+
|
58
|
+
require 'active_record/hierarchical_query'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Category < ActiveRecord::Base
|
2
|
+
@@generator = Enumerator.new do |y|
|
3
|
+
abc = ('a'..'z').to_a
|
4
|
+
sequence = abc.product(abc, abc).map(&:join).to_enum
|
5
|
+
|
6
|
+
loop do
|
7
|
+
# category aaa
|
8
|
+
# category aab
|
9
|
+
# ...
|
10
|
+
# category aaz
|
11
|
+
# category aba
|
12
|
+
# ...
|
13
|
+
y << "category #{sequence.next}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
belongs_to :parent, :class_name => 'Category'
|
18
|
+
has_many :children, :class_name => 'Category'
|
19
|
+
|
20
|
+
before_save :generate_name, :unless => :name?
|
21
|
+
before_save :count_depth
|
22
|
+
before_save :count_position
|
23
|
+
|
24
|
+
def generate_name
|
25
|
+
self.name = @@generator.next
|
26
|
+
end
|
27
|
+
|
28
|
+
def count_depth
|
29
|
+
self.depth = ancestors.count
|
30
|
+
end
|
31
|
+
|
32
|
+
def count_position
|
33
|
+
self.position = (self.class.where(:parent_id => parent_id).maximum(:position) || 0) + 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def ancestors
|
37
|
+
parent ? parent.ancestors + [parent] : []
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-hierarchical_query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexei Mikhailov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 10.1.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 10.1.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.14.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pg
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.17.1
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.17.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: database_cleaner
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.2.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.2.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.8.2
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.8.2
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- amikhailov83@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- lib/active_record/hierarchical_query.rb
|
119
|
+
- lib/active_record/hierarchical_query/adapters.rb
|
120
|
+
- lib/active_record/hierarchical_query/adapters/postgresql.rb
|
121
|
+
- lib/active_record/hierarchical_query/builder.rb
|
122
|
+
- lib/active_record/hierarchical_query/cte/columns.rb
|
123
|
+
- lib/active_record/hierarchical_query/cte/cycle_detector.rb
|
124
|
+
- lib/active_record/hierarchical_query/cte/join_builder.rb
|
125
|
+
- lib/active_record/hierarchical_query/cte/non_recursive_term.rb
|
126
|
+
- lib/active_record/hierarchical_query/cte/orderings.rb
|
127
|
+
- lib/active_record/hierarchical_query/cte/query.rb
|
128
|
+
- lib/active_record/hierarchical_query/cte/recursive_term.rb
|
129
|
+
- lib/active_record/hierarchical_query/cte/union_term.rb
|
130
|
+
- lib/active_record/hierarchical_query/version.rb
|
131
|
+
- lib/arel/nodes/postgresql.rb
|
132
|
+
- spec/active_record/hierarchical_query_spec.rb
|
133
|
+
- spec/database.travis.yml
|
134
|
+
- spec/database.yml
|
135
|
+
- spec/schema.rb
|
136
|
+
- spec/spec_helper.rb
|
137
|
+
- spec/support/models.rb
|
138
|
+
- README.md
|
139
|
+
- LICENSE.txt
|
140
|
+
homepage: https://github.com/take-five/activerecord-hierarchical_query
|
141
|
+
licenses:
|
142
|
+
- MIT
|
143
|
+
metadata: {}
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 2.1.10
|
161
|
+
signing_key:
|
162
|
+
specification_version: 4
|
163
|
+
summary: Recursively traverse trees using a single SQL query
|
164
|
+
test_files:
|
165
|
+
- spec/active_record/hierarchical_query_spec.rb
|
166
|
+
- spec/database.travis.yml
|
167
|
+
- spec/database.yml
|
168
|
+
- spec/schema.rb
|
169
|
+
- spec/spec_helper.rb
|
170
|
+
- spec/support/models.rb
|
171
|
+
has_rdoc:
|