File Coverage

blib/lib/Locale/TextDomain/OO/Lexicon/Role/File.pm
Criterion Covered Total %
statement 128 131 97.7
branch 34 40 85.0
condition n/a
subroutine 17 17 100.0
pod 1 1 100.0
total 180 189 95.2


line stmt bran cond sub pod time code
1             package Locale::TextDomain::OO::Lexicon::Role::File; ## no critic (TidyCode)
2            
3 22     22   22341 use strict;
  22         65  
  22         729  
4 22     22   145 use warnings;
  22         48  
  22         750  
5 22     22   152 use Carp qw(confess);
  22         49  
  22         1263  
6 22     22   1261 use Encode qw(decode FB_CROAK);
  22         20017  
  22         1134  
7 22     22   572 use English qw(-no_match_vars $OS_ERROR);
  22         3579  
  22         170  
8 22     22   3334 use Locale::TextDomain::OO::Singleton::Lexicon;
  22         54  
  22         693  
9 22     22   10573 use Locale::TextDomain::OO::Util::ExtractHeader;
  22         31173  
  22         770  
10 22     22   163 use Locale::TextDomain::OO::Util::JoinSplitLexiconKeys;
  22         49  
  22         627  
11 22     22   122 use Moo::Role;
  22         48  
  22         178  
12 22     22   8666 use MooX::Types::MooseLike::Base qw(CodeRef);
  22         57  
  22         1095  
13 22     22   17654 use Path::Tiny qw(path);
  22         246981  
  22         1456  
14 22     22   210 use namespace::autoclean;
  22         54  
  22         176  
