gedcom 0.9.0 → 0.9.1

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 (60) hide show
  1. data/.gemtest +0 -0
  2. data/History.txt +8 -2
  3. data/Manifest.txt +4 -0
  4. data/README.txt +1 -1
  5. data/Rakefile +13 -5
  6. data/lib/gedcom.rb +3 -1
  7. data/lib/gedcom/address_record.rb +0 -0
  8. data/lib/gedcom/adoption_record.rb +0 -0
  9. data/lib/gedcom/association_record.rb +3 -3
  10. data/lib/gedcom/cause_record.rb +0 -0
  11. data/lib/gedcom/change_date_record.rb +0 -0
  12. data/lib/gedcom/character_set_record.rb +0 -0
  13. data/lib/gedcom/citation_data_record.rb +0 -0
  14. data/lib/gedcom/citation_event_type_record.rb +0 -0
  15. data/lib/gedcom/corporate_record.rb +0 -0
  16. data/lib/gedcom/date_record.rb +18 -0
  17. data/lib/gedcom/encoded_line_record.rb +0 -0
  18. data/lib/gedcom/event_age_record.rb +0 -0
  19. data/lib/gedcom/event_record.rb +13 -3
  20. data/lib/gedcom/events_list_record.rb +0 -0
  21. data/lib/gedcom/families_individuals.rb +2 -2
  22. data/lib/gedcom/family_record.rb +165 -3
  23. data/lib/gedcom/ged_string.rb +49 -0
  24. data/lib/gedcom/gedcom_all.rb +2 -0
  25. data/lib/gedcom/gedcom_base.rb +45 -15
  26. data/lib/gedcom/gedcom_record.rb +0 -0
  27. data/lib/gedcom/header_data_record.rb +0 -0
  28. data/lib/gedcom/header_record.rb +0 -0
  29. data/lib/gedcom/header_source_record.rb +0 -0
  30. data/lib/gedcom/individual_attribute_record.rb +25 -2
  31. data/lib/gedcom/individual_record.rb +436 -13
  32. data/lib/gedcom/multimedia_citation_record.rb +0 -0
  33. data/lib/gedcom/multimedia_record.rb +0 -0
  34. data/lib/gedcom/name_record.rb +70 -0
  35. data/lib/gedcom/note_citation_record.rb +0 -0
  36. data/lib/gedcom/note_record.rb +0 -0
  37. data/lib/gedcom/place_record.rb +6 -0
  38. data/lib/gedcom/refn_record.rb +0 -0
  39. data/lib/gedcom/repository_caln.rb +0 -0
  40. data/lib/gedcom/repository_citation_record.rb +0 -0
  41. data/lib/gedcom/repository_record.rb +0 -0
  42. data/lib/gedcom/source_citation_record.rb +0 -0
  43. data/lib/gedcom/source_record.rb +0 -0
  44. data/lib/gedcom/source_scope_record.rb +0 -0
  45. data/lib/gedcom/submission_record.rb +0 -0
  46. data/lib/gedcom/submitter_record.rb +0 -0
  47. data/lib/gedcom/text_record.rb +0 -0
  48. data/lib/gedcom/trailer_record.rb +0 -0
  49. data/lib/gedcom/transmission.rb +9 -0
  50. data/lib/gedcom/transmission_base.rb +81 -37
  51. data/lib/gedcom/xref.rb +8 -0
  52. data/lib/parser/class_tracker.rb +0 -0
  53. data/lib/parser/ged_line.rb +0 -0
  54. data/lib/parser/gedcom_parser.rb +0 -0
  55. data/lib/parser/instruction.rb +0 -0
  56. data/lib/parser/parse_state.rb +0 -0
  57. data/test/lds_gedcom_test.rb +61 -0
  58. data/test/ruby_version.rb +43 -0
  59. data/test/test_gedcom.rb +0 -0
  60. metadata +57 -16
File without changes
@@ -1,4 +1,10 @@
1
- === 0.9.0 / 2009-03-14
1
+ === 0.9.1 / 2012-19-27
2
+
3
+ * transcription of code from original C source base.
4
+ * Bug fix to GEDCOMBase.xref_check, which didn't qualify index lookup when printing an error message.
5
+ * Added Transmission.self_check, which calls new FamilyRecord.self_check and IndividualRecord.self_check
6
+ Checks the gedcom file FAMC, FAMS, HUSB, WIFE, CHIL and SUBM xrefs resolve to valid records.
7
+ Required adding IndividualRecord.child? and IndividualRecord.spouse? for tests.
8
+ Fixed bug in IndividualRecord.spouses and IndividualRecord.parents_family as neither worked and were used by tests.
2
9
 
