File Coverage

blib/lib/Class/DBI/Plugin/Senna.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # $Id: Senna.pm 2 2005-06-20 03:01:23Z daisuke $
2             #
3             # Daisuke Maki
4             # All rights reserved.
5              
6             package Class::DBI::Plugin::Senna;
7 1     1   937 use strict;
  1         2  
  1         133  
8 1     1   6 use base qw(Class::Data::Inheritable);
  1         3  
  1         817  
9 1     1   801 use Senna;
  0            
  0            
10             our $VERSION = '0.01';
11              
12             sub import
13             {
14             my $class = shift;
15             my %args = @_;
16              
17             my($pkg) = caller();
18              
19             $pkg->isa('Class::DBI') or Carp::croak("Calling class is not a Class::DBI");
20              
21             $pkg->mk_classdata('index_filename');
22             $pkg->mk_classdata('index_column');
23             $pkg->mk_classdata('index_createargs');
24             $pkg->mk_classdata('senna_index');
25              
26             if (! $args{index_filename}) {
27             Carp::croak("Required parameter index_filename not specified");
28             }
29              
30             if (! $args{index_column}) {
31             Carp::croak("Required parameter index_column not specified");
32             }
33              
34             $pkg->index_filename($args{index_filename});
35             $pkg->index_column($args{index_column});
36             $pkg->index_createargs($args{index_createargs});
37              
38             my @args = $pkg->index_createargs ? @{ $pkg->index_createargs } : ();
39             my $index = Senna::Index->open($pkg->index_filename) ||
40             Senna::Index->create($pkg->index_filename, @args);
41             $index or Carp::croak("Failed to create index file " . $pkg->index_filename);
42             $pkg->senna_index($index);
43             {
44             no strict 'refs';
45             *{"${pkg}::fulltext_search"} = \&senna_search;
46             }
47              
48             $pkg->add_trigger(after_create => \&_after_create_trigger);
49             $pkg->add_trigger(after_update => \&_after_update_trigger);
50             $pkg->add_trigger(before_delete => \&_before_delete_trigger);
51             $pkg->add_trigger(after_delete => \&_after_delete_trigger);
52             $pkg->add_trigger("before_set_$args{index_column}", \&_before_set_trigger);
53             }
54              
55             sub senna_search
56             {
57             my $class = shift;
58             my $query = shift;
59             my $index = $class->senna_index();
60             my $cursor = $index->search($query);
61              
62             my $iter = Class::DBI::Plugin::Senna::Iterator->new($class, $cursor);
63             if (wantarray) {
64             my @ret;
65             while (my $e = $iter->next) {
66             push @ret, $e;
67             }
68             return @ret;
69             } else {
70             return $iter;
71             }
72             }
73              
74             sub _after_create_trigger
75             {
76             my $self = shift;
77             my $index = $self->senna_index;
78             my $column = $self->index_column;
79             $index->put($self->id, $self->$column);
80             }
81              
82             sub _before_set_trigger
83             {
84             my $self = shift;
85             return unless ref($self);
86             $self->{__preval}->{$self->index_column} = $self->get($self->index_column);
87             }
88              
89             sub _after_update_trigger
90             {
91             my $self = shift;
92             my %args = @_;
93              
94             my $column = $self->index_column;
95             # Don't do anything if discard_records does not contain what
96             # we're looking for.
97             return if !grep { $_ eq $column } @{$args{discard_columns}};
98             # if it does, then get the previous value and update the index
99             my $index = $self->senna_index;
100             my $prev = delete $self->{__preval}->{$self->index_column};
101             $index->replace($self->id, $prev, $self->$column()) or die "Failed to replace";
102             }
103              
104             sub _before_delete_trigger
105             {
106             my $self = shift;
107             $self->{__preval}->{$self->index_column} = $self->get($self->index_column);
108             }
109              
110             sub _after_delete_trigger
111             {
112             my $self = shift;
113             my $index = $self->senna_index;
114             my $prev = delete $self->{__preval}->{$self->index_column};
115             $index->del($self->id, $prev);
116             }
117              
118             package Class::DBI::Plugin::Senna::Iterator;
119             use strict;
120             use overload
121             '0+' => 'count',
122             fallback => 1
123             ;
124              
125             sub new
126             {
127             my($class, $them, $cursor) = @_;
128             bless {
129             _class => $them,
130             _cursor => $cursor,
131             }, $class;
132             }
133              
134             sub count
135             {
136             my $self = shift;
137             my $cursor = $self->{_cursor};
138             return $cursor->hits;
139             }
140              
141             sub reset
142             {
143             my $self = shift;
144             my $cursor = $self->{_cursor};
145             $cursor->rewind;
146             }
147              
148             sub first
149             {
150             my $self = shift;
151             $self->reset;
152             return $self->next;
153             }
154              
155             sub next
156             {
157             my $self = shift;
158             my $class = $self->{_class};
159             my $cursor = $self->{_cursor};
160              
161             my $result = $cursor->next();
162             my $obj;
163             if (defined $result) {
164             $obj = $class->retrieve($result->key);
165             }
166             return $obj;
167             }
168              
169             sub delete_all
170             {
171             my $self = shift;
172             $self->count or return;
173              
174             $self->first->delete;
175             while (my $obj = $self->next) {
176             $obj->delete;
177             }
178             1;
179             }
180            
181              
182             __END__