mongoid-sleeping_king_studios 0.3.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9872d41d59ce51e97ead17d8219802dd80df2069
4
- data.tar.gz: 008ca4394448aa380c101d1aad7b719ca80d7416
3
+ metadata.gz: cc9bfc29b8eaf26a12fdf6e8779468794c67d352
4
+ data.tar.gz: a927885e26c3aa375696654acc8d412b96ecc5b6
5
5
  SHA512:
6
- metadata.gz: 3a8324ecbcdc29f818bf8e2b83b82b48b1df78e1c0ad45938000acf86b388e35fb0eefb8816dfe956b6f63ae8af482aad997f61e352e1a27842060a1c1032c2c
7
- data.tar.gz: 67391ada74283759fd019872dbcb2ca97817c72f2e39c35ff52f226fd157529c4917bf436b0165f2b87bd4eabd80ebb90a6a0a5e8904c793d06494f25424e86a
6
+ metadata.gz: c2913f5069f689cbeedf2d8257096fb88a90755971ea45924771a1e8d3f09c1ed9af2acfd97e57a9e816bceb08b5a083245af455860f6888899d45f9f57b401d
7
+ data.tar.gz: a0216507421c39e39548b138cb9277a185a0fbd1db69594975e6ecf256fc3e78e4aab7655db2eb45b11584b260354f3e24b588743eb80470accb118ca044da8b
data/README.md CHANGED
@@ -5,77 +5,87 @@ documents and collections.
5
5
 
6
6
  ## The Mixins
7
7
 
8
- ### Sluggable
8
+ ### HasTree
9
9
 
10
- require 'mongoid/sleeping_king_studios/sluggable'
10
+ require 'mongoid/sleeping_king_studios/has_tree'
11
11
 
12
- Adds a slug field that automatically tracks a base attribute and stores a
13
- short, url-friendly version.
12
+ Sets up a basic tree structure by adding belongs_to :parent and has_many
13
+ :children relations, as well as some helper methods.
14
+
15
+ From 0.2.0 to 0.3.1, was Mongoid::SleepingKingStudios::Tree
14
16
 
15
17
  **How To Use:**
16
18
 
17
- class SluggableDocument
19
+ class TreeDocument
18
20
  include Mongoid::Document
19
- include Mongoid::SleepingKingStudios::Sluggable
20
-
21
- field :title, :type => String
21
+ include Mongoid::SleepingKingStudios::HasTree
22
22
 
23
- slugify :title
23
+ has_tree
24
24
  end # class
25
25
 
26
26
  #### Options
27
27
 
28
- ##### Lockable
28
+ You can pass customisation options for the generated relations into the
29
+ ::has\_tree method.
29
30
 
30
- slugify :title, :lockable => true
31
-
32
- Allows the slug to be specified manually. Adds an additional slug_lock field
33
- that is automatically set to true when #slug= is called. To resume tracking the
34
- base attribute, set :slug_lock to false.
31
+ class EvilEmployee
32
+ include Mongoid::Document
33
+ include Mongoid::SleepingKingStudios::HasTree
35
34
 
36
- ### Tree
35
+ has_tree :parent => { :relation_name => :overlord },
36
+ :children => { :relation_name => :minions, :dependent => :destroy }
37
+ end # class
37
38
 
38
- require 'mongoid/sleeping_king_studios/tree'
39
+ Available options include the standard Mongoid options for a :belongs_to and a
40
+ :has_many relationship, respectively. In addition, you can set a :relation_name
41
+ option to change the name of the created relation (see example above). The
42
+ concern will automatically update the respective :inverse_of options to match
43
+ the updated relation names.
39
44
 
40
- Sets up a basic tree structure by adding belongs_to :parent and has_many
41
- :children relations, as well as some helper methods.
45
+ #### Cache Ancestry
42
46
 
43
- **How To Use:**
47
+ Stores the chain of ancestors in an :ancestor_ids array field, and adds the
48
+ \#ancestors and #descendents scopes.
44
49
 
