ruby-fs-stack 0.1.7

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.
Files changed (37) hide show
  1. data/.document +5 -0
  2. data/.gitignore +7 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +26 -0
  5. data/Rakefile +51 -0
  6. data/VERSION +1 -0
  7. data/examples/familytree_example.rb +26 -0
  8. data/examples/login_example.rb +11 -0
  9. data/lib/ruby-fs-stack/assets/entrust-ca.crt +104 -0
  10. data/lib/ruby-fs-stack/enunciate/LICENSE +15 -0
  11. data/lib/ruby-fs-stack/enunciate/README +6 -0
  12. data/lib/ruby-fs-stack/enunciate/familytree.rb +12608 -0
  13. data/lib/ruby-fs-stack/enunciate/identity.rb +964 -0
  14. data/lib/ruby-fs-stack/familytree.rb +827 -0
  15. data/lib/ruby-fs-stack/fs_communicator.rb +109 -0
  16. data/lib/ruby-fs-stack/fs_utils.rb +27 -0
  17. data/lib/ruby-fs-stack/identity.rb +45 -0
  18. data/lib/ruby-fs-stack/warning_suppressor.rb +18 -0
  19. data/lib/ruby-fs-stack.rb +2 -0
  20. data/spec/communicator_spec.rb +214 -0
  21. data/spec/familytree_v2/familytree_communicator_spec.rb +309 -0
  22. data/spec/familytree_v2/json/match_KW3B-NNM.js +1 -0
  23. data/spec/familytree_v2/json/person/KJ86-3VD_all.js +1 -0
  24. data/spec/familytree_v2/json/person/KJ86-3VD_version.js +1 -0
  25. data/spec/familytree_v2/json/person/post_response.js +1 -0
  26. data/spec/familytree_v2/json/person/relationship_not_found.js +1 -0
  27. data/spec/familytree_v2/json/person/relationship_read.js +1 -0
  28. data/spec/familytree_v2/json/person/relationship_update.js +1 -0
  29. data/spec/familytree_v2/json/search.js +1 -0
  30. data/spec/familytree_v2/person_spec.rb +563 -0
  31. data/spec/familytree_v2/search_results_spec.rb +131 -0
  32. data/spec/fs_utils_spec.rb +33 -0
  33. data/spec/identity_v1/identity_spec.rb +50 -0
  34. data/spec/identity_v1/json/login.js +1 -0
  35. data/spec/ruby-fs-stack_spec.rb +6 -0
  36. data/spec/spec_helper.rb +27 -0
  37. metadata +119 -0
