genealogy 2.1.1 → 2.2.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 +5 -5
- data/lib/genealogy.rb +1 -0
- data/lib/genealogy/complex_query_methods.rb +46 -0
- data/lib/genealogy/genealogy.rb +18 -13
- data/lib/genealogy/query_methods.rb +46 -12
- data/lib/genealogy/version.rb +1 -1
- metadata +12 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0b07b270d80c0d915e01f45424b27989f04b920a79bb4cd973484589d47530a6
|
|
4
|
+
data.tar.gz: 40753227d2d9e72a7779aa6fa28365f2a37e6e5121df1b0a821d71a835850de7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67a933f6335a5a8d95dffa4284e8a198062a09cca098888e6d785608a22fbf463164322232376e1c83d7f1ea0362254ebbfac4e7e36bc8c500bb97510eb06b21
|
|
7
|
+
data.tar.gz: edb60383749cd28a0458252102e994b676796025a1fb55e716cc16a23f20810f23c3c9801163a4e93c06bbae45078cd41d71be40cbdd3808ddf4d8e1145b7845
|
data/lib/genealogy.rb
CHANGED
|
@@ -3,6 +3,7 @@ require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/exception
|
|
|
3
3
|
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/util_methods')
|
|
4
4
|
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/ineligible_methods')
|
|
5
5
|
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/query_methods')
|
|
6
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/complex_query_methods')
|
|
6
7
|
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/alter_methods')
|
|
7
8
|
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/current_spouse_methods')
|
|
8
9
|
require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/genealogy')
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Genealogy
|
|
2
|
+
module ComplexQueryMethods
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
include Constants
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
|
|
8
|
+
# Takes a splat argument of gclass objects and @return [ActiveRecord::Relation] of the first individual(s) to show up in the ancestors of the given people
|
|
9
|
+
# It moves up one generation at a time for each individual, stopping when there is a shared ancestor id or when there are no more ancestors
|
|
10
|
+
# If called on two full siblings, it will return both parents, as they appears in the same generation
|
|
11
|
+
# If called on two half siblings, it will return only the shared parent
|
|
12
|
+
# If one individual is the ancestor of the other, it will return that individual as the least common shared ancestors
|
|
13
|
+
# If none are found, it will return an empty AR relation.
|
|
14
|
+
def lowest_common_ancestors(*people)
|
|
15
|
+
raise ArgumentError, "all inputs must be an instance of the #{gclass} class" if people.select{|record| !record.is_a?(gclass)}.length > 0
|
|
16
|
+
|
|
17
|
+
parent_ids_temp = people.map{|person| [person.id]}
|
|
18
|
+
parent_ids_store = parent_ids_temp.clone
|
|
19
|
+
|
|
20
|
+
generation_count = 1
|
|
21
|
+
|
|
22
|
+
while parent_ids_temp.select{|array_of_ids| array_of_ids.length > 0}.length > 0
|
|
23
|
+
next_gen_ids = parent_ids_temp.map{|ids| gclass.where(id: ids).select([:father_id, :mother_id]).map{|result| [result.father_id, result.mother_id]}.flatten.compact}
|
|
24
|
+
|
|
25
|
+
next_gen_ids.each_with_index do |ids, index|
|
|
26
|
+
parent_ids_store[index] += ids
|
|
27
|
+
parent_ids_temp[index] = ids
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if parent_ids_store.reduce(:&).length > 0
|
|
31
|
+
return gclass.where(id: (parent_ids_store.reduce(:&)))
|
|
32
|
+
else
|
|
33
|
+
generation_count += 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
gclass.where(id: nil)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def lowest_common_ancestors(*people)
|
|
41
|
+
people << self
|
|
42
|
+
self.class.lowest_common_ancestors(*people)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/genealogy/genealogy.rb
CHANGED
|
@@ -3,36 +3,41 @@ module Genealogy
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
module ClassMethods
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
include Genealogy::Constants
|
|
8
8
|
|
|
9
9
|
# gives to ActiveRecord model geneaogy capabilities. Modules UtilMethods QueryMethods IneligibleMethods AlterMethods and SpouseMethods are included
|
|
10
10
|
# @param [Hash] options
|
|
11
11
|
# @option options [Boolean] current_spouse (false) specifies whether to track or not individual's current spouse
|
|
12
12
|
# @option options [Boolean] perform_validation (true) specifies whether to perform validation or not while altering pedigree that is before updating relatives external keys
|
|
13
|
-
# @option options [Boolean, Symbol] ineligibility (:pedigree) specifies ineligibility setting. If `false` ineligibility checks will be disabled and you can assign, as a relative, any individuals you want.
|
|
13
|
+
# @option options [Boolean, Symbol] ineligibility (:pedigree) specifies ineligibility setting. If `false` ineligibility checks will be disabled and you can assign, as a relative, any individuals you want.
|
|
14
14
|
# This can be dangerous because you can build nosense loop (in terms of pedigree). If pass one of symbols `:pedigree` or `:pedigree_and_dates` ineligibility checks will be enabled.
|
|
15
|
-
# More specifically with `:pedigree` (or `true`) checks will be based on pedigree topography, i.e., ineligible children will include ancestors. With `:pedigree_and_dates` check will also be based on
|
|
15
|
+
# More specifically with `:pedigree` (or `true`) checks will be based on pedigree topography, i.e., ineligible children will include ancestors. With `:pedigree_and_dates` check will also be based on
|
|
16
16
|
# procreation ages (min and max, male and female) and life expectancy (male and female), i.e. an individual born 200 years before is an ineligible mother
|
|
17
|
-
# @option options [Hash] limit_ages (min_male_procreation_age:12, max_male_procreation_age:75, min_female_procreation_age:9, max_female_procreation_age:50, max_male_life_expectancy:110, max_female_life_expectancy:110)
|
|
17
|
+
# @option options [Hash] limit_ages (min_male_procreation_age:12, max_male_procreation_age:75, min_female_procreation_age:9, max_female_procreation_age:50, max_male_life_expectancy:110, max_female_life_expectancy:110)
|
|
18
18
|
# specifies one or more limit ages different than defaults
|
|
19
19
|
# @option options [Hash] column_names (sex:'sex', father_id:'father_id', mother_id:'mother_id', current_spouse_id:'current_spouse_id', birth_date:'birth_date', death_date:'death_date') specifies column names to map database individual table
|
|
20
|
-
# @option options [Array] sex_values (['M','F']) specifies values used in database sex column
|
|
21
|
-
# @return [void]
|
|
20
|
+
# @option options [Array] sex_values (['M','F']) specifies values used in database sex column
|
|
21
|
+
# @return [void]
|
|
22
|
+
|
|
22
23
|
def has_parents options = {}
|
|
23
24
|
|
|
24
25
|
include Genealogy::UtilMethods
|
|
25
26
|
include Genealogy::QueryMethods
|
|
27
|
+
include Genealogy::ComplexQueryMethods
|
|
26
28
|
include Genealogy::IneligibleMethods
|
|
27
29
|
include Genealogy::AlterMethods
|
|
28
30
|
include Genealogy::CurrentSpouseMethods
|
|
29
31
|
|
|
32
|
+
|
|
30
33
|
check_has_parents_options(options)
|
|
31
34
|
|
|
32
35
|
# keep track of the original extend class to prevent wrong scopes in query method in case of STI
|
|
33
36
|
class_attribute :gclass, instance_writer: false
|
|
34
|
-
self.gclass = self
|
|
35
|
-
|
|
37
|
+
self.gclass = self
|
|
38
|
+
|
|
39
|
+
self.extend(Genealogy::ComplexQueryMethods::ClassMethods)
|
|
40
|
+
|
|
36
41
|
class_attribute :ineligibility_level, instance_accessor: false
|
|
37
42
|
self.ineligibility_level = case options[:ineligibility]
|
|
38
43
|
when :pedigree
|
|
@@ -77,7 +82,7 @@ module Genealogy
|
|
|
77
82
|
self.sex_values = options[:sex_values] || DEFAULTS[:sex_values]
|
|
78
83
|
self.sex_male_value = self.sex_values.first
|
|
79
84
|
self.sex_female_value = self.sex_values.last
|
|
80
|
-
|
|
85
|
+
|
|
81
86
|
# validation
|
|
82
87
|
validates_presence_of :sex
|
|
83
88
|
validates_format_of :sex, with: /[#{sex_values.join}]/
|
|
@@ -85,14 +90,14 @@ module Genealogy
|
|
|
85
90
|
tracked_relatives = [:father, :mother]
|
|
86
91
|
tracked_relatives << :current_spouse if current_spouse_enabled
|
|
87
92
|
tracked_relatives.each do |k|
|
|
88
|
-
belongs_to k, class_name: self, foreign_key: self.send("#{k}_id_column")
|
|
93
|
+
belongs_to k, class_name: self.name, foreign_key: self.send("#{k}_id_column")
|
|
89
94
|
end
|
|
90
95
|
|
|
91
|
-
has_many :children_as_father, class_name: self, foreign_key: self.father_id_column, dependent: :nullify
|
|
92
|
-
has_many :children_as_mother, class_name: self, foreign_key: self.mother_id_column, dependent: :nullify
|
|
96
|
+
has_many :children_as_father, class_name: self.name, foreign_key: self.father_id_column, dependent: :nullify
|
|
97
|
+
has_many :children_as_mother, class_name: self.name, foreign_key: self.mother_id_column, dependent: :nullify
|
|
93
98
|
|
|
94
99
|
end
|
|
95
100
|
|
|
96
101
|
end
|
|
97
102
|
|
|
98
|
-
end
|
|
103
|
+
end
|
|
@@ -128,27 +128,61 @@ module Genealogy
|
|
|
128
128
|
|
|
129
129
|
# get list of known ancestrors iterateing over parents
|
|
130
130
|
# @param [Hash] options
|
|
131
|
-
# @
|
|
131
|
+
# @option options [Symbol] generations lets you limit how many generations will be included in the output.
|
|
132
|
+
# @return [ActiveRecord::Relation] list of ancestors (limited by a number of generations if so indicated)
|
|
132
133
|
def ancestors(options = {})
|
|
133
134
|
ids = []
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
if options[:generations]
|
|
136
|
+
raise ArgumentError, ":generations option must be an Integer" unless options[:generations].is_a? Integer
|
|
137
|
+
generation_count = 0
|
|
138
|
+
generation_ids = parents.compact.map(&:id)
|
|
139
|
+
while (generation_count < options[:generations]) && (generation_ids.length > 0)
|
|
140
|
+
next_gen_ids = []
|
|
141
|
+
ids += generation_ids
|
|
142
|
+
until generation_ids.empty?
|
|
143
|
+
ids.unshift(generation_ids.shift)
|
|
144
|
+
next_gen_ids += gclass.find(ids.first).parents.compact.map(&:id)
|
|
145
|
+
end
|
|
146
|
+
generation_ids = next_gen_ids
|
|
147
|
+
generation_count += 1
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
remaining_ids = parents.compact.map(&:id)
|
|
151
|
+
until remaining_ids.empty?
|
|
152
|
+
ids << remaining_ids.shift
|
|
153
|
+
remaining_ids += gclass.find(ids.last).parents.compact.map(&:id)
|
|
154
|
+
end
|
|
138
155
|
end
|
|
139
156
|
gclass.where(id: ids)
|
|
140
157
|
end
|
|
141
158
|
|
|
142
159
|
|
|
143
160
|
# get list of known descendants iterateing over children ...
|
|
144
|
-
# @
|
|
145
|
-
|
|
161
|
+
# @param [Hash] options
|
|
162
|
+
# @option options [Symbol] generations lets you limit how many generations will be included in the output.
|
|
163
|
+
# @return [ActiveRecord::Relation] list of descendants (limited by a number of generations if so indicated)
|
|
164
|
+
def descendants(options = {})
|
|
146
165
|
ids = []
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
if options[:generations]
|
|
167
|
+
generation_count = 0
|
|
168
|
+
generation_ids = children.map(&:id)
|
|
169
|
+
while (generation_count < options[:generations]) && (generation_ids.length > 0)
|
|
170
|
+
next_gen_ids = []
|
|
171
|
+
ids += generation_ids
|
|
172
|
+
until generation_ids.empty?
|
|
173
|
+
ids.unshift(generation_ids.shift)
|
|
174
|
+
next_gen_ids += gclass.find(ids.first).children.map(&:id)
|
|
175
|
+
end
|
|
176
|
+
generation_ids = next_gen_ids
|
|
177
|
+
generation_count += 1
|
|
178
|
+
end
|
|
179
|
+
else
|
|
180
|
+
remaining_ids = children.map(&:id)
|
|
181
|
+
until remaining_ids.empty?
|
|
182
|
+
ids << remaining_ids.shift
|
|
183
|
+
remaining_ids += gclass.find(ids.last).children.pluck(:id)
|
|
184
|
+
# break if (remaining_ids - ids).empty? can be necessary in case of loop. Idem for ancestors method
|
|
185
|
+
end
|
|
152
186
|
end
|
|
153
187
|
gclass.where(id: ids)
|
|
154
188
|
end
|
data/lib/genealogy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: genealogy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- masciugo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-06-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '4.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - ">"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '4.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: activesupport
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '4.0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '4.0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: sqlite3
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -244,6 +244,7 @@ extra_rdoc_files: []
|
|
|
244
244
|
files:
|
|
245
245
|
- lib/genealogy.rb
|
|
246
246
|
- lib/genealogy/alter_methods.rb
|
|
247
|
+
- lib/genealogy/complex_query_methods.rb
|
|
247
248
|
- lib/genealogy/constants.rb
|
|
248
249
|
- lib/genealogy/current_spouse_methods.rb
|
|
249
250
|
- lib/genealogy/exceptions.rb
|
|
@@ -272,9 +273,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
272
273
|
version: '0'
|
|
273
274
|
requirements: []
|
|
274
275
|
rubyforge_project: "[none]"
|
|
275
|
-
rubygems_version: 2.
|
|
276
|
+
rubygems_version: 2.7.6
|
|
276
277
|
signing_key:
|
|
277
278
|
specification_version: 4
|
|
278
279
|
summary: Make ActiveRecord model act as a pedigree
|
|
279
280
|
test_files: []
|
|
280
|
-
has_rdoc:
|