45
- class TreeDocument
50
+ class AncestryTree
46
51
  include Mongoid::Document
47
- include Mongoid::SleepingKingStudios::Tree
52
+ include Mongoid::SleepingKingStudios::HasTree
53
+
54
+ has_tree :cache_ancestry => true
48
55
  end # class
49
56
 
50
- #### Options
57
+ **Warning:** using this option will make many write operations much, much
58
+ slower and more resource-intensive. Do not use this option outside of
59
+ read-heavy applications with very specific requirements, e.g. a directory
60
+ structure where you must access all parent directories on each page view.
51
61
 
52
- To customise the created #parent and #children relations, you can define
53
- ::options_for_parent and ::options_for_children class methods before including
54
- the Tree concern. These methods must return a hash if defined.
62
+ ### Sluggable
55
63
 
56
- As of 0.3.1, you can change the name of the class methods used to find the
57
- customised options by setting Tree.options_for_parent_name and
58
- Tree.options_for_children_name.
64
+ require 'mongoid/sleeping_king_studios/sluggable'
59
65
 
60
- class EvilEmployee
61
- include Mongoid::Document
66
+ Adds a slug field that automatically tracks a base attribute and stores a
67
+ short, url-friendly version.
62
68
 
63
- def self.options_for_parent
64
- { :relation_name => :overlord }
65
- end # class method options_for_parent
69
+ **How To Use:**
66
70
 
67
- def self.options_for_children
68
- { :relation_name => :minions, :dependent => :destroy }
69
- end # class method options_for_children
71
+ class SluggableDocument
72
+ include Mongoid::Document
73
+ include Mongoid::SleepingKingStudios::Sluggable
74
+
75
+ field :title, :type => String
70
76
 
71
- include Mongoid::SleepingKingStudios::Tree
77
+ slugify :title
72
78
  end # class
73
79
 
74
- Available options include the default Mongoid options for a :belongs_to and a
75
- :has_many relationship, respectively. In addition, you can set a :relation_name
76
- option to change the name of the created relation (see example above). The
77
- concern will automatically update the respective :inverse_of options to match
78
- the updated relation names.
80
+ #### Options
81
+
82
+ ##### Lockable
83
+
84
+ slugify :title, :lockable => true
85
+
86
+ Allows the slug to be specified manually. Adds an additional slug_lock field
87
+ that is automatically set to true when #slug= is called. To resume tracking the
88
+ base attribute, set :slug_lock to false.
79
89
 
80
90
  ## License
81
91
 