@@ -0,0 +1,827 @@
1
+ require 'rubygems'
2
+ require 'ruby-fs-stack/fs_communicator'
3
+ require 'ruby-fs-stack/fs_utils'
4
+
5
+ # Including more than one enunciate library raises a warning of
6
+ # already initialized constant.
7
+ require 'ruby-fs-stack/warning_suppressor'
8
+ with_warnings_suppressed do
9
+ require 'ruby-fs-stack/enunciate/familytree'
10
+ end
11
+
12
+
13
+ module FamilytreeV2
14
+
15
+ # This method gets mixed into the FsCommunicator so that
16
+ # you can make calls on the familytree_v2 module
17
+ def familytree_v2
18
+ @familytree_v2_com ||= Communicator.new self # self at this point refers to the FsCommunicator instance
19
+ end
20
+
21
+ class Communicator
22
+ Base = '/familytree/v2/'
23
+
24
+ # ===params
25
+ # fs_communicator: FsCommunicator instance
26
+ def initialize(fs_communicator)
27
+ @fs_communicator = fs_communicator
28
+ end
29
+
30
+ # ===params
31
+ # <tt>id</tt> should be a string of the persons identifier. For the 'me' person, use :me or 'me'
32
+ # <tt>options</tt> accepts a hash of parameters as documented by the API.
33
+ # For full parameter documentation, see DevNet[https://devnet.familysearch.org/docs/api-manual-reference-system/familytree-v2/r_api_family_tree_person_read_v2.html]
34
+ #
35
+ # ===Example
36
+ # # communicator is an authenticated FsCommunicator object
37
+ # # Request a person with no assertions, only the version.
38
+ # p = communicator.familytree_v2.person :me, :names => 'none', :genders => 'none', :events => 'none'
39
+ #
40
+ # p.version # => '90194378772'
41
+ # p.id # => 'KW3B-NNM'
42
+ def person(id, options = {})
43
+ id = id.to_s
44
+ if id == 'me'
45
+ url = Base + 'person'
46
+ else
47
+ url = Base + 'person/' + id
48
+ end
49
+ url += "?"+FsUtils.querystring_from_hash(options) unless options.empty?
50
+ response = @fs_communicator.get(url)
51
+ familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.from_json JSON.parse(response.body)
52
+ person = familytree.persons.find{|p| p.requestedId == id }
53
+ person ||= familytree.persons.first if id == 'me'
54
+ person
55
+ end
56
+
57
+ def save_person(person)
58
+ if person.id.nil?
59
+ url = Base + 'person'
60
+ else
61
+ url = Base + 'person/' + person.id
62
+ end
63
+ familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.new
64
+ familytree.persons = [person]
65
+ response = @fs_communicator.post(url,familytree.to_json)
66
+ res_familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.from_json JSON.parse(response.body)
67
+ person = res_familytree.persons.first
68
+ return person
69
+ end
70
+
71
+ # ====Params
72
+ # <tt>search_params</tt> - A hash of search parameters matching API doc
73
+ def search(search_params)
74
+ url = Base + 'search'
75
+ url += "?" + FsUtils.querystring_from_hash(search_params) unless search_params.empty?
76
+ response = @fs_communicator.get(url)
77
+ familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.from_json JSON.parse(response.body)
78
+ # require 'pp'
79
+ # pp familytree
80
+ familytree.searches[0]
81
+ end
82
+
83
+ # ====Params
84
+ # * <tt>base_id</tt> - The root person for creating the relationship
85
+ # * <tt>options</tt> - Should include either :parent, :spouse, or :child. :lineage and :event is optional
86
+ #
87
+ # :lineage can be set to the following values:
88
+ # * 'Biological'
89
+ # * 'Adoptive'
90
+ # * 'Foster'
91
+ # * 'Guardianship'
92
+ # * 'Step'
93
+ # * 'Other'
94
+ #
95
+ # :event should be a hash with the following values
96
+ # ** :type - "Marriage", etc. (REQUIRED)
97
+ # ** :place - "Utah, United States" (optional)
98
+ # ** :date - "Nov 2009"
99
+ #
100
+ # :ordinance should be a hash with the following values
101
+ # ** :type - "Sealing_to_Spouse", etc. (REQUIRED)
102
+ # ** :place - "Utah, United States" (optional)
103
+ # ** :date - "Nov 2009"
104
+ # ** :temple - 'SLAKE'
105
+ #
106
+ # If the :lineage is set, the parent-child relationships will be written via a characteristic.
107
+ # Otherwise, an exists assertion will be created to just establish the relationship.
108
+ # ====Example
109
+ #
110
+ # communicator.familytree_v2.write_relationship 'KWQS-BBQ', :parent => 'KWQS-BBT', :lineage => 'Biological'
111
+ # communicator.familytree_v2.write_relationship 'KWQS-BBQ', :parent => 'KWQS-BBT', :lineage => 'Adoptive'
112
+ # communicator.familytree_v2.write_relationship 'KWQS-BBQ', :spouse => 'KWRT-BBZ', :event => {:type => 'Marriage', :date => '15 Aug 1987', :place => 'Utah, United States'}
113
+ def write_relationship(base_id,options)
114
+
115
+ relationship_type = get_relationship_type(options)
116
+ with_id = options[relationship_type.to_sym]
117
+
118
+ url = "#{Base}person/#{base_id}/#{relationship_type}/#{with_id}"
119
+
120
+ # Get the existing person/relationship or create a new person
121
+ unless person = relationship(base_id,options)
122
+ person = Org::Familysearch::Ws::Familytree::V2::Schema::Person.new
123
+ person.id = base_id
124
+ end
125
+
126
+ # Add the relationship to the person with all of the correct options
127
+ r_options = {:type => relationship_type, :with => with_id}
128
+ r_options[:event] = options[:event] if options[:event]
129
+ r_options[:ordinance] = options[:ordinance] if options[:ordinance]
130
+ r_options[:lineage] = options[:lineage] if options[:lineage]
131
+ person.create_relationship r_options
132
+
133
+ # Create the payload
134
+ familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.new
135
+ familytree.persons = [person]
136
+
137
+ # Post the response and return the resulting person/relationship record from response
138
+ response = @fs_communicator.post(url,familytree.to_json)
139
+ res_familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.from_json JSON.parse(response.body)
140
+ person = res_familytree.persons.first
141
+ return person
142
+ end
143
+
144
+ # ====Params
145
+ # * <tt>base_id</tt> - The root person for creating the relationship
146
+ # * <tt>options</tt> - Should include either :parent, :spouse, or :child. :lineage and :event is optional
147
+ #
148
+ # If the :lineage is set, the parent-child relationships will be written via a characteristic.
149
+ # Otherwise, an exists assertion will be created to just establish the relationship.
150
+ # ====Example
151
+ #
152
+ # communicator.familytree_v2.relationship 'KWQS-BBQ', :parent => 'KWQS-BBT'
153
+ # communicator.familytree_v2.relationship 'KWQS-BBQ', :parent => 'KWQS-BBT'
154
+ def relationship(base_id,options)
155
+ r_type = get_relationship_type(options)
156
+ with_id = options[r_type.to_sym]
157
+ url = "#{Base}person/#{base_id}/#{r_type}/#{with_id}"
158
+ res = @fs_communicator.get(url)
159
+ if res.code == '404'
160
+ return nil
161
+ else
162
+ familytree = Org::Familysearch::Ws::Familytree::V2::Schema::FamilyTree.from_json JSON.parse(res.body)
163
+ person = familytree.persons.find{|p|p.id == base_id}
164
+ return person
165
+ end
166
+ end
167
+
168
+ private
169
+ #options will either have a :parent, :child, or :spouse key. We need to find which one
170
+ def get_relationship_type(options)
171
+ keys = options.keys.collect{|k|k.to_s}
172
+ key = keys.find{|k| ['parent','child','spouse'].include? k}
173
+ key
174
+ end
175
+ end
176
+
177
+ end
178
+
179
+ # Mix in the module so that the fs_familytree_v1 can be called
180
+ class FsCommunicator
181
+ include FamilytreeV2
182
+ end
183
+
184
+
185
+
186
+ module Org::Familysearch::Ws::Familytree::V2::Schema
187
+
188
+ class GenderAssertion
189
+ def add_value(value)
190
+ self.value = GenderValue.new
191
+ self.value.type = value
192
+ end
193
+ end
194
+
195
+ class NameForm
196
+ def set_name(name)
197
+ split_pieces = name.match(/(.*)\/(.*)\//)
198
+ # if there is a name like John Jacob /Felch/, split to name pieces, otherwise use fullText
199
+ if split_pieces
200
+ given_pieces = split_pieces[1]
201
+ family_pieces = split_pieces[2]
202
+ self.pieces = given_pieces.split(" ").collect do |piece|
203
+ p = NamePiece.new
204
+ p.type = "Given"
205
+ p.postdelimiters = " "
206
+ p.value = piece
207
+ p
208
+ end
209
+ self.pieces = self.pieces + family_pieces.split(" ").collect do |piece|
210
+ p = NamePiece.new
211
+ p.type = "Family"
212
+ p.predelimiters = ""
213
+ p.value = piece
214
+ p
215
+ end
216
+ else
217
+ self.fullText = name
218
+ end
219
+ end
220
+
221
+ def buildFullText
222
+ self.pieces.collect{|piece| "#{piece.predelimiters}#{piece.value}#{piece.postdelimiters}"}.join('')
223
+ end
224
+ end
225
+
226
+ class NameValue
227
+ def add_form(value)
228
+ self.forms = []
229
+ f = NameForm.new
230
+ f.set_name(value)
231
+ self.forms << f
232
+ end
233
+
234
+ end
235
+
236
+ class NameAssertion
237
+ def add_value(value)
238
+ self.value = NameValue.new
239
+ self.value.add_form(value)
240
+ end
241
+ end
242
+
243
+ class EventValue
244
+ def add_date(value)
245
+ self.date = GenDate.new
246
+ self.date.original = value
247
+ end
248
+
249
+ def add_place(value)
250
+ self.place = Place.new
251
+ self.place.original = value
252
+ end
253
+ end
254
+
255
+ class EventAssertion
256
+ # ====Params
257
+ # * <tt>options</tt> - requires a :type option and accepts an (optional) :date and :place option
258
+ #
259
+ # ====Example
260
+ #
261
+ # person.add_birth :date => '12 Aug 1902', :place => 'United States'
262
+ def add_value(options)
263
+ raise ArgumentError, "missing option[:type]" if options[:type].nil?
264
+ self.value = EventValue.new
265
+ self.value.type = options[:type]
266
+ self.value.add_date(options[:date]) if options[:date]
267
+ self.value.add_place(options[:place]) if options[:place]
268
+ end
269
+
270
+ # To make porting code from v1 to v2 easier, date will reference
271
+ # value.date
272
+ def date
273
+ value.date
274
+ end
275
+
276
+ # To make porting code from v1 to v2 easier, date will reference
277
+ # value.date
278
+ def place
279
+ value.place
280
+ end
281
+ end
282
+
283
+ class OrdinanceValue
284
+
285
+ def add_date(value)
286
+ self.date = GenDate.new
287
+ self.date.original = value
288
+ end
289
+
290
+ def add_place(value)
291
+ self.place = Place.new
292
+ self.place.original = value
293
+ end
294
+
295
+ def add_mother(mother_id)
296
+ add_parent('Female',mother_id)
297
+ end
298
+
299
+ def add_father(father_id)
300
+ add_parent('Male',father_id)
301
+ end
302
+
303
+ def add_parent(gender, id)
304
+ add_parents!
305
+ parent = PersonReference.new
306
+ parent.id = id
307
+ parent.gender = gender
308
+ self.parents << parent
309
+ end
310
+
311
+ private
312
+ def add_parents!
313
+ self.parents ||= []
314
+ end
315
+
316
+ end
317
+
318
+ class OrdinanceAssertion
319
+
320
+ def add_value(options)
321
+ raise ArgumentError, "missing option[:type]" if options[:type].nil?
322
+ raise ArgumentError, "missing option[:place]" if options[:place].nil?
323
+ self.value = OrdinanceValue.new
324
+ self.value.type = options[:type]
325
+ self.value.add_date(options[:date]) if options[:date]
326
+ self.value.add_place(options[:place]) if options[:place]
327
+ self.value.temple = options[:temple] if options[:temple]
328
+ if options[:type] == OrdinanceType::Sealing_to_Parents
329
+ self.value.add_mother(options[:mother])
330
+ self.value.add_father(options[:father])
331
+ end
332
+ end
333
+ end
334
+
335
+ class PersonAssertions
336
+ def add_gender(value)
337
+ self.genders ||= []
338
+ g = GenderAssertion.new
339
+ g.add_value(value)
340
+ self.genders << g
341
+ end
342
+
343
+ def add_name(value)
344
+ self.names ||= []
345
+ n = NameAssertion.new
346
+ n.add_value(value)
347
+ self.names << n
348
+ end
349
+
350
+ def add_event(options)
351
+ self.events ||= []
352
+ e = EventAssertion.new
353
+ e.add_value(options)
354
+ self.events << e
355
+ end
356
+
357
+ def add_ordinance(options)
358
+ self.ordinances ||= []
359
+ o = OrdinanceAssertion.new
360
+ o.add_value(options)
361
+ self.ordinances << o
362
+ end
363
+ end
364
+
365
+ class CharacteristicAssertion
366
+ # ====Params
367
+ # * <tt>options</tt> - same as RelationshipAssertions#add_characteristic
368
+ def add_value(options)
369
+ self.value = CharacteristicValue.new
370
+ self.value.type = options[:type]
371
+ self.value.lineage = options[:lineage] if options[:lineage]
372
+ end
373
+ end
374
+
375
+ class ExistsAssertion
376
+ def add_value
377
+ self.value = ExistsValue.new
378
+ end
379
+ end
380
+
381
+ class RelationshipAssertions
382
+ # ====Params
383
+ # * <tt>options</tt> - :type ('Lineage' or valid CharacteristicType), :lineage => 'Biological', etc.
384
+ def add_characteristic(options)
385
+ self.characteristics ||= []
386
+ characteristic = CharacteristicAssertion.new
387
+ characteristic.add_value(options)
388
+ self.characteristics << characteristic
389
+ end
390
+
391
+ # ====Params
392
+ # * <tt>options</tt> - Accepts the following options
393
+ # ** :type - 'Marriage', etc. REQUIRED
394
+ # ** :date - 'Utah, United States' (optional)
395
+ # ** :place - '16 Nov 1987' (optional)
396
+ def add_event(options)
397
+ self.events ||= []
398
+ event = EventAssertion.new
399
+ event.add_value(options)
400
+ self.events << event
401
+ end
402
+
403
+ # ====Params
404
+ # * <tt>options</tt> - Accepts the following options
405
+ # ** :type - 'Sealing_to_Spouse', etc. REQUIRED
406
+ # ** :date - 'Utah, United States' (optional)
407
+ # ** :place - '16 Nov 1987' (optional)
408
+ # ** :temple - 'SLAKE'
409
+ def add_ordinance(options)
410
+ self.ordinances ||= []
411
+ ordinance = OrdinanceAssertion.new
412
+ ordinance.add_value(options)
413
+ self.ordinances << ordinance
414
+ end
415
+
416
+ def add_exists
417
+ self.exists ||= []
418
+ exist = ExistsAssertion.new
419
+ exist.add_value
420
+ self.exists << exist
421
+ end
422
+ end
423
+
424
+ class Relationship
425
+ def add_lineage_characteristic(lineage)
426
+ add_assertions!
427
+ self.assertions.add_characteristic(:type => 'Lineage', :lineage => lineage)
428
+ end
429
+
430
+ def add_exists
431
+ add_assertions!
432
+ self.assertions.add_exists
433
+ end
434
+
435
+ # ====Params
436
+ # * <tt>event_hash</tt> - Accepts the following options
437
+ # ** :type - 'Marriage', etc. REQUIRED
438
+ # ** :date - 'Utah, United States' (optional)
439
+ # ** :place - '16 Nov 1987' (optional)
440
+ def add_event(event_hash)
441
+ add_assertions!
442
+ self.assertions.add_event(event_hash)
443
+ end
444
+
445
+ # ====Params
446
+ # * <tt>ordinance_hash</tt> - Accepts the following options
447
+ # ** :type - 'Sealing_to_Spouse', etc. REQUIRED
448
+ # ** :date - 'Utah, United States' (optional)
449
+ # ** :place - '16 Nov 1987' (optional)
450
+ # ** :temple - 'SLAKE'
451
+ def add_ordinance(ordinance_hash)
452
+ add_assertions!
453
+ self.assertions.add_ordinance(ordinance_hash)
454
+ end
455
+
456
+ private
457
+ def add_assertions!
458
+ self.assertions ||= RelationshipAssertions.new
459
+ end
460
+ end
461
+
462
+ class PersonRelationships
463
+ def initialize
464
+ @parents = []
465
+ @spouses = []
466
+ @children = []
467
+ end
468
+
469
+ # ====Params
470
+ # * <tt>options</tt> - requires the following:
471
+ # ** :type - 'parent', 'child', 'spouse'
472
+ # ** :with - ID of the person with whom you are making the relationship
473
+ # ** :lineage (optional) - 'Biological', 'Adoptive', etc.
474
+ # ** :event - a hash with values {:type => 'Marriage', :date => '15 Nov 2007', :place => 'Utah, United States'}
475
+ # ** :ordinance - a hash with values {:date => '15 Nov 2007', :temple => 'SLAKE', :place => 'Utah, United States', :type => "Sealing_to_Spouse"}
476
+ def add_relationship(options)
477
+ g_command = get_command(options[:type])
478
+ relationship = self.send(g_command.to_sym).find{|r|r.id == options[:with]}
479
+ if relationship.nil?
480
+ relationship = Relationship.new
481
+ relationship.id = options[:with]
482
+ end
483
+ if options[:lineage]
484
+ relationship.add_lineage_characteristic(options[:lineage]) if options[:lineage]
485
+ else
486
+ relationship.add_exists
487
+ end
488
+ if options[:event]
489
+ relationship.add_event(options[:event])
490
+ end
491
+ if options[:ordinance]
492
+ relationship.add_ordinance(options[:ordinance])
493
+ end
494
+ s_command = set_command(options[:type])
495
+ self.send(s_command.to_sym,[relationship])
496
+ end
497
+
498
+ # Overriding the Enunciate code because of a bug (parents, spouses, and children were not pluralized)
499
+ # the json hash for this PersonRelationships
500
+ def to_jaxb_json_hash
501
+ _h = {}
502
+ if !parents.nil?
503
+ _ha = Array.new
504
+ parents.each { | _item | _ha.push _item.to_jaxb_json_hash }
505
+ _h['parents'] = _ha
506
+ end
507
+ if !spouses.nil?
508
+ _ha = Array.new
509
+ spouses.each { | _item | _ha.push _item.to_jaxb_json_hash }
510
+ _h['spouses'] = _ha
511
+ end
512
+ if !children.nil?
513
+ _ha = Array.new
514
+ children.each { | _item | _ha.push _item.to_jaxb_json_hash }
515
+ _h['children'] = _ha
516
+ end
517
+ return _h
518
+ end
519
+
520
+ # Overriding the Enunciate code because of a bug
521
+ #initializes this PersonRelationships with a json hash
522
+ def init_jaxb_json_hash(_o)
523
+ if !_o['parents'].nil?
524
+ @parents = Array.new
525
+ _oa = _o['parents']
526
+ _oa.each { | _item | @parents.push Org::Familysearch::Ws::Familytree::V2::Schema::Relationship.from_json(_item) }
527
+ end
528
+ if !_o['spouses'].nil?
529
+ @spouses = Array.new
530
+ _oa = _o['spouses']
531
+ _oa.each { | _item | @spouses.push Org::Familysearch::Ws::Familytree::V2::Schema::Relationship.from_json(_item) }
532
+ end
533
+ if !_o['children'].nil?
534
+ @children = Array.new
535
+ _oa = _o['children']
536
+ _oa.each { | _item | @children.push Org::Familysearch::Ws::Familytree::V2::Schema::Relationship.from_json(_item) }
537
+ end
538
+ end
539
+
540
+ private
541
+ def get_command(type)
542
+ (type == 'child') ? 'children' : "#{type}s"
543
+ end
544
+
545
+ def set_command(type)
546
+ get_command(type)+"="
547
+ end
548
+ end
549
+
550
+ class Person
551
+
552
+ def full_names
553
+ if assertions && assertions.names
554
+ return assertions.names.collect do |name|
555
+ (name.value.forms[0].fullText.nil?) ? name.value.forms[0].buildFullText : name.value.forms[0].fullText
556
+ end
557
+ else
558
+ []
559
+ end
560
+ end
561
+
562
+ def full_name
563
+ self.full_names.first
564
+ end
565
+
566
+ def gender
567
+ if assertions && assertions.genders && assertions.genders[0] && assertions.genders[0].value
568
+ assertions.genders[0].value.type
569
+ else
570
+ nil
571
+ end
572
+ end
573
+
574
+ # Convenience method for adding the gender.
575
+ #
576
+ # ====Params
577
+ # <tt>value</tt> - 'Male' or 'Female'
578
+ def add_gender(value)
579
+ add_assertions!
580
+ assertions.add_gender(value)
581
+ end
582
+
583
+ # Convenience method for adding a name. It fills in the necessary
584
+ # structure underneath to create the name.
585
+ #
586
+ # ====Params
587
+ # <tt>value</tt> - the name to be added
588
+ #
589
+ # ====Example
590
+ #
591
+ # person.add_name 'Parker Felch' # Sets the fullText to "Parker Felch"
592
+ # person.add_name 'Parker Jones /Felch/' # Does not set the fullText, but sets the name pieces.
593
+ def add_name(value)
594
+ add_assertions!
595
+ assertions.add_name(value)
596
+ end
597
+
598
+ def births
599
+ select_events('Birth')
600
+ end
601
+
602
+ # It should return the selected birth assertion unless it is
603
+ # not set in which case it will return the first
604
+ def birth
605
+ birth = births.find{|b|!b.selected.nil?}
606
+ birth ||= births[0]
607
+ birth
608
+ end
609
+
610
+ def deaths
611
+ select_events('Death')
612
+ end
613
+
614
+ # It should return the selected death assertion unless it is
615
+ # not set in which case it will return the first
616
+ def death
617
+ death = deaths.find{|b|!b.selected.nil?}
618
+ death ||= deaths[0]
619
+ death
620
+ end
621
+
622
+ # Add an event with type of Birth
623
+ #
624
+ # ====Params
625
+ # * <tt>options</tt> - accepts a :date and :place option
626
+ #
627
+ # ====Example
628
+ #
629
+ # person.add_birth :date => '12 Aug 1902', :place => 'United States'
630
+ def add_birth(options)
631
+ add_assertions!
632
+ options[:type] = 'Birth'
633
+ assertions.add_event(options)
634
+ end
635
+
636
+ # Add an event with type of Birth
637
+ #
638
+ # ====Params
639
+ # * <tt>options</tt> - accepts a :date and :place option
640
+ #
641
+ # ====Example
642
+ #
643
+ # person.add_birth :date => '12 Aug 1902', :place => 'United States'
644
+ def add_death(options)
645
+ add_assertions!
646
+ options[:type] = 'Death'
647
+ assertions.add_event(options)
648
+ end
649
+
650
+ def baptisms
651
+ select_ordinances('Baptism')
652
+ end
653
+
654
+ def confirmations
655
+ select_ordinances('Confirmation')
656
+ end
657
+
658
+ def initiatories
659
+ select_ordinances('Initiatory')
660
+ end
661
+
662
+ def endowments
663
+ select_ordinances('Endowment')
664
+ end
665
+
666
+ def sealing_to_parents
667
+ select_ordinances(OrdinanceType::Sealing_to_Parents)
668
+ end
669
+
670
+ def sealing_to_spouses(id)
671
+ select_relationship_ordinances(:relationship_type => 'spouse', :id => id, :type => OrdinanceType::Sealing_to_Spouse)
672
+ end
673
+
674
+ # Add a baptism ordinance
675
+ #
676
+ # ====Params
677
+ # * <tt>options</tt> - accepts a :date, :place, and :temple option
678
+ #
679
+ # ====Example
680
+ #
681
+ # person.add_baptism :date => '14 Aug 2009', :temple => 'SGEOR', :place => 'Salt Lake City, Utah'
682
+ def add_baptism(options)
683
+ add_assertions!
684
+ options[:type] = 'Baptism'
685
+ assertions.add_ordinance(options)
686
+ end
687
+
688
+ # Add a confirmation ordinance
689
+ #
690
+ # ====Params
691
+ # * <tt>options</tt> - accepts a :date, :place, and :temple option
692
+ #
693
+ # ====Example
694
+ #
695
+ # person.add_confirmation :date => '14 Aug 2009', :temple => 'SGEOR', :place => 'Salt Lake City, Utah'
696
+ def add_confirmation(options)
697
+ add_assertions!
698
+ options[:type] = 'Confirmation'
699
+ assertions.add_ordinance(options)
700
+ end
701
+
702
+ # Add a initiatory ordinance
703
+ #
704
+ # ====Params
705
+ # * <tt>options</tt> - accepts a :date, :place, and :temple option
706
+ #
707
+ # ====Example
708
+ #
709
+ # person.add_initiatory :date => '14 Aug 2009', :temple => 'SGEOR', :place => 'Salt Lake City, Utah'
710
+ def add_initiatory(options)
711
+ add_assertions!
712
+ options[:type] = 'Initiatory'
713
+ assertions.add_ordinance(options)
714
+ end
715
+
716
+ # Add a endowment ordinance
717
+ #
718
+ # ====Params
719
+ # * <tt>options</tt> - accepts a :date, :place, and :temple option
720
+ #
721
+ # ====Example
722
+ #
723
+ # person.add_endowment :date => '14 Aug 2009', :temple => 'SGEOR', :place => 'Salt Lake City, Utah'
724
+ def add_endowment(options)
725
+ add_assertions!
726
+ options[:type] = 'Endowment'
727
+ assertions.add_ordinance(options)
728
+ end
729
+
730
+ # Add a sealing to parents ordinance
731
+ #
732
+ # ====Params
733
+ # * <tt>options</tt> - accepts a :date, :place, :temple, :mother, and :father option
734
+ #
735
+ # ====Example
736
+ #
737
+ # person.add_sealing_to_parents :date => '14 Aug 2009', :temple => 'SGEOR', :place => 'Salt Lake City, Utah'
738
+ def add_sealing_to_parents(options)
739
+ raise ArgumentError, ":mother option is required" if options[:mother].nil?
740
+ raise ArgumentError, ":father option is required" if options[:father].nil?
741
+ add_assertions!
742
+ options[:type] = OrdinanceType::Sealing_to_Parents
743
+ assertions.add_ordinance(options)
744
+ end
745
+
746
+ # This method should really only be called from FamilytreeV2::Communicator#write_relationships
747
+ #
748
+ # ====Params
749
+ # * <tt>options</tt> - requires the following:
750
+ # ** :type - 'parent', 'child', 'spouse'
751
+ # ** :with - ID of the person with whom you are making the relationship
752
+ # ** :lineage (optional) - 'Biological', 'Adoptive', etc.
753
+ # ** :event - a hash with values {:type => 'Marriage', :date => '15 Nov 2007', :place => 'Utah, United States'}
754
+ def create_relationship(options)
755
+ raise ArgumentError, ":type option is required" if options[:type].nil?
756
+ raise ArgumentError, ":with option is required" if options[:with].nil?
757
+ add_relationships!
758
+ self.relationships.add_relationship(options)
759
+ end
760
+
761
+ private
762
+
763
+ def add_relationships!
764
+ self.relationships ||= PersonRelationships.new
765
+ end
766
+
767
+ def add_assertions!
768
+ if assertions.nil?
769
+ self.assertions = PersonAssertions.new
770
+ end
771
+ end
772
+
773
+ def select_events(type)
774
+ if assertions && assertions.events
775
+ assertions.events.select{|e| e.value.type == type}
776
+ else
777
+ []
778
+ end
779
+ end
780
+
781
+ def select_ordinances(type)
782
+ if assertions && assertions.ordinances
783
+ assertions.ordinances.select{|e| e.value.type == type}
784
+ else
785
+ []
786
+ end
787
+ end
788
+
789
+ # only ordinance type is Sealing_to_Spouse
790
+ def select_relationship_ordinances(options)
791
+ raise ArgumentError, ":id required" if options[:id].nil?
792
+ if self.relationships
793
+ spouse_relationship = self.relationships.spouses.find{|s|s.id == options[:id]}
794
+ if spouse_relationship && spouse_relationship.assertions && spouse_relationship.assertions.ordinances
795
+ spouse_relationship.assertions.ordinances
796
+ else
797
+ []
798
+ end
799
+ end
800
+ end
801
+
802
+ end
803
+
804
+ class SearchPerson
805
+ alias :name :full_name
806
+ def events
807
+ (assertions && assertions.events) ? assertions.events : []
808
+ end
809
+
810
+ # Always will return nil. Method is here for v1 backwards compatibility
811
+ def marriage
812
+ nil
813
+ end
814
+ end
815
+
816
+ class SearchResult
817
+ alias :ref :id
818
+
819
+ def father
820
+ parents.find{|p|p.gender == 'Male'}
821
+ end
822
+
823
+ def mother
824
+ parents.find{|p|p.gender == 'Female'}
825
+ end
826
+ end
827
+ end