gedcom 0.9.0 → 0.9.1

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