File Coverage

blib/lib/Alice/Config.pm
Criterion Covered Total %
statement 90 107 84.1
branch 15 32 46.8
condition 6 17 35.2
subroutine 23 25 92.0
pod 1 12 8.3
total 135 193 69.9


line stmt bran cond sub pod time code
1             package Alice::Config;
2              
3 2     2   1613 use FindBin;
  2         2078  
  2         75  
4 2     2   1919 use Data::Dumper;
  2         13519  
  2         206  
5 2     2   2007 use File::ShareDir qw/dist_dir/;
  2         18177  
  2         205  
6 2     2   23 use List::MoreUtils qw/any/;
  2         5  
  2         84  
7 2     2   2718 use Getopt::Long;
  2         24798  
  2         14  
8 2     2   353 use Any::Moose;
  2         4  
  2         29  
9 2     2   1192 use POSIX;
  2         6  
  2         22  
10              
11 2     2   4805 use AnyEvent::AIO;
  2         4  
  2         111  
12 2     2   12 use IO::AIO;
  2         6  
  2         3528  
13              
14             has assetdir => (
15             is => 'ro',
16             isa => 'Str',
17             default => sub {
18             if (-e "$FindBin::Bin/../share/templates") {
19             return "$FindBin::Bin/../share";
20             }
21             return dist_dir('App-Alice');
22             }
23             );
24              
25             has [qw/images avatars alerts audio animate/] => (
26             is => 'rw',
27             isa => 'Str',
28             default => "show",
29             );
30              
31             has first_run => (
32             is => 'rw',
33             isa => 'Bool',
34             default => 1,
35             );
36              
37             has style => (
38             is => 'rw',
39             isa => 'Str',
40             default => 'default',
41             );
42              
43             has timeformat => (
44             is => 'rw',
45             isa => 'Str',
46             default => '24',
47             );
48              
49             has quitmsg => (
50             is => 'rw',
51             isa => 'Str',
52             default => 'alice.',
53             );
54              
55             has port => (
56             is => 'rw',
57             isa => 'Str',
58             default => "8080",
59             );
60              
61             has address => (
62             is => 'rw',
63             isa => 'Str',
64             default => '127.0.0.1',
65             );
66              
67             has auth => (
68             is => 'rw',
69             isa => 'HashRef[Str]',
70             default => sub {{}},
71             );
72              
73             has tabsets => (
74             is => 'rw',
75             isa => 'HashRef[ArrayRef]',
76             default => sub {{}},
77             );
78              
79             has [qw/highlights order monospace_nicks/]=> (
80             is => 'rw',
81             isa => 'ArrayRef[Str]',
82             default => sub {[]},
83             );
84              
85             has ignore => (
86             is => 'rw',
87             isa => 'HashRef[ArrayRef]',
88             default => sub {
89             +{ msg => [], 'join' => [], part => [], nick => [] }
90             }
91             );
92              
93              
94             has servers => (
95             is => 'rw',
96             isa => 'HashRef[HashRef]',
97             default => sub {{}},
98             );
99              
100             has path => (
101             is => 'ro',
102             isa => 'Str',
103             default => sub {$ENV{ALICE_DIR} || "$ENV{HOME}/.alice"},
104             );
105              
106             has file => (
107             is => 'ro',
108             isa => 'Str',
109             default => "config",
110             );
111              
112             has fullpath => (
113             is => 'ro',
114             isa => 'Str',
115             lazy => 1,
116             default => sub {$_[0]->path ."/". $_[0]->file},
117             );
118              
119             has commandline => (
120             is => 'ro',
121             isa => 'HashRef',
122             default => sub {{}},
123             );
124              
125             has static_prefix => (
126             is => 'rw',
127             isa => 'Str',
128             default => '/static/',
129             );
130              
131             has image_prefix => (
132             is => 'rw',
133             isa => 'Str',
134             default => 'https://noembed.com/i/',
135             );
136              
137             has message_store => (
138             is => 'rw',
139             isa => 'Str',
140             default => 'Memory',
141             );
142              
143             has callback => (
144             is => 'ro',
145             isa => 'CodeRef',
146             );
147              
148             sub BUILD {
149 1     1 1 4 my $self = shift;
150 1         5 $self->load;
151 1 50       249 mkdir $self->path unless -d $self->path;
152             }
153              
154             sub load {
155 1     1 0 2 my $self = shift;
156 1         3 my $config = {};
157              
158             my $loaded = sub {
159 1     1   5 $self->read_commandline_args;
160 1         12 $self->merge($config);
161 1         10 $self->callback->();
162              
163 1         20 my $class = "Alice::MessageStore::".$self->message_store;
164 1         90 eval "require $class";
165              
166 1         59 delete $self->{callback};
167 1         129 $self->{loaded} = 1;
168 1         6 };
169              
170 1 50       7 if (-e $self->fullpath) {
171 1         3 my $body;
172             aio_load $self->fullpath, $body, sub {
173 1     1   591 $config = eval $body;
174              
175             # upgrade ignore to new format
176 1 50 33     9 if ($config->{ignore} and ref $config->{ignore} eq "ARRAY") {
177 0         0 $config->{ignore} = {msg => $config->{ignore}};
178             }
179              
180 1 50       6 if ($@) {
181 0         0 warn "error loading config: $@\n";
182             }
183 1         5 $loaded->();
184             }
185 1         13 }
186             else {
187 0         0 say STDERR "No config found, writing a few config to ".$self->fullpath;
188 0         0 $self->write($loaded);
189             }
190             }
191              
192             sub read_commandline_args {
193 1     1 0 3 my $self = shift;
194 1         2 my ($port, $debug, $address, $log);
195 1         30 GetOptions("port=i" => \$port, "debug=s" => \$debug, "log=s" => \$log, "address=s" => \$address);
196 1 50 33     567 $self->commandline->{port} = $port if $port and $port =~ /\d+/;
197 1 50       5 $self->commandline->{address} = $address if $address;
198              
199 1   50     40 $AnyEvent::Log::FILTER->level($debug || "info");
200              
201 1 50       114 if ($log) {
202 0   0     0 $AnyEvent::Log::COLLECT->attach(AnyEvent::Log::Ctx->new(
203             level => ($debug || "info"),
204             log_to_file => $log
205             ));
206             }
207             }
208              
209             sub http_port {
210 0     0 0 0 my $self = shift;
211 0 0       0 if ($self->commandline->{port}) {
212 0         0 return $self->commandline->{port};
213             }
214 0         0 return $self->port;
215             }
216              
217             sub http_address {
218 0     0 0 0 my $self = shift;
219 0 0       0 if ($self->commandline->{address}) {
220 0         0 return $self->commandline->{address};
221             }
222 0 0       0 if ($self->address eq "localhost") {
223 0         0 $self->address("127.0.0.1");
224             }
225 0         0 return $self->address;
226             }
227              
228             sub merge {
229 2     2 0 4 my ($self, $config) = @_;
230 2         28 for my $key (keys %$config) {
231 3 50 33     40 if (exists $config->{$key} and my $attr = $self->meta->get_attribute($key)) {
232 3 100       73 $self->$key($config->{$key}) if $attr->has_write_method;
233             }
234             else {
235 0         0 say STDERR "$key is not a valid config option";
236             }
237             }
238             }
239              
240             sub write {
241 2     2 0 11 my $self = shift;
242 2         4 my $callback = pop;
243 2 50       81 mkdir $self->path if !-d $self->path;
244             aio_open $self->fullpath, POSIX::O_CREAT | POSIX::O_WRONLY | POSIX::O_TRUNC, 0644, sub {
245 2     2   7499 my $fh = shift;
246 2 50       9 if ($fh) {
247 2         17 local $Data::Dumper::Terse = 1;
248 2         11 local $Data::Dumper::Indent = 1;
249 2         9 my $config = Dumper $self->serialized;
250             aio_write $fh, 0, length $config, $config, 0, sub {
251 2 50       103 $callback->() if $callback;
252 2         1386 };
253             }
254             else {
255 0         0 warn "Can not write config file: $!\n";
256             }
257             }
258 2         107 }
259              
260             sub serialized {
261 2     2 0 4 my $self = shift;
262             return {
263 42         208 map {
264 54         516 my $name = $_->name;
265 42         379 $name => $self->$name;
266 2         52 } grep {$_->has_write_method}
267             $self->meta->get_all_attributes
268             };
269             }
270              
271             sub ignores {
272 3     3 0 5 my ($self, $type) = @_;
273 3   50     9 $type ||= "msg";
274 3 100       4 @{$self->ignore->{$type} || []}
  3         68  
275             }
276              
277             sub is_ignore {
278 2     2 0 5 my ($self, $type, $nick) = @_;
279 2   50     7 $type ||= "msg";
280 2     1   64 any {$_ eq $nick} $self->ignores($type);
  1         7  
281             }
282              
283             sub add_ignore {
284 1     1 0 15 my ($self, $type, $nick) = @_;
285 1         4 push @{$self->ignore->{$type}}, $nick;
  1         8  
286 1         17 $self->write;
287             }
288              
289             sub remove_ignore {
290 1     1 0 4 my ($self, $type, $nick) = @_;
291 1         4 $self->ignore->{$type} = [ grep {$nick ne $_} $self->ignores($type) ];
  1         6  
292 1         5 $self->write;
293             }
294              
295             __PACKAGE__->meta->make_immutable;
296             1;