module Genealogy::QueryMethods

Module QueryMethods provides methods to run genealogy queries to retrive relatives by role. It’s included by the genealogy enabled AR model

Public Instance Methods

ancestors(options = {}) click to toggle source

get list of known ancestrors iterateing over parents @param [Hash] options @option options [Symbol] generations lets you limit how many generations will be included in the output. @return [ActiveRecord::Relation] list of ancestors (limited by a number of generations if so indicated)

# File lib/genealogy/query_methods.rb, line 131
def ancestors(options = {})
  ids = []
  if options[:generations]
    raise ArgumentError, ":generations option must be an Integer" unless options[:generations].is_a? Integer
    generation_count = 0
    generation_ids = parents.compact.map(&:id)
    while (generation_count < options[:generations]) && (generation_ids.length > 0)
      next_gen_ids = []
      ids += generation_ids
      until generation_ids.empty?
        ids.unshift(generation_ids.shift)
        next_gen_ids += gclass.find(ids.first).parents.compact.map(&:id)
      end
      generation_ids = next_gen_ids
      generation_count += 1
    end
  else
    remaining_ids = parents.compact.map(&:id)
    until remaining_ids.empty?
      ids << remaining_ids.shift
      remaining_ids += gclass.find(ids.last).parents.compact.map(&:id)
    end
  end
  gclass.where(id: ids)
end
aunts(options = {}) click to toggle source

@see uncles_and_aunts

# File lib/genealogy/query_methods.rb, line 222
def aunts(options = {})
  uncles_and_aunts(options).females
end
children(options = {}) click to toggle source

@param [Hash] options @option options [Object] spouse to filter children by spouse @return [ActiveRecord::Relation] children

# File lib/genealogy/query_methods.rb, line 50
def children(options = {})
  raise SexError, "Sex value not valid for #{self}. It's needed to look for children" unless gclass.sex_values.include? sex_before_type_cast
  result = gclass.where("#{gclass::SEX2PARENT[ssex]}_id" => self)
  if options.keys.include? :spouse
    check_indiv(spouse = options[:spouse],opposite_ssex)
    result = result.where("#{gclass::SEX2PARENT[opposite_ssex]}_id" => spouse ) if spouse
  end
  result
end
cousins(options = {}) click to toggle source

@param [Hash] options @option options [Symbol] lineage to filter uncles by lineage: :paternal or :maternal @option options [Symbol] half to filter uncles (see siblings) @return [ActiveRecord::Relation] list of uncles and aunts’ children

# File lib/genealogy/query_methods.rb, line 250
def cousins(options = {})
  ids = uncles_and_aunts(options).inject([]){|memo,uncle| memo |= uncle.children.pluck(:id)}
  gclass.where(id: ids)
end
descendants(options = {}) click to toggle source

get list of known descendants iterateing over children … @param [Hash] options @option options [Symbol] generations lets you limit how many generations will be included in the output. @return [ActiveRecord::Relation] list of descendants (limited by a number of generations if so indicated)

# File lib/genealogy/query_methods.rb, line 162
def descendants(options = {})
  ids = []
  if options[:generations]
    generation_count = 0
    generation_ids = children.map(&:id)
    while (generation_count < options[:generations]) && (generation_ids.length > 0)
      next_gen_ids = []
      ids += generation_ids
      until generation_ids.empty?
        ids.unshift(generation_ids.shift)
        next_gen_ids += gclass.find(ids.first).children.map(&:id)
      end
      generation_ids = next_gen_ids
      generation_count += 1
    end
  else
    remaining_ids = children.map(&:id)
    until remaining_ids.empty?
      ids << remaining_ids.shift
      remaining_ids += gclass.find(ids.last).children.pluck(:id)
      # break if (remaining_ids - ids).empty? can be necessary in case of loop. Idem for ancestors method
    end
  end
  gclass.where(id: ids)
end
extended_family(options = {}) click to toggle source

family with option extended: :true @see family

# File lib/genealogy/query_methods.rb, line 355
def extended_family(options = {})
  family(options.merge(extended: true))
end
extended_family_hash(options = {}) click to toggle source

family_hash with option extended: :true @see family_hash

# File lib/genealogy/query_methods.rb, line 336
def extended_family_hash(options = {})
  family_hash(options.merge(extended: true))
end
family(options = {}) click to toggle source

family individuals @return [Array] @see family_hash

# File lib/genealogy/query_methods.rb, line 343
def family(options = {})
  family_hash(options).inject([]) do |memo, r|
    [r.last].compact.flatten.each do |relative|
      relative.role_as_relative = r.first.to_s.singularize.to_sym
      memo << relative
    end
    memo
  end
end
family_hash(options = {}) click to toggle source

family hash with roles as keys? :spouse and individuals as values. Defaults roles are :father, :mother, :children, :siblings and current_spouse if enabled @option options [Symbol] half to filter siblings (see siblings) @option options [Boolean] extended to include roles for grandparents, grandchildren, uncles, aunts, nieces, nephews and cousins @option options [Boolean] singular_role to use singularized role as keys @return [Hash] family hash with roles as keys? :spouse and individuals as values.

