genealogy 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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