15            
16             our $VERSION = '1.034';
17            
18             with qw(
19             Locale::TextDomain::OO::Lexicon::Role::GettextToMaketext
20             Locale::TextDomain::OO::Role::Logger
21             );
22            
23             requires qw(
24             read_messages
25             );
26            
27             has decode_code => (
28             is => 'ro',
29             isa => CodeRef,
30             lazy => 1,
31             default => sub {
32             sub {
33             my ($charset, $text) = @_;
34             defined $text
35             or return $text;
36            
37             return decode( $charset, $text, FB_CROAK );
38             };
39             },
40             );
41            
42             sub _decode_messages {
43 39     39   111 my ($self, $messages_ref) = @_;
44            
45 39         143 my $charset = lc $messages_ref->[0]->{charset};
46 39         84 for my $value ( @{$messages_ref} ) {
  39         109  
47 296         3882 for my $key ( qw( msgid msgid_plural msgstr ) ) {
48 888 100       20730 if ( exists $value->{$key} ) {
49 553         1054 for my $text ( $value->{$key} ) {
50 553         10542 $text = $self->decode_code->($charset, $text);
51             }
52             }
53             }
54 296 100       7189 if ( exists $value->{msgstr_plural} ) {
55 92         156 my $got = @{ $value->{msgstr_plural} };
  92         204  
56 92         199 my $expected = $messages_ref->[0]->{nplurals};
57             $got <= $expected or confess sprintf
58             'Count of msgstr_plural=%s but nplurals=%s for msgid="%s" msgid_plural="%s"',
59             $got,
60             $expected,
61             ( exists $value->{msgid} ? $value->{msgid} : q{} ),
62 92 50       263 ( exists $value->{msgid_plural} ? $value->{msgid_plural} : q{} );
    50          
    100          
63 91         164 for my $text ( @{ $value->{msgstr_plural} } ) {
  91         241  
64 219         9463 $text = $self->decode_code->($charset, $text);
65             }
66             }
67             }
68            
69 38         219 return;
70             }
71            
72             sub _my_glob {
73 25     25   81 my ($self, $file) = @_;
74            
75 25         127 my $dirname = $file->parent;
76 25         2733 my $filename = $file->basename;
77            
78             # only one * allowed at all
79 25         354 my $dir_star_count = () = $dirname =~ m{ [*] }xmsg;
80 25         210 my $file_star_count = () = $filename =~ m{ [*] }xmsg;
81 25         63 my $count = $dir_star_count + $file_star_count;
82 25 100       177 $count
83             or return $file;
84 24 50       106 $count > 1
85             and confess 'Only one * in dirname/filename is allowd to reference the language';
86            
87             # one * in filename
88 24 100       80 if ( $file_star_count ) {
89 2         12 ( my $file_regex = quotemeta $filename ) =~ s{\\[*]}{.*?}xms;
90             return +(
91 2         53 sort $dirname->children( qr{\A $file_regex \z}xms )
92             );
93             }
94            
95             # one * in dir
96             # split that dir into left, inner with * and right
97 22         234 my ( $left_dir, $inner_dir, $right_dir )
98             = split qr{( [^/*]* [*] [^/]* )}xms, $dirname;
99 22         402 ( my $inner_dir_regex = quotemeta $inner_dir ) =~ s{\\[*]}{.*?}xms;
100             my @left_and_inner_dirs
101 22         97 = path($left_dir)->children( qr{$inner_dir_regex}xms );
102            
103             return +(
104             sort
105             grep {
106 106         2886 $_->is_file;
107             }
108             map {
109 22         8145 path("$_$right_dir")->child($filename);
  106         5690  
110             }
111             @left_and_inner_dirs
112             );
113             }
114            
115             sub _run_extra_commands {
116 28     28   107 my ($self, $identifier, $instance, $next_data_code) = @_;
117            
118 28 100       106 if ( $identifier eq 'merge_lexicon' ) {
119 1         4 my ( $from1, $from2, $to ) = (
120             $next_data_code->(),
121             $next_data_code->(),
122             $next_data_code->(),
123             );
124 1         25 $instance->merge_lexicon( $from1, $from2, $to );
125 1         9 return 1;
126             }
127 27 50       124 if ( $identifier eq 'copy_lexicon' ) {
128 0         0 my ( $from, $to ) = ( $next_data_code->(), $next_data_code->() );
129 0         0 $instance->copy_lexicon( $from, $to );
130 0         0 return 1;
131             }
132 27 100       94 if ( $identifier eq 'move_lexicon' ) {
133 1         4 my ( $from, $to ) = ( $next_data_code->(), $next_data_code->() );
134 1         7 $instance->move_lexicon( $from, $to );
135 1         10 return 1;
136             }
137 26 100       111 if ( $identifier eq 'delete_lexicon' ) {
138 1         4 my $name = $next_data_code->();
139 1         6 $instance->delete_lexicon($name);
140 1         8 return 1;
141             }
142            
143 25         90 return;
144             }
145            
146             sub lexicon_ref {
147 21     21 1 52488 my ($self, $file_lexicon_ref) = @_;
148            
149 21         229 my $instance = Locale::TextDomain::OO::Singleton::Lexicon->instance;
150 21 100       508 $self->logger and $instance->logger( $self->logger );
151             my $search_dirs = $file_lexicon_ref->{search_dirs}
152 21 50       960 or confess 'Hash key "search_dirs" expected';
153 21         208 my $header_util = Locale::TextDomain::OO::Util::ExtractHeader->instance;
154 21         232 my $key_util = Locale::TextDomain::OO::Util::JoinSplitLexiconKeys->instance;
155 21         107 my $data = $file_lexicon_ref->{data};
156 21         52 my $index = 0;
157             DATA:
158 21         49 while ( $index < @{ $file_lexicon_ref->{data} } ) {
  48         11848  
159 28         85 my $identifier = $data->[ $index++ ];
160             $self->_run_extra_commands(
161             $identifier,
162             $instance,
163 6     6   17 sub { return $data->[ $index++ ] },
164 28 100       212 ) and next DATA;
165 25         517 my ( $lexicon_key, $lexicon_value )
166             = ( $identifier, $data->[ $index++ ] );
167 25         74 for my $dir ( @{ $search_dirs } ) {
  25         88  
168 25         125 my $file = path( $dir, $lexicon_value );
169 25         1421 my @files = $self->_my_glob($file);
170 25         1142 for ( @files ) {
171 40         7984 my $filename = $_->canonpath;
172 40         239 my $lexicon_language_key = $lexicon_key;
173 40         107 my $language = $filename;
174 40         165 my @parts = split m{[*]}xms, $file;
175 40 100       353 if ( @parts == 2 ) {
176 39         156 substr $language, 0, length $parts[0], q{};
177 39         182 substr $language, - length $parts[1], length $parts[1], q{};
178 39         224 $lexicon_language_key =~ s{[*]}{$language}xms;
179             }
180 40         230 my $messages_ref = $self->read_messages($filename);
181             my $header_msgstr = $messages_ref->[0]->{msgstr}
182 40 50       101726 or confess 'msgstr of header not found';
183 40         127 my $header_ref = $messages_ref->[0];
184 40         9862 %{$header_ref} = (
185             msgid => $header_ref->{msgid},
186 40         97 %{ $header_util->extract_header_msgstr( $header_ref->{msgstr} ) },
  40         368  
187             );
188             $file_lexicon_ref->{gettext_to_maketext}
189 40 100       308 and $self->gettext_to_maketext($messages_ref);
190             $file_lexicon_ref->{decode}
191 40 100       257 and $self->_decode_messages($messages_ref);
192             $instance->data->{$lexicon_language_key} = {
193             map { ## no critic (ComplexMappings)
194 303         507 my $message_ref = $_;
195             my $msg_key = $key_util->join_message_key({(
196             map {
197 303         549 $_ => delete $message_ref->{$_};
  909         3202  
198             }
199             qw( msgctxt msgid msgid_plural )
200             )});
201 303         11319 ( $msg_key => $message_ref );
202 39         84 } @{$messages_ref}
  39         118  
203             };
204 39 100       973 $self->logger and $self->logger->(
205             qq{Lexicon "$lexicon_language_key" loaded from file "$filename".},
206             {
207             object => $self,
208             type => 'debug',
209             event => 'lexicon,load',
210             },
211             );
212             }
213             }
214             }
215            
216 20         99 return $self;
217             }
218            
219             1;
220            
221             __END__