# File lib/genealogy/query_methods.rb, line 278
def family_hash(options = {})
  roles = [:father, :mother, :children, :siblings]
  roles += [:current_spouse] if self.class.current_spouse_enabled
  roles += case options[:half]
    when nil
      []
    when :include
      [:half_siblings]
    when :include_separately
      [:paternal_half_siblings, :maternal_half_siblings]
    when :father
      [:paternal_half_siblings]
    when :mother
      [:maternal_half_siblings]
    else
      raise ArgumentError, "Admitted values for :half options are: :father, :mother, :include, :include_separately, nil"
  end
  roles += [:paternal_grandfather, :paternal_grandmother, :maternal_grandfather, :maternal_grandmother, :grandchildren, :uncles_and_aunts, :nieces_and_nephews, :cousins] if options[:extended] == true
  res = roles.inject({}){|res,role| res.merge!({role => self.send(role)})}
  if options[:singular_role] == true
    res2 = {
      father: res[:father],
      mother: res[:mother],
      daughter: res[:children].females,
      son: res[:children].males,
      sister: res[:siblings].females,
      brother: res[:siblings].males
    }

    res2[:current_spouse] = res[:current_spouse] if res.has_key? :current_spouse

    res2.merge!({
      paternal_grandfather: res[:paternal_grandfather],
      paternal_grandmother: res[:paternal_grandmother],
      maternal_grandfather: res[:maternal_grandfather],
      maternal_grandmother: res[:maternal_grandmother],
      grandchild: res[:grandchildren],
      uncle: res[:uncles_and_aunts].males,
      aunts: res[:uncles_and_aunts].females,
      nephew: res[:nieces_and_nephews].males,
      niece: res[:nieces_and_nephews].females,
      cousin: res[:cousins]
    }) if options[:extended] == true

    res2.merge!({
      paternal_half_brother: res[:paternal_half_siblings].males,
      paternal_half_sister: res[:paternal_half_siblings].females,
      maternal_half_brother: res[:maternal_half_siblings].males,
      maternal_half_sister: res[:maternal_half_siblings].females,
    }) if options[:half] == :include_separately

    res = res2
  end
  res
end
grandchildren() click to toggle source

@return [ActiveRecord::Relation] list of grandchildren

# File lib/genealogy/query_methods.rb, line 189
def grandchildren
  result = children.inject([]){|memo,child| memo |= child.children}
end
grandparents() click to toggle source

@return [4-elements Array] paternal_grandfather, paternal_grandmother, maternal_grandfather, maternal_grandmother

# File lib/genealogy/query_methods.rb, line 38
def grandparents
  [paternal_grandfather, paternal_grandmother, maternal_grandfather, maternal_grandmother]
end
great_grandchildren() click to toggle source

@return [ActiveRecord::Relation] list of grat-grandchildren

# File lib/genealogy/query_methods.rb, line 194
def great_grandchildren
  result = grandchildren.compact.inject([]){|memo,grandchild| memo |= grandchild.children}
end
great_grandparents() click to toggle source

@return [8-elements Array] paternal_grandfather‘s father, paternal_grandmother’s father, maternal_grandfather‘s father, maternal_grandmother’s father, paternal_grandfather‘s mother, paternal_grandmother’s mother, maternal_grandfather‘s mother, maternal_grandmother’s mother

# File lib/genealogy/query_methods.rb, line 43
def great_grandparents
  grandparents.inject([]){|memo, gp| memo += (gp.try(:parents) || [nil,nil]) }.flatten
end
half_siblings(options = {}) click to toggle source

siblings with option half: :only @see siblings

# File lib/genealogy/query_methods.rb, line 111
def half_siblings(options = {})
  siblings(options.merge(half: :only))
end
maternal_aunts(options = {}) click to toggle source

@see uncles_and_aunts

# File lib/genealogy/query_methods.rb, line 242
def maternal_aunts(options = {})
  aunts(options.merge(lineage: :maternal))
end
maternal_grandfather() click to toggle source

@return [ActiveRecord, NilClass]

# File lib/genealogy/query_methods.rb, line 19
def maternal_grandfather
  mother && mother.father
end
maternal_grandmother() click to toggle source

@return [ActiveRecord, NilClass]

# File lib/genealogy/query_methods.rb, line 23
def maternal_grandmother
  mother && mother.mother
end
maternal_grandparents() click to toggle source

@return [2-elements Array] maternal_grandfather and maternal_grandmother

# File lib/genealogy/query_methods.rb, line 33
def maternal_grandparents
  (mother && mother.parents) || [nil,nil]
end
maternal_half_siblings(options = {}) click to toggle source

siblings with option half: :mother @see siblings

# File lib/genealogy/query_methods.rb, line 123
def maternal_half_siblings(options = {})
  siblings(options.merge(half: :mother))
end
maternal_uncles(options = {}) click to toggle source

@see uncles_and_aunts

# File lib/genealogy/query_methods.rb, line 232
def maternal_uncles(options = {})
  uncles(options.merge(lineage: :maternal))
