File Coverage

blib/lib/Chemistry/Obj.pm
Criterion Covered Total %
statement 58 72 80.5
branch 12 16 75.0
condition 1 6 16.6
subroutine 16 19 84.2
pod 4 12 33.3
total 91 125 72.8


line stmt bran cond sub pod time code
1             package Chemistry::Obj;
2              
3             our $VERSION = '0.39'; # VERSION
4             # $Id$
5              
6 17     17   57776 use 5.006;
  17         60  
7              
8 17     17   127 use strict;
  17         27  
  17         350  
9 17     17   71 use Carp;
  17         30  
  17         3702  
10              
11             =head1 NAME
12              
13             Chemistry::Obj - Abstract chemistry object
14              
15             =head1 SYNOPSIS
16              
17             package MyObj;
18             use base "Chemistry::Obj";
19             Chemistry::Obj::accessor('color', 'flavor');
20              
21             package main;
22             my $obj = MyObj->new(name => 'bob', color => 'red');
23             $obj->attr(size => 42);
24             $obj->color('blue');
25             my $color = $obj->color;
26             my $size = $obj->attr('size');
27              
28             =head1 DESCRIPTION
29              
30             This module implements some generic methods that are used by L,
31             L, L, L, etc.
32              
33             =head2 Common Attributes
34              
35             There are some common attributes that may be found in molecules, bonds, and
36             atoms, such as id, name, and type. They are all accessed through the methods of
37             the same name. For example, to get the id, call C<< $obj->id >>; to set the id,
38             call C<< $obj->id('new_id') >>.
39              
40             =over 4
41              
42             =item id
43              
44             Objects should have a unique ID. The user has the responsibility for uniqueness
45             if he assigns ids; otherwise a unique ID is assigned sequentially.
46              
47             =item name
48              
49             An arbitrary name for an object. The name doesn't need to be unique.
50              
51             =item type
52              
53             The interpretation of this attribute is not specified here, but it's typically
54             used for bond orders and atom types.
55              
56             =item attr
57              
58             A space where the user can store any kind of information about the object. The
59             accessor method for attr expects the attribute name as the first parameter, and
60             (optionally) the new value as the second parameter. It can also take a hash or
61             hashref with several attributes. Examples:
62              
63             $color = $obj->attr('color');
64             $obj->attr(color => 'red');
65             $obj->attr(color => 'red', flavor => 'cherry');
66             $obj->attr({color => 'red', flavor => 'cherry'});
67              
68             =cut
69              
70             sub attr {
71 16     16 1 57 my $self = shift;
72 16         24 my ($attr) = @_;
73 16 100       51 if (ref $attr eq 'HASH') {
    100          
    100          
74 1         5 $self->{attr} = { %$attr };
75             } elsif (@_ == 1) {
76 7         46 return $self->{attr}{$attr};
77             } elsif (@_ == 0) {
78 2         3 return {%{$self->{attr}}};
  2         9  
79             } else {
80 6         15 while (@_ > 1) {
81 7         9 $attr = shift;
82 7         23 $self->{attr}{$attr} = shift;
83             }
84             }
85 7         12 $self;
86             }
87              
88             =back
89              
90             =head1 OTHER METHODS
91              
92             =over
93              
94             =item $obj->del_attr($attr_name)
95              
96             Delete an attribute.
97              
98             =cut
99              
100             sub del_attr {
101 1     1 1 501 my $self = shift;
102 1         3 my $attr = shift;
103 1         3 delete $self->{attr}{$attr};
104             }
105              
106             # A generic class attribute set/get method generator
107             sub accessor {
108 127     127 0 581 my $pkg = caller;
109 17     17   105 no strict 'refs';
  17         31  
  17         5855  
110 127         249 for my $attribute (@_) {
111 199         936 *{"${pkg}::$attribute"} =
112             sub {
113 963     963   2313 my $self = shift;
114 963 100       2755 return $self->{$attribute} unless @_;
115 162         874 $self->{$attribute} = shift;
116 162         314 return $self;
117 199         631 };
118             }
119             }
120              
121             sub print_attr {
122 0     0 0 0 my $self = shift;
123 0         0 my ($indent) = @_;
124 0         0 my $ret = '';
125            
126 0         0 for my $attr (keys %{$self->{attr}}) {
  0         0  
127 0         0 $ret .= "$attr: ".$self->attr($attr)."\n";
128             }
129 0 0       0 $ret and $ret =~ s/^/" "x$indent/gem;
  0         0  
130 0         0 $ret;
131             }
132              
133             my $N = 0; # atom ID counter
134 26     26 0 208 sub nextID { "obj".++$N; }
135 0     0 0 0 sub reset_id { $N = 0; }
136              
137             =item $class->new(name => value, name => value...)
138              
139             Generic object constructor. It will automatically call each "name" method with
140             the parameter "value". For example,
141              
142             $bob = Chemistry::Obj->new(name => 'bob', attr => {size => 42});
143              
144             is equivalent to
145              
146             $bob = Chemistry::Obj->new;
147             $bob->name('bob');
148             $bob->attr({size => 42});
149              
150             =cut
151              
152             sub new {
153 26     26 1 845 my $class = shift;
154 26         87 my %args = @_;
155 26   33     112 my $self = bless {
156             id => $class->nextID,
157             #$class->default_args,
158             }, ref $class || $class;
159 26         183 $self->$_($args{$_}) for (keys %args);
160 26         116 return $self;
161             }
162              
163             #sub default_args { (id => shift->nextID) }
164              
165             =back
166              
167             =head1 OPERATOR OVERLOADING
168              
169             Chemistry::Obj overloads a couple of operators for convenience.
170              
171             =over
172              
173             =cut
174              
175             use overload
176 17         113 '""' => "stringify",
177             'cmp' => "obj_cmp",
178             '0+', => "as_number",
179             fallback => 1,
180 17     17   9307 ;
  17         8057  
