Module: Genealogy::QueryMethods

Extended by:
ActiveSupport::Concern
Defined in:
lib/genealogy/query_methods.rb

Overview

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

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#ancestors(options = {}) ⇒ ActiveRecord::Relation

get list of known ancestrors iterateing over parents

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • generations (Symbol)

    lets you limit how many generations will be included in the output.

Returns:

  • (ActiveRecord::Relation)

    list of ancestors (limited by a number of generations if so indicated)



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# 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 = {}) ⇒ Object

See Also:



222
223
224
# File 'lib/genealogy/query_methods.rb', line 222

def aunts(options = {})
  uncles_and_aunts(options).females
end

#children(options = {}) ⇒ ActiveRecord::Relation

Returns children.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • spouse (Object)

    to filter children by spouse

Returns:

  • (ActiveRecord::Relation)

    children

Raises:



50
51
52
53
54
55
56
57
58
# 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 = {}) ⇒ ActiveRecord::Relation

Returns list of uncles and aunts' children.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • lineage (Symbol)

    to filter uncles by lineage: :paternal or :maternal

  • half (Symbol)

    to filter uncles (see #siblings)

Returns:

  • (ActiveRecord::Relation)

    list of uncles and aunts' children



250
251
252
253
# 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 = {}) ⇒ ActiveRecord::Relation

get list of known descendants iterateing over children …

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • generations (Symbol)

    lets you limit how many generations will be included in the output.

Returns:

  • (ActiveRecord::Relation)

    list of descendants (limited by a number of generations if so indicated)



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# 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 = {}) ⇒ Object

family with option extended: :true

See Also:



355
356
357
# File 'lib/genealogy/query_methods.rb', line 355

def extended_family(options = {})
  family(options.merge(extended: true))
end

#extended_family_hash(options = {}) ⇒ Object

family_hash with option extended: :true

See Also:



336
337
338
# File 'lib/genealogy/query_methods.rb', line 336

def extended_family_hash(options = {})
  family_hash(options.merge(extended: true))
end

#family(options = {}) ⇒ Array

family individuals

Returns:

  • (Array)

See Also:



343
344
345
346
347
348
349
350
351
# 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 = {}) ⇒ Hash

family hash with roles as keys? :spouse and individuals as values. Defaults roles are :father, :mother, :children, :siblings and current_spouse if enabled

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • half (Symbol)

    to filter siblings (see #siblings)

  • extended (Boolean)

    to include roles for grandparents, grandchildren, uncles, aunts, nieces, nephews and cousins

  • singular_role (Boolean)

    to use singularized role as keys

Returns:

  • (Hash)

    family hash with roles as keys? :spouse and individuals as values.



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# 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

#grandchildrenActiveRecord::Relation

Returns list of grandchildren.

Returns:

  • (ActiveRecord::Relation)

    list of grandchildren



189
190
191
# File 'lib/genealogy/query_methods.rb', line 189

def grandchildren
  result = children.inject([]){|memo,child| memo |= child.children}
end

#grandparents4-elements Array

Returns paternal_grandfather, paternal_grandmother, maternal_grandfather, maternal_grandmother.

Returns:

  • (4-elements Array)

    paternal_grandfather, paternal_grandmother, maternal_grandfather, maternal_grandmother



38
39
40
# File 'lib/genealogy/query_methods.rb', line 38

def grandparents
  [paternal_grandfather, paternal_grandmother, maternal_grandfather, maternal_grandmother]
end

#great_grandchildrenActiveRecord::Relation

Returns list of grat-grandchildren.

Returns:

  • (ActiveRecord::Relation)

    list of grat-grandchildren



194
195
196
# File 'lib/genealogy/query_methods.rb', line 194

def great_grandchildren
  result = grandchildren.compact.inject([]){|memo,grandchild| memo |= grandchild.children}
end

#great_grandparents8-elements Array

Returns 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.

Returns:

  • (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



43
44
45
# 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 = {}) ⇒ Object

siblings with option half: :only

See Also:



111
112
113
# File 'lib/genealogy/query_methods.rb', line 111

def half_siblings(options = {})
  siblings(options.merge(half: :only))
end

#maternal_aunts(options = {}) ⇒ Object

See Also:



242
243
244
# File 'lib/genealogy/query_methods.rb', line 242

def maternal_aunts(options = {})
  aunts(options.merge(lineage: :maternal))
end

#maternal_grandfatherActiveRecord, NilClass

Returns:

  • (ActiveRecord, NilClass)


19
20
21
# File 'lib/genealogy/query_methods.rb', line 19

def maternal_grandfather
  mother && mother.father
end

#maternal_grandmotherActiveRecord, NilClass

Returns:

  • (ActiveRecord, NilClass)


23
24
25
# File 'lib/genealogy/query_methods.rb', line 23

def maternal_grandmother
  mother && mother.mother
end

#maternal_grandparents2-elements Array

Returns maternal_grandfather and maternal_grandmother.

Returns:

  • (2-elements Array)

    maternal_grandfather and maternal_grandmother



33
34
35
# File 'lib/genealogy/query_methods.rb', line 33

def maternal_grandparents
  (mother && mother.parents) || [nil,nil]
end

#maternal_half_siblings(options = {}) ⇒ Object

siblings with option half: :mother

See Also:



123
124
125
# File 'lib/genealogy/query_methods.rb', line 123

def maternal_half_siblings(options = {})
  siblings(options.merge(half: :mother))
end

#maternal_uncles(options = {}) ⇒ Object

See Also:



232
233
234
# File 'lib/genealogy/query_methods.rb', line 232

def maternal_uncles(options = {})
  uncles(options.merge(lineage: :maternal))
end

#nephews(options = {}) ⇒ Object



264
265
266
# File 'lib/genealogy/query_methods.rb', line 264

def nephews(options = {})
  nieces_and_nephews.males
end

#nieces(options = {}) ⇒ Object



269
270
271
# File 'lib/genealogy/query_methods.rb', line 269

def nieces(options = {})
  nieces_and_nephews.females
end

#nieces_and_nephews(options = {}) ⇒ ActiveRecord::Relation

Returns list of nieces and nephews.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • half (Symbol)

    to filter siblings (see #siblings)

Returns:

  • (ActiveRecord::Relation)

    list of nieces and nephews



258
259
260
261
# 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

#parents2-elements Array

Returns father and mother.

Returns:

  • (2-elements Array)

    father and mother



7
8
9
# File 'lib/genealogy/query_methods.rb', line 7

def parents
  [father,mother]
end

#paternal_aunts(options = {}) ⇒ Object

See Also:



237
238
239
# File 'lib/genealogy/query_methods.rb', line 237

def paternal_aunts(options = {})
  aunts(options.merge(lineage: :paternal))
end

#paternal_grandfatherActiveRecord, NilClass

Returns:

  • (ActiveRecord, NilClass)


11
12
13
# File 'lib/genealogy/query_methods.rb', line 11

def paternal_grandfather
  father && father.father
end

#paternal_grandmotherActiveRecord, NilClass

Returns:

  • (ActiveRecord, NilClass)


15
16
17
# File 'lib/genealogy/query_methods.rb', line 15

def paternal_grandmother
  father && father.mother
end

#paternal_grandparents2-elements Array

Returns paternal_grandfather and paternal_grandmother.

Returns:

  • (2-elements Array)

    paternal_grandfather and paternal_grandmother



28
29
30
# File 'lib/genealogy/query_methods.rb', line 28

def paternal_grandparents
  (father && father.parents) || [nil,nil]
end

#paternal_half_siblings(options = {}) ⇒ Object

siblings with option half: :father

See Also:



117
118
119
# File 'lib/genealogy/query_methods.rb', line 117

def paternal_half_siblings(options = {})
  siblings(options.merge(half: :father))
end

#paternal_uncles(options = {}) ⇒ Object

See Also:



227
228
229
# File 'lib/genealogy/query_methods.rb', line 227

def paternal_uncles(options = {})
  uncles(options.merge(lineage: :paternal))
end

#siblings(options = {}) ⇒ ActiveRecord::Relation

Returns list of fullsiblings and/or halfsiblings.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • half (Symbol)

    let you filter siblings. Possible values are: :father for paternal halfsiblings :mother for maternal halfsiblings :only for all halfsiblings :include for fullsiblings and halfsiblings

Returns:

  • (ActiveRecord::Relation)

    list of fullsiblings and/or halfsiblings



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
# 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

#spousesActiveRecord::Relation

Returns list of individuals with whom has had children.

Returns:

  • (ActiveRecord::Relation)

    list of individuals with whom has had children



61
62
63
# 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 = {}) ⇒ Object

See Also:



217
218
219
# File 'lib/genealogy/query_methods.rb', line 217

def uncles(options = {})
  uncles_and_aunts(options).males
end

#uncles_and_aunts(options = {}) ⇒ ActiveRecord::Relation

list of uncles and aunts iterating through parents' siblings

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • lineage (Symbol)

    to filter by lineage: :paternal or :maternal

  • half (Symbol)

    to filter by half siblings (see #siblings)

Returns:

  • (ActiveRecord::Relation)

    list of uncles and aunts



203
204
205
206
207
208
209
210
211
212
213
214
# 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