end
nephews(options = {}) click to toggle source

@see nieces_and_nephews

# File lib/genealogy/query_methods.rb, line 264
def nephews(options = {})
  nieces_and_nephews.males
end
nieces(options = {}) click to toggle source

@see nieces_and_nephews

# File lib/genealogy/query_methods.rb, line 269
def nieces(options = {})
  nieces_and_nephews.females
end
nieces_and_nephews(options = {}) click to toggle source

@param [Hash] options @option options [Symbol] half to filter siblings (see siblings) @return [ActiveRecord::Relation] list of nieces and nephews

# File lib/genealogy/query_methods.rb, line 258
def nieces_and_nephews(options = {})
  ids = siblings(options).inject([]){|memo,sib| memo |= sib.children.pluck(:id)}
  gclass.where(id: ids)
end
parents() click to toggle source

@return [2-elements Array] father and mother

# File lib/genealogy/query_methods.rb, line 7
def parents
  [father,mother]
end
paternal_aunts(options = {}) click to toggle source

@see uncles_and_aunts

# File lib/genealogy/query_methods.rb, line 237
def paternal_aunts(options = {})
  aunts(options.merge(lineage: :paternal))
end
paternal_grandfather() click to toggle source

@return [ActiveRecord, NilClass]

# File lib/genealogy/query_methods.rb, line 11
def paternal_grandfather
  father && father.father
end
paternal_grandmother() click to toggle source

@return [ActiveRecord, NilClass]

# File lib/genealogy/query_methods.rb, line 15
def paternal_grandmother
  father && father.mother
end
paternal_grandparents() click to toggle source

@return [2-elements Array] paternal_grandfather and paternal_grandmother

# File lib/genealogy/query_methods.rb, line 28
def paternal_grandparents
  (father && father.parents) || [nil,nil]
end
paternal_half_siblings(options = {}) click to toggle source

siblings with option half: :father @see siblings

# File lib/genealogy/query_methods.rb, line 117
def paternal_half_siblings(options = {})
  siblings(options.merge(half: :father))
end
paternal_uncles(options = {}) click to toggle source

@see uncles_and_aunts

# File lib/genealogy/query_methods.rb, line 227
def paternal_uncles(options = {})
  uncles(options.merge(lineage: :paternal))
end
siblings(options = {}) click to toggle source

@param [Hash] options @option options [Symbol] half let you filter siblings. Possible values are:

:father for paternal halfsiblings
:mother for maternal halfsiblings
:only for all halfsiblings
:include for fullsiblings and halfsiblings

@return [ActiveRecord::Relation] list of fullsiblings and/or halfsiblings

# File lib/genealogy/query_methods.rb, line 72
def siblings(options = {})
  spouse = options[:spouse]
  result = gclass.where.not(id: id)
  case options[:half]
  when nil # only full siblings
    result.all_with(:parents).where(father_id: father, mother_id: mother)
  when :father # common father
    result = result.all_with(:father).where(father_id: father)
    if spouse
      check_indiv(spouse, :female)
      result.where(mother_id: spouse)
    elsif mother
      result.where("mother_id != ? or mother_id is ?", mother_id, nil)
    else
      result
    end
  when :mother # common mother
    result = result.all_with(:mother).where(mother_id: mother)
    if spouse
      check_indiv(spouse, :male)
      result.where(father_id: spouse)
    elsif father
      result.where("father_id != ? or father_id is ?", father_id, nil)
    else
      result
    end
  when :only # only half siblings
    ids = siblings(half: :father).pluck(:id) | siblings(half: :mother).pluck(:id)
    result.where(id: ids)
  when :include # including half siblings
    result.where("father_id = ? or mother_id = ?", father_id, mother_id)
  else
    raise ArgumentError, "Admitted values for :half options are: :father, :mother, false, true or nil"
  end

end
spouses() click to toggle source

@return [ActiveRecord::Relation] list of individuals with whom has had children

# File lib/genealogy/query_methods.rb, line 61
def spouses
  gclass.where(id: children.pluck("#{gclass::SEX2PARENT[opposite_ssex]}_id".to_sym).compact.uniq) | (self.class.current_spouse_enabled ? [] : [current_spouse])
end
uncles(options = {}) click to toggle source

@see uncles_and_aunts

# File lib/genealogy/query_methods.rb, line 217
def uncles(options = {})
  uncles_and_aunts(options).males
end
uncles_and_aunts(options={}) click to toggle source

list of uncles and aunts iterating through parents’ siblings @param [Hash] options @option options [Symbol] lineage to filter by lineage: :paternal or :maternal @option options [Symbol] half to filter by half siblings (see siblings) @return [ActiveRecord::Relation] list of uncles and aunts

# File lib/genealogy/query_methods.rb, line 203
def uncles_and_aunts(options={})
  relation = case options[:lineage]
  when :paternal
    [father]
  when :maternal
    [mother]
  else
    parents
  end
  ids = relation.compact.inject([]){|memo,parent| memo |= parent.siblings(half: options[:half]).pluck(:id)}
  gclass.where(id: ids)
end