genealogy 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 471eb61c7e2d5e29edac1c8796a0f35ab53d9405
4
- data.tar.gz: ae2209d3c81a59f012649877ad9678eff6b4ddd4
2
+ SHA256:
3
+ metadata.gz: 0b07b270d80c0d915e01f45424b27989f04b920a79bb4cd973484589d47530a6
4
+ data.tar.gz: 40753227d2d9e72a7779aa6fa28365f2a37e6e5121df1b0a821d71a835850de7
5
5
  SHA512:
6
- metadata.gz: 4fe4635b6ad80fe2cbd6dfb6d6fb1c8740e7bb9c8e429e8aac7fb82231c3e1520c9d19a10587f6e0485d984030cfde9e6f625f5b94cc303a0b82b9c20aae0700
7
- data.tar.gz: ff633aa6af193a025ae015edbaca533956b8e2fa2f391640bd213aacb08ecaccc00bfebf27d654b516cf053bb5f44b8efa09de049f22357b0c5474376b1cc2b9
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
@@ -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
- # @return [ActiveRecord::Relation] list of ancestors
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
- remaining_ids = parents.compact.map(&:id)
135
- until remaining_ids.empty?
136
- ids << remaining_ids.shift
137
- remaining_ids += gclass.find(ids.last).parents.compact.map(&:id)
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
- # @return [ActiveRecord::Relation] list of descendants
145
- def descendants
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
- remaining_ids = children.map(&:id)
148
- until remaining_ids.empty?
149
- ids << remaining_ids.shift
150
- remaining_ids += gclass.find(ids.last).children.pluck(:id)
151
- # break if (remaining_ids - ids).empty? can be necessary in case of loop. Idem for ancestors method
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
@@ -1,3 +1,3 @@
1
1
  module Genealogy
2
- VERSION = "2.1.1"
2
+ VERSION = "2.2.0"
3
3
  end
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.1.1
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: 2015-05-11 00:00:00.000000000 Z
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: '3.2'
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: '3.2'
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: '3.2'
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: '3.2'
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.2.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: