genealogy 1.5.0 → 2.0.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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- N2JhNjJiMTU0MmE0MGMwMmI1YTI2NjAyZWRhOGZhODRjMDlkMzUyMw==
5
- data.tar.gz: !binary |-
6
- ZTU5ZWM1OWI2NWE5MDc1MmNlOTM1MDhlMzg3YTFmN2YxZjJkNTIyZA==
2
+ SHA1:
3
+ metadata.gz: f135505bdbf0d836cdac669c1ee684c8959cedce
4
+ data.tar.gz: d90f6befee994c37e2a900d7c85e37e0c7f3229c
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MGIxNGE2YzUxNTA1ZmU4NzE3NGIwMTMxNzBkYjcxMWFmOGZmZGMzMGViNzg3
10
- MGI5NmYwZGE1OTNlY2JhNDU0YzM2ZGUzMjNhMmU3YWFkODZjMzcwZmQwNWMz
11
- YTM4MmEwN2ZmZDJmNmQ5OGMxMGFlMmJhYmMzYTM5YjMwNzIxNWU=
12
- data.tar.gz: !binary |-
13
- NDljNjIxODUzMDBmZjgxNzk5Y2RlMTk0ZWI3NTMxYTM3MDcxNjMwMjQxY2Qx
14
- M2IxZDNiNzg5MDJlMDFhZWEzYjY2ZWY2MDc3ZmJmZWM4MjZjODU3YTkxYTJm
15
- ZGU5ZDNmM2MwOWExY2E5YzgxZWYwMTlhM2M4OTllYjlmMTdkNGU=
6
+ metadata.gz: f1efa40c2b50b47caad8d4608157195d0a0d68f1659c08c1eb44d31381d61ec8bb738c61ada1369de74baad8f4c693aa3f0d54dd624c354897b7349eb2d0f081
7
+ data.tar.gz: 443d273ad5240f9a5ecad5c902f8535a421f7c3f46e5d37b5a8c363c42713c582ca498dc582748f13fd89cb64c26ee7fd616d3de332d5f0c790528706ee95554
data/lib/genealogy.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/constants')
2
2
  require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/exceptions')
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/util_methods')
4
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/ineligible_methods')
3
5
  require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/query_methods')
4
6
  require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/alter_methods')
5
- require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/spouse_methods')
7
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/current_spouse_methods')
6
8
  require File.join(File.expand_path(File.dirname(__FILE__)), 'genealogy/genealogy')
7
9
 
8
- ActiveRecord::Base.send :extend, Genealogy
10
+ ActiveRecord::Base.send :include, Genealogy
9
11
 
@@ -1,39 +1,54 @@
1
1
  module Genealogy
2
+ # Module AlterMethods provides methods to alter genealogy. It's included by the genealogy enabled AR model
2
3
  module AlterMethods
3
4
  extend ActiveSupport::Concern
4
5
 
5
- # parents
6
- [:father, :mother].each do |parent|
6
+ include Constants
7
7
 
8
- # add method
8
+ # @!macro [attach] generate
9
+ # @method add_$1(parent)
10
+ # Add $1
11
+ # @param [Object] parent
12
+ # @raise [Exception] if perform validation is enabled and self is invalid
13
+ # @return [Boolean]
14
+ def self.generate_method_add_parent(parent)
9
15
  define_method "add_#{parent}" do |relative|
10
- unless relative.nil?
11
- raise IncompatibleObjectException, "Both linked objects must be instances of class with genealogy enabled. Got classes #{relative.class} and #{self.genealogy_class}" unless relative.class.respond_to?(:genealogy_enabled)
12
- incompatible_parents = self.offspring | self.siblings | [self]
13
- raise IncompatibleRelationshipException, "#{relative} can't be #{parent} of #{self}" if incompatible_parents.include? relative
14
- raise WrongSexException, "Can't add a #{relative.sex} #{parent}" unless (parent == :father and relative.is_male?) or (parent == :mother and relative.is_female?)
15
- end
16
- if perform_validation
16
+ check_incompatible_relationship(parent,relative)
17
+ if gclass.perform_validation_enabled
17
18
  self.send("#{parent}=",relative)
18
19
  save!
19
20
  else
20
21
  self.update_attribute(parent,relative)
21
22
  end
22
23
  end
23
-
24
- # remove method
24
+ end
25
+ generate_method_add_parent(:father)
26
+ generate_method_add_parent(:mother)
27
+
28
+ # @!macro [attach] generate
29
+ # @method remove_$1
30
+ # remove $1. Foreign_key set to nil
31
+ # @raise [Exception] if perform validation is enabled and self is invalid
32
+ # @return [Boolean]
33
+ def self.generate_method_remove_parent(parent)
25
34
  define_method "remove_#{parent}" do
26
- if perform_validation
35
+ if gclass.perform_validation_enabled
27
36
  self.send("#{parent}=",nil)
28
37
  save!
29
38
  else
30
39
  self.update_attribute(parent,nil)
31
40
  end
32
41
  end
33
-
34
42
  end
35
-
36
- # add both
43
+ generate_method_remove_parent(:father)
44
+ generate_method_remove_parent(:mother)
45
+
46
+ # add both parents calling #add_father and #add_mother in a transaction
47
+ # @param [Object] father
48
+ # @param [Object] mother
49
+ # @see #add_father
50
+ # @see #add_mother
51
+ # @return [Boolean]
37
52
  def add_parents(father,mother)
38
53
  transaction do
39
54
  add_father(father)
@@ -41,7 +56,10 @@ module Genealogy
41
56
  end
42
57
  end
43
58
 
44
- # remove both
59
+ # remove both parents calling #remove_father and #remove_mother in a transaction
60
+ # @see #remove_father
61
+ # @see #remove_mother
62
+ # @return [Boolean]
45
63
  def remove_parents
46
64
  transaction do
47
65
  remove_father
@@ -49,37 +67,87 @@ module Genealogy
49
67
  end
50
68
  end
51
69
 
52
- # grandparents
53
- [:father, :mother].each do |parent|
54
- [:father, :mother].each do |grandparent|
55
- # add one
56
- define_method "add_#{Genealogy::PARENT2LINEAGE[parent]}_grand#{grandparent}" do |relative|
57
- raise IncompatibleRelationshipException, "#{self} can't be grand#{grandparent} of itself" if relative == self
58
- raise_if_gap_on(parent)
59
- send(parent).send("add_#{grandparent}",relative)
60
- end
61
- # remove one
62
- define_method "remove_#{Genealogy::PARENT2LINEAGE[parent]}_grand#{grandparent}" do
63
- raise_if_gap_on(parent)
64
- send(parent).send("remove_#{grandparent}")
65
- end
70
+ # @!macro [attach] generate
71
+ # @method add_$1_grand$2(grandparent)
72
+ # Add $1 grand$2
73
+ # @param [Object] gp grandparent
74
+ # @raise [Exception] if perform validation is enabled and self is invalid
75
+ # @return [Boolean]
76
+ def self.generate_method_add_grandparent(lineage,grandparent)
77
+ relationship = "#{lineage}_grand#{grandparent}"
78
+ define_method "add_#{relationship}" do |gp|
79
+ parent = LINEAGE2PARENT[lineage]
80
+ raise_if_gap_on(parent)
81
+ check_incompatible_relationship(relationship,gp)
82
+ send(parent).send("add_#{grandparent}",gp)
66
83
  end
67
84
  end
68
-
69
- [:father, :mother].each do |parent|
70
- # add two by lineage
71
- define_method "add_#{Genealogy::PARENT2LINEAGE[parent]}_grandparents" do |grandfather,grandmother|
85
+ generate_method_add_grandparent(:paternal,:father)
86
+ generate_method_add_grandparent(:paternal,:mother)
87
+ generate_method_add_grandparent(:maternal,:father)
88
+ generate_method_add_grandparent(:maternal,:mother)
89
+
90
+ # @!macro [attach] generate
91
+ # @method remove_$1_grand$2
92
+ # remove $1 grand$2
93
+ # @raise [Exception] if perform validation is enabled and self is invalid
94
+ # @return [Boolean]
95
+ def self.generate_method_remove_grandparent(lineage,grandparent)
96
+ relationship = "#{lineage}_grand#{grandparent}"
97
+ define_method "remove_#{relationship}" do
98
+ parent = LINEAGE2PARENT[lineage]
72
99
  raise_if_gap_on(parent)
73
- send(parent).send("add_parents",grandfather,grandmother)
100
+ send(parent).send("remove_#{grandparent}")
74
101
  end
75
- # remove two by lineage
76
- define_method "remove_#{Genealogy::PARENT2LINEAGE[parent]}_grandparents" do
102
+ end
103
+ generate_method_remove_grandparent(:paternal,:father)
104
+ generate_method_remove_grandparent(:paternal,:mother)
105
+ generate_method_remove_grandparent(:maternal,:father)
106
+ generate_method_remove_grandparent(:maternal,:mother)
107
+
108
+ # @!macro [attach] generate
109
+ # @method add_$1_grandparents
110
+ # Add $1 grandparents
111
+ # @param [Object] gf grandfather
112
+ # @param [Object] gm grandmother
113
+ # @raise [Exception] if perform validation is enabled and self is invalid
114
+ # @return [Boolean]
115
+ def self.generate_method_add_grandparents_by_lineage(lineage)
116
+ relationship = "#{lineage}_grandparents"
117
+ define_method "add_#{relationship}" do |gf,gm|
118
+ parent = LINEAGE2PARENT[lineage]
119
+ raise_if_gap_on(parent)
120
+ send(parent).send("add_parents",gf,gm)
121
+ end
122
+ end
123
+ generate_method_add_grandparents_by_lineage(:paternal)
124
+ generate_method_add_grandparents_by_lineage(:maternal)
125
+
126
+ # @!macro [attach] generate
127
+ # @method remove_$1_grandparents
128
+ # remove $1 grandparents
129
+ # @raise [Exception] if perform validation is enabled and self is invalid
130
+ # @return [Boolean]
131
+ def self.generate_method_remove_grandparents_by_lineage(lineage)
132
+ relationship = "#{lineage}_grandparents"
133
+ define_method "remove_#{relationship}" do
134
+ parent = LINEAGE2PARENT[lineage]
77
135
  raise_if_gap_on(parent)
78
136
  send(parent).send("remove_parents")
79
137
  end
80
138
  end
81
-
82
- # add all
139
+ generate_method_remove_grandparents_by_lineage(:paternal)
140
+ generate_method_remove_grandparents_by_lineage(:maternal)
141
+
142
+
143
+ # add all grandparents calling #add_paternal_grandparents and #add_maternal_grandparents in a transaction
144
+ # @param [Object] pgf paternal grandfather
145
+ # @param [Object] pgm paternal grandmother
146
+ # @param [Object] mgf maternal grandfather
147
+ # @param [Object] mgm maternal grandmother
148
+ # @see #add_paternal_grandparents
149
+ # @see #add_maternal_grandparents
150
+ # @return [Boolean]
83
151
  def add_grandparents(pgf,pgm,mgf,mgm)
84
152
  transaction do
85
153
  add_paternal_grandparents(pgf,pgm)
@@ -87,160 +155,152 @@ module Genealogy
87
155
  end
88
156
  end
89
157
 
90
- # remove all
91
- def remove_grandparents
158
+ # remove all grandparents calling #remove_paternal_grandparents and #remove_maternal_grandparents in a transaction
159
+ # @see #remove_paternal_grandparents
160
+ # @see #remove_maternal_grandparents
161
+ # @return [Boolean]
162
+ def remove_grandparents
92
163
  transaction do
93
164
  remove_paternal_grandparents
94
165
  remove_maternal_grandparents
95
166
  end
96
167
  end
97
168
 
98
- ## siblings
169
+ # add siblings by assigning same parents to individuals passed as arguments
170
+ # @overload add_siblings(*siblings,options={})
171
+ # @param [Object] siblings list of siblings
172
+ # @param [Hash] options
173
+ # @option options [Symbol] half :father for paternal half siblings and :mother for maternal half siblings
174
+ # @option options [Object] spouse if specified, passed individual will be used as mother in case of half sibling
175
+ # @return [Boolean]
99
176
  def add_siblings(*args)
100
177
  options = args.extract_options!
101
- raise IncompatibleRelationshipException, "Can't add an ancestor as sibling" unless (ancestors.to_a & args).empty?
178
+ check_incompatible_relationship(:sibling, *args)
102
179
  transaction do
103
180
  args.inject(true) do |res,sib|
104
181
  res &= case options[:half]
105
182
  when :father
106
183
  raise LineageGapException, "Can't add paternal halfsiblings without a father" unless father
107
- sib.add_father(self.father)
108
184
  sib.add_mother(options[:spouse]) if options[:spouse]
185
+ sib.add_father(father)
109
186
  when :mother
110
187
  raise LineageGapException, "Can't add maternal halfsiblings without a mother" unless mother
111
188
  sib.add_father(options[:spouse]) if options[:spouse]
112
- sib.add_mother(self.mother)
189
+ sib.add_mother(mother)
113
190
  when nil
114
191
  raise LineageGapException, "Can't add siblings without parents" unless father and mother
115
- sib.add_father(self.father)
116
- sib.add_mother(self.mother)
192
+ sib.add_father(father)
193
+ sib.add_mother(mother)
117
194
  else
118
- raise WrongOptionValueException, "Admitted values for :half options are: :father, :mother or nil"
195
+ raise ArgumentError, "Admitted values for :half options are: :father, :mother or nil"
119
196
  end
120
197
  end
121
198
  end
122
199
  end
123
200
 
124
- def add_sibling(sib,options={})
125
- add_siblings(sib,options)
201
+ # @see #add_siblings
202
+ def add_sibling(sibling,options={})
203
+ add_siblings(sibling,options)
126
204
  end
127
205
 
206
+
207
+ # remove siblings by nullifying parents of passed individuals
208
+ # @overload remove_siblings(*siblings,options={})
209
+ # @param [Object] siblings list of siblings
210
+ # @param [Hash] options
211
+ # @option options [Symbol] half :father for paternal half siblings and :mother for maternal half siblings
212
+ # @option options [Boolean] remove_other_parent if specified, passed individuals' mother will also be nullified
213
+ # @return [Boolean] true if at least one sibling was affected, false otherwise
128
214
  def remove_siblings(*args)
129
215
  options = args.extract_options!
130
-
131
- raise WrongOptionException.new("Unknown option value: :half => #{options[:half]}.") if (options[:half] and ![:father,:mother].include?(options[:half]))
132
-
216
+ raise ArgumentError.new("Unknown option value: half: #{options[:half]}.") if (options[:half] and ![:father,:mother].include?(options[:half]))
133
217
  resulting_indivs = if args.blank?
134
218
  siblings(options)
135
219
  else
136
220
  args & siblings(options)
137
221
  end
138
-
139
222
  transaction do
140
-
141
223
  resulting_indivs.each do |sib|
142
224
  case options[:half]
143
225
  when :father
144
226
  sib.remove_father
145
- sib.remove_mother if options[:affect_spouse] == true
227
+ sib.remove_mother if options[:remove_other_parent] == true
146
228
  when :mother
147
- sib.remove_father if options[:affect_spouse] == true
229
+ sib.remove_father if options[:remove_other_parent] == true
148
230
  sib.remove_mother
149
231
  when nil
150
232
  sib.remove_parents
151
233
  end
152
234
  end
153
-
154
235
  end
155
-
156
236
  !resulting_indivs.empty? #returned value must be true if self has at least a siblings to affect
157
-
158
237
  end
159
238
 
239
+ # @see #remove_siblings
160
240
  def remove_sibling(sib,options={})
161
241
  remove_siblings(sib,options)
162
242
  end
163
243
 