@@ -0,0 +1,9 @@
1
+ # lib/mongoid/sleeping_king_studios/error.rb
2
+
3
+ require 'mongoid/sleeping_king_studios'
4
+
5
+ module Mongoid::SleepingKingStudios
6
+ # Base class for errors thrown by extensions in the
7
+ # Mongoid::SleepingKingStudios namespace.
8
+ class Error < StandardError; end
9
+ end # module
@@ -0,0 +1,169 @@
1
+ # lib/mongoid/sleeping_king_studios/tree.rb
2
+
3
+ require 'mongoid/sleeping_king_studios'
4
+ require 'mongoid/sleeping_king_studios/has_tree/cache_ancestry'
5
+
6
+ module Mongoid::SleepingKingStudios
7
+ # Adds a belongs_to parent relation and a has_many children relation to set
8
+ # up a basic tree structure, as well as several helper methods.
9
+ #
10
+ # @note From 0.2.0 to 0.3.1, was Mongoid::SleepingKingStudios::Tree.
11
+ #
12
+ # @example Setting up the tree:
13
+ # class SluggableDocument
14
+ # include Mongoid::Document
15
+ # include Mongoid::SleepingKingStudios::Tree
16
+ #
17
+ # has_tree
18
+ # end # class
19
+ #
20
+ # Since 0.4.1, you must call the class method ::has_tree in order to set up
21
+ # the parent and children relations. You can pass optional parameters into
22
+ # this method to customise the created relations, including the names of the
23
+ # relations.
24
+ #
25
+ # @example Setting up the tree with alternate relation names:
26
+ # class EvilEmployee
27
+ # include Mongoid::Document
28
+ # include Mongoid::SleepingKingStudios::Tree
29
+ #
30
+ # has_tree :parent => { :relation_name => 'overlord' },
31
+ # :children => { :relation_name => 'minions', :dependent => :destroy }
32
+ # end # class
33
+ #
34
+ # @since 0.2.0
35
+ module HasTree
36
+ extend ActiveSupport::Concern
37
+
38
+ # Get the valid options allowed with this concern.
39
+ #
40
+ # @return [ Array<Symbol> ] The valid options.
41
+ #
42
+ # @since 0.4.1
43
+ def self.valid_options
44
+ %i(
45
+ cache_ancestry
46
+ children
47
+ parent
48
+ ) # end Array
49
+ end # class method valid_options
50
+
51
+ # @!method parent
52
+ # Returns the parent object, or nil if the object is a root.
53
+ #
54
+ # @return [Tree, nil]
55
+
56
+ # @!method children
57
+ # Returns the list of child objects.
58
+ #
59
+ # @return [Array<Tree>]
60
+
61
+ # Class methods added to the base class via #extend.
62
+ module ClassMethods
63
+ # @overload has_tree(options = {})
64
+ # Sets up the relations necessary for the tree structure.
65
+ #
66
+ # @param [Hash] options The options for the relation and the concern as a
67
+ # whole.
68
+ # @option options [Hash] :parent ({}) The options for the parent
69
+ # relation. Supports the :relation_name option, which sets the name of
70
+ # the tree's :belongs_to relation, as well as any options normally
71
+ # supported by a :belongs_to relation.
72
+ # @option options [Hash] :children ({}) The options for the children
73
+ # relation. Supports the :relation_name options, which sets the name of
74
+ # the tree's :has_many relation, as well as any options normally
75
+ # supported by a :has_many relation.
76
+ # @option options [Boolean] :cache_ancestry (false) Stores the chain of
77
+ # ancestors in an :ancestor_ids array field. Adds the #ancestors and
78
+ # #descendents scopes.
79
+ #
80
+ # Warning: using this option will make many write operations much,
81
+ # much slower and more resource-intensive. Do not use this option
82
+ # outside of read-heavy applications with very specific requirements,
83
+ # e.g. a directory structure where you must access all parent
84
+ # directories on each page view.
85
+ #
86
+ # @see Mongoid::SleepingKingStudios::HasTree::CacheAncestry
87
+ #
88
+ # @raise [ Mongoid::Errors::InvalidOptions ] If the options are invalid.
89
+ #
90
+ # @since 0.4.0
91
+ def has_tree **options
92
+ validate_options options
93
+
94
+ # Create Relations
95
+ p_opts = { :relation_name => :parent, :class_name => self.name }
96
+ c_opts = { :relation_name => :children, :class_name => self.name }
97
+
98
+ p_opts.update(options[:parent]) if Hash === options[:parent]
99
+ c_opts.update(options[:children]) if Hash === options[:children]
100
+
101
+ p_opts.update :inverse_of => c_opts[:relation_name]
102
+ c_opts.update :inverse_of => p_opts[:relation_name]
103
+
104
+ belongs_to p_opts.delete(:relation_name), p_opts
105
+ has_many c_opts.delete(:relation_name), c_opts
106
+
107
+ options[:parent] = p_opts
108
+ options[:children] = c_opts
109
+
110
+ # Set Up Ancestry Cache
111
+ if options.has_key? :cache_ancestry
112
+ self.send :include, Mongoid::SleepingKingStudios::HasTree::CacheAncestry
113
+ self.send :cache_ancestry, **options
114
+ end # if
115
+
116
+ self
117
+ end # class method has_tree
118
+
119
+ # Returns a Criteria specifying all root objects, e.g. objects with no
120
+ # parent object.
121
+ #
122
+ # @return [Mongoid::Criteria]
123
+ def roots
124
+ where({ :parent_id => nil })
125
+ end # scope routes
126
+
127
+ private
128
+
129
+ # Determine if the provided options are valid for the concern.
130
+ #
131
+ # @param [ Hash ] options The options to check.
132
+ #
133
+ # @raise [ Mongoid::Errors::InvalidOptions ] If the options are invalid.
134
+ def validate_options options
135
+ valid_options = Mongoid::SleepingKingStudios::HasTree.valid_options
136
+ options.keys.each do |key|
137
+ if !valid_options.include?(key)
138
+ raise Mongoid::Errors::InvalidOptions.new(
139
+ :has_tree,
140
+ key,
141
+ valid_options
142
+ ) # end InvalidOptions
143
+ end # if
144
+ end # each
145
+ end # class method validate_options
146
+ end # module
147
+
148
+ # Returns the root object of the current object's tree.
149
+ #
150
+ # @return [Tree]
151
+ def root
152
+ parent ? parent.root : self
153
+ end # method root
154
+
155
+ # Returns true if the object is a leaf object, e.g. has no child objects.
156
+ #
157
+ # @return [Boolean] True if the object has no children; otherwise false.
158
+ def leaf?
159
+ children.empty?
160
+ end # method leaf?
161
+
162
+ # Returns true if the object is a root object, e.g. has no parent object.
163
+ #
164
+ # @return [Boolean] True if the object has no parent; otherwise false.
165
+ def root?
166
+ parent.nil?
167
+ end # method root?
168
+ end # module
169
+ end # module
@@ -0,0 +1,150 @@
1
+ # lib/mongoid/sleeping_king_studios/has_tree/cache_ancestry.rb
2
+
3
+ require 'mongoid/sleeping_king_studios'
4
+ require 'mongoid/sleeping_king_studios/has_tree/errors'
5
+
6
+ module Mongoid::SleepingKingStudios
7
+ module HasTree
8
+ # Adds #ancestors and #descendents methods for accessing ancestors and
9
+ # subtrees with a single read operation. Do not include this module
10
+ # directly; rather, add a :cache_ancestry => true options to the call
11
+ # to ::has_tree.
12
+ #
13
+ # @example Setting up a tree with ancestry cache:
14
+ # class SluggableDocument
15
+ # include Mongoid::Document
16
+ # include Mongoid::SleepingKingStudios::Tree
17
+ #
18
+ # has_tree :cache_ancestry => true
19
+ # end # class
20
+ #
21
+ # @since 0.5.0
22
+ module CacheAncestry
23
+ extend ActiveSupport::Concern
24
+
25
+ # @!attribute [r] ancestor_ids
26
+ # Stores the ids of the object's ancestors, starting from the root
27
+ # object of the current subtree to the object's current parent. If
28
+ # the object has no parent, returns an empty array.
29
+ #
30
+ # @return [Array] The ancestors' ids.
31
+ #
32
+ # @since 0.5.0
33
+
34
+ # Class methods added to the base class via #extend.
35
+ module ClassMethods
36
+ # @overload cache_ancestry()
37
+ # Adds the :ancestry_id field, the #ancestors and #descendents
38
+ # scopes, and redefines #parent_id= to update the :ancestry_id
39
+ # field on the object and its descendents.
40
+ #
41
+ # Do not call this method directly; rather, add a
42
+ # :cache_ancestry => true options to the call to ::has_tree.
43
+ def cache_ancestry **options
44
+ parent_name = options[:children][:inverse_of]
45
+ children_name = options[:parent][:inverse_of]
46
+
47
+ field :ancestor_ids, :type => Array, :default => []
48
+
49
+ alias_method :"set_parent_id", :"#{parent_name}_id="
50
+ private :set_parent_id
51
+ re_define_method "#{parent_name}_id=" do |value|
52
+ old_ancestor_ids = ancestor_ids.dup
53
+
54
+ set_parent_id value
55
+ new_ancestor_ids = parent ? parent.ancestor_ids + [parent.id] : []
56
+
57
+ descendents.each do |descendent|
58
+ ary = descendent.ancestor_ids.dup
59
+ ary[0..old_ancestor_ids.count] = new_ancestor_ids + [id]
60
+ descendent.update_attributes :ancestor_ids => ary
61
+ end # each
62
+
63
+ self.send :ancestor_ids=, new_ancestor_ids
64
+ end # method #{parent_name}_id=
65
+ end # class method cache_ancestry
66
+ end # module ClassMethods
67
+
68
+ # Returns an array of the current object's ancestors, from the root
69
+ # object to the current parent. If the object has no parent, returns an
70
+ # empty array. If an error is raised, consider calling #rebuild_ancestry!
71
+ #
72
+ # @raise [Mongoid::SleepingKingStudios::HasTree::Errors::MissingAncestor]
73
+ # If one or more of the ancestors is not found in the datastore (the id
74
+ # is wrong, the object is not persisted, there is a nil value in
75
+ # ancestor_ids, and so on).
76
+ #
77
+ # @return [Array] The objects' ancestors
78
+ def ancestors
79
+ self.class.find(ancestor_ids)
80
+ rescue Mongoid::Errors::DocumentNotFound, Mongoid::Errors::InvalidFind
81
+ raise Mongoid::SleepingKingStudios::HasTree::Errors::MissingAncestor.new ancestor_ids
82
+ end # method ancestors
83
+
84
+ # Returns a scope for all of the descendents of the current object, i.e.
85
+ # all objects that have the current object as an ancestor.
86
+ #
87
+ # @return [Mongoid::Criteria] The criteria for finding the descendents.
88
+ def descendents
89
+ criteria = self.class.all
90
+
91
+ ancestor_ids.each_with_index do |ancestor_id, index|
92
+ criteria = criteria.where(:"ancestor_ids.#{index}" => ancestor_id)
93
+ end # each
94
+
95
+ criteria.where(:"ancestor_ids.#{ancestor_ids.count}" => id)
96
+ end # scope descendents
97
+
98
+ # Travels up the tree using the #parent method and saves the ancestors to
99
+ # the :ancestor_ids field. This overwrites the value of :ancestor_ids on
100
+ # the current object, but not on any of its ancestors.
101
+ #
102
+ # @raise [Mongoid::SleepingKingStudios::HasTree::Errors::MissingAncestor]
103
+ # If the current object or an ancestor has an invalid #parent.
104
+ def rebuild_ancestry!
105
+ ary, object = [], self
106
+ while object.parent
107
+ ary.unshift object.parent.id
108
+ object = object.parent
109
+ end # while
110
+ self.send :ancestor_ids=, ary
111
+ rescue Mongoid::Errors::DocumentNotFound
112
+ raise Mongoid::SleepingKingStudios::HasTree::Errors::MissingAncestor.new object.parent_id
113
+ end # method rebuild_ancestry!
114
+
115
+ # Travels up the tree using the :ancestor_ids and ensures that each
116
+ # ancestor exists and is persisted to the database, and that the
117
+ # object's parent correctly matches the last value in its own
118
+ # :ancestor_ids field.
119
+ #
120
+ # @raise [Mongoid::SleepingKingStudios::HasTree::Errors::MissingAncestor]
121
+ # If any of the ancestors is not found in the datastore (the id is
122
+ # wrong, the object is not persisted, there is a nil value in
123
+ # ancestor_ids, and so on).
124
+ #
125
+ # @raise [Mongoid::SleepingKingStudios::HasTree::Errors::UnexpectedAncestor]
126
+ # If there is a mismatch between an object's #parent and the last value
127
+ # in the object's :ancestor_ids field.
128
+ def validate_ancestry!
129
+ return if ancestor_ids.empty?
130
+
131
+ ancestors = []
132
+ ancestor_ids.each_with_index do |ancestor_id, index|
133
+ begin
134
+ ancestor = self.class.find(ancestor_id)
135
+ ancestors << ancestor
136
+
137
+ if index > 0 && ancestor.parent_id != ancestor_ids[index - 1]
138
+ # If the ancestor's parent is not the same as the previous
139
+ # ancestor.
140
+ raise Mongoid::SleepingKingStudios::HasTree::Errors::UnexpectedAncestor.new ancestor.parent_id, ancestor_ids[index - 1]
141
+ end # if
142
+ rescue Mongoid::Errors::InvalidFind, Mongoid::Errors::DocumentNotFound
143
+ # If the ancestor id is nil, or the ancestor does not exist.
144
+ raise Mongoid::SleepingKingStudios::HasTree::Errors::MissingAncestor.new ancestor_id
145
+ end # begin-rescue
146
+ end # each
147
+ end # method validate_ancestry!
148
+ end # module
149
+ end # module
150
+ end # module
@@ -0,0 +1,24 @@
1
+ # lib/mongoid/sleeping_king_studios/has_tree/errors.rb
2
+
3
+ require 'mongoid/sleeping_king_studios/error'
4
+
5
+ module Mongoid::SleepingKingStudios
6
+ module HasTree
7
+ module Errors
8
+ class MissingAncestor < Mongoid::SleepingKingStudios::Error
9
+ def initialize ancestor_id
10
+ message = Array === ancestor_id ?
11
+ 'ancestors with ids' :
12
+ 'ancestor with id'
13
+ super "unable to find #{message} #{ancestor_id.inspect}"
14
+ end # constructor
15
+ end # class
16
+
17
+ class UnexpectedAncestor < Mongoid::SleepingKingStudios::Error
18
+ def initialize expected_id, received_id
19
+ super "expected ancestor with id #{expected_id.inspect}, but received id #{received_id.inspect}"
20
+ end # constructor
21
+ end # class
22
+ end # module Errors
23
+ end # module
24
+ end # module
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Mongoid
4
4
  module SleepingKingStudios