3
- * transcription of code from original Rails source base.
4
10
 
@@ -17,6 +17,7 @@ lib/gedcom/event_record.rb
17
17
  lib/gedcom/events_list_record.rb
18
18
  lib/gedcom/families_individuals.rb
19
19
  lib/gedcom/family_record.rb
20
+ lib/gedcom/ged_string.rb
20
21
  lib/gedcom/gedcom_all.rb
21
22
  lib/gedcom/gedcom_base.rb
22
23
  lib/gedcom/gedcom_record.rb
@@ -45,12 +46,15 @@ lib/gedcom/text_record.rb
45
46
  lib/gedcom/trailer_record.rb
46
47
  lib/gedcom/transmission.rb
47
48
  lib/gedcom/transmission_base.rb
49
+ lib/gedcom/xref.rb
48
50
  lib/gedcom.rb
49
51
  lib/parser/class_tracker.rb
50
52
  lib/parser/ged_line.rb
51
53
  lib/parser/gedcom_parser.rb
52
54
  lib/parser/instruction.rb
53
55
  lib/parser/parse_state.rb
56
+ test/ruby_version.rb
57
+ test/lds_gedcom_test.rb
54
58
  test/test_gedcom.rb
55
59
  test_data/Document.RTF
56
60
  test_data/Document.tex
data/README.txt CHANGED
@@ -15,7 +15,7 @@ unknown tags hierarchies as a Note class.
15
15
  * CR line termination causes issues for systems with native LF line termination (CRLF is fine).
16
16
  * Dates are currently just strings, but I want to parse these and test their validatity.
17
17
  This is not as easy as it may seem at first, as dates may be in many formats,
18
- they may be partial, or may actually be strings decribing the date.
18
+ they may be partial, or may actually be strings describing the date.
19
19
  * For my own use, I bend the GEDCOM 5.5 standard by allowing the reading of the following types in non-standard ways.
20
20
  These will not affect the reading and writing of valid GEDCOM 5.5.
21
21
  * * 'NOTE' type to appear in places it is not defined to exist in GEDCOM.
data/Rakefile CHANGED
@@ -2,12 +2,20 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'hoe'
5
- require 'lib/gedcom.rb'
5
+ #require 'lib/gedcom.rb'
6
6
 
7
- Hoe.new('gedcom', Gedcom::VERSION) do |p|
8
- p.rubyforge_name = "gedcom"
9
- p.developer( "Rob Burrowes","rob@burrowes.org")
10
- p.remote_rdoc_dir = '' # Release to root
7
+ Hoe.spec 'gedcom' do
8
+ self.rubyforge_name = 'gedcom'
9
+ developer 'Rob Burrowes', 'r.burrowes@auckland.ac.nz'
10
+ remote_rdoc_dir = '' # Release to root
11
+ #extra_deps << 'whatevs'
11
12
  end
12
13
 
14
+ #Old V1 format.
15
+ #Hoe.new('gedcom', Gedcom::VERSION) do |p|
16
+ # p.rubyforge_name = "gedcom"
17
+ # p.developer( "Rob Burrowes","rob@burrowes.org")
18
+ # p.remote_rdoc_dir = '' # Release to root
19
+ #end
20
+
13
21
  # vim: syntax=Ruby
@@ -58,11 +58,13 @@ end
58
58
 
59
59
  $: << "#{path(__FILE__)}gedcom"
60
60
  $: << "#{path(__FILE__)}parser"
61
+ $: << "#{path(__FILE__)}chart"
61
62
 
62
63
  require 'gedcom_parser.rb'
64
+ require 'chart.rb'
63
65
 
64
66
  class Gedcom
65
- VERSION = '0.9.0'
67
+ VERSION = '0.9.1'
66
68
  attr_accessor :transmissions
67
69
 
68
70
  def initialize(transmission = nil)
File without changes
File without changes
@@ -59,9 +59,9 @@ class Association_record < GEDCOMBase
59
59
  #validate that the record referenced by the XREF actually exists in this transmission.
60
60
  #Genearte a warning if it does not. It does not stop the processing of this line.
61
61
  #Association_records default to :individual, but the TYPE field can override this.