181              
182             =item ""
183              
184             The stringification operator. Stringify an object as its id. For example, If an
185             object $obj has the id 'a1', print "$obj" will print 'a1' instead of something
186             like 'Chemistry::Obj=HASH(0x810bbdc)'. If you really want to get the latter,
187             you can call C. See L for details.
188              
189             =cut
190              
191             sub stringify {
192 11     11 0 1091 my $self = shift;
193 11         26 $self->id;
194             }
195              
196             sub as_number {
197 19     19 0 462 $_[0];
198             }
199              
200             =item cmp
201              
202             Compare objects by ID. This automatically overloads C, C, C, C,
203             C, and C as well. For example, C<$obj1 eq $obj2> returns true if both
204             objects have the same id, even if they are different objects with different
205             memory addresses. In contrast, C<$obj1 == $obj2> will return true only if
206             C<$obj1> and C<$obj2> point to the same object, with the same memory address.
207              
208             =cut
209              
210             sub obj_cmp {
211 151     151 0 373 my ($a, $b) = @_;
212 17     17   3844 no warnings;
  17         31  
  17         3929  
213              
214 151         531 return $a->{id} cmp $b->{id};
215             }
216              
217             =back
218              
219             =cut
220              
221             accessor(qw(name type));
222              
223             sub id {
224 687     687 1 798 my $self = shift;
225 687 100       2192 return $self->{id} unless @_;
226 9 100       24 if ($self->{parent}) {
227 4         7 my $new_id = shift;
228 4         7 my $old_id = $self->{id};
229 4         6 $self->{id} = $new_id;
230 4         10 $self->{parent}->_change_id($old_id, $new_id);
231             } else {
232 5         14 $self->{id} = shift;
233             }
234             }
235              
236             # this is an experimental method and shouldn't be used!
237             sub use {
238 0     0 0   my ($pack, $module, @args) = @_;
239 0   0       $pack = ref $pack || $pack;
240 0 0         my $args = @args ? "(@args)" : '';
241 0           eval "package $pack; use $module $args";
242             }
243              
244             1;
245              
246             =head1 SOURCE CODE REPOSITORY
247              
248             L
249              
250             =head1 SEE ALSO
251              
252             L, L, L
253              
254             =head1 AUTHOR
255              
256             Ivan Tubert-Brohman Eitub@cpan.orgE
257              
258             =head1 COPYRIGHT
259              
260             Copyright (c) 2005 Ivan Tubert-Brohman. All rights reserved. This program is
261             free software; you can redistribute it and/or modify it under the same terms as
262             Perl itself.
263              
264             =cut
265