Module: ContentfulModel::Associations::HasManyNested::ClassMethods
- Defined in:
- lib/contentful_model/associations/has_many_nested.rb
Overview
Class method
Instance Method Summary collapse
-
#has_many_nested(association_name, options = {}) ⇒ Object
has_many_nested allows you to set up a tree relationship it calls has_many and belongs_to_many on the class, and sets up some methods to find a deeply-nested instance’s parents.
- #has_many_nested_name ⇒ Object
Instance Method Details
#has_many_nested(association_name, options = {}) ⇒ Object
has_many_nested allows you to set up a tree relationship it calls has_many and belongs_to_many on the class, and sets up some methods to find a deeply-nested instance’s parents
To set this up in contentful, add a multi-entry field validated to the same model as the parent, and give it a name. For example, Page might have a field called childPages:
has_many_nested :child_pages, root: -> { Page.find(“some_id”) }
would setup up an instance attribute called parent_pages which lists all the direct parents of this page. It would also create methods to find a page based on an array of its ancestors, and generate an array of ancestors. Note that this builds an array of the ancestors which called the object; because ‘many’ associations in Contentful are actually ‘belongs_to_many’ from the child end, we might have several ancestors to a page. You will need to write your own recursion for this, because it’s probably an implementation-specific problem. rubocop:disable Style/PredicateName
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/contentful_model/associations/has_many_nested.rb', line 27 def has_many_nested(association_name, = {}) has_many association_name, class_name: to_s, inverse_of: :"parent_#{has_many_nested_name}" belongs_to_many :"parent_#{has_many_nested_name.pluralize}", class_name: to_s root_method = [:root] if [:root].is_a?(Proc) # If there's a root method defined, set up a class method called root_[class name]. In our example this would be # Page.root_page. # @return [Object] the root entity returned from the proc defined in has_many_nested if defined?(root_method) && root_method.is_a?(Proc) # @return [Object] the root entity define_method :"root_#{has_many_nested_name}" do root_method.call end end # A utility method which returns the parent object; saves passing around interpolated strings define_method :parent do parents = send(:"parent_#{self.class.has_many_nested_name.pluralize}") parents.first unless parents.nil? end # Determine if the object has any parents. If it doesn't, it's considered a root. # This only works if the objects are called through their parents' 'child_[whatever]' method define_method :root? do parent.nil? end # Iterate over parents until you reach the root. # @param [Proc] a block to call on each ancestor # @return [Enumerable] which you can iterate over define_method :find_ancestors do |&block| return enum_for(:find_ancestors) unless block if parent.nil? # this *is* the parent return self end block[parent] parent.find_ancestors { |a| block[a] } if parent && !parent.root? end # A utility method to return the results of `find_ancestors` as an array # @return [Array] of ancestors in reverse order (root last) define_method :ancestors do find_ancestors.to_a end # If this entry is the root, return self. # Otherwise, return the last member of the ancestors, which is the root # @return the root instance of this object define_method :root do return self if root? find_ancestors.to_a.last end # @return [Boolean] whether or not this instance has children define_method :children? do !send(association_name).empty? end # @return [Array] a collection of child objects, based on the association name define_method :children do send(association_name) end # @return [Hash] a hash of nested child objects define_method :nested_children do children.each_with_object({}) do |e, a| children = e.children? ? e.nested_children : nil a[e] = children end end # Return a nested hash of children, returning the field specified # @param field [Symbol] the field you want to return, nested for each child # @return [Hash] of nested children, by that field define_method :nested_children_by do |field| children.each_with_object({}) do |e, a| children = e.children? ? e.nested_children_by(field) : nil a[e.send(field)] = children end end # Return a flattened hash of children by the specified field define_method :all_child_paths_by do |field, opts = {}| = { prefix: nil }.merge!(opts) flatten_hash(nested_children_by(field)).keys.collect do |path| [:prefix] ? path.unshift([:prefix]) : path end end # Search for a child by a certain field. This is called on the parent(s). # e.g. Page.root.find_child_path_by(:slug, "some-slug"). Accepts a prefix if you want to # prefix the children with the parent define_method :find_child_path_by do |field, value, opts = {}| all_child_paths_by(field, opts).select { |child| child.include?(value) } end # Private method to flatten a hash. Courtesy Cary Swoveland http://stackoverflow.com/a/23861946 define_method :flatten_hash do |h, f = [], g = {}| return g.update(f => h) unless h.is_a? Hash h.each { |k, r| flatten_hash(r, f + [k], g) } g end send(:private, :flatten_hash) end |
#has_many_nested_name ⇒ Object
135 136 137 |
# File 'lib/contentful_model/associations/has_many_nested.rb', line 135 def has_many_nested_name to_s.demodulize.underscore end |