File Coverage

blib/lib/Music/Note/Role/Operators.pm
Criterion Covered Total %
statement 45 45 100.0
branch 5 6 83.3
condition 10 12 83.3
subroutine 13 13 100.0
pod 1 8 12.5
total 74 84 88.1


line stmt bran cond sub pod time code
1             package Music::Note::Role::Operators;
2             $Music::Note::Role::Operators::VERSION = '0.04';
3             # ABSTRACT: Adds operator overloading, clone and interval calculation to Music::Note
4              
5 2     2   2150 use Storable ();
  2         5464  
  2         44  
6 2     2   11 use Role::Tiny;
  2         3  
  2         11  
7 2     2   1186 use Music::Intervals;
  2         222721  
  2         139  
8              
9             requires 'format';
10              
11             use overload
12 2         17 '>' => 'gt',
13             '<' => 'lt',
14             '==' => 'eq',
15             '>=' => 'gte',
16             '<=' => 'lte',
17             '-' => 'subtract',
18             fallback => 1,
19 2     2   17 ;
  2         3  
20              
21             =head1 NAME
22              
23             Music::Note::Role::Operators
24              
25             =head2 DESCRIPTION
26              
27             L to be applied on top L with comparison methods
28             added and overloaded operators. Also adds a clone method and a way to
29             generate Music::Interval objects via a Music::Note.
30              
31             =head2 SYNOPSIS
32              
33             If you're working with a L subclass:
34              
35             package Music::MyNote;
36             use parent 'Music::Note';
37             use Role::Tiny::With;
38             with 'Music::Note::Role::Operators';
39             # etc
40              
41             Or if you're working in a script and just want the behaviour:
42              
43             use Music::Note;
44             use Role::Tiny (); # Don't import R::T into current namespace for cleanliness
45             Role::Tiny->apply_roles_to_package('Music::Note', 'Music::Note::Role::Operators');
46              
47             =head2 SUMMARY
48              
49             Assuming you're working in a script:
50              
51             my $note = Music::Note->new('C#');
52             my $other = Music::Note->new('E');
53              
54             my $true = $other->gt($note);
55             $true = $other > $note;
56              
57             $true = $note->lt($other);
58             $true = $note < $other;
59              
60             $true = $note->eq($note->clone);
61             $true = $note == $note->clone;
62              
63             $true = $note->gte($note->clone);
64             $true = $note >= $note->clone;
65              
66             $true = $note->lte($note->clone);
67             $true = $note <= $note->clone;
68              
69             my $interval = $note->interval($other);
70             my $conveneince_interval = $note->interval(%args_for_music_interval);
71              
72             =head2 CAVEAT
73              
74             Don't try to do something like C<$note == 90>>. The overloading expects a
75             L on both sides. To perform comparisons versus note and not a
76             note you should be doing C<< $note->format('midi') == 90 >>.
77              
78             =head3 AUTHOR
79              
80             Kieren Diment L
81              
82             =head3 LICENSE
83              
84             This code can be redistributed on the same terms as perl itself
85              
86             =cut
87              
88             sub gt {
89 3     3 0 4661 my ($self, $other) = @_;
90 3         11 $self->_maybe_bail_on_comparison($other);
91 3         10 return $self->format('midinum') > $other->format('midinum');
92             }
93              
94             sub lt {
95 3     3 0 988 my ($self, $other) = @_;
96 3         10 $self->_maybe_bail_on_comparison($other);
97 2         6 return $self->format('midinum') < $other->format('midinum');
98             }
99              
100             sub eq {
101 4     4 0 1149 my ($self, $other) = @_;
102 4         11 $self->_maybe_bail_on_comparison($other);
103 4         14 return $self->format('midinum') == $other->format('midinum');
104             }
105              
106             sub gte {
107 1     1 0 308 my ($self, $other) = @_;
108 1         3 $self->_maybe_bail_on_comparison($other);
109 1         3 return $self->format('midinum') >= $other->format('midinum');
110             }
111              
112             sub lte {
113 2     2 0 323 my ($self, $other) = @_;
114 2         8 $self->_maybe_bail_on_comparison($other);
115 2         5 return $self->format('midinum') <= $other->format('midinum');
116             }
117              
118             sub subtract {
119 4     4 0 1873 my ($self, $other) = @_;
120 4         11 $self->_maybe_bail_on_comparison($other);
121 4         12 return $self->format('midinum') - $other->format('midinum');
122             }
123              
124             =head2 get_interval
125              
126             If called with a single Music::Note as argument is returns a
127             Music::Interval object
128              
129             my $interval = $self->get_interval($other);
130              
131             If called with an arguments hash
132              
133             my $interval = $self->get_interval(%args_for_music_interval)
134              
135             Note that this will default to 1 for the following constructor attributes,
136             so if you don't want these values you'll have to explicitly set them to
137             something else in the constructor.
138              
139             NOTE: It would be nice to have the subtract method return a Music::Interval
140             but it's a complex module, and only seems to deal with intervals inside a
141             single octave.
142              
143             =cut
144              
145             sub get_interval {
146 2     2 1 2473 my $self = shift;
147 2         3 my ($other, %args);
148 2 100 66     11 if (ref $_[0] && $_[0]->isa('Music::Note') ) {
149 1         2 ($other, %args) = @_;
150             }
151             else {
152 1         3 %args = @_;
153             }
154              
155 2 50 66     8 $self->_maybe_bail_on_comparison($other) unless $other || $args{notes};
156 2   100     33 $args{notes} ||= [ $self->format('isobase'), $other->format('isobase') ];
157 2   100     58 $args{$_} ||= 1 for qw/chords equalt freqs interval cents prime integer/;
158 2   100     4 $args{size} ||= scalar @{$args{notes}};
  1         16  
159 2         27 my $interval = Music::Intervals->new(%args);
160 2         16498 return $interval;
161             }
162              
163             sub clone {
164 2     2 0 1155 return Storable::dclone($_[0]);
165             }
166              
167             sub _maybe_bail_on_comparison {
168 17     17   25 my ($self, $other) = @_;
169 17 100       76 die "$other is not a Music::Note" unless $other->isa('Music::Note');
170             }
171              
172             1;