File Coverage

blib/lib/Object/I18n.pm
Criterion Covered Total %
statement 75 99 75.7
branch 13 24 54.1
condition 10 18 55.5
subroutine 16 21 76.1
pod 4 8 50.0
total 118 170 69.4


line stmt bran cond sub pod time code
1             package Object::I18n;
2              
3 2     2   59032 use 5.008003;
  2         8  
  2         73  
4 2     2   18 use strict;
  2         5  
  2         75  
5 2     2   10 use warnings;
  2         3  
  2         139  
6              
7             # Not using Exporter
8             our @EXPORT = qw(i18n);
9             our $VERSION = '0.02';
10              
11 2     2   10 use Carp;
  2         2  
  2         191  
12 2     2   10 use Scalar::Util qw(weaken);
  2         3  
  2         566  
13              
14             =head1 NAME
15              
16             Object::I18n - Internationalize objects
17              
18             =head1 SYNOPSIS
19              
20             package Greeting;
21             sub new { my $x = $_[1]; bless \$x; }
22             sub greeting { @_ ? ${$_[1]} : (${$_[1]} = $_[2]) }
23             use Object::I18n qw(id);
24             __PACKAGE__->i18n->storage('Greeting::CDBI::I18n');
25             __PACKAGE__->i18n->register('greeting');
26            
27             package main;
28             my $obj = Greeting->new("Hello, world\n");
29             print $obj->greeting;# "Hello, world\n"
30             $obj->i18n->language('fr');
31             print $obj->greeting;# exception
32             $obj->greeting("Bonjour, monde\n");
33             print $obj->greeting;# "Bonjour, monde\n"
34            
35             =cut
36              
37             my %i18n_object;
38              
39             sub i18n {
40 21     21 0 2939 my $self = shift;
41 21   66     74 my $class = ref $self || $self;
42 21         30 my $i18n = $i18n_object{$class};
43 21 100       78 return $i18n unless ref $self;
44              
45 11         27 _prune($i18n->{instance});
46 11         20 my $oid_method = $i18n->{oid_method};
47 11         31 my $oid = $self->$oid_method;
48 11         43 my $instance_i18n = $i18n->{instance}{$oid};
49            
50 11   66     56 $i18n->{instance}{$oid} ||= $i18n->_clone($self);
51             }
52              
53             sub import {
54 2     2   18 my ($class, %opts) = (@_);
55 2         6 my ($pkg) = caller;
56 2         11 $class->_init_i18n_object($pkg, %opts);
57 2     2   10 no strict 'refs';
  2         3  
  2         835  
58 2         3 *{ "$pkg\::i18n" } = \&i18n;
  2         98  
59             }
60              
61             sub oid {
62 0     0 0 0 my $self = shift;
63 0         0 my $oid_method = $self->{oid_method};
64 0         0 return $self->{object}->$oid_method;
65             }
66              
67             sub _prune {
68 11     11   13 my ($instance) = @_;
69 11         31 for my $oid (keys %$instance) {
70 20 100       90 next if $instance->{$oid}{object};
71 1         2 delete $instance->{$oid};
72             }
73             }
74              
75             sub _clone {
76 4     4   6 my $self = shift;
77 4         5 my ($obj) = @_;
78             # XXX - may need deeper cloning than this
79 4         17 my $clone = { %$self };
80 4         11 delete $clone->{instance};
81 4         6 delete $clone->{language};
82 4         6 $clone->{object} = $obj;
83             # $clone->{object} will be undef when the object is DESTROYed.
84 4         14 weaken($clone->{object});
85 4         28 return bless $clone, __PACKAGE__;
86             }
87              
88             # The i18n object has these attributes:
89             # class - The package of the class or object
90             # oid_method - A method in "class" that returns a unique object id
91             # language - The current language for the class or object
92             # object - The object that contains the i18n object
93             # instance - A hash of i18n instances stored in the class i18n
94             # registered_methods - A hash of overridden methods
95             #
96             sub _init_i18n_object {
97 2     2   3 my $class = shift;
98 2         4 my ($pkg, %attrs) = @_;
99 2   50     19 $attrs{oid_method} ||= 'id';
100 2   50     10 $attrs{storage_class} ||= 'Object::I18n::Storage::MemHash';
101 2 50       12 croak "invalid storage_class" if $attrs{storage_class} =~ /[^\w:]/;
102 2 50       109 eval "require $attrs{storage_class}; 1" or die $@;
103              
104 2         12 my $obj = bless {
105             class => $pkg,
106             %attrs,
107             }, $class;
108 2         8 $i18n_object{$pkg} = $obj;
109             }
110              
111             sub register {
112 3     3 1 1332 my $self = shift;
113 3         7 my @methods = @_;
114 3   100     33 my $registered = $self->{registered_methods} ||= {};
115 3 50       31 my $class = $self->{class}
116             or die "register must be called as class method";
117 3         5 for my $method (@methods) {
118 4 100       214 croak "No such method '$method' found for class '$class'"
119             unless my $code = $class->can($method);
120 3         6 $registered->{$method} = $code;
121 2     2   8 no strict 'refs';
  2         3  
  2         43  
122 2     2   8 no warnings 'redefine';
  2         3  
  2         981  
123 3         17 *{ "$class\::$method" } = sub {
124 0     0   0 my $self = shift;
125 0 0       0 return $self->$code(@_) unless $self->i18n->language;
126              
127 0         0 my $storage_class = $self->i18n->{storage_class};
128 0 0       0 my $storage = $storage_class->new($self, $method)
129             or croak "Could not create instance of '$storage_class'"
130             . "for '$method'";
131 0 0       0 return $storage->store(@_) if @_;
132 0         0 return $storage->fetch;
133 3         14 };
134             }
135 2         8 keys %$registered;
136             }
137              
138             sub storage_class {
139 0     0 1 0 my $self = shift;
140 0         0 $self->{storage_class};
141             }
142              
143             sub registered_methods {
144 1     1 0 2 my $self = shift;
145 1         2 keys %{ $self->{registered_methods} };
  1         6  
146             }
147              
148             sub language {
149 14     14 1 15 my $self = shift;
150 14 100       32 return $self->{language} = shift if @_;
151              
152 11 100       49 return $self->{language} unless exists $self->{object};
153 5   66     23 return $self->{language} || $self->{class}->i18n->language;
154             }
155              
156             # ->inject(attr=>'question',language=>'fr',notes=>$notes, data=>$data);
157             sub inject {
158 0     0 1   my $self = shift;
159 0           my %opts = @_;
160              
161 0           my $storage_class = $self->{storage_class};
162 0           local($self->{language}) = $opts{language};
163 0           my $storage = $storage_class->new($self->{object}, $opts{attr});
164 0           $storage->store($opts{data});
165 0 0 0       return unless $opts{notes} and my $history_class = $self->{history_class};
166              
167 0           $history_class->record($opts{notes});
168             }
169              
170             sub is_injected {
171 0     0 0   my $self = shift;
172 0           my %opts = @_;
173              
174 0           my $storage_class = $self->{storage_class};
175 0           my $storage = $storage_class->new($self->{object}, $opts{attr});
176 0           return $storage->fetch;
177             }
178              
179             =head1 DESCRIPTION
180              
181             Object::I18n overrides methods in your class to return international content.
182             It provides one mixin method, i18n(), which returns an Object::I18n object.
183             The object returned is different depending on whether you call it on your
184             class or an instance of your class. See L<"METHODS"> below.
185              
186             =head2 METHODS
187              
188             Most methods can be either class methods or object methods but this
189             doesn't mean what you may be accustomed to. A method is considered to
190             act as a class method if it is called on an Object::I18n object returned
191             from the class form of the i18n() method. It acts as an object method
192             when called on the object returned from the object form of the i18n()
193             method.
194              
195             =over
196              
197             =item language [LANGUAGE]
198              
199             Returns and optionally sets the current language. If called as a class
200             method it affects all instances of a class, except those that have set
201             language() for themselves. If unset, methods should behave as if
202             Object::I18n was not being used at all.
203              
204             =item register METHODLIST
205              
206             Registers the list of method names as i14able. The methods will be
207             overridden so that they return i14ed content when C is set.
208              
209             =item storage_class [CLASS]
210              
211             Returns and optionally set the class that controls how translations
212             are stored.
213              
214             =item inject OPTIONS
215              
216             Injects a new translation into your C. The options are:
217              
218             =over
219              
220             =item language
221              
222             The language the translation is in. If not set then the language
223             returned by the language() method will be used.
224              
225             =item attr
226              
227             The attribute (method) in your class that the translation is for.
228              
229             =item data
230              
231             The actual translated text to be stored.
232              
233             =item notes
234              
235             Any notes to be saved along with the translation. Requires you
236             to have configured a C.
237              
238             =back
239              
240             =back
241              
242             =head1 EXPORT
243              
244             C.
245              
246             =head1 AUTHOR
247              
248             Rick Delaney, Erick@bort.caE
249              
250             =head1 ACKNOWLEDGEMENTS
251              
252             To be filled in.
253              
254             =head1 COPYRIGHT AND LICENSE
255              
256             Copyright (C) 2005 by Rick Delaney
257              
258             This library is free software; you can redistribute it and/or modify
259             it under the same terms as Perl itself, either Perl version 5.8.3 or,
260             at your option, any later version of Perl 5 you may have available.
261              
262              
263             =cut
264              
265             1;