acts_as_recursive_tree 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +1 -0
  5. data/.rubocop_todo.yml +321 -0
  6. data/CHANGELOG.md +17 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +175 -0
  10. data/Rakefile +15 -0
  11. data/acts_as_recursive_tree.gemspec +30 -0
  12. data/lib/acts_as_recursive_tree/acts_macro.rb +28 -0
  13. data/lib/acts_as_recursive_tree/associations.rb +25 -0
  14. data/lib/acts_as_recursive_tree/builders/ancestors.rb +17 -0
  15. data/lib/acts_as_recursive_tree/builders/descendants.rb +9 -0
  16. data/lib/acts_as_recursive_tree/builders/leaves.rb +26 -0
  17. data/lib/acts_as_recursive_tree/builders/relation_builder.rb +121 -0
  18. data/lib/acts_as_recursive_tree/builders.rb +13 -0
  19. data/lib/acts_as_recursive_tree/model.rb +125 -0
  20. data/lib/acts_as_recursive_tree/options/depth_condition.rb +48 -0
  21. data/lib/acts_as_recursive_tree/options/query_options.rb +21 -0
  22. data/lib/acts_as_recursive_tree/options/values.rb +77 -0
  23. data/lib/acts_as_recursive_tree/options.rb +9 -0
  24. data/lib/acts_as_recursive_tree/railtie.rb +9 -0
  25. data/lib/acts_as_recursive_tree/scopes.rb +16 -0
  26. data/lib/acts_as_recursive_tree/version.rb +3 -0
  27. data/lib/acts_as_recursive_tree.rb +14 -0
  28. data/spec/builders_spec.rb +136 -0
  29. data/spec/db/database.rb +22 -0
  30. data/spec/db/database.yml +12 -0
  31. data/spec/db/models.rb +37 -0
  32. data/spec/db/schema.rb +34 -0
  33. data/spec/model/location_spec.rb +55 -0
  34. data/spec/model/node_spec.rb +129 -0
  35. data/spec/model/relation_spec.rb +63 -0
  36. data/spec/spec_helper.rb +119 -0
  37. data/spec/values_spec.rb +86 -0
  38. 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
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /.idea
16
+ db.log
17
+ test.sqlite3
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in acts_as_recursive_tree.gemspec
4
+ gemspec
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