rsinger-enhanced_marc 0.1 → 0.1.2

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.
@@ -0,0 +1,38 @@
1
+ module MARC
2
+
3
+ # A class that represents an individual MARC record. Every record
4
+ # is made up of a collection of MARC::Field objects.
5
+
6
+ class MapRecord < Record
7
+
8
+ def initialize
9
+ super
10
+ @leader[6] = 'e' if @leader[6,1] == ' '
11
+ @leader[7] = 'm' if @leader[7,1] == ' '
12
+ @record_type = 'MAP'
13
+ @bibliographic_level = @leader.get_blvl
14
+ self.extend MapType
15
+ self.inspect_fixed_fields
16
+ end
17
+
18
+ def is_valid_type?
19
+ return false unless @leader[6,1].match(/[ef]{1}/)
20
+ return true
21
+ end
22
+ def is_valid_blvl?
23
+ return true if @leader[7,1].match(/[acdim]{1}/) and @leader[6,1].match('f')
24
+ return true if @leader[7,1].match(/[abcdims]{1}/) and @leader[6,1].match('e')
25
+ return false
26
+ end
27
+ def self.new_from_record(record)
28
+ rec = MapRecord.new
29
+ record.instance_variables.each { | var |
30
+ rec.instance_variable_set(var, record.instance_variable_get(var))
31
+ }
32
+ return Exception.new("Incorrect type declaration in leader") unless rec.is_valid_type?
33
+ return Exception.new("Incorrect bibliographic declaration in leader") unless rec.is_valid_blvl?
34
+ return rec
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,99 @@
1
+ module MapType
2
+ include RecordType
3
+ public :is_govdoc?, :form, :has_index?
4
+ def cartographic_type(human_readable=false)
5
+ crtp_map = {'a'=>'Map', 'b'=>'Map series', 'c'=>'Map serial', 'd'=>'Globe', 'e'=>'Atlas',
6
+ 'f'=>'Supplement', 'g'=>'Bound as part of another work', 'u'=>'Unknown', 'z'=>'Other'}
7
+ human_readable = crtp_map if human_readable
8
+ return self.field_parser({:match=>'MAP', :start=>25,:end=>1}, {:match=>/[ef]{1}/, :start=>8,:end=>1}, human_readable)
9
+ end
10
+
11
+ def relief(human_readable=false)
12
+ relief_map = {'a'=>'Contours', 'b'=>'Shading', 'c'=>'Grading and bathymetric tints',
13
+ 'd'=>'Hachures', 'e'=>'Bathymetry, soundings', 'f'=>'Form lines', 'g'=>'Spot heights',
14
+ 'h'=>'Color', 'i'=>'Pictorially', 'j'=>'Land forms', 'k'=>'Bathymetry, isolines',
15
+ 'm'=>'Rock drawings', 'z'=>'Other'
16
+ }
17
+ contents = []
18
+ if self.record_type == 'MAP'
19
+ self['008'].value[18,4].split(//).each { | char |
20
+ next if char == " "
21
+ if human_readable
22
+ contents << relief_map[char]
23
+ else
24
+ contents << char
25
+ end
26
+ }
27
+ end
28
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
29
+ next unless fxd_fld.value[0,1].match(/[ef]{1}/)
30
+ fxd_fld.value[1,4].split(//).each { | char |
31
+ next if char == " "
32
+ if human_readable
33
+ contents << relief_map[char]
34
+ else
35
+ contents << char
36
+ end
37
+ }
38
+ }
39
+ return false if contents.empty?
40
+ return contents
41
+ end
42
+
43
+ def projection(human_readable=false)
44
+ proj_map = {'Azimuthal'=>{'aa'=>'Aitoff','ab'=>'Gnomic','ac'=>"Lambert's equal area",
45
+ 'ad'=>'Orthographic','ae'=>'Azithumal equidistant', 'af'=>'Stereographic',
46
+ 'ag'=>'General vertical near-sided','am'=>'Modified stereographic for Alaska',
47
+ 'an'=>'Chamberlin trimetric','ap'=>'Polar stereographic','au'=>'Unknown','az'=>'Other'},
48
+ 'Cylindrical'=>{'ba'=>'Gall','bb'=>"Goode's homolographic",'bc'=>"Lambert's equal area",
49
+ 'bd'=>'Mercator','be'=>'Miller','bf'=>'Mollweide','bg'=>'Sinusoidal',
50
+ 'bh'=>'Transverse Mercator','bi'=>'Gauss-Kruger','bj'=>'Equirectangular',
51
+ 'bo'=>'Oblique Mercator','br'=>'Robinson','bs'=>'Space oblique Mercator',
52
+ 'bu'=>'Unknown','bz'=>'Other'
53
+ },
54
+ 'Conic'=>{'ca'=>"Alber's equal area",'cb'=>'Bonne','cc'=>"Lambert's",'ce'=>'Equidistant conic',
55
+ 'cp'=>'Polyconic','cu'=>'Unknown','cz'=>'Other'
56
+ },
57
+ 'Other'=>{'da'=>'Armadillo','db'=>'Butterfly','dc'=>'Eckert','dd'=>"Goode's homolosine",
58
+ 'de'=>"Miller's bipolar oblique conformal conic",'df'=>'Van Der Grinten',
59
+ 'dg'=>'Dymaxion','dh'=>'Cordiform','dl'=>'Lambert conformal','zz'=>'Other'
60
+ }
61
+ }
62
+ if @record_type == "MAP"
63
+ unless self['008'].value[22,2] == ' '
64
+ if human_readable
65
+ proj_map.each_key { | general |
66
+ next unless proj_map[general].keys.index(self['008'].value[22,2])
67
+ return [general,proj_map[general][self['008'].value[22,2]]]
68
+ }
69
+ else
70
+ return self['008'].value[22,2]
71
+ end
72
+ end
73
+ end
74
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
75
+ next unless fxd_fld.value[0,1].match(/[ef]{1}/)
76
+ unless fxd_fld.value[5,2] == ' '
77
+ if human_readable
78
+ proj_map.each_key { | general |
79
+ next unless proj_map[general].keys.index(fxd_fld.value[5,2])
80
+ return [general,proj_map[general][fxd_fld.value[5,2]]]
81
+ }
82
+ else
83
+ return fxd_fld.value[5,2]
84
+ end
85
+ end
86
+ }
87
+ return false
88
+ end
89
+
90
+ def special_format(human_readable=false)
91
+ spfm_map = {'a'=>'Blueprint photocopy','b'=>'Other photocopy','c'=>'Negative photocopy',
92
+ 'd'=>'Film negative','f'=>'Facsimile','g'=>'Relief model','h'=>'Rare (pre-1800)',
93
+ 'e'=>'Manuscript','j'=>'Picture/post card','k'=>'Calendar','l'=>'Puzzle',
94
+ 'm'=>'Braille, tactile','n'=>'Game','o'=>'Wall map','p'=>'Playing cards',
95
+ 'q'=>'Loose-leaf','z'=>'Other'}
96
+ human_readable = spfm_map if human_readable
97
+ return self.field_parser({:match=>'MAP', :start=>33,:end=>2}, {:match=>/[ef]{1}/, :start=>16,:end=>2}, human_readable)
98
+ end
99
+ end
@@ -0,0 +1,37 @@
1
+ module MARC
2
+
3
+ # A class that represents an individual MARC record. Every record
4
+ # is made up of a collection of MARC::Field objects.
5
+
6
+ class MixedRecord < Record
7
+
8
+ def initialize
9
+ super
10
+ @leader[6] = 'p' if @leader[6,1] == ' '
11
+ @leader[7] = 'c' if @leader[7,1] == ' '
12
+ @record_type = 'MIX'
13
+ @bibliographic_level = @leader.get_blvl
14
+ self.extend MixedType
15
+ self.inspect_fixed_fields
16
+ end
17
+
18
+ def is_valid_type?
19
+ return false unless @leader[6,1].match('p')
20
+ return true
21
+ end
22
+ def is_valid_blvl?
23
+ return false unless @leader[7,1].match(/[cdi]{1}/)
24
+ return true
25
+ end
26
+ def self.new_from_record(record)
27
+ rec = MixedRecord.new
28
+ record.instance_variables.each { | var |
29
+ rec.instance_variable_set(var, record.instance_variable_get(var))
30
+ }
31
+ return Exception.new("Incorrect type declaration in leader") unless rec.is_valid_type?
32
+ return Exception.new("Incorrect bibliographic declaration in leader") unless rec.is_valid_blvl?
33
+ return rec
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ module MixedType
2
+ include RecordType
3
+ public :form
4
+ end
@@ -0,0 +1,101 @@
1
+ module MARC
2
+ class Reader
3
+ # A static method for turning raw MARC data in transission
4
+ # format into a MARC::Record object.
5
+ def self.decode(marc, params={})
6
+ leader = Leader.new(marc[0..LEADER_LENGTH-1])
7
+ record = case leader.get_type
8
+ when 'BKS' then MARC::BookRecord.new
9
+ when 'SER' then MARC::SerialRecord.new
10
+ when 'VIS' then MARC::VisualRecord.new
11
+ when 'MIX' then MARC::MixedRecord.new
12
+ when 'MAP' then MARC::MapRecord.new
13
+ when 'SCO' then MARC::ScoreRecord.new
14
+ when 'REC' then MARC::SoundRecord.new
15
+ when 'COM' then MARC::ComputerRecord.new
16
+ else MARC::Record.new
17
+ end
18
+ record.leader = leader
19
+
20
+ # where the field data starts
21
+ base_address = record.leader[12..16].to_i
22
+
23
+ # get the byte offsets from the record directory
24
+ directory = marc[LEADER_LENGTH..base_address-1]
25
+
26
+ throw "invalid directory in record" if directory == nil
27
+
28
+ # the number of fields in the record corresponds to
29
+ # how many directory entries there are
30
+ num_fields = directory.length / DIRECTORY_ENTRY_LENGTH
31
+
32
+ # when operating in forgiving mode we just split on end of
33
+ # field instead of using calculated byte offsets from the
34
+ # directory
35
+ all_fields = marc[base_address..-1].split(END_OF_FIELD)
36
+
37
+ 0.upto(num_fields-1) do |field_num|
38
+
39
+ # pull the directory entry for a field out
40
+ entry_start = field_num * DIRECTORY_ENTRY_LENGTH
41
+ entry_end = entry_start + DIRECTORY_ENTRY_LENGTH
42
+ entry = directory[entry_start..entry_end]
43
+
44
+ # extract the tag
45
+ tag = entry[0..2]
46
+
47
+ # get the actual field data
48
+ # if we were told to be forgiving we just use the
49
+ # next available chuck of field data that we
50
+ # split apart based on the END_OF_FIELD
51
+ field_data = ''
52
+ if params[:forgiving]
53
+ field_data = all_fields.shift()
54
+
55
+ # otherwise we actually use the byte offsets in
56
+ # directory to figure out what field data to extract
57
+ else
58
+ length = entry[3..6].to_i
59
+ offset = entry[7..11].to_i
60
+ field_start = base_address + offset
61
+ field_end = field_start + length - 1
62
+ field_data = marc[field_start..field_end]
63
+ end
64
+
65
+ # remove end of field
66
+ field_data.delete!(END_OF_FIELD)
67
+
68
+ # add a control field or data field
69
+ if tag < '010'
70
+ record.append(MARC::ControlField.new(tag,field_data))
71
+ else
72
+ field = MARC::DataField.new(tag)
73
+
74
+ # get all subfields
75
+ subfields = field_data.split(SUBFIELD_INDICATOR)
76
+
77
+ # must have at least 2 elements (indicators, and 1 subfield)
78
+ # TODO some sort of logging?
79
+ next if subfields.length() < 2
80
+
81
+ # get indicators
82
+ indicators = subfields.shift()
83
+ field.indicator1 = indicators[0,1]
84
+ field.indicator2 = indicators[1,1]
85
+
86
+ # add each subfield to the field
87
+ subfields.each() do |data|
88
+ subfield = MARC::Subfield.new(data[0,1],data[1..-1])
89
+ field.append(subfield)
90
+ end
91
+
92
+ # add the field to the record
93
+ record.append(field)
94
+ end
95
+ end
96
+
97
+ return record
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,81 @@
1
+ module MARC
2
+ class Record
3
+
4
+ attr_reader :record_type, :bibliographic_level
5
+
6
+ # Creates a new MARC::Record using MARC::Leader
7
+ # to work with the leader, rather than a string
8
+ def initialize
9
+ @fields = []
10
+ @leader = Leader.new(' ' * 24)
11
+ end
12
+
13
+ def contains_type?(record_type)
14
+ type_map = {"BKS"=>/[at]{1}/, "COM"=>"m", "MAP"=>/[ef]{1}/,"MIX"=>"p", "SCO"=>/[cd]{1}/, "REC"=>/[ij]{1}/, "SER"=>"s", "VIS"=>/[gkor]{1}/}
15
+ matching_fields = []
16
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
17
+ matching_fields << fxd_fld if fxd_fld.value[0,1].match(type_map[record_type])
18
+
19
+ }
20
+ return nil if matching_fields.empty?
21
+ return matching_fields
22
+ end
23
+
24
+ def self.new_from_record(record)
25
+ leader = Leader.new(record.leader)
26
+ case leader.get_type
27
+ when 'BKS' then return MARC::BookRecord.new_from_record(record)
28
+ when 'SER' then return MARC::SerialRecord.new_from_record(record)
29
+ when 'VIS' then return MARC::VisualRecord.new_from_record(record)
30
+ when 'MIX' then return MARC::MixedRecord.new_from_record(record)
31
+ when 'MAP' then return MARC::MapRecord.new_from_record(record)
32
+ when 'SCO' then return MARC::ScoreRecord.new_from_record(record)
33
+ when 'REC' then return MARC::SoundRecord.new_from_record(record)
34
+ when 'COM' then return MARC::ComputerRecord.new_from_record(record)
35
+ end
36
+ end
37
+
38
+ def to_typed_record
39
+ return self.new_from_record(self)
40
+ end
41
+
42
+ def is_archival?
43
+ return @leader.is_archival?
44
+ end
45
+
46
+
47
+ def composition_form(human_readable=false)
48
+ end
49
+
50
+
51
+ def publication_country
52
+ return self['008'].value[15,2] unless self['008'].value[15,2] == ' '
53
+ return false
54
+ end
55
+
56
+ def get_dates
57
+
58
+ end
59
+
60
+ def created_on
61
+ unless self['008'].value[0,6] == (' '*6)
62
+ return Date.parse(self['008'].value[0,2]+'-'+self['008'].value[2,2]+'-'+self['008'].value[4,2], false)
63
+ else
64
+ return Date.today
65
+ end
66
+ end
67
+
68
+ def inspect_fixed_fields
69
+ type_map = {/[at]{1}/=>BookType,'m'=>ComputerType,/[ef]{1}/=>MapType,
70
+ 'p'=>MixedType,/[cd]{1}/=>ScoreType,/[ij]{1}/=>SoundType,'s'=>SerialType,
71
+ /[gkor]{1}/=>VisualType}
72
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
73
+ type_map.each_key { | key |
74
+ if fxd_fld.value[0,1].match(key)
75
+ self.extend type_map[key]
76
+ end
77
+ }
78
+ }
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,309 @@
1
+ module RecordType
2
+
3
+ private
4
+
5
+ def is_govdoc?(human_readable=false)
6
+ govdoc_map={'a'=>'Autonomous or semiautonomous components', 'c'=>'Multilocal', 'f'=>'Federal',
7
+ 'i'=>'International', 'l'=>'Local', 'm'=>'Multistate', 'o'=>'Undetermined', 's'=>'State', 'u'=>'Unknown', 'z'=>'Other'}
8
+ human_readable = govdoc_map if human_readable
9
+ return self.field_parser({:match=>/^BKS$|^COM$|^MAP$|^SER$|^VIS$/, :start=>28,:end=>1}, {:match=>/[atmefsgkor]{1}/, :start=>11,:end=>1}, human_readable)
10
+ end
11
+
12
+ def nature_of_contents(human_readable=false)
13
+ cont_map = {'a'=>'Abstracts','b'=>'Bibliography','c'=>'Catalog','d'=>'Dictionary',
14
+ 'e'=>'Encyclopedia', 'f'=>'Handbook', 'g'=>'Legal article', 'h'=>'Biography', 'i'=>'Index',
15
+ 'j'=>'Patent document', 'k'=>'Discography', 'l'=>'Legislation', 'm'=>'Thesis', 'n'=>'Literature survey',
16
+ 'o'=>'Review', 'p'=>'Programmed text', 'q'=>'Filmography', 'r'=>'Directory', 's'=>'Statistics',
17
+ 't'=>'Technical report', 'u'=>'Standard/specification', 'v'=>'Legal case', 'w'=>'Law report', 'x'=>'Other report',
18
+ 'z'=>'Treaty'}
19
+ contents = []
20
+ idx = nil
21
+ if self.record_type == 'BKS'
22
+ idx = 24
23
+ len = 4
24
+ elsif self.record_type == 'SER'
25
+ idx = 25
26
+ len = 3
27
+ end
28
+ if idx
29
+ self['008'].value[idx,len].split(//).each { | char |
30
+ next if char == " "
31
+ if human_readable
32
+ contents << cont_map[char]
33
+ else
34
+ contents << char
35
+ end
36
+ }
37
+ end
38
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
39
+ idx = nil
40
+ if fxd_fld.value[0,1].match(/[at]{1}/)
41
+ idx = 7
42
+ len = 4
43
+ elsif fxd_fld.value[0,1].match('s')
44
+ idx = 8
45
+ len = 3
46
+ end
47
+ if idx
48
+ fxd_fld.value[idx,len].split(//).each { | char |
49
+ next if char == " "
50
+ if human_readable
51
+ contents << cont_map[char]
52
+ else
53
+ contents << char
54
+ end
55
+ }
56
+ end
57
+ }
58
+ return false if contents.empty?
59
+ return contents
60
+ end
61
+
62
+
63
+ def is_conference?
64
+ return true if self['008'].value[29,1] == '1' && @record_type.match(/^BKS$|^SER$/)
65
+ return true if self['008'].value[30,2].match(/c/) && @record_type.match(/^SCO$|^REC$/)
66
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
67
+ return true if fxd_fld.value[12,1] == '1' && fxd_fld.value[0,1].match(/[ats]{1}/)
68
+
69
+ return true if fxd_fld.value[13,2].match(/c/) && fxd_fld.value[0,1].match(/[cdij]{1}/)
70
+ }
71
+ return false
72
+ end
73
+
74
+ def set_conference(value=false, field=nil)
75
+ if field
76
+ return Exception.new("Field is not an 006") unless field.tag == '006'
77
+ return Exception.new("Field is not a BKS or SER") unless field.value[0,1].match(/[ats]{1}/)
78
+ if value
79
+ field.value[12] = '1'
80
+ else
81
+ field.value[12] = '0'
82
+ end
83
+ else
84
+ field = @fields['008']
85
+ field = MARC::Controlfield.new('008') unless field
86
+ if value
87
+ field[29] = '1'
88
+ else
89
+ field[29] = '0'
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ def accompanying_matter(human_readable=false)
96
+ accm_map = {'a'=>'Discography','b'=>'Bibliography','c'=>'Thematic index','d'=>'Libretto',
97
+ 'e'=>'Composer biography', 'f'=>'Performer biography', 'g'=>'Technical/historical information on instruments',
98
+ 'h'=>'Technical information on music', 'i'=>'Historical information',
99
+ 'k'=>'Ethnological information', 'r'=>'Instructional materials', 's'=>'Music',
100
+ 'z'=>'Other accompanying matter'}
101
+ matter = []
102
+
103
+ if ['SCO', 'REC'].index(@record_type)
104
+ self['008'].value[24,6].split(//).each { | char |
105
+ next if char == " "
106
+ if human_readable
107
+ matter << accm_map[char]
108
+ else
109
+ matter << char
110
+ end
111
+ }
112
+ end
113
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
114
+
115
+ if fxd_fld.value[0,1].match(/[cdij]{1}/)
116
+ fxd_fld.value[7,6].split(//).each { | char |
117
+ next if char == " "
118
+ if human_readable
119
+ matter << accm_map[char]
120
+ else
121
+ matter << char
122
+ end
123
+ }
124
+ end
125
+ }
126
+ return false if matter.empty?
127
+ return matter
128
+ end
129
+
130
+ def audience_level(human_readable=false)
131
+ audn_map = {'a'=>'Preschool', 'b'=>'Children age 6-8', 'c'=>'Children age 9-13',
132
+ 'd'=>'Adolescent', 'e'=>'Adult', 'f'=>'Specialized', 'g'=>'General', 'j'=>'Juvenile'
133
+ }
134
+ human_readable = audn_map if human_readable
135
+ return self.field_parser({:match=>/^BKS$|^VIS$|^MIX$|^MAP$|^SCO$|^REC$|^COM$/, :start=>22,:end=>1}, {:match=>/[atmefpcdijgkor]{1}/, :start=>5,:end=>1}, human_readable)
136
+ end
137
+
138
+ def form(human_readable=false)
139
+ form_map = {'a'=>'Microfilm', 'b'=>'Microfiche', 'c'=>'Microopaque',
140
+ 'd'=>'Large print', 'f'=>'Braille', 'r'=>'Reproduction', 's'=>'Electronic'
141
+ }
142
+ idx = nil
143
+ if self.record_type.match(/^MAP$|^VIS$/)
144
+ idx = 29
145
+ else
146
+ idx = 23
147
+ end
148
+ unless self['008'].value[idx,1] == ' '
149
+ if human_readable
150
+ return form_map[self['008'].value[idx,1]]
151
+ else
152
+ return self['008'].value[idx,1]
153
+ end
154
+ end
155
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
156
+ idx = nil
157
+ if fxd_fld.value[0,1].match(/[efgkor]{1}/)
158
+ idx = 6
159
+ else
160
+ idx = 12
161
+ end
162
+ next if fxd_fld.value[idx,1] == ' '
163
+ if human_readable
164
+ return form_map[fxd_fld.value[idx,1]]
165
+ else
166
+ return fxd_fld.value[idx,1]
167
+ end
168
+ }
169
+ return false
170
+ end
171
+
172
+ def has_index?
173
+ return true if self['008'].value[31,1] == '1'
174
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
175
+ return true if fxd_fld.value[14,1] == '1'
176
+ }
177
+ return false
178
+ end
179
+
180
+ def composition_form(human_readable=false)
181
+ comp_map = {'an'=>'Anthem','bd'=>'Ballad','bt'=>'Ballet','bg'=>'Bluegrass music',
182
+ 'bl'=>'Blues','cn'=>'Canon or round','ct'=>'Catata','cz'=>'Canzona','cr'=>'Carol',
183
+ 'ca'=>'Chaconne','cs'=>'Chance composition','cp'=>'Polyphonic chanson','cc'=>'Christian chant',
184
+ 'cb'=>'Chant','cl'=>'Chorale prelude','ch'=>'Chorale','cg'=>'Concerti grossi','co'=>'Concerto',
185
+ 'cy'=>'Country music','df'=>'Dance form','dv'=>'Divertimento/serenade/cassation/divertissement/notturni',
186
+ 'ft'=>'Fantasia','fm'=>'Folk music','fg'=>'Fugue','gm'=>'Gospel music','hy'=>"Hymn",'jz'=>'Jazz',
187
+ 'md'=>'Madrigal','mr'=>'March','ms'=>'Mass','mz'=>'Mazurka','mi'=>'Minuet','mo'=>'Motet',
188
+ 'mp'=>'Motion picture music','mu'=>'Multiple forms','mc'=>'Musical revue/comedy',
189
+ 'nc'=>'Nocturne','nn'=>'Not a musical recording','op'=>'Opera','or'=>'Oratorio',
190
+ 'ov'=>'Overture','pt'=>'Part-song','ps'=>'Passacaglia','pm'=>'Passion music',
191
+ 'pv'=>'Pavanes','po'=>'Polonaises','pp'=>'Popular music','pr'=>'Prelude','pg'=>'Program music',
192
+ 'rg'=>'Ragtime music','rq'=>'Requiem','rp'=>'Rhapsody','ri'=>'Ricercars','rc'=>'Rock music',
193
+ 'rd'=>'Rondo','sn'=>'Sonata','sg'=>'Song','sd'=>'Square dance music','st'=>'Study/exercise',
194
+ 'su'=>'Suite','sp'=>'Symphonic poem','sy'=>'Symphony','tc'=>'Toccata','ts'=>'Trio-sonata',
195
+ 'uu'=>'Unknown','vr'=>'Variation','wz'=>'Waltz','zz'=>'Other'
196
+ }
197
+ if @record_type.match(/^SCO$|^REC$/)
198
+ unless self['008'].value[18,2] == ' '
199
+ if human_readable
200
+ return comp_map[self['008'].value[18,2]]
201
+ else
202
+ return self['008'].value[18,2]
203
+ end
204
+ end
205
+ end
206
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
207
+ next unless fxd_fld.value[0,1].match(/[cdij]{1}/)
208
+ unless fxd_fld.value[1,2] == ' '
209
+ if human_readable
210
+ return comp_map[fxd_fld.value[1,2]]
211
+ else
212
+ return fxd_fld.value[1,2]
213
+ end
214
+ end
215
+ }
216
+ return false
217
+ end
218
+
219
+ def music_format(human_readable=false)
220
+ fmus_map = {'a'=>'Full score','b'=>'Full score, miniature/study size','c'=>'Accompaniment reduced for keyboard',
221
+ 'd'=>'Voice score','e'=>'Condensed score','g'=>'Close score','m'=>'Multiple formats','n'=>'N/A',
222
+ 'u'=>'Unknown','z'=>'Other'}
223
+ human_readable = fmus_map if human_readable
224
+ return self.field_parser({:match=>/^SCO$|^REC$/, :start=>20,:end=>1}, {:match=>/[cdij]{1}/, :start=>3,:end=>1}, human_readable)
225
+ # if self.record_type.match(/^SCO$|^REC$/)
226
+ # unless self['008'].value[20,1] == ' '
227
+ # if human_readable
228
+ # return fmus_map[self['008'].value[20,1]]
229
+ # else
230
+ # return self['008'].value[20,1]
231
+ # end
232
+ # end
233
+ # end
234
+ ## @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
235
+ # next unless fxd_fld.value[0,1].match(/[cdij]{1}/)
236
+ # next if fxd_fld.value[3,1] == ' '
237
+ # if human_readable
238
+ # return fmus_map[fxd_fld.value[3,1]]
239
+ # else
240
+ # return fxd_fld.value[3,1]
241
+ # end
242
+ # }
243
+ # return false
244
+ end
245
+
246
+ def has_index?
247
+ return true if self['008'].value[31,1] == '1'
248
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
249
+ return true if fxd_fld.value[14,1] == '1'
250
+ }
251
+ return false
252
+ end
253
+
254
+ def literary_text(human_readable=true)
255
+ ltxt_map = {'a'=>'Autobiography', 'b'=>'Biography','c'=>'Conference proceeding','d'=>'Drama',
256
+ 'e'=>'Essay','f'=>'Fiction','g'=>'Reporting','h'=>'History','i'=>'Instruction','j'=>'Language instruction',
257
+ 'k'=>'Comedy','l'=>'Lecture/speech','m'=>'Memoir','n'=>'N/A','o'=>'Folktale','p'=>'Poetry','r'=>'Rehearsal',
258
+ 's'=>'Sounds','t'=>'Interview','z'=>'Other'}
259
+ txts = []
260
+
261
+ if ['SCO', 'REC'].index(@record_type)
262
+ self['008'].value[30,2].split(//).each { | char |
263
+ next if char == " "
264
+ if human_readable
265
+ txts << ltxt_map[char]
266
+ else
267
+ txts << char
268
+ end
269
+ }
270
+ end
271
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
272
+
273
+ if fxd_fld.value[0,1].match(/[cdij]{1}/)
274
+ fxd_fld.value[13,2].split(//).each { | char |
275
+ next if char == " "
276
+ if human_readable
277
+ txts << ltxt_map[char]
278
+ else
279
+ txts << char
280
+ end
281
+ }
282
+ end
283
+ }
284
+ return false if txts.empty?
285
+ return txts
286
+ end
287
+ protected
288
+ def field_parser(eight, six, human_readable_map=nil)
289
+ if self.record_type.match(eight[:match])
290
+ unless self['008'].value[eight[:start],eight[:end]] == ' '*eight[:end]
291
+ if human_readable_map
292
+ return human_readable_map[self['008'].value[eight[:start],eight[:end]]]
293
+ else
294
+ return self['008'].value[eight[:start],eight[:end]]
295
+ end
296
+ end
297
+ end
298
+ @fields.find_all {|f| ('006') === f.tag}.each { | fxd_fld |
299
+ next unless fxd_fld.value[0,1].match(six[:match])
300
+ next if fxd_fld.value[six[:start],six[:end]] == ' '*six[:end]
301
+ if human_readable_map
302
+ return human_readable_map[fxd_fld.value[six[:start],six[:end]]]
303
+ else
304
+ return fxd_fld.value[six[:start],six[:end]]
305
+ end
306
+ }
307
+ return false
308
+ end
309
+ end