5
- VERSION = '0.3.1'
5
+ VERSION = '0.5.0'
6
6
  end # module
7
7
  end # module
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-sleeping_king_studios
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob "Merlin" Smith
@@ -146,8 +146,11 @@ executables: []
146
146
  extensions: []
147
147
  extra_rdoc_files: []
148
148
  files:
149
+ - lib/mongoid/sleeping_king_studios/error.rb
150
+ - lib/mongoid/sleeping_king_studios/has_tree/cache_ancestry.rb
151
+ - lib/mongoid/sleeping_king_studios/has_tree/errors.rb
152
+ - lib/mongoid/sleeping_king_studios/has_tree.rb
149
153
  - lib/mongoid/sleeping_king_studios/sluggable.rb
150
- - lib/mongoid/sleeping_king_studios/tree.rb
151
154
  - lib/mongoid/sleeping_king_studios/version.rb
152
155
  - lib/mongoid/sleeping_king_studios.rb
153
156
  - LICENSE
@@ -1,126 +0,0 @@
1
- # lib/mongoid/sleeping_king_studios/tree.rb
2
-
3
- require 'mongoid/sleeping_king_studios'
4
-
5
- module Mongoid::SleepingKingStudios
6
- # Adds a belongs_to parent relation and a has_many children relation to set
7
- # up a basic tree structure, as well as several helper methods.
8
- #
9
- # @example Setting up the tree:
10
- # class SluggableDocument
11
- # include Mongoid::Document
12
- # include Mongoid::SleepingKingStudios::Tree
13
- # end # class
14
- #
15
- # Since 0.3.0, you can customise the generated :parent and :children
16
- # relations by defining optional class methods ::options_for_parent and
17
- # ::options_for_children, which must return hashes of valid options. These
18
- # must be defined prior to including this mixin, or the default options will
19
- # be applied instead.
20
- #
21
- # In addition, you can customise the names of the relations by adding a
22
- # :relation_name key to either ::options_for hash. The concern will
23
- # automatically update the respective :inverse_of options to match the
24
- # updated relation names.
25
- #
26
- # Since 0.3.1, you can additionally customise the name of the class methods
27
- # used to determine the customised options by changing the value of
28
- # Tree.options_for_parent_name and Tree.options_for_children_name.
29
- #
30
- # @example Setting up the tree with alternate relation names:
31
- # class EvilEmployee
32
- # include Mongoid::Document
33
- #
34
- # def self.options_for_parent
35
- # { :relation_name => "overlord" }
36
- # end # class method options_for_parent
37
- #
38
- # def self.options_for_children
39
- # { :relation_name => "minions", :dependent => :destroy }
40
- # end # class method options_for_children
41
- #
42
- # include Mongoid::SleepingKingStudios::Tree
43
- # end # class
44
- #
45
- # @since 0.2.0
46
- module Tree
47
- extend ActiveSupport::Concern
48
-
49
- class << self
50
- # Gets the name of the method on the base module that is used to find
51
- # customisation options for the :parent relation.
52
- #
53
- # @return [Symbol] By default, returns :options_for_parent.
54
- #
55
- # @since 0.3.1
56
- attr_accessor :options_for_parent_name
57
-
58
- # Gets the name of the method on the base module that is used to find
59
- # customisation options for the :children relation.
60
- #
61
- # @return [Symbol] By default, returns :options_for_children.
62
- #
63
- # @since 0.3.1
64
- attr_accessor :options_for_children_name
65
- end # class << self
66
-
67
- self.options_for_parent_name = :options_for_parent
68
- self.options_for_children_name = :options_for_children
69
-
70
- # @!method parent
71
- # Returns the parent object, or nil if the object is a root.
72
- #
73
- # @return [Tree, nil]
74
-
75
- # @!method children
76
- # Returns the list of child objects.
77
- #
78
- # @return [Array<Tree>]
79
-
80
- included do |base|
81
- p_opts = { :relation_name => :parent, :class_name => base.name }
82
- c_opts = { :relation_name => :children, :class_name => base.name }
83
-
84
- p_opts.update(send Tree.options_for_parent_name) if respond_to?(Tree.options_for_parent_name)
85
- c_opts.update(send Tree.options_for_children_name) if respond_to?(Tree.options_for_children_name)
86
-
87
- p_opts.update :inverse_of => c_opts[:relation_name]
88
- c_opts.update :inverse_of => p_opts[:relation_name]
89
-
90
- belongs_to p_opts.delete(:relation_name), p_opts
91
- has_many c_opts.delete(:relation_name), c_opts
92
- end # included
93
-
94
- # Class methods added to the base class via #extend.
95
- module ClassMethods
96
- # Returns a Criteria specifying all root objects, e.g. objects with no
97
- # parent object.
98
- #
99
- # @return [Mongoid::Criteria]
100
- def roots
101
- where({ :parent_id => nil })
102
- end # scope routes
103
- end # module
104
-
105
- # Returns the root object of the current object's tree.
106
- #
107
- # @return [Tree]
108
- def root
109
- parent ? parent.root : self
110
- end # method root
111
-
112
- # Returns true if the object is a leaf object, e.g. has no child objects.
113
- #
114
- # @return [Boolean] True if the object has no children; otherwise false.
115
- def leaf?
116
- children.empty?
117
- end # method leaf?
118
-
119
- # Returns true if the object is a root object, e.g. has no parent object.
120
- #
121
- # @return [Boolean] True if the object has no parent; otherwise false.
122
- def root?
123
- parent.nil?
124
- end # method root?
125
- end # module
126
- end # module