62
- def xref_check(level, tag, index, xref)
62
+ def xref_check(level, tag, xref)
63
63
  asso_index = case @associated_record_tag
64
- when nil then index
64
+ when nil then xref.index #this should be the default :individual
65
65
  when 'FAM' then :family
66
66
  when 'INDI' then :individual
67
67
  when 'NOTE' then :note
@@ -73,7 +73,7 @@ class Association_record < GEDCOMBase
73
73
  else :individual #which will be the default individual index.
74
74
  end
75
75
 
76
- super(level, tag, asso_index, xref)
76
+ super(level, tag, Xref.new(asso_index, xref.xref_value) )
77
77
  end
78
78
  end
79
79
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -203,4 +203,22 @@ class Date_record < GEDCOMBase
203
203
  [ :walk, nil, :note_citation_record]
204
204
  ]
205
205
  end
206
+
207
+ #If you want just one date, then this returns the first DATE record (probably the only one).
208
+ #GEDCOM says that this should be the most vaild record. If you need all the dates, use date_value,
209
+ #which will give you an array of DATE values.
210
+ def date
211
+ if @date_value
212
+ @date_value.first
213
+ else
214
+ ''
215
+ end
216
+ end
217
+
218
+ #If you want just one date, then this returns the first TIME record (probably the only one).
219
+ #GEDCOM says that this should be the most vaild record. If you need all the dates, use date_value,
220
+ #which will give you an array of TIME values.
221
+ def time
222
+ @time_value.first
223
+ end
206
224
  end
File without changes
File without changes
@@ -244,13 +244,23 @@ class Event_record < GEDCOMBase
244
244
  end
245
245
  end
246
246
 
247
- def is_event(tag)
248
- @event_type.to_s == tag
247
+ def is_event?(tag)
248
+ @event_type.first.to_s == tag #all attributes are arrays, even the single value ones.
249
249
  end
250
250
 
251
251
  def date
252
252
  if @date_record != nil
253
- @date_record[0].date
253
+ @date_record.first.date
254
+ else
255
+ nil
256
+ end
257
+ end
258
+
259
+ #where the event took place. We are reporting only the first place as a string. If you want all the places recorded,
260
+ #then you should access Event_record#place_record, which will return an array of PLAC records in the event.
261
+ def place
262
+ if @place_record != nil
263
+ @place_record.first.place
254
264
  else
255
265
  nil
256
266
  end
File without changes
@@ -61,7 +61,7 @@ class Families_individuals < GEDCOMBase
61
61
 
62
62
  def parents_family
63
63
  if @parents_family_ref != nil
64
- find(:family, @parents_family_ref[0])
64
+ find(:family, @parents_family_ref)
65
65
  else
66
66
  nil
67
67
  end
@@ -69,7 +69,7 @@ class Families_individuals < GEDCOMBase
69
69
 
70
70
  def own_family
71
71
  if @family_ref != nil
72
- find(:family, @family_ref[0])
72
+ find(:family, @family_ref)
73
73
  else
74
74
  nil
75
75
  end
@@ -44,7 +44,7 @@ class Family_record < GEDCOMBase
44
44
  attr_accessor :note_citation_record, :refn_record, :automated_record_id, :change_date_record
45
45
 
46
46
  ClassTracker << :Family_record
47
-
47
+
48
48
  #new sets up the state engine arrays @this_level and @sub_level, which drive the to_gedcom method generating GEDCOM output.
49
49
  def initialize(*a)
50
50
  super(*a)
@@ -70,20 +70,182 @@ class Family_record < GEDCOMBase
70
70
  @family_ref
71
71
  end
72
72
 
73
+ #There should only ever be one husband record in a Family_record. If a women has
74
+ #multiple husbands, as some cultures do, then each should be in their own FAM record.
75
+ #The reasoning is that we are recording parentage, not marriages, so we want to be
76
+ #able to uniquely identify which husband is the actual parent (not that we could be
77
+ #that certain in the case of polygamy). The term husband is also used loosely. It
78
+ #refers to the father of children, not necessarily a spouse.
73
79
  def husband
74
80
  if @husband_ref != nil
75
- find(@husband_ref[0], @husband_ref[1])
81
+ find(@husband_ref.first.index, @husband_ref.first.xref_value)
76
82
  else
77
83
  nil
78
84
  end
79
85
  end
80
86
 
