ancestry 3.0.6 → 4.0.0
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 +4 -4
- data/CHANGELOG.md +286 -0
- data/README.md +86 -202
- data/lib/ancestry.rb +28 -1
- data/lib/ancestry/class_methods.rb +60 -52
- data/lib/ancestry/has_ancestry.rb +39 -33
- data/lib/ancestry/instance_methods.rb +62 -106
- data/lib/ancestry/locales/en.yml +16 -0
- data/lib/ancestry/materialized_path.rb +100 -27
- data/lib/ancestry/materialized_path_pg.rb +23 -0
- data/lib/ancestry/version.rb +1 -1
- metadata +24 -51
- data/ancestry.gemspec +0 -53
- data/init.rb +0 -1
- data/install.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7d01ac0d4c8faa7ad66a0a8e5454625d723fc9226a7e0eb84b358fd25003148
|
4
|
+
data.tar.gz: e27cf882086e2c674972251bf35a52eca003d601dcf5d9ba390c1785f8900033
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2b1a7a69fa27d6f40f1c39a700c5926d3fa1b5da4637800b87d1ad274458cb0ca39a497d505f2b6224031914a484349cef8c433b191ba131e1396431fce767d
|
7
|
+
data.tar.gz: 9fd95019be895afcc8eeee14be8a7776a90a29ded86d25a3cb82a92097cfa7a141f8a765a062e4ac6f5665e6bf7656d8e9fbde030329b713f286f9d96fa179b7
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
# Ancestry Changelog
|
2
|
+
|
3
|
+
Doing our best at supporting [SemVer](http://semver.org/) with
|
4
|
+
a nice looking [Changelog](http://keepachangelog.com).
|
5
|
+
|
6
|
+
## Version [HEAD] <sub><sup>now</sub></sup>
|
7
|
+
|
8
|
+
## Version [4.0.0] <sub><sup>2021-04-12</sub></sup>
|
9
|
+
|
10
|
+
* dropped support for rails 4.2 and 5.0 (thx @d-m-u)
|
11
|
+
* better documentation counter cache option (thx @pustomytnyk)
|
12
|
+
* clean up code (thx @amatsuda @d-m-u)
|
13
|
+
* fixed rails 6.1 support (thx @cmr119 @d-staehler @danini-the-panini )
|
14
|
+
* phasing out `parent_id?`, `ancestors?` and using `has_parent?` instead
|
15
|
+
* fixed postgres order bug on rails 6.2 and higher (thx @smoyt)
|
16
|
+
|
17
|
+
## Version [3.2.1] <sub><sup>2020-09-23</sub></sup>
|
18
|
+
|
19
|
+
* fixed gemspec to include locales and pg (thx @HectorMF)
|
20
|
+
|
21
|
+
## Version [3.2.0] <sub><sup>2020-09-23</sub></sup>
|
22
|
+
|
23
|
+
* introduce i18n
|
24
|
+
* pg sql optimization for ancestry changes (thx @suonlight and @geis)
|
25
|
+
* pg sql optimization for sorting (thx @brendon and @d-m-u)
|
26
|
+
* fix to humanise model name (thx @mkllnk)
|
27
|
+
* able to convert to ancestry from a parent_id column with a different name
|
28
|
+
* documentation fixes for better diagrams and grammar (thx @dtamais, @d-m-u, and @CamilleDrapier)
|
29
|
+
|
30
|
+
## Version [3.1.0] <sub><sup>2020-08-03</sub></sup>
|
31
|
+
|
32
|
+
* `:primary_key_format` method lets you change syntax. good for uuids.
|
33
|
+
* changed code from being `ancestry` string to `ancestry_ids` focused. May break monkey patches.
|
34
|
+
* Moved many methods from `has_ancestry` and `InstanceMethods` to `MaterializedPath`. May break monkey patches.
|
35
|
+
* Removed tests for `mysql` driver. Starting with rails 4.1, it supports `mysql2` driver.
|
36
|
+
* Better documentation for relationships (thnx @dtamai and @d-m-u)
|
37
|
+
* Fix creating children in `after_*` callbacks (thx @jstirk)
|
38
|
+
|
39
|
+
## Version [3.0.7] <sub><sup>2018-11-06</sub></sup>
|
40
|
+
|
41
|
+
* Fixed rails 5.1 change detection (thx @jrafanie)
|
42
|
+
* Introduce counter cache (thx @hw676018683)
|
43
|
+
|
44
|
+
## Version [3.0.6] <sub><sup>2018-11-06</sub></sup>
|
45
|
+
|
46
|
+
* Fixed rails 4.1 version check (thx @myxoh)
|
47
|
+
|
48
|
+
## Version [3.0.5] <sub><sup>2018-11-06</sub></sup>
|
49
|
+
|
50
|
+
## Changed
|
51
|
+
|
52
|
+
* Added indirect children support (thx @tilo)
|
53
|
+
* Fixed test sorting for pg on mac osx
|
54
|
+
|
55
|
+
## Fixes
|
56
|
+
|
57
|
+
* Reduced memory footprint of parsing ancestry column (thx @NickLaMuro)
|
58
|
+
|
59
|
+
## Version [3.0.4] <sub><sup>2018-10-27</sub></sup>
|
60
|
+
|
61
|
+
## Fixes
|
62
|
+
|
63
|
+
* Properly detects non-integer columns (thx @adam101)
|
64
|
+
* Arrange no longer drops nodes due to missing parents (thx @trafium)
|
65
|
+
|
66
|
+
## Version [3.0.3] <sub><sup>2018-10-23</sub></sup>
|
67
|
+
|
68
|
+
This branch (3.x) should still be compatible with rails 3 and 4.
|
69
|
+
Rails 5.1 and 5.2 support were introduced in this version, but ongoing support
|
70
|
+
has been moved to ancestry 4.0
|
71
|
+
|
72
|
+
## Fixes
|
73
|
+
|
74
|
+
* Reduce object allocation (thx @NickLaMuro)
|
75
|
+
* Rails 5.1 fixes (thx @ctrombley)
|
76
|
+
* Avoid redundant query to DB in subtree_of scope (thx @Slike9)
|
77
|
+
* Syntax tweaks (thx @ekohl, @richardonrails)
|
78
|
+
* Fixed reverse ordering
|
79
|
+
* Dropped builds for ruby 1.9.3, 2.0, 2.1, and 2.2
|
80
|
+
* Dropped builds for Rails 3.x and 4.x (will use Active Record `or` syntax)
|
81
|
+
|
82
|
+
## Version [3.0.2] <sub><sup>2018-04-24</sub></sup>
|
83
|
+
|
84
|
+
## Fixes
|
85
|
+
|
86
|
+
* fixed `order_by_ancestry` bug
|
87
|
+
* fixed order tests for postgres on mac (it uses a different collation)
|
88
|
+
* fixed documentation (thx @besquared, @danfrenette, @eiwi1101, @isimluk, @mabusaad, @tilsammans)
|
89
|
+
* added missing `Ancestry::version`
|
90
|
+
* added Rails 5.2 support (thx @jjuliano)
|
91
|
+
|
92
|
+
## Version [3.0.1] <sub><sup>2017-07-05</sub></sup>
|
93
|
+
|
94
|
+
## Fixes
|
95
|
+
|
96
|
+
* added gem metadata
|
97
|
+
* fixed keep a changelog link (thx @mattbrictson)
|
98
|
+
* added alias has_parent?
|
99
|
+
* fixed bug where unscoping too much (thx @brendon)
|
100
|
+
* fixed tests on mysql 5.7 and rails 3.2
|
101
|
+
* Dropped 3.1 scope changes
|
102
|
+
|
103
|
+
## Version [3.0.0] <sub><sup>2017-05-18</sub></sup>
|
104
|
+
|
105
|
+
## Changed
|
106
|
+
|
107
|
+
* Dropping Rails 3.0, and 3.1. Added Rails 5.1 support (thx @ledermann)
|
108
|
+
* Dropping Rails 4.0, 4.1 for build reasons. Since 4.2 is supported, all 4.x should still work.
|
109
|
+
|
110
|
+
## Fixes
|
111
|
+
|
112
|
+
* Performance: Use `pluck` vs `map` for ids (thx @njakobsen and @culturecode)
|
113
|
+
* Fixed acts_as_tree compatibility (thx @crazymykl)
|
114
|
+
* Fixed loading ActiveRails prematurely (thx @vovimayhem)
|
115
|
+
* Fixes exist (thx @ledermann)
|
116
|
+
* Properly touches parents when different class for STI (thx @samtgarson)
|
117
|
+
* Fixed issues with parent_id (only present on master) (thx @domcleal)
|
118
|
+
|
119
|
+
## Version [2.2.2] <sub><sup>2016-11-01</sub></sup>
|
120
|
+
|
121
|
+
### Changed
|
122
|
+
|
123
|
+
* Use `COALESCE` only for sorting versions greater than 5.0
|
124
|
+
* Fixed bug with explicit order clauses (introduced in 2.2.0)
|
125
|
+
* No longer load schema on `has_ancestry` load (thx @ledermann)
|
126
|
+
|
127
|
+
## Version [2.2.1] <sub><sup>2016-10-25</sub></sup>
|
128
|
+
|
129
|
+
Sorry for blip, local master got out of sync with upstream master.
|
130
|
+
Missed 2 commits (which are feature adds)
|
131
|
+
|
132
|
+
### Added
|
133
|
+
* Use like (vs ilike) for rails 5.0 (performance enhancement)
|
134
|
+
* Use `COALESCE` for sorting on pg, mysql, and sqlite vs `CASE`
|
135
|
+
|
136
|
+
## Version [2.2.0] <sub><sup>2016-10-25</sub></sup>
|
137
|
+
|
138
|
+
### Added
|
139
|
+
* Predicates for scopes: e.g.: `ancestor_of?`, `parent_of?` (thx @neglectedvalue)
|
140
|
+
* Scope `path_of`
|
141
|
+
|
142
|
+
### Changed
|
143
|
+
* `arrange` now accepts blocks (thx @mastfish)
|
144
|
+
* Performance tuning `arrange_node` (thx @fryguy)
|
145
|
+
* In orphan strategy, set `ancestry` to `nil` for no parents (thx @haslinger)
|
146
|
+
* Only updates `updated_at` when a record is changed (thx @brocktimus)
|
147
|
+
* No longer casts text primary key as an integer
|
148
|
+
* Upgrading tests for ruby versions (thx @brocktimus, @fryguy, @yui-knk)
|
149
|
+
* Fix non-default ancestry not getting used properly (thx @javiyu)
|
150
|
+
|
151
|
+
## Version [2.1.0] <sub><sup>2014-04-16</sub></sup>
|
152
|
+
* Added arrange_serializable (thx @krishandley, @chicagogrrl)
|
153
|
+
* Add the :touch to update ancestors on save (thx @adammck)
|
154
|
+
* Change conditions into arel (thx @mlitwiniuk)
|
155
|
+
* Added children? & siblings? alias (thx @bigtunacan)
|
156
|
+
* closure_tree compatibility (thx @gzigzigzeo)
|
157
|
+
* Performance tweak (thx @mjc)
|
158
|
+
* Improvements to organization (thx @xsuchy, @ryakh)
|
159
|
+
|
160
|
+
## Version [2.0.0] <sub><sup>2013-05-17</sub></sup>
|
161
|
+
* Removed rails 2 compatibility
|
162
|
+
* Added table name to condition constructing methods (thx @aflatter)
|
163
|
+
* Fix depth_cache not being updated when moving up to ancestors (thx @scottatron)
|
164
|
+
* add alias :root? to existing is_root? (thx @divineforest)
|
165
|
+
* Add block to sort_by_ancestry (thx @Iliya)
|
166
|
+
* Add attribute query method for parent_id (thx @sj26)
|
167
|
+
* Fixed and tested for rails 4 (thx @adammck, @Nihad, @Systho, @Philippe, e.a.)
|
168
|
+
* Fixed overwriting ActiveRecord::Base.base_class (thx @Rozhnov)
|
169
|
+
* New adopt strategy (thx unknown)
|
170
|
+
* Many more improvements
|
171
|
+
|
172
|
+
## Version [1.3.0] <sub><sup>2012-05-04</sub></sup>
|
173
|
+
* Ancestry now ignores default scopes when moving or destroying nodes, ensuring tree consistency
|
174
|
+
* Changed ActiveRecord dependency to 2.3.14
|
175
|
+
|
176
|
+
## Version [1.2.5] <sub><sup>2012-03-15</sub></sup>
|
177
|
+
* Fixed warnings: "parenthesize argument(s) for future version"
|
178
|
+
* Fixed a bug in the restore_ancestry_integrity! method (thx Arthur Holstvoogd)
|
179
|
+
|
180
|
+
## Version [1.2.4] <sub><sup>2011-04-22</sub></sup>
|
181
|
+
* Prepended table names to column names in queries (thx @raelik)
|
182
|
+
* Better check to see if acts_as_tree can be overloaded (thx @jims)
|
183
|
+
* Performance inprovements (thx @kueda)
|
184
|
+
|
185
|
+
## Version [1.2.3] <sub><sup>2010-10-28</sub></sup>
|
186
|
+
* Fixed error with determining ActiveRecord version
|
187
|
+
* Added option to specify :primary_key_format (thx @rolftimmermans)
|
188
|
+
|
189
|
+
## Version [1.2.2] <sub><sup>2010-10-24</sub></sup>
|
190
|
+
* Fixed all deprecation warnings for rails 3.0.X
|
191
|
+
* Added `:report` option to `check_ancestry_integrity!`
|
192
|
+
* Changed ActiveRecord dependency to 2.2.2
|
193
|
+
* Tested and fixed for ruby 1.8.7 and 1.9.2
|
194
|
+
* Changed usage of `update_attributes` to `update_attribute` to allow ancestry column protection
|
195
|
+
|
196
|
+
## Version [1.2.0] <sub><sup>2009-11-07</sub></sup>
|
197
|
+
* Removed some duplication in has_ancestry
|
198
|
+
* Cleaned up plugin pattern according to http://yehudakatz.com/2009/11/12/better-ruby-idioms/
|
199
|
+
* Moved parts of ancestry into seperate files
|
200
|
+
* Made it possible to pass options into the arrange method
|
201
|
+
* Renamed acts_as_tree to has_ancestry
|
202
|
+
* Aliased has_ancestry as acts_as_tree if acts_as_tree is available
|
203
|
+
* Added subtree_of scope
|
204
|
+
* Updated ordered_by_ancestry scope to support Microsoft SQL Server
|
205
|
+
* Added empty hash as parameter to exists? calls for older ActiveRecord versions
|
206
|
+
|
207
|
+
## Version [1.1.4] <sub><sup>2009-11-07</sub></sup>
|
208
|
+
* Thanks to a patch from tom taylor, Ancestry now works with different primary keys
|
209
|
+
|
210
|
+
## Version [1.1.3] <sub><sup>2009-11-01</sub></sup>
|
211
|
+
* Fixed a pretty bad bug where several operations took far too many queries
|
212
|
+
|
213
|
+
## Version [1.1.2] <sub><sup>2009-10-29</sub></sup>
|
214
|
+
* Added validation for depth cache column
|
215
|
+
* Added STI support (reported broken)
|
216
|
+
|
217
|
+
## Version [1.1.1] <sub><sup>2009-10-28</sub></sup>
|
218
|
+
* Fixed some parentheses warnings that where reported
|
219
|
+
* Fixed a reported issue with arrangement
|
220
|
+
* Fixed issues with ancestors and path order on postgres
|
221
|
+
* Added ordered_by_ancestry scope (needed to fix issues)
|
222
|
+
|
223
|
+
## Version [1.1.0] <sub><sup>2009-10-22</sub></sup>
|
224
|
+
* Depth caching (and cache rebuilding)
|
225
|
+
* Depth method for nodes
|
226
|
+
* Named scopes for selecting by depth
|
227
|
+
* Relative depth options for tree navigation methods:
|
228
|
+
* ancestors
|
229
|
+
* path
|
230
|
+
* descendants
|
231
|
+
* descendant_ids
|
232
|
+
* subtree
|
233
|
+
* subtree_ids
|
234
|
+
* Updated README
|
235
|
+
* Easy migration from existing plugins/gems
|
236
|
+
* acts_as_tree checks unknown options
|
237
|
+
* acts_as_tree checks that options are hash
|
238
|
+
* Added a bang (!) to the integrity functions
|
239
|
+
* Since these functions should only be used from ./script/console and not
|
240
|
+
from your application, this change is not considered as breaking backwards
|
241
|
+
compatibility and the major version wasn't bumped.
|
242
|
+
* Updated install script to point to documentation
|
243
|
+
* Removed rails specific init
|
244
|
+
* Removed uninstall script
|
245
|
+
|
246
|
+
## Version 1.0.0 <sub><sup>2009-10-16</sub></sup>
|
247
|
+
* Initial version
|
248
|
+
* Tree building
|
249
|
+
* Tree navigation
|
250
|
+
* Integrity checking / restoration
|
251
|
+
* Arrangement
|
252
|
+
* Orphan strategies
|
253
|
+
* Subtree movement
|
254
|
+
* Named scopes
|
255
|
+
* Validations
|
256
|
+
|
257
|
+
|
258
|
+
[HEAD]: https://github.com/stefankroes/ancestry/compare/v4.0.0...HEAD
|
259
|
+
[4.0.0]: https://github.com/stefankroes/ancestry/compare/v3.2.1...v4.0.0
|
260
|
+
[3.2.1]: https://github.com/stefankroes/ancestry/compare/v3.2.0...v3.2.1
|
261
|
+
[3.2.0]: https://github.com/stefankroes/ancestry/compare/v3.1.0...v3.2.0
|
262
|
+
[3.1.0]: https://github.com/stefankroes/ancestry/compare/v3.0.7...v3.1.0
|
263
|
+
[3.0.7]: https://github.com/stefankroes/ancestry/compare/v3.0.6...v3.0.7
|
264
|
+
[3.0.6]: https://github.com/stefankroes/ancestry/compare/v3.0.5...v3.0.6
|
265
|
+
[3.0.5]: https://github.com/stefankroes/ancestry/compare/v3.0.4...v3.0.5
|
266
|
+
[3.0.4]: https://github.com/stefankroes/ancestry/compare/v3.0.3...v3.0.4
|
267
|
+
[3.0.3]: https://github.com/stefankroes/ancestry/compare/v3.0.2...v3.0.3
|
268
|
+
[3.0.2]: https://github.com/stefankroes/ancestry/compare/v3.0.1...v3.0.2
|
269
|
+
[3.0.1]: https://github.com/stefankroes/ancestry/compare/v3.0.0...v3.0.1
|
270
|
+
[3.0.0]: https://github.com/stefankroes/ancestry/compare/v2.2.2...v3.0.0
|
271
|
+
[2.2.2]: https://github.com/stefankroes/ancestry/compare/v2.2.1...v2.2.2
|
272
|
+
[2.2.1]: https://github.com/stefankroes/ancestry/compare/v2.2.0...v2.2.1
|
273
|
+
[2.2.0]: https://github.com/stefankroes/ancestry/compare/v2.1.0...v2.2.0
|
274
|
+
[2.1.0]: https://github.com/stefankroes/ancestry/compare/v2.0.0...v2.1.0
|
275
|
+
[2.0.0]: https://github.com/stefankroes/ancestry/compare/v1.3.0...v2.0.0
|
276
|
+
[1.3.0]: https://github.com/stefankroes/ancestry/compare/v1.2.5...v1.3.0
|
277
|
+
[1.2.5]: https://github.com/stefankroes/ancestry/compare/v1.2.4...v1.2.5
|
278
|
+
[1.2.4]: https://github.com/stefankroes/ancestry/compare/v1.2.3...v1.2.4
|
279
|
+
[1.2.3]: https://github.com/stefankroes/ancestry/compare/v1.2.2...v1.2.3
|
280
|
+
[1.2.2]: https://github.com/stefankroes/ancestry/compare/v1.2.0...v1.2.2
|
281
|
+
[1.2.0]: https://github.com/stefankroes/ancestry/compare/v1.1.4...v1.2.0
|
282
|
+
[1.1.4]: https://github.com/stefankroes/ancestry/compare/v1.1.3...v1.1.4
|
283
|
+
[1.1.3]: https://github.com/stefankroes/ancestry/compare/v1.1.2...v1.1.3
|
284
|
+
[1.1.2]: https://github.com/stefankroes/ancestry/compare/v1.1.1...v1.1.2
|
285
|
+
[1.1.1]: https://github.com/stefankroes/ancestry/compare/v1.1.0...v1.1.1
|
286
|
+
[1.1.0]: https://github.com/stefankroes/ancestry/compare/v1.0.0...v1.1.0
|
data/README.md
CHANGED
@@ -3,24 +3,24 @@
|
|
3
3
|
# Ancestry
|
4
4
|
|
5
5
|
Ancestry is a gem that allows the records of a Ruby on Rails
|
6
|
-
ActiveRecord model to be organised as a tree structure (or hierarchy). It
|
7
|
-
|
8
|
-
relations (ancestors, parent, root, children, siblings, descendants)
|
9
|
-
of them
|
6
|
+
ActiveRecord model to be organised as a tree structure (or hierarchy). It employs
|
7
|
+
the materialised path pattern and exposes all the standard tree structure
|
8
|
+
relations (ancestors, parent, root, children, siblings, descendants), allowing all
|
9
|
+
of them to be fetched in a single SQL query. Additional features include STI
|
10
10
|
support, scopes, depth caching, depth constraints, easy migration from older
|
11
11
|
gems, integrity checking, integrity restoration, arrangement of
|
12
|
-
(sub)
|
12
|
+
(sub)trees into hashes, and various strategies for dealing with orphaned
|
13
13
|
records.
|
14
14
|
|
15
15
|
NOTE:
|
16
16
|
|
17
|
-
- Ancestry
|
17
|
+
- Ancestry 2.x supports Rails 4.1 and earlier
|
18
|
+
- Ancestry 3.x supports Rails 5.0 and 4.2
|
18
19
|
- Ancestry 4.0 only supports rails 5.0 and higher
|
19
20
|
|
20
21
|
# Installation
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
Follow these simple steps to apply Ancestry to any ActiveRecord model:
|
24
24
|
|
25
25
|
## Install
|
26
26
|
|
@@ -54,7 +54,7 @@ $ rake db:migrate
|
|
54
54
|
|
55
55
|
|
56
56
|
## Add ancestry to your model
|
57
|
-
* Add to
|
57
|
+
* Add to app/models/[model.rb]:
|
58
58
|
|
59
59
|
```ruby
|
60
60
|
# app/models/[model.rb]
|
@@ -68,9 +68,9 @@ Your model is now a tree!
|
|
68
68
|
|
69
69
|
# Using acts_as_tree instead of has_ancestry
|
70
70
|
|
71
|
-
In version 1.2.0 the **acts_as_tree** method was **renamed to has_ancestry**
|
71
|
+
In version 1.2.0, the **acts_as_tree** method was **renamed to has_ancestry**
|
72
72
|
in order to allow usage of both the acts_as_tree gem and the ancestry gem in a
|
73
|
-
single application.
|
73
|
+
single application. The `acts_as_tree` method will continue to be supported in the future.
|
74
74
|
|
75
75
|
# Organising records into a tree
|
76
76
|
|
@@ -81,64 +81,38 @@ parent_id can be set using parent= and parent_id= on a record or by including
|
|
81
81
|
them in the hash passed to new, create, create!, update_attributes and
|
82
82
|
update_attributes!. For example:
|
83
83
|
|
84
|
-
|
85
|
-
TreeNode.create! :name => 'Stinky', :parent => TreeNode.create!(:name => 'Squeeky')
|
86
|
-
```
|
84
|
+
`TreeNode.create! :name => 'Stinky', :parent => TreeNode.create!(:name => 'Squeeky')`.
|
87
85
|
|
88
|
-
|
86
|
+
Children can be created through the children relation on a node: `node.children.create :name => 'Stinky'`.
|
89
87
|
|
90
|
-
|
91
|
-
|
92
|
-
|
88
|
+
# Tree Navigation
|
89
|
+
|
90
|
+
The node with the large border is the reference node (the node from which the navigation method is invoked.)
|
91
|
+
The yellow nodes are those returned by the method.
|
92
|
+
|
93
|
+
| | | |
|
94
|
+
|:-: |:-: |:-: |
|
95
|
+
|**parent** |**root**<sup><a href="#fn1" id="ref1">1</a></sup> |**ancestors** |
|
96
|
+
| | | |
|
97
|
+
| nil for a root node |self for a root node |root..parent |
|
98
|
+
| `parent_id` |`root_id` |`ancestor_ids` |
|
99
|
+
| `has_parent?` |`is_root?` |`ancestors?` |
|
100
|
+
|`parent_of?` |`root_of?` |`ancestor_of?` |
|
101
|
+
|**children** |**descendants** |**indirects** |
|
102
|
+
| | | |
|
103
|
+
| `child_ids` |`descendant_ids` |`indirect_ids` |
|
104
|
+
| `has_children?` | | |
|
105
|
+
| `child_of?` |`descendant_of?` |`indirect_of?` |
|
106
|
+
|**siblings** |**subtree** |**path** |
|
107
|
+
| | | |
|
108
|
+
| includes self |self..indirects |root..self |
|
109
|
+
|`sibling_ids` |`subtree_ids` |`path_ids` |
|
110
|
+
|`has_siblings?` | | |
|
111
|
+
|`sibling_of?(node)` | | |
|
93
112
|
|
94
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
| method |return value|
|
99
|
-
|-------------------|------------|
|
100
|
-
|`parent` |parent of the record, nil for a root node|
|
101
|
-
|`parent_id` |parent id of the record, nil for a root node|
|
102
|
-
|`root` |root of the record's tree, self for a root node|
|
103
|
-
|`root_id` |root id of the record's tree, self for a root node|
|
104
|
-
|`root?` <br/> `is_root?` | true if the record is a root node, false otherwise|
|
105
|
-
|`ancestors` |ancestors of the record, starting with the root and ending with the parent|
|
106
|
-
|`ancestors?` |true if the record has ancestors (aka not a root node)|
|
107
|
-
|`ancestor_ids` |ancestor ids of the record|
|
108
|
-
|`path` |path of the record, starting with the root and ending with self|
|
109
|
-
|`path_ids` |a list the path ids, starting with the root id and ending with the node's own id|
|
110
|
-
|`children` |direct children of the record|
|
111
|
-
|`child_ids` |direct children's ids|
|
112
|
-
|`has_parent?` <br/> `ancestors?` |true if the record has a parent, false otherwise|
|
113
|
-
|`has_children?` <br/> `children?` |true if the record has any children, false otherwise|
|
114
|
-
|`is_childless?` <br/> `childless?` |true is the record has no children, false otherwise|
|
115
|
-
|`siblings` |siblings of the record, the record itself is included*|
|
116
|
-
|`sibling_ids` |sibling ids|
|
117
|
-
|`has_siblings?` <br/> `siblings?` |true if the record's parent has more than one child|
|
118
|
-
|`is_only_child?` <br/> `only_child?` |true if the record is the only child of its parent|
|
119
|
-
|`descendants` |direct and indirect children of the record|
|
120
|
-
|`descendant_ids` |direct and indirect children's ids of the record|
|
121
|
-
|`indirects` |indirect children of the record|
|
122
|
-
|`indirect_ids` |indirect children's ids of the record|
|
123
|
-
|`subtree` |the model on descendants and itself|
|
124
|
-
|`subtree_ids` |a list of all ids in the record's subtree|
|
125
|
-
|`depth` |the depth of the node, root nodes are at depth 0|
|
126
|
-
|
127
|
-
\* If the record is a root, other root records are considered siblings
|
128
|
-
\* Siblings returns the record itself
|
129
|
-
|
130
|
-
There are also instance methods to determine the relationship between 2 nodes:
|
131
|
-
|
132
|
-
|method |return value|
|
133
|
-
|-------------------|---------------|
|
134
|
-
|`parent_of?(node)` | node's parent is this record|
|
135
|
-
|`root_of?(node)` | node's root is this record|
|
136
|
-
|`ancestor_of?(node)`| node's ancestors include this record|
|
137
|
-
|`child_of?(node)` | node is record's parent|
|
138
|
-
|`descendant_of?(node)` | node is one of this record's ancestors|
|
139
|
-
|`indirect_of?(node)` | node is one of this record's ancestors but not a parent|
|
140
|
-
|
141
|
-
# Options for `has_ancestry`
|
113
|
+
<sup id="fn1">1. [other root records are considered siblings]<a href="#ref1" title="Jump back to footnote 1.">↩</a></sup>
|
114
|
+
|
115
|
+
# `has_ancestry` options
|
142
116
|
|
143
117
|
The has_ancestry method supports the following options:
|
144
118
|
|
@@ -147,24 +121,25 @@ The has_ancestry method supports the following options:
|
|
147
121
|
:destroy All children are destroyed as well (default)
|
148
122
|
:rootify The children of the destroyed node become root nodes
|
149
123
|
:restrict An AncestryException is raised if any children exist
|
150
|
-
:adopt The orphan subtree is added to the parent of the deleted node
|
151
|
-
If the deleted node is Root, then rootify the orphan subtree
|
124
|
+
:adopt The orphan subtree is added to the parent of the deleted node
|
125
|
+
If the deleted node is Root, then rootify the orphan subtree
|
152
126
|
:cache_depth Cache the depth of each node in the 'ancestry_depth' column (default: false)
|
153
127
|
If you turn depth_caching on for an existing model:
|
154
128
|
- Migrate: add_column [table], :ancestry_depth, :integer, :default => 0
|
155
129
|
- Build cache: TreeNode.rebuild_depth_cache!
|
156
130
|
:depth_cache_column Pass in a symbol to store depth cache in a different column
|
157
|
-
:primary_key_format Supply a regular expression that matches the format of your primary key
|
158
|
-
By default, primary keys only match integers ([0-9]+)
|
131
|
+
:primary_key_format Supply a regular expression that matches the format of your primary key
|
132
|
+
By default, primary keys only match integers ([0-9]+)
|
159
133
|
:touch Instruct Ancestry to touch the ancestors of a node when it changes, to
|
160
134
|
invalidate nested key-based caches. (default: false)
|
135
|
+
:counter_cache Boolean whether to create counter cache column accessor.
|
136
|
+
Default column name is `children_count`.
|
137
|
+
Pass symbol to use different column name (default: false)
|
161
138
|
|
162
139
|
# (Named) Scopes
|
163
140
|
|
164
|
-
|
165
|
-
|
166
|
-
the result can be either retrieved, counted or checked for existence. For
|
167
|
-
example:
|
141
|
+
The navigation methods return scopes instead of records, where possible. Additional ordering,
|
142
|
+
conditions, limits, etc. can be applied and the results can be retrieved, counted, or checked for existence:
|
168
143
|
|
169
144
|
```ruby
|
170
145
|
node.children.where(:name => 'Mary').exists?
|
@@ -172,7 +147,7 @@ node.subtree.order(:name).limit(10).each { ... }
|
|
172
147
|
node.descendants.count
|
173
148
|
```
|
174
149
|
|
175
|
-
|
150
|
+
A couple of class-level named scopes are included:
|
176
151
|
|
177
152
|
roots Root nodes
|
178
153
|
ancestors_of(node) Ancestors of node, node can be either a record or an id
|
@@ -182,8 +157,7 @@ For convenience, a couple of named scopes are included at the class level:
|
|
182
157
|
subtree_of(node) Subtree of node, node can be either a record or an id
|
183
158
|
siblings_of(node) Siblings of node, node can be either a record or an id
|
184
159
|
|
185
|
-
|
186
|
-
through the children and siblings scopes:
|
160
|
+
It is possible thanks to some convenient rails magic to create nodes through the children and siblings scopes:
|
187
161
|
|
188
162
|
node.children.create
|
189
163
|
node.siblings.create!
|
@@ -192,8 +166,8 @@ through the children and siblings scopes:
|
|
192
166
|
|
193
167
|
# Selecting nodes by depth
|
194
168
|
|
195
|
-
|
196
|
-
scopes can be used to select nodes
|
169
|
+
With depth caching enabled (see has_ancestry options), an additional five named
|
170
|
+
scopes can be used to select nodes by depth:
|
197
171
|
|
198
172
|
before_depth(depth) Return nodes that are less deep than depth (node.depth < depth)
|
199
173
|
to_depth(depth) Return nodes up to a certain depth (node.depth <= depth)
|
@@ -201,42 +175,27 @@ scopes can be used to select nodes on their depth:
|
|
201
175
|
from_depth(depth) Return nodes starting from a certain depth (node.depth >= depth)
|
202
176
|
after_depth(depth) Return nodes that are deeper than depth (node.depth > depth)
|
203
177
|
|
204
|
-
|
205
|
-
descendant_ids
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
node.subtree.to_depth(5) Subtree of node to an absolute depth of 5
|
210
|
-
node.descendants(:at_depth => 2) Descendant of node, at depth node.depth + 2 (grandchildren)
|
211
|
-
node.descendants.at_depth(10) Descendants of node at an absolute depth of 10
|
212
|
-
node.ancestors.to_depth(3) The oldest 4 ancestors of node (its root and 3 more)
|
213
|
-
node.path(:from_depth => -2) The node's grandparent, parent and the node itself
|
178
|
+
Depth scopes are also available through calls to `descendants`,
|
179
|
+
`descendant_ids`, `subtree`, `subtree_ids`, `path` and `ancestors` (with relative depth).
|
180
|
+
Note that depth constraints cannot be passed to `ancestor_ids` or `path_ids` as both relations
|
181
|
+
can be fetched directly from the ancestry column without needing a query. Use
|
182
|
+
`ancestors(depth_options).map(&:id)` or `ancestor_ids.slice(min_depth..max_depth)` instead.
|
214
183
|
|
215
184
|
node.ancestors(:from_depth => -6, :to_depth => -4)
|
216
185
|
node.path.from_depth(3).to_depth(4)
|
217
186
|
node.descendants(:from_depth => 2, :to_depth => 4)
|
218
187
|
node.subtree.from_depth(10).to_depth(12)
|
219
188
|
|
220
|
-
Please note that depth constraints cannot be passed to ancestor_ids and
|
221
|
-
path_ids. The reason for this is that both these relations can be fetched
|
222
|
-
directly from the ancestry column without performing a database query. It
|
223
|
-
would require an entirely different method of applying the depth constraints
|
224
|
-
which isn't worth the effort of implementing. You can use
|
225
|
-
ancestors(depth_options).map(&:id) or ancestor_ids.slice(min_depth..max_depth)
|
226
|
-
instead.
|
227
|
-
|
228
189
|
# STI support
|
229
190
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
you do only want nodes of a specific subclass you'll have to add a condition
|
234
|
-
on type for that.
|
191
|
+
To use with STI: create a STI inheritance hierarchy and build a tree from the different
|
192
|
+
classes/models. All Ancestry relations that were described above will return nodes of any model type. If
|
193
|
+
you do only want nodes of a specific subclass, a type condition is required.
|
235
194
|
|
236
195
|
# Arrangement
|
237
196
|
|
238
|
-
|
239
|
-
|
197
|
+
A subtree can be arranged into nested hashes for easy navigation after database retrieval.
|
198
|
+
`TreeNode.arrange` could, for instance, return:
|
240
199
|
|
241
200
|
```ruby
|
242
201
|
{
|
@@ -249,43 +208,20 @@ after retrieval from the database. TreeNode.arrange could for example return:
|
|
249
208
|
}
|
250
209
|
```
|
251
210
|
|
252
|
-
The arrange method
|
253
|
-
|
254
|
-
|
255
|
-
TreeNode.find_by_name('Crunchy').subtree.arrange
|
256
|
-
```
|
257
|
-
|
258
|
-
The arrange method takes `ActiveRecord` find options. If you want your hashes to
|
259
|
-
be ordered, you should pass the order to the arrange method instead of to the
|
260
|
-
scope. example:
|
211
|
+
The `arrange` method can work on a scoped class (`TreeNode.find_by(:name => 'Crunchy').subtree.arrange`),
|
212
|
+
and can take ActiveRecord find options. If you want ordered hashes, pass the order to the method instead of
|
213
|
+
the scope as follows:
|
261
214
|
|
262
|
-
|
263
|
-
TreeNode.find_by_name('Crunchy').subtree.arrange(:order => :name)
|
264
|
-
```
|
215
|
+
`TreeNode.find_by(:name => 'Crunchy').subtree.arrange(:order => :name)`.
|
265
216
|
|
266
|
-
|
217
|
+
The `arrange_serializable` method returns the arranged nodes as a nested array of hashes. Order
|
218
|
+
can be passed in the same fashion as to the `arrange` method:
|
219
|
+
`TreeNode.arrange_serializable(:order => :name)` The result can easily be serialized to json with `to_json`
|
220
|
+
or other formats. You can also supply your own serialization logic with blocks.
|
267
221
|
|
268
|
-
|
222
|
+
Using `ActiveModel` serializers:
|
269
223
|
|
270
|
-
|
271
|
-
[
|
272
|
-
{
|
273
|
-
"ancestry" => nil, "id" => 1, "children" => [
|
274
|
-
{ "ancestry" => "1", "id" => 2, "children" => [] }
|
275
|
-
]
|
276
|
-
}
|
277
|
-
]
|
278
|
-
```
|
279
|
-
|
280
|
-
You can also supply your own serialization logic using blocks:
|
281
|
-
|
282
|
-
For example, using `ActiveModel` Serializers:
|
283
|
-
|
284
|
-
```ruby
|
285
|
-
TreeNode.arrange_serializable do |parent, children|
|
286
|
-
MySerializer.new(parent, children: children)
|
287
|
-
end
|
288
|
-
```
|
224
|
+
`TreeNode.arrange_serializable { |parent, children| MySerializer.new(parent, children: children) }`.
|
289
225
|
|
290
226
|
Or plain hashes:
|
291
227
|
|
@@ -298,39 +234,22 @@ TreeNode.arrange_serializable do |parent, children|
|
|
298
234
|
end
|
299
235
|
```
|
300
236
|
|
301
|
-
The result of arrange_serializable can easily be serialized to json with
|
302
|
-
`to_json`, or some other format:
|
303
|
-
|
304
|
-
```
|
305
|
-
TreeNode.arrange_serializable.to_json
|
306
|
-
```
|
307
|
-
|
308
|
-
You can also pass the order to the arrange_serializable method just as you can
|
309
|
-
pass it to the arrange method:
|
310
|
-
|
311
|
-
```
|
312
|
-
TreeNode.arrange_serializable(:order => :name)
|
313
|
-
```
|
314
|
-
|
315
237
|
# Sorting
|
316
238
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
TreeNode.sort_by_ancestry(array_of_nodes)
|
322
|
-
```
|
323
|
-
|
324
|
-
Note that since materialised path trees don't support ordering within a rank,
|
325
|
-
the order of siblings depends on their order in the original array.
|
239
|
+
The `sort_by_ancestry` class method: `TreeNode.sort_by_ancestry(array_of_nodes)` can be used
|
240
|
+
to sort an array of nodes as if traversing in preorder. (Note that since materialised path
|
241
|
+
trees do not support ordering within a rank, the order of siblings is
|
242
|
+
dependant upon their original array order.)
|
326
243
|
|
327
244
|
# Migrating from plugin that uses parent_id column
|
328
245
|
|
329
246
|
Most current tree plugins use a parent_id column (has_ancestry,
|
330
|
-
awesome_nested_set, better_nested_set, acts_as_nested_set). With
|
331
|
-
easy to migrate from any of these plugins
|
332
|
-
build_ancestry_from_parent_ids
|
333
|
-
|
247
|
+
awesome_nested_set, better_nested_set, acts_as_nested_set). With Ancestry it is
|
248
|
+
easy to migrate from any of these plugins. To do so, use the
|
249
|
+
`build_ancestry_from_parent_ids!` method on your ancestry model.
|
250
|
+
|
251
|
+
<details>
|
252
|
+
<summary>Details</summary>
|
334
253
|
|
335
254
|
1. Add ancestry column to your table
|
336
255
|
* Create migration: **rails g migration [add_ancestry_to_](table)
|
@@ -340,7 +259,7 @@ provide a more detailed explanation:
|
|
340
259
|
* Migrate your database: **rake db:migrate**
|
341
260
|
|
342
261
|
|
343
|
-
2. Remove old tree gem and add in Ancestry to
|
262
|
+
2. Remove old tree gem and add in Ancestry to Gemfile
|
344
263
|
* See 'Installation' for more info on installing and configuring gems
|
345
264
|
|
346
265
|
|
@@ -351,7 +270,7 @@ provide a more detailed explanation:
|
|
351
270
|
|
352
271
|
|
353
272
|
4. Generate ancestry columns
|
354
|
-
* In
|
273
|
+
* In rails console: **[model].build_ancestry_from_parent_ids!**
|
355
274
|
* Make sure it worked ok: **[model].check_ancestry_integrity!**
|
356
275
|
|
357
276
|
|
@@ -365,42 +284,7 @@ provide a more detailed explanation:
|
|
365
284
|
* Create migration: `rails g migration [remove_parent_id_from_](table)`
|
366
285
|
* Add to migration: `remove_column [table], :parent_id`
|
367
286
|
* Migrate your database: `rake db:migrate`
|
368
|
-
|
369
|
-
# Integrity checking and restoration
|
370
|
-
|
371
|
-
I don't see any way Ancestry tree integrity could get compromised without
|
372
|
-
explicitly setting cyclic parents or invalid ancestry and circumventing
|
373
|
-
validation with update_attribute, if you do, please let me know.
|
374
|
-
|
375
|
-
Ancestry includes some methods for detecting integrity problems and restoring
|
376
|
-
integrity just to be sure. To check integrity use:
|
377
|
-
[Model].check_ancestry_integrity!. An AncestryIntegrityException will be
|
378
|
-
raised if there are any problems. You can also specify :report => :list to
|
379
|
-
return an array of exceptions or :report => :echo to echo any error messages.
|
380
|
-
To restore integrity use: [Model].restore_ancestry_integrity!.
|
381
|
-
|
382
|
-
For example, from IRB:
|
383
|
-
|
384
|
-
```
|
385
|
-
>> stinky = TreeNode.create :name => 'Stinky'
|
386
|
-
$ #<TreeNode id: 1, name: "Stinky", ancestry: nil>
|
387
|
-
>> squeeky = TreeNode.create :name => 'Squeeky', :parent => stinky
|
388
|
-
$ #<TreeNode id: 2, name: "Squeeky", ancestry: "1">
|
389
|
-
>> stinky.update_attribute :parent, squeeky
|
390
|
-
$ true
|
391
|
-
>> TreeNode.all
|
392
|
-
$ [#<TreeNode id: 1, name: "Stinky", ancestry: "1/2">, #<TreeNode id: 2, name: "Squeeky", ancestry: "1/2/1">]
|
393
|
-
>> TreeNode.check_ancestry_integrity!
|
394
|
-
!! Ancestry::AncestryIntegrityException: Conflicting parent id in node 1: 2 for node 1, expecting nil
|
395
|
-
>> TreeNode.restore_ancestry_integrity!
|
396
|
-
$ [#<TreeNode id: 1, name: "Stinky", ancestry: 2>, #<TreeNode id: 2, name: "Squeeky", ancestry: nil>]
|
397
|
-
```
|
398
|
-
|
399
|
-
Additionally, if you think something is wrong with your depth cache:
|
400
|
-
|
401
|
-
```
|
402
|
-
>> TreeNode.rebuild_depth_cache!
|
403
|
-
```
|
287
|
+
</details>
|
404
288
|
|
405
289
|
# Running Tests
|
406
290
|
|