acts_as_recursive_tree 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +321 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +175 -0
- data/Rakefile +15 -0
- data/acts_as_recursive_tree.gemspec +30 -0
- data/lib/acts_as_recursive_tree/acts_macro.rb +28 -0
- data/lib/acts_as_recursive_tree/associations.rb +25 -0
- data/lib/acts_as_recursive_tree/builders/ancestors.rb +17 -0
- data/lib/acts_as_recursive_tree/builders/descendants.rb +9 -0
- data/lib/acts_as_recursive_tree/builders/leaves.rb +26 -0
- data/lib/acts_as_recursive_tree/builders/relation_builder.rb +121 -0
- data/lib/acts_as_recursive_tree/builders.rb +13 -0
- data/lib/acts_as_recursive_tree/model.rb +125 -0
- data/lib/acts_as_recursive_tree/options/depth_condition.rb +48 -0
- data/lib/acts_as_recursive_tree/options/query_options.rb +21 -0
- data/lib/acts_as_recursive_tree/options/values.rb +77 -0
- data/lib/acts_as_recursive_tree/options.rb +9 -0
- data/lib/acts_as_recursive_tree/railtie.rb +9 -0
- data/lib/acts_as_recursive_tree/scopes.rb +16 -0
- data/lib/acts_as_recursive_tree/version.rb +3 -0
- data/lib/acts_as_recursive_tree.rb +14 -0
- data/spec/builders_spec.rb +136 -0
- data/spec/db/database.rb +22 -0
- data/spec/db/database.yml +12 -0
- data/spec/db/models.rb +37 -0
- data/spec/db/schema.rb +34 -0
- data/spec/model/location_spec.rb +55 -0
- data/spec/model/node_spec.rb +129 -0
- data/spec/model/relation_spec.rb +63 -0
- data/spec/spec_helper.rb +119 -0
- data/spec/values_spec.rb +86 -0
- metadata +182 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 78b84c59d5275ca8152223f64df66c813a6b9b03
|
4
|
+
data.tar.gz: 7aa3a0fb72cc5ed2c5bd8f93d312b972273e06dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f84ae5d103dc3df1ea1184db885771a082cd9d4a7b83af99c10fef9ef68d51f134d7e5150ed930fbc3abf7fcf98ee5ee567ccb528537dafb3e46b6df4ed002c0
|
7
|
+
data.tar.gz: fcb95ae5256b96813a63658897462e99b596512ab8cae861e4236de41ce746bc22261cf2649a61ee4535ef92a876e337f600558a3aecf87c392373a028f66b2e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,321 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2018-01-22 09:50:37 +0100 using RuboCop version 0.51.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 4
|
10
|
+
# Cop supports --auto-correct.
|
11
|
+
# Configuration parameters: SupportedStyles, IndentOneStep, IndentationWidth.
|
12
|
+
# SupportedStyles: case, end
|
13
|
+
Layout/CaseIndentation:
|
14
|
+
EnforcedStyle: end
|
15
|
+
|
16
|
+
# Offense count: 1
|
17
|
+
# Cop supports --auto-correct.
|
18
|
+
Layout/CommentIndentation:
|
19
|
+
Exclude:
|
20
|
+
- 'spec/spec_helper.rb'
|
21
|
+
|
22
|
+
# Offense count: 1
|
23
|
+
# Cop supports --auto-correct.
|
24
|
+
Layout/EmptyLineAfterMagicComment:
|
25
|
+
Exclude:
|
26
|
+
- 'acts_as_recursive_tree.gemspec'
|
27
|
+
|
28
|
+
# Offense count: 1
|
29
|
+
# Cop supports --auto-correct.
|
30
|
+
# Configuration parameters: AllowAdjacentOneLineDefs, NumberOfEmptyLines.
|
31
|
+
Layout/EmptyLineBetweenDefs:
|
32
|
+
Exclude:
|
33
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
34
|
+
|
35
|
+
# Offense count: 5
|
36
|
+
# Cop supports --auto-correct.
|
37
|
+
Layout/EmptyLines:
|
38
|
+
Exclude:
|
39
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
40
|
+
- 'spec/db/models.rb'
|
41
|
+
- 'spec/model/node_spec.rb'
|
42
|
+
- 'spec/spec_helper.rb'
|
43
|
+
|
44
|
+
# Offense count: 25
|
45
|
+
# Cop supports --auto-correct.
|
46
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
47
|
+
# SupportedStyles: empty_lines, no_empty_lines
|
48
|
+
Layout/EmptyLinesAroundBlockBody:
|
49
|
+
Exclude:
|
50
|
+
- 'lib/acts_as_recursive_tree/scopes.rb'
|
51
|
+
- 'spec/db/schema.rb'
|
52
|
+
- 'spec/model/location_spec.rb'
|
53
|
+
- 'spec/model/node_spec.rb'
|
54
|
+
- 'spec/model/relation_spec.rb'
|
55
|
+
- 'spec/values_spec.rb'
|
56
|
+
|
57
|
+
# Offense count: 11
|
58
|
+
# Cop supports --auto-correct.
|
59
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
60
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
|
61
|
+
Layout/EmptyLinesAroundClassBody:
|
62
|
+
Exclude:
|
63
|
+
- 'lib/acts_as_recursive_tree/builders/ancestors.rb'
|
64
|
+
- 'lib/acts_as_recursive_tree/builders/leaves.rb'
|
65
|
+
- 'lib/acts_as_recursive_tree/builders/relation_builder.rb'
|
66
|
+
- 'lib/acts_as_recursive_tree/options/depth_condition.rb'
|
67
|
+
- 'lib/acts_as_recursive_tree/options/query_options.rb'
|
68
|
+
- 'spec/db/models.rb'
|
69
|
+
|
70
|
+
# Offense count: 6
|
71
|
+
# Cop supports --auto-correct.
|
72
|
+
Layout/EmptyLinesAroundMethodBody:
|
73
|
+
Exclude:
|
74
|
+
- 'lib/acts_as_recursive_tree/acts_macro.rb'
|
75
|
+
- 'lib/acts_as_recursive_tree/builders/leaves.rb'
|
76
|
+
- 'lib/acts_as_recursive_tree/options/values.rb'
|
77
|
+
- 'spec/model/node_spec.rb'
|
78
|
+
- 'spec/model/relation_spec.rb'
|
79
|
+
|
80
|
+
# Offense count: 2
|
81
|
+
# Cop supports --auto-correct.
|
82
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
83
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
|
84
|
+
Layout/EmptyLinesAroundModuleBody:
|
85
|
+
Exclude:
|
86
|
+
- 'lib/acts_as_recursive_tree/acts_macro.rb'
|
87
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
88
|
+
|
89
|
+
# Offense count: 6
|
90
|
+
# Cop supports --auto-correct.
|
91
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
92
|
+
# SupportedStyles: consistent, special_for_inner_method_call, special_for_inner_method_call_in_parentheses
|
93
|
+
Layout/FirstParameterIndentation:
|
94
|
+
Exclude:
|
95
|
+
- 'lib/acts_as_recursive_tree/builders/leaves.rb'
|
96
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
97
|
+
- 'spec/model/relation_spec.rb'
|
98
|
+
|
99
|
+
# Offense count: 1
|
100
|
+
# Cop supports --auto-correct.
|
101
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, SupportedStylesForEmptyBraces.
|
102
|
+
# SupportedStyles: space, no_space
|
103
|
+
# SupportedStylesForEmptyBraces: space, no_space
|
104
|
+
Layout/SpaceBeforeBlockBraces:
|
105
|
+
Exclude:
|
106
|
+
- 'spec/values_spec.rb'
|
107
|
+
|
108
|
+
# Offense count: 2
|
109
|
+
# Cop supports --auto-correct.
|
110
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
111
|
+
# SupportedStyles: require_no_space, require_space
|
112
|
+
Layout/SpaceInLambdaLiteral:
|
113
|
+
Exclude:
|
114
|
+
- 'lib/acts_as_recursive_tree/scopes.rb'
|
115
|
+
- 'spec/builders_spec.rb'
|
116
|
+
|
117
|
+
# Offense count: 2
|
118
|
+
# Cop supports --auto-correct.
|
119
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
|
120
|
+
# SupportedStyles: space, no_space
|
121
|
+
# SupportedStylesForEmptyBraces: space, no_space
|
122
|
+
Layout/SpaceInsideBlockBraces:
|
123
|
+
Exclude:
|
124
|
+
- 'spec/values_spec.rb'
|
125
|
+
|
126
|
+
# Offense count: 2
|
127
|
+
# Cop supports --auto-correct.
|
128
|
+
Layout/SpaceInsidePercentLiteralDelimiters:
|
129
|
+
Exclude:
|
130
|
+
- 'Rakefile'
|
131
|
+
|
132
|
+
# Offense count: 10
|
133
|
+
# Cop supports --auto-correct.
|
134
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
135
|
+
# SupportedStyles: final_newline, final_blank_line
|
136
|
+
Layout/TrailingBlankLines:
|
137
|
+
Exclude:
|
138
|
+
- 'lib/acts_as_recursive_tree/acts_macro.rb'
|
139
|
+
- 'lib/acts_as_recursive_tree/options/depth_condition.rb'
|
140
|
+
- 'lib/acts_as_recursive_tree/options/values.rb'
|
141
|
+
- 'spec/db/database.rb'
|
142
|
+
- 'spec/db/models.rb'
|
143
|
+
- 'spec/db/schema.rb'
|
144
|
+
- 'spec/model/location_spec.rb'
|
145
|
+
- 'spec/model/node_spec.rb'
|
146
|
+
- 'spec/model/relation_spec.rb'
|
147
|
+
- 'spec/values_spec.rb'
|
148
|
+
|
149
|
+
# Offense count: 14
|
150
|
+
Lint/AmbiguousRegexpLiteral:
|
151
|
+
Exclude:
|
152
|
+
- 'spec/builders_spec.rb'
|
153
|
+
- 'spec/values_spec.rb'
|
154
|
+
|
155
|
+
# Offense count: 1
|
156
|
+
# Cop supports --auto-correct.
|
157
|
+
Lint/DeprecatedClassMethods:
|
158
|
+
Exclude:
|
159
|
+
- 'Rakefile'
|
160
|
+
|
161
|
+
# Offense count: 1
|
162
|
+
# Cop supports --auto-correct.
|
163
|
+
# Configuration parameters: EnforcedStyleAlignWith, SupportedStylesAlignWith, AutoCorrect.
|
164
|
+
# SupportedStylesAlignWith: keyword, variable, start_of_line
|
165
|
+
Lint/EndAlignment:
|
166
|
+
Exclude:
|
167
|
+
- 'lib/acts_as_recursive_tree/options/values.rb'
|
168
|
+
|
169
|
+
# Offense count: 1
|
170
|
+
Lint/HandleExceptions:
|
171
|
+
Exclude:
|
172
|
+
- 'Rakefile'
|
173
|
+
|
174
|
+
# Offense count: 1
|
175
|
+
# Cop supports --auto-correct.
|
176
|
+
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
177
|
+
Lint/UnusedMethodArgument:
|
178
|
+
Exclude:
|
179
|
+
- 'lib/acts_as_recursive_tree/builders/leaves.rb'
|
180
|
+
|
181
|
+
# Offense count: 5
|
182
|
+
Metrics/AbcSize:
|
183
|
+
Max: 44
|
184
|
+
|
185
|
+
# Offense count: 8
|
186
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
187
|
+
Metrics/BlockLength:
|
188
|
+
Max: 89
|
189
|
+
|
190
|
+
# Offense count: 41
|
191
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
192
|
+
# URISchemes: http, https
|
193
|
+
Metrics/LineLength:
|
194
|
+
Max: 291
|
195
|
+
|
196
|
+
# Offense count: 2
|
197
|
+
# Configuration parameters: CountComments.
|
198
|
+
Metrics/MethodLength:
|
199
|
+
Max: 13
|
200
|
+
|
201
|
+
# Offense count: 1
|
202
|
+
# Cop supports --auto-correct.
|
203
|
+
Security/YAMLLoad:
|
204
|
+
Exclude:
|
205
|
+
- 'spec/db/database.rb'
|
206
|
+
|
207
|
+
# Offense count: 1
|
208
|
+
# Cop supports --auto-correct.
|
209
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
210
|
+
# SupportedStyles: prefer_alias, prefer_alias_method
|
211
|
+
Style/Alias:
|
212
|
+
Exclude:
|
213
|
+
- 'lib/acts_as_recursive_tree/acts_macro.rb'
|
214
|
+
|
215
|
+
# Offense count: 1
|
216
|
+
# Cop supports --auto-correct.
|
217
|
+
Style/BlockComments:
|
218
|
+
Exclude:
|
219
|
+
- 'spec/spec_helper.rb'
|
220
|
+
|
221
|
+
# Offense count: 1
|
222
|
+
# Cop supports --auto-correct.
|
223
|
+
Style/ColonMethodCall:
|
224
|
+
Exclude:
|
225
|
+
- 'spec/db/database.rb'
|
226
|
+
|
227
|
+
# Offense count: 20
|
228
|
+
Style/Documentation:
|
229
|
+
Exclude:
|
230
|
+
- 'spec/**/*'
|
231
|
+
- 'test/**/*'
|
232
|
+
- 'lib/acts_as_recursive_tree.rb'
|
233
|
+
- 'lib/acts_as_recursive_tree/acts_macro.rb'
|
234
|
+
- 'lib/acts_as_recursive_tree/associations.rb'
|
235
|
+
- 'lib/acts_as_recursive_tree/builders.rb'
|
236
|
+
- 'lib/acts_as_recursive_tree/builders/ancestors.rb'
|
237
|
+
- 'lib/acts_as_recursive_tree/builders/descendants.rb'
|
238
|
+
- 'lib/acts_as_recursive_tree/builders/leaves.rb'
|
239
|
+
- 'lib/acts_as_recursive_tree/builders/relation_builder.rb'
|
240
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
241
|
+
- 'lib/acts_as_recursive_tree/options.rb'
|
242
|
+
- 'lib/acts_as_recursive_tree/options/depth_condition.rb'
|
243
|
+
- 'lib/acts_as_recursive_tree/options/query_options.rb'
|
244
|
+
- 'lib/acts_as_recursive_tree/options/values.rb'
|
245
|
+
- 'lib/acts_as_recursive_tree/railtie.rb'
|
246
|
+
- 'lib/acts_as_recursive_tree/scopes.rb'
|
247
|
+
|
248
|
+
# Offense count: 2
|
249
|
+
# Cop supports --auto-correct.
|
250
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
251
|
+
# SupportedStyles: compact, expanded
|
252
|
+
Style/EmptyMethod:
|
253
|
+
Exclude:
|
254
|
+
- 'lib/acts_as_recursive_tree/options/values.rb'
|
255
|
+
|
256
|
+
# Offense count: 2
|
257
|
+
# Cop supports --auto-correct.
|
258
|
+
Style/Encoding:
|
259
|
+
Exclude:
|
260
|
+
- 'acts_as_recursive_tree.gemspec'
|
261
|
+
- 'spec/db/schema.rb'
|
262
|
+
|
263
|
+
# Offense count: 1
|
264
|
+
# Cop supports --auto-correct.
|
265
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
|
266
|
+
# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
|
267
|
+
Style/HashSyntax:
|
268
|
+
Exclude:
|
269
|
+
- 'spec/db/schema.rb'
|
270
|
+
|
271
|
+
# Offense count: 2
|
272
|
+
# Cop supports --auto-correct.
|
273
|
+
# Configuration parameters: MaxLineLength.
|
274
|
+
Style/IfUnlessModifier:
|
275
|
+
Exclude:
|
276
|
+
- 'spec/model/node_spec.rb'
|
277
|
+
- 'spec/model/relation_spec.rb'
|
278
|
+
|
279
|
+
# Offense count: 1
|
280
|
+
# Cop supports --auto-correct.
|
281
|
+
# Configuration parameters: InverseMethods, InverseBlocks.
|
282
|
+
Style/InverseMethods:
|
283
|
+
Exclude:
|
284
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
285
|
+
|
286
|
+
# Offense count: 6
|
287
|
+
# Cop supports --auto-correct.
|
288
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
289
|
+
# SupportedStyles: line_count_dependent, lambda, literal
|
290
|
+
Style/Lambda:
|
291
|
+
Exclude:
|
292
|
+
- 'lib/acts_as_recursive_tree/scopes.rb'
|
293
|
+
|
294
|
+
# Offense count: 1
|
295
|
+
# Cop supports --auto-correct.
|
296
|
+
Style/MultilineIfModifier:
|
297
|
+
Exclude:
|
298
|
+
- 'lib/acts_as_recursive_tree/scopes.rb'
|
299
|
+
|
300
|
+
# Offense count: 2
|
301
|
+
# Cop supports --auto-correct.
|
302
|
+
# Configuration parameters: PreferredDelimiters.
|
303
|
+
Style/PercentLiteralDelimiters:
|
304
|
+
Exclude:
|
305
|
+
- 'Rakefile'
|
306
|
+
- 'acts_as_recursive_tree.gemspec'
|
307
|
+
|
308
|
+
# Offense count: 17
|
309
|
+
# Cop supports --auto-correct.
|
310
|
+
Style/RedundantSelf:
|
311
|
+
Exclude:
|
312
|
+
- 'lib/acts_as_recursive_tree/acts_macro.rb'
|
313
|
+
- 'lib/acts_as_recursive_tree/associations.rb'
|
314
|
+
- 'lib/acts_as_recursive_tree/model.rb'
|
315
|
+
- 'lib/acts_as_recursive_tree/scopes.rb'
|
316
|
+
|
317
|
+
# Offense count: 2
|
318
|
+
# Cop supports --auto-correct.
|
319
|
+
Style/UnneededPercentQ:
|
320
|
+
Exclude:
|
321
|
+
- 'acts_as_recursive_tree.gemspec'
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
### Version 2.0.0
|
2
|
+
- drop support for rails < 5.0
|
3
|
+
- support for polymorphic parent relations
|
4
|
+
|
5
|
+
### Version 1.1.1
|
6
|
+
- BUGFIX: not checking presence of relation with _present?_ method - this causes execution of the relation
|
7
|
+
- added missing != method for depth
|
8
|
+
|
9
|
+
### Version 1.1.0
|
10
|
+
- scopes and method can now be passed a Proc instance for additional modifications of the query
|
11
|
+
- new option to specify the depth to query
|
12
|
+
|
13
|
+
### Version 1.0.1
|
14
|
+
- BUGFIX: ordering result when querying ancestors
|
15
|
+
|
16
|
+
### Version 1.0.0
|
17
|
+
- inital release using AREL
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Wolfgang Wedelich-John
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# ActsAsRecursiveTree
|
2
|
+
|
3
|
+
Use the power of recursive SQL statements in your Rails application.
|
4
|
+
|
5
|
+
When you have tree based data in your application, you always to struggle with retrieving data. There are solutions, but the always come at a price:
|
6
|
+
|
7
|
+
* Nested Set is fast at retrieval, but when inserting you might have to rearrange bounds, which can be very complex
|
8
|
+
* Closure_Tree stores additional data in a separate table, which has be kept up to date
|
9
|
+
|
10
|
+
Luckily, there is already a SQL standard that makes it very easy to retrieve data in the traditional parent/child relation. Currently this is only supported in sqlite and Postgres. With this it is possible to query complete trees without the need of extra tables or indices.
|
11
|
+
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'acts_as_recursive_tree'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install acts_as_recursive_tree
|
28
|
+
|
29
|
+
|
30
|
+
In your model class add following line:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class Node < ActiveRecord::Base
|
34
|
+
recursive_tree
|
35
|
+
end
|
36
|
+
```
|
37
|
+
That's it. This will assume that your model has a column named `parent_id` which will be used for traversal. If your column is something different, then you can specifiy it in the call to `recursive_tree`:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
recursive_tree parent_key: :some_other_column
|
41
|
+
```
|
42
|
+
|
43
|
+
Some extra special stuff - if your parent relation is also polymorphic, the specify the polymorphic column:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
recursive_tree parent_type_column: :some_other_type_column
|
47
|
+
```
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
After you set up a model for usage, there are now several methods you can use.
|
52
|
+
|
53
|
+
### Associations
|
54
|
+
|
55
|
+
You have access to following associations:
|
56
|
+
|
57
|
+
* `parent` - the parent of this instance
|
58
|
+
* `children` - all children (parent_id = self.id)
|
59
|
+
* `self_and_siblings` - all node where parent_id = self.parent_id
|
60
|
+
|
61
|
+
### Class Methods
|
62
|
+
|
63
|
+
* `roots` - all root elements (parent_id = nil)
|
64
|
+
* `self_and_descendants_of(reference)` - the complete tree of `reference` __including__ `reference` in the result
|
65
|
+
* `descendants_of(reference)` - the complete tree of `reference` __excluding__ `reference` in the result
|
66
|
+
* `leaves_of(reference)` - special case of descendants where only those elements are returned, that do not have any children
|
67
|
+
* `self_and_ancestors_of(reference)` - the complete ancestor list of `reference` __including__ `reference` in the result
|
68
|
+
* `ancestors_of(reference)` - the complete ancestor list of `reference` __excluding__ `reference` in the result
|
69
|
+
* `roots_of(reference)` - special case of ancestors where only those elements are returned, that do not have any parent
|
70
|
+
|
71
|
+
You can pass in following argument types for `reference`, that will be accepted:
|
72
|
+
* `integer` - simple integer value
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
Node.descendants_of(1234)
|
76
|
+
```
|
77
|
+
|
78
|
+
* `array` - array of integer value
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Node.descendants_of([1234, 5678])
|
82
|
+
```
|
83
|
+
|
84
|
+
* `ActiveRecord::Base` - instance of an AR::Model class
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Node.descendants_of(some_node)
|
88
|
+
```
|
89
|
+
|
90
|
+
* `ActiveRecord::Relation` - an AR::Relation form the same type
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
Node.descendants_of(Node.where(foo: :bar))
|
94
|
+
```
|
95
|
+
|
96
|
+
|
97
|
+
### Instance Methods
|
98
|
+
|
99
|
+
For nearly all mentioned scopes and associations there is a corresponding instance method:
|
100
|
+
|
101
|
+
* `root` - returns the root element of this node
|
102
|
+
* `self_and_descendants` - the complete tree __including__ `self` in the result
|
103
|
+
* `descendants` - the complete tree __excluding__ `self` in the result
|
104
|
+
* `leaves` - only leaves of this node
|
105
|
+
* `self_and_ancestors` - the complete ancestor list __including__ `self` in the result
|
106
|
+
* `ancestors` - the complete ancestor list __excluding__ `self` in the result
|
107
|
+
|
108
|
+
Those methods simply delegate to the corresponding scope and pass `self` as reference.
|
109
|
+
|
110
|
+
__Additional methods:__
|
111
|
+
* `siblings` - return all elements where parent_id = self.parent_id __excluding__ `self`
|
112
|
+
* `self_and_children` - return all children and self as a Relation
|
113
|
+
|
114
|
+
__Utility methods:__
|
115
|
+
* `root?` - returns true if this node is a root node
|
116
|
+
* `leaf?` - returns true if this node is a leave node
|
117
|
+
|
118
|
+
|
119
|
+
## Customizing the recursion
|
120
|
+
|
121
|
+
All *ancestors* and *descendants* methods/scopes can take an additional block argument. The block receives ans `opts` argument with which you are able to customize the recursion.
|
122
|
+
|
123
|
+
|
124
|
+
__Depth__
|
125
|
+
|
126
|
+
Specify a depth condition. Only the elements matching the depth are returned.
|
127
|
+
Supported operations are:
|
128
|
+
* `==` exact match - can be Integer or Range or Array. When specifying a Range this will result in a `depth BETWEEN min AND max` query.
|
129
|
+
* `!=` except - can be Integer or Array
|
130
|
+
* `>` greater than - only Integer
|
131
|
+
* `>=` greater than or equals - only Integer
|
132
|
+
* `<` less than - only Integer
|
133
|
+
* `<=` less than or equals - only Integer
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
Node.descendants_of(1){|opts| opts.depth == 3..6 }
|
137
|
+
node_instance.descendants{ |opts| opts.depth <= 4 }
|
138
|
+
node_instance.descendants{ |opts| opts.depth != [4, 7] }
|
139
|
+
```
|
140
|
+
NOTE: `depth == 1` is the same as `children/parent`
|
141
|
+
|
142
|
+
__Condition__
|
143
|
+
|
144
|
+
Pass in an additional relation. Only those elements are returned where the condition query matches.
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
Node.descendants_of(1){|opts| opts.condition = Node.where(active: true) }
|
148
|
+
node_instance.descendants{ |opts| opts.condition = Node.where(active: true) }
|
149
|
+
```
|
150
|
+
NOTE: In contrast to depth, which first gathers the complete tree and then discards all non matching results, this will stop the recursive traversal when the relation is not met. Following two lines are completely different when executed:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
node_instance.descendants.where(active: true) # => returns the complete tree and filters than out only the active ones
|
154
|
+
node_instance.descendants{ |opts| opts.condition = Node.where(active: true) } # => stops the recursion when encountering a non active node, which may return less results than the one above
|
155
|
+
```
|
156
|
+
|
157
|
+
__Ordering__
|
158
|
+
All the *ancestor* methods will order the result depending on the depth of the recursion. Ordering for the *descendants* methods is disabled by default, but can be enabled if needed.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
Node.descendants_of(1){|opts| opts.ensure_ordering! }
|
162
|
+
node_instance.descendants{ |opts| opts.ensure_ordering! }
|
163
|
+
```
|
164
|
+
|
165
|
+
NOTE: if there are many descendants this may cause a severe increase in execution time!
|
166
|
+
|
167
|
+
|
168
|
+
|
169
|
+
## Contributing
|
170
|
+
|
171
|
+
1. Fork it ( https://github.com/1and1/acts_as_recursive_tree/fork )
|
172
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
173
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
174
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
175
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
begin
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task default: [:spec]
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Deletes temporary files'
|
11
|
+
task :clean_tmp_files do
|
12
|
+
%w( db.log test.sqlite3 ).each do |file|
|
13
|
+
File.delete(file) if File.exists?(file)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'acts_as_recursive_tree/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'acts_as_recursive_tree'
|
8
|
+
spec.version = ActsAsRecursiveTree::VERSION
|
9
|
+
spec.authors = ['Wolfgang Wedelich-John']
|
10
|
+
spec.email = ['wolfgang.wedelich@1und1.de']
|
11
|
+
spec.summary = %q{Drop in replacement for acts_as_tree but using recursive queries}
|
12
|
+
spec.description = %q{
|
13
|
+
This is a ruby gem that provides drop in replacement for acts_as_tree but makes use of SQL recursive statement. Be sure to have a DBMS that supports recursive queries when using this gem (e.g. PostgreSQL or SQLite). }
|
14
|
+
spec.homepage = 'https://github.com/1and1/acts_as_recursive_tree'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.required_ruby_version = '>= 2.0.0'
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'activerecord', '>= 5.0.0', '< 6.0.0'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
26
|
+
spec.add_development_dependency 'database_cleaner', '~> 1.5.0'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec-rails', '~> 3.5.0'
|
29
|
+
spec.add_development_dependency 'sqlite3', '~> 1.3.10'
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module ActsAsRecursiveTree
|
4
|
+
module ActsMacro
|
5
|
+
|
6
|
+
##
|
7
|
+
# Configuration options are:
|
8
|
+
#
|
9
|
+
# * <tt>foreign_key</tt> - specifies the column name to use for tracking
|
10
|
+
# of the tree (default: +parent_id+)
|
11
|
+
def recursive_tree(parent_key: :parent_id, parent_type_column: nil)
|
12
|
+
|
13
|
+
class_attribute :_recursive_tree_config
|
14
|
+
self._recursive_tree_config = OpenStruct.new(
|
15
|
+
primary_key: self.primary_key.to_sym,
|
16
|
+
parent_key: parent_key.to_sym,
|
17
|
+
parent_type_column: parent_type_column.try(:to_sym),
|
18
|
+
depth_column: :recursive_depth
|
19
|
+
)
|
20
|
+
|
21
|
+
include ActsAsRecursiveTree::Model
|
22
|
+
include ActsAsRecursiveTree::Associations
|
23
|
+
include ActsAsRecursiveTree::Scopes
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :acts_as_tree, :recursive_tree
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module ActsAsRecursiveTree
|
4
|
+
module Associations
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
belongs_to :parent,
|
9
|
+
class_name: self.base_class.to_s,
|
10
|
+
foreign_key: self._recursive_tree_config.parent_key,
|
11
|
+
inverse_of: :children,
|
12
|
+
optional: true
|
13
|
+
|
14
|
+
has_many :children,
|
15
|
+
class_name: self.base_class.to_s,
|
16
|
+
foreign_key: self._recursive_tree_config.parent_key,
|
17
|
+
inverse_of: :parent
|
18
|
+
|
19
|
+
has_many :self_and_siblings,
|
20
|
+
class_name: self.base_class.to_s,
|
21
|
+
primary_key: self._recursive_tree_config.parent_key,
|
22
|
+
foreign_key: self._recursive_tree_config.parent_key
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|