164
- [:father, :mother].each do |parent|
165
-
166
- # add paternal/maternal half_siblings
167
- define_method "add_#{Genealogy::PARENT2LINEAGE[parent]}_half_siblings" do | *args |
168
- options = args.extract_options!
169
- options[:half] = parent
170
- args << options
171
- send("add_siblings",*args)
172
- end
173
-
174
- # add paternal/maternal half_sibling
175
- define_method "add_#{Genealogy::PARENT2LINEAGE[parent]}_half_sibling" do | sib,options={} |
176
- options[:half] = parent
177
- send("add_sibling",sib,options)
178
- end
179
-
180
- # remove paternal/maternal half_siblings
181
- define_method "remove_#{Genealogy::PARENT2LINEAGE[parent]}_half_siblings" do | *args |
182
- options = args.extract_options!
183
- options[:half] = parent
184
- args << options
185
- send("remove_siblings",*args)
186
- end
187
-
188
- # remove paternal/maternal half_sibling
189
- define_method "remove_#{Genealogy::PARENT2LINEAGE[parent]}_half_sibling" do | sib,options={} |
190
- options[:half] = parent
191
- send("remove_sibling",sib,options)
192
- end
193
- end
194
-
195
- # offspring
196
- def add_offspring(*args)
244
+ # add children by assigning self as parent
245
+ # @overload add_children(*children,options={})
246
+ # @param [Object] children list of children
247
+ # @param [Hash] options
248
+ # @option options [Object] spouse if specified, children will have that spouse
249
+ # @return [Boolean]
250
+ def add_children(*args)
197
251
  options = args.extract_options!
198
-
199
252
  raise_if_sex_undefined
200
-
253
+ check_incompatible_relationship(:children, *args)
201
254
  transaction do
202
- args.each do |child|
203
- case sex
204
- when sex_male_value
205
- child.add_father(self)
255
+ args.inject(true) do |res,child|
256
+ res &= case sex
257
+ when gclass.sex_male_value
206
258
  child.add_mother(options[:spouse]) if options[:spouse]
207
- when sex_female_value
259
+ child.add_father(self)
260
+ when gclass.sex_female_value
208
261
  child.add_father(options[:spouse]) if options[:spouse]
209
262
  child.add_mother(self)
210
263
  else
211
- raise WrongSexException, "Sex value not valid for #{self}"
264
+ raise SexError, "Sex value not valid for #{self}"
212
265
  end
213
266
  end
214
267
  end
215
268
  end
216
269
 
270
+ # see #add_children
217
271
  def add_child(child,options={})
218
- add_offspring(child,options)
272
+ add_children(child,options)
219
273
  end
220
274
 
221
- def remove_offspring(*args)
275
+ # remove children by nullifying the parent corresponding to self
276
+ # @overload remove_children(*children,options={})
277
+ # @param [Object] children list of children
278
+ # @param [Hash] options
279
+ # @option options [Boolean] remove_other_parent if specified, passed individuals' mother will also be nullified
280
+ # @return [Boolean] true if at least one child was affected, false otherwise
281
+ def remove_children(*args)
222
282
  options = args.extract_options!
223
283
 
224
284
  raise_if_sex_undefined
225
285
 
226
286
  resulting_indivs = if args.blank?
227
- offspring(options)
287
+ children(options)
228
288
  else
229
- args & offspring(options)
289
+ args & children(options)
230
290
  end
231
291
 
232
292
  transaction do
233
293
  resulting_indivs.each do |child|
234
- if options[:affect_spouse] == true
294
+ if options[:remove_other_parent] == true
235
295
  child.remove_parents
236
296
  else
237
297
  case sex
238
- when sex_male_value
298
+ when gclass.sex_male_value
239
299
  child.remove_father
240
- when sex_female_value
300
+ when gclass.sex_female_value
241
301
  child.remove_mother
242
302
  else
243
- raise WrongSexException, "Sex value not valid for #{self}"
303
+ raise SexError, "Sex value not valid for #{self}"
244
304
  end
245
305
  end
246
306
  end
@@ -248,8 +308,9 @@ module Genealogy
248
308
  !resulting_indivs.empty? #returned value must be true if self has at least a siblings to affect
249
309
  end
250
310
 
311
+ # see #remove_children
251
312
  def remove_child(child,options={})
252
- remove_offspring(child,options)
313
+ remove_children(child,options)
253
314
  end
254
315
 
255
316
  private
@@ -259,7 +320,7 @@ module Genealogy
259
320
  end
260
321
 
261
322
  def raise_if_sex_undefined
262
- raise WrongSexException, "Can't proceed if sex undefined for #{self}" unless is_male? or is_female?
323
+ raise SexError, "Can't proceed if sex undefined for #{self}" unless is_male? or is_female?
263
324
  end
264
325
 
265
326
  end