87
+ #There should only ever be one wife record in a Family_record. If a man has
88
+ #multiple wives, as some cultures do, then each should be in their own FAM record.
89
+ #The reasoning is that we are recording parentage, not marriages, so we want to be
90
+ #able to uniquely identify which wife is the actual parent. The term wife is used
91
+ #fairly loosely. It refers to the mother of the children, not necessarily a spouse.
81
92
  def wife
82
93
  if @wife_ref != nil
83
- find(@wife_ref[0], @wife_ref[1])
94
+ find(@wife_ref.first.index, @wife_ref.first.xref_value)
84
95
  else
85
96
  nil
86
97
  end
87
98
  end
88
99
 
100
+ #Returns an array of children, or if a block is present, yields them one by one.
101
+ def children
102
+ if @child_ref != nil
103
+ children = []
104
+ @child_ref.each do |c|
105
+ if (child = find(c.index, c.xref_value)) != nil
106
+ yield child if block_given?
107
+ children << c
108
+ end
109
+ end
110
+ return children if children.length > 0
111
+ end
112
+ return nil
113
+ end
114
+
115
+ #Event looks in the Family_record for events, as specified by the type argument,
116
+ #returning an array of the events found. Returns nil if there were
117
+ #no events of this type in this Family_record.
118
+ #
119
+ #If a block is given, then yields each event to the block.
120
+ def event(type)
121
+ if @event_record != nil
122
+ events = []
123
+ @event_record.each do |e|
124
+ if e.is_event?(type)
125
+ yield e if block_given?
126
+ events << e
127
+ end
128
+ end
129
+ return events if events.length > 0
130
+ end
131
+ return nil
132
+ end
133
+
134
+ #Short hand for event('ENGA')
135
+ #passes on any block to the event method.
136
+ #(The block is the &p argument, so you don't pass any arguments to this method).
137
+ def engagement(&p)
138
+ if block_given? then event('ENGA',&p) else event('ENGA') end
139
+ end
140
+
141
+ #Short hand for event('MARB')
142
+ #passes on any block to the event method.
143
+ #(The block is the &p argument, so you don't pass any arguments to this method).
144
+ def marriage_bann(&p)
145
+ if block_given? then event('MARB',&p) else event('MARB') end
146
+ end
147
+
148
+ #Short hand for event('MARL')
149
+ #passes on any block to the event method.
150
+ #(The block is the &p argument, so you don't pass any arguments to this method).
151
+ def marriage_license(&p)
152
+ if block_given? then event('MARL',&p) else event('MARL') end
153
+ end
154
+
155
+ #Short hand for event('MARC')
156
+ #passes on any block to the event method.
157
+ #(The block is the &p argument, so you don't pass any arguments to this method).
158
+ def marriage_contract(&p)
159
+ if block_given? then event('MARC',&p) else event('MARC') end
160
+ end
161
+
162
+ #Short hand for event('MARS')
163
+ #passes on any block to the event method.
164
+ #(The block is the &p argument, so you don't pass any arguments to this method).
165
+ def marriage_settlement(&p)
166
+ if block_given? then event('MARS',&p) else event('MARS') end
167
+ end
168
+
169
+ #Short hand for event('MARR')
170
+ #passes on any block to the event method.
171
+ #(The block is the &p argument, so you don't pass any arguments to this method).
172
+ def marriage(&p)
173
+ if block_given? then event('MARR',&p) else event('MARR') end
174
+ end
175
+
176
+ #Short hand for event('ANUL')
177
+ #passes on any block to the event method.
178
+ #(The block is the &p argument, so you don't pass any arguments to this method).
179
+ def annulment(&p)
180
+ if block_given? then event('ANUL',&p) else event('ANUL') end
181
+ end
182
+
183
+ #Short hand for event('DIVF')
184
+ #passes on any block to the event method.
185
+ #(The block is the &p argument, so you don't pass any arguments to this method).
186
+ def divorce_filed(&p)
187
+ if block_given? then event('DIVF',&p) else event('DIVF') end
188
+ end
189
+
190
+ #Short hand for event('DIV')
191
+ #passes on any block to the event method.
192
+ #(The block is the &p argument, so you don't pass any arguments to this method).
193
+ def divorce(&p)
194
+ if block_given? then event('DIV',&p) else event('DIV') end
195
+ end
196
+
197
+ #Short hand for event('CENS')
198
+ #passes on any block to the event method.
199
+ #(The block is the &p argument, so you don't pass any arguments to this method).
200
+ def census(&p)
201
+ if block_given? then event('CENS',&p) else event('CENS') end
202
+ end
203
+
204
+ #Check the xrefs are valid.
205
+ def self_check
206
+ if @husband_ref != nil
207
+ puts "More than one HUSB? in FAM #{@family_ref.first.xref_value}" if @husband_ref.length > 1 #should be just one
208
+ @husband_ref.each do |h|
209
+ if (husb = find(h.index, h.xref_value)) == nil
210
+ puts "Missing INDI record #{h.xref_value} for HUSB in FAM #{@family_ref.first.xref_value}"
211
+ else
212
+ puts "Husband's INDI #{h.xref_value} record doesn't have FAMS #{@family_ref.first.xref_value}" if husb.spouse?(self) == false
213
+ if (sex = husb.sex) != nil
214
+ sex.each do |s|
215
+ puts "Husband Female? INDI #{h.xref_value}, FAMS #{@family_ref.first.xref_value}" if s.value.first == 'F'
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+ if @wife_ref != nil
222
+ puts "More than one WIFE? in FAM #{@family_ref.first.xref_value}" if @wife_ref.length > 1 #should be just one
223
+ @wife_ref.each do |w|
224
+ if (wife = find(w.index, w.xref_value)) == nil
225
+ puts "Missing INDI record #{w.xref_value} for WIFE in FAM #{@family_ref.first.xref_value}"
226
+ else
227
+ puts "Wife's INDI #{w.xref_value} record doesn't have FAMS #{@family_ref.first.xref_value}" if wife.spouse?(self) == false
228
+ if (sex = wife.sex) != nil
229
+ sex.each { |s| puts "Wife Male? INDI #{w.xref_value}, FAMS #{@family_ref.first.xref_value}" if s.value.first == 'M' }
230
+ end
231
+ end
232
+ end
233
+ end
234
+ if @child_ref != nil
235
+ #1..many Children
236
+ @child_ref.each do |c|
237
+ if (child = find(c.index, c.xref_value)) == nil
238
+ puts "Missing INDI record for #{c.xref_value} for CHIL in FAM #{@family_ref.first.xref_value}"
239
+ else
240
+ puts "Child's INDI #{c.xref_value} record doesn't have FAMC #{@family_ref.first.xref_value}" if child.child?(self) == false
241
+ end
242
+ end
243
+ end
244
+ if @submitter_ref != nil
245
+ @submitter_ref.each do |s|
246
+ puts "Missing SUBM record for #{s.xref_value} in FAM #{@family_ref.first.xref_value}" if find(s.index, s.xref_value) == nil
247
+ end
248
+ end
249
+ end
250
+
89
251
  end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby1.9
