1use strict; 2use warnings; 3 4package Jifty::Plugin; 5use base qw/Class::Accessor::Fast Jifty::Object/; 6__PACKAGE__->mk_accessors('_pre_init'); 7 8=head1 NAME 9 10Jifty::Plugin - Describes a plugin to the Jifty framework 11 12=head1 DESCRIPTION 13 14Plugins are like mini-apps. They come in packages with share 15directories which provide static and template files; they provide 16actions; they have dispatcher rules. To create the skeleton of a new 17plugin, you can use the command: 18 jifty plugin --name SomePlugin 19 20To use a plugin in your Jifty application, find the C<Plugins:> line 21in the C<config.yml> file: 22 23 Plugins: 24 - SpiffyThing: {} 25 - SomePlugin: 26 arguments: to 27 the: constructor 28 29The dispatcher for a plugin should live in 30C<Jifty::Plugin::I<name>::Dispatcher>; it is written like any other 31L<Jifty::Dispatcher>. Plugin dispatcher rules are checked before the 32application's rules; however, see L<Jifty::Dispatcher/Plugins and rule 33ordering> for how to manually specify exceptions to this. 34 35Actions and models under a plugin's namespace are automatically 36discovered and made available to applications. 37 38=cut 39 40=head2 new 41 42Sets up a new instance of this plugin. This is called by L<Jifty> 43after reading the configuration file, and is supplied whatever 44plugin-specific settings were in the config file. Note that because 45plugins affect Mason's component roots, adding plugins during runtime 46is not supported. 47 48=cut 49 50sub new { 51 my $class = shift; 52 my $self = $class->SUPER::new( { @_ }); 53 54 # XXX TODO: Add .po path 55 $self->init(@_); 56 57 Jifty->plugins( Jifty->plugins, $self ); 58 59 # Pull in the dispatcher 60 Jifty::Util->require($class->dispatcher); 61 62 return $self; 63} 64 65 66=head2 init [ARGS] 67 68Called by L</new>, this does any custom configuration that the plugin 69might need. It is passed the same parameters as L</new>, gleaned from 70the configuration file. 71 72=cut 73 74sub init { 75 1; 76} 77 78=head2 new_request 79 80Called right before every request. By default, does nothing. 81 82=cut 83 84sub new_request { 85} 86 87sub _calculate_share { 88 my $self = shift; 89 my $class = ref($self); 90 91 return $self->{share} if exists $self->{share}; 92 93 my $class_to_path = $class; 94 $class_to_path =~ s|::|/|g; 95 unless ( $self->{share} and -d $self->{share} ) { 96 # If we've got a Jifty in @INC, and the plugin is core, the 97 # right thing to do is to strip off lib/ and replace it with 98 # share/plugins/Jifty/Plugin/Whatever/ 99 $self->{share} = $INC{ $class_to_path . '.pm' }; 100 $self->{share} =~ s{lib/+\Q$class_to_path.pm}{share/plugins/$class_to_path}; 101 $self->{share} = File::Spec->rel2abs( $self->{share} ); 102 } 103 unless ( $self->{share} and -d $self->{share} ) { 104 # As above, but only tack on share/, for when we have a 105 # non-core plugin in @INC. We do this before the 106 # File::ShareDir, because File::ShareDir only looks at install 107 # locations, and the plugin could be hand-set in @INC. 108 $self->{share} = $INC{ $class_to_path . '.pm' }; 109 $self->{share} =~ s{lib/+\Q$class_to_path.pm}{share}; 110 $self->{share} = File::Spec->rel2abs( $self->{share} ); 111 } 112 unless ( $self->{share} and -d $self->{share} ) { 113 # If it's an installed non-core plugin, File::ShareDir's 114 # dist_dir will find it for us 115 my $dist = $class; 116 $dist =~ s/::/-/g; 117 local $@; 118 eval { $self->{share} = File::ShareDir::dist_dir($dist) }; 119 } 120 unless ( $self->{share} and -d $self->{share} ) { 121 # We try this last, so plugins that moved out of core, but 122 # were installed at when they _were_ in core, will get the 123 # updated plugin 124 125 # Core plugins live in jifty's share/plugins/Jifty/Plugin/Whatever/ 126 $self->{share} = Jifty::Util->share_root; 127 $self->{share} .= "/plugins/" . $class_to_path; 128 } 129 unless ( $self->{share} and -d $self->{share} ) { 130 $self->{share} = undef; 131 } 132 return $self->{share}; 133} 134 135 136=head2 template_root 137 138Returns the root of the C<HTML::Mason> template directory for this plugin 139 140=cut 141 142sub template_root { 143 my $self = shift; 144 my $dir = $self->_calculate_share(); 145 return unless $dir; 146 return $dir."/web/templates"; 147} 148 149=head2 po_root 150 151Returns the plugin's message catalog directory. Returns undef if it doesn't exist. 152 153=cut 154 155sub po_root { 156 my $self = shift; 157 my $dir = $self->_calculate_share(); 158 return unless $dir; 159 return $dir."/po"; 160} 161 162=head2 template_class 163 164Returns the Template::Declare view package for this plugin 165 166=cut 167 168sub template_class { 169 my $self = shift; 170 my $class = ref($self) || $self; 171 return $class.'::View'; 172} 173 174 175=head2 static_root 176 177Returns the root of the static directory for this plugin 178 179=cut 180 181sub static_root { 182 my $self = shift; 183 my $dir = $self->_calculate_share(); 184 return unless $dir; 185 return $dir."/web/static"; 186} 187 188=head2 dispatcher 189 190Returns the classname of the dispatcher class for this plugin 191 192=cut 193 194sub dispatcher { 195 my $self = shift; 196 my $class = ref($self) || $self; 197 return $class."::Dispatcher"; 198} 199 200=head2 prereq_plugins 201 202Returns an array of plugin module names that this plugin depends on. 203 204=cut 205 206sub prereq_plugins { 207 return (); 208} 209 210=head2 version 211 212Returns the database version of the plugin. Needs to be bumped any time the database schema needs to be updated. Plugins that do not directly define any models don't need to worry about this. 213 214=cut 215 216sub version { 217 return '0.0.1'; 218} 219 220=head2 bootstrapper 221 222Returns the name of the class that can be used to bootstrap the database models. This normally returns the plugin's class name with C<::Bootstrap> added to the end. Plugin bootstrappers can be built in exactly the same way as application bootstraps. 223 224See L<Jifty::Bootstrap>. 225 226=cut 227 228sub bootstrapper { 229 my $self = shift; 230 my $class = ref $self; 231 return $class . '::Bootstrap'; 232} 233 234=head2 upgrade_class 235 236Returns the name of the class that can be used to upgrade the database models and schema (such as adding new data, fixing default values, and renaming columns). This normally returns the plugin's class name with C<::Upgrade> added to the end. Plugin upgrade classes can be built in exactly the same was as application upgrade classes. 237 238See L<Jifty::Upgrade>. 239 240=cut 241 242sub upgrade_class { 243 my $self = shift; 244 my $class = ref $self; 245 return $class . '::Upgrade'; 246} 247 248=head2 table_prefix 249 250Returns a prefix that will be placed in the front of all table names for plugin models. Be default, the plugin name is converted to an identifier based upon the class name. 251 252=cut 253 254sub table_prefix { 255 my $self = shift; 256 my $class = ref $self; 257 $class =~ s/\W+/_/g; 258 $class .= '_'; 259 return lc $class; 260} 261 262=head2 wrap 263 264Takes a PSGI-$app closure and returns the wrapped one if your plugin 265wants to do something to the request handling process. See also 266L<Plack::Middleware>. 267 268=cut 269 270sub wrap { 271 my ($self, $app) = @_; 272 return $app; 273} 274 275=head2 psgi_app_static 276 277Returns a PSGI-$app that serves the static content of the plugin if 278any. The default is a <Plack::App::File> app with root set to 279plugin's C<static_root> 280 281=cut 282 283sub psgi_app_static { 284 my $self = shift; 285 my $static_root = $self->static_root; 286 return unless defined $static_root && -d $static_root && -r $static_root; 287 Plack::App::File->new(root => $static_root)->to_app 288} 289 2901; 291