File Coverage

blib/lib/Catmandu/Fix/marc_map.pm
Criterion Covered Total %
statement 31 31 100.0
branch 2 2 100.0
condition 8 8 100.0
subroutine 6 6 100.0
pod 0 1 0.0
total 47 48 97.9


line stmt bran cond sub pod time code
1             package Catmandu::Fix::marc_map;
2              
3 14     14   310285 use Catmandu::Sane;
  14         46  
  14         129  
4 14     14   6523 use Catmandu::MARC;
  14         43  
  14         475  
5 14     14   112 use Moo;
  14         43  
  14         97  
6 14     14   7628 use Catmandu::Fix::Has;
  14         7137  
  14         100  
7              
8             with 'Catmandu::Fix::Base';
9              
10             our $VERSION = '1.19';
11              
12             has marc_path => (fix_arg => 1);
13             has path => (fix_arg => 1);
14             has split => (fix_opt => 1);
15             has join => (fix_opt => 1);
16             has value => (fix_opt => 1);
17             has pluck => (fix_opt => 1);
18             has nested_arrays => (fix_opt => 1);
19              
20             sub emit {
21 56     56 0 126081 my ($self,$fixer) = @_;
22 56         297 my $path = $fixer->split_path($self->path);
23 56         1578 my $key = $path->[-1];
24 56         336 my $marc_obj = Catmandu::MARC->instance;
25              
26             # Precompile the marc_path to gain some speed
27 56         2501 my $marc_context = $marc_obj->compile_marc_path($self->marc_path,subfield_wildcard => 1);
28 56         985 my $marc = $fixer->capture($marc_obj);
29 56         4763 my $marc_path = $fixer->capture($marc_context);
30 56 100 100     5318 my $marc_opt = $fixer->capture({
      100        
      100        
      100        
31             '-join' => $self->join // '' ,
32             '-split' => $self->split // 0 ,
33             '-pluck' => $self->pluck // 0 ,
34             '-nested_arrays' => $self->nested_arrays // 0 ,
35             '-value' => $self->value ,
36             '-force_array' => ($key =~ /^(\$.*|[0-9]+)$/) ? 1 : 0
37             });
38              
39 56         5176 my $var = $fixer->var;
40 56         568 my $result = $fixer->generate_var;
41 56         2787 my $current_value = $fixer->generate_var;
42              
43 56         2625 my $perl = "";
44 56         259 $perl .= $fixer->emit_declare_vars($current_value, "[]");
45 56         852 $perl .=<<EOF;
46             if (defined(my ${result} = ${marc}->marc_map(
47             ${var},
48             ${marc_path},
49             ${marc_opt})) ) {
50             ${result} = ref(${result}) ? ${result} : [${result}];
51             for ${current_value} (\@{${result}}) {
52             EOF
53              
54             $perl .= $fixer->emit_create_path(
55             $var,
56             $path,
57             sub {
58 56     56   7223 my $var2 = shift;
59 56         236 "${var2} = ${current_value}"
60             }
61 56         473 );
62              
63 56         594 $perl .=<<EOF;
64             }
65             }
66             EOF
67 56         261 $perl;
68             }
69              
70             1;
71              
72             =head1 NAME
73              
74             Catmandu::Fix::marc_map - copy marc values of one field to a new field
75              
76             =head1 SYNOPSIS
77              
78             # Append all 245 subfields to my.title field the values are joined into one string
79             marc_map('245','my.title')
80              
81             # Append al 245 subfields to the my.title keeping all subfields as an array
82             marc_map('245','my.title', split:1)
83              
84             # Copy the 245-$a$b$c subfields into the my.title hash in the order provided in the record
85             marc_map('245abc','my.title')
86              
87             # Copy the 245-$c$b$a subfields into the my.title hash in the order c,b,a
88             marc_map('245cba','my.title', pluck:1)
89              
90             # Add the 100 subfields into the my.authors array
91             marc_map('100','my.authors.$append')
92              
93             # Add the 710 subfields into the my.authors array
94             marc_map('710','my.authors.$append')
95              
96             # Add the 600-$x subfields into the my.subjects array while packing each into a genre.text hash
97             marc_map('600x','my.subjects.$append.genre.text')
98              
99             # Copy the 008 characters 35-37 into the my.language hash
100             marc_map('008/35-37','my.language')
101              
102             # Copy all the 600 fields into a my.stringy hash joining them by '; '
103             marc_map('600','my.stringy', join:'; ')
104              
105             # When 024 field exists create the my.has024 hash with value 'found'
106             marc_map('024','my.has024', value:found)
107              
108             # When 260c field exists create the my.has260c hash with value 'found'
109             marc_map('260c','my.has260c', value:found)
110              
111             # Copy all 100 subfields except the digits to the 'author' field
112             marc_map('100^0123456789','author')
113              
114             # Map all the 500 - 599 fields to my.notes
115             marc_map('5..','my.motes')
116              
117             # Map the 100-a field where indicator-1 is 3
118             marc_map('100[3]a','name.family')
119              
120             # Map the 245-a field where indicator-2 is 0
121             marc_map('245[,0]a','title')
122              
123             # Map the 245-a field where indicator-1 is 1 and indicator-2 is 0
124             marc_map('245[1,0]a','title')
125              
126             =head1 DESCRIPTION
127              
128             Copy data from a MARC field to JSON path.
129              
130             This module implements a small subset of the L<MARCspec|http://marcspec.github.io/MARCspec/>
131             specification to map MARC fields. For a more extensive MARC path implementation
132             please take a look at Casten Klee's MARCSpec module: L<Catmandu::Fix::marc_spec>
133              
134             =head1 METHODS
135              
136             =head2 marc_map(MARC_PATH, JSON_PATH, OPT:VAL, OPT2:VAL,...)
137              
138             Copy the value(s) of the data found at a MARC_PATH to a JSON_PATH.
139              
140             The MARC_PATH can point to a MARC field. For instance:
141              
142             marc_path('245',title)
143             marc_path('020',isbn)
144              
145             The MARC_PATH can point to one or more MARC subfields. For instamce:
146              
147             marc_path('245a',title)
148             marc_path('245ac',title)
149              
150             You can also use dollar signs to indicate subfields
151              
152             marc_path('245$a$c',title)
153              
154             Wildcards are allowed in the field names:
155              
156             # Map all the 200-fields to a title
157             marc_map('2..'',title)
158              
159             To filter out specific fields indicators can be used:
160              
161             # Only map the MARC fields with indicator-1 is '1' to title
162             marc_map('245[1,]',title)
163              
164             Also a substring of a field value can be mapped:
165              
166             # Map 008 position 35 to 37 to the language field
167             marc_map('008/35-37',language)
168              
169             By default all matched fields in a MARC_PATH will be joined into one string.
170             This behavior can be changed using one more more options (see below).
171              
172             Visit our Wiki L<https://github.com/LibreCat/Catmandu-MARC/wiki/Mapping-rules>
173             for a complete overview of all allowed mappings.
174              
175             =head1 OPTIONS
176              
177             =head2 split: 0|1
178              
179             When split is set to 1 then all mapped values will be joined into an array
180             instead of a string.
181              
182             # The subject field will contain an array of strings (one string
183             # for each 500 field found)
184             marc_map('500',subject, split: 1)
185              
186             # The subject field will contain a string
187             marc_map('500', subject)
188              
189             =head2 join: Str
190              
191             By default all the values are joined into a string without a field separator.
192             Use the join function to set the separator.
193              
194             # All subfields of the 245 field will be separated with a space " "
195             marc_map('245',title, join: " ")
196              
197             =head2 pluck: 0|1
198              
199             Be default, all subfields are added to the mapping in the order they are found
200             in the record. Using the pluck option, one can select the required order of
201             subfields to map.
202              
203             # First write the subfield-c to the title, then the subfield_a
204             marc_map('245ca',title, pluck:1)
205              
206             =head2 value: Str
207              
208             Don't write the value of the MARC (sub)field to the JSON_PATH but the specified
209             string value.
210              
211             # has_024_a will contain the value 'Y' if the MARC field 024 subfield-a
212             # exists
213             marc_map('024a',has_024_a,value:Y)
214              
215             =head2 nested_arrays: 0|1
216              
217             When the split option is specified the output of the mapping will always be an
218             array of strings (one string for each subfield found). Using the nested_array
219             option the output will be an array of array of strings (one array item for
220             each matched field, one array of strings for each matched subfield).
221              
222             =head1 INLINE
223              
224             This Fix can be used inline in a Perl script:
225              
226             use Catmandu::Fix::marc_map as => 'marc_map';
227              
228             my $data = { record => [...] };
229              
230             $data = marc_map($data,'245a','title');
231              
232             print $data->{title} , "\n";
233              
234             =head1 SEE ALSO
235              
236             L<Catmandu::Fix>
237             L<Catmandu::Fix::marc_spec>
238              
239             =cut