2
+ #Our parser tokenize strings into word arrays. This is helpful in word wrapping for CONT and CONC,
3
+ #but pretty silly for most other uses. I'm still thinking about the best way to deal with this.
4
+ #
5
+ #The current thought is to flatten the array and store the value as a space separated string, and tokenize
6
+ #tokenize it again if we output GEDCOM. Most of the time, this is what we want, and it makes dealing with
7
+ #Data bases easier too.
8
+
9
+ class GedString < String
10
+
11
+ #takes a word array from the parser and creates a space separated string.
12
+ #Also excepts a String, assigning this to the
13
+ def initialize(word_array)
14
+ if(word_array.class == String)
15
+ super word_array
16
+ elsif(word_array.class == Array)
17
+ super(word_array.inject('') do |v, w|
18
+ if v == ''
19
+ w
20
+ elsif w == "\n" || v[-1,1] == "\n"
21
+ v + w
22
+ else
23
+ v + ' ' + w
24
+ end
25
+ end
26
+ )
27
+ else
28
+ raise "GedString word_array passed in as #{word_array.class}"
29
+ end
30
+ end
31
+
32
+ #yields the string word by word. Line separators in the string will also be yielded as words.
33
+ def each_word
34
+ self.gsub(/\n/," \n ").split(/ /).each { |w| yield w }
35
+ end
36
+
37
+ #yields the string word by word. Line separators in the string will also be yielded as words.
38
+ def each_word_with_index
39
+ self.gsub(/\n/," \n ").split(/ /).each_with_index { |w,i| yield(w, i) }
40
+ end
41
+
42
+ #We aren't an array, but to simplify some code, the method each is defined to return our 1 value.
43
+ def each
44
+ yield self
45
+ end
46
+
47
+ alias each_with_index each_word_with_index
48
+
49
+ end