| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Ado::Command::generate::crud; | 
| 2 | 4 |  |  | 4 |  | 1681 | use Mojo::Base 'Ado::Command::generate'; | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 24 |  | 
| 3 | 4 |  |  | 4 |  | 591 | use Mojo::Util qw(camelize class_to_path decamelize); | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 295 |  | 
| 4 | 4 |  |  | 4 |  | 18 | use Getopt::Long qw(GetOptionsFromArray :config no_auto_abbrev no_ignore_case); | 
|  | 4 |  |  |  |  | 6 |  | 
|  | 4 |  |  |  |  | 28 |  | 
| 5 | 4 |  |  | 4 |  | 1333 | use Time::Piece (); | 
|  | 4 |  |  |  |  | 4570 |  | 
|  | 4 |  |  |  |  | 90 |  | 
| 6 | 4 |  |  | 4 |  | 23 | use List::Util qw(first); | 
|  | 4 |  |  |  |  | 6 |  | 
|  | 4 |  |  |  |  | 3786 |  | 
| 7 |  |  |  |  |  |  | File::Spec::Functions->import(qw(catfile catdir splitdir)); | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | has description => "Generates directory structures for Ado-specific CRUD..\n"; | 
| 10 |  |  |  |  |  |  | has usage => sub { shift->extract_usage }; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | has routes => sub { | 
| 13 |  |  |  |  |  |  | $_[0]->{routes} = []; | 
| 14 |  |  |  |  |  |  | foreach my $t (@{$_[0]->args->{tables}}) { | 
| 15 |  |  |  |  |  |  | my $controller = camelize($t); | 
| 16 |  |  |  |  |  |  | my $route      = decamelize($controller); | 
| 17 |  |  |  |  |  |  | push @{$_[0]->{routes}}, | 
| 18 |  |  |  |  |  |  | { route => "/$route", | 
| 19 |  |  |  |  |  |  | via   => ['GET'], | 
| 20 |  |  |  |  |  |  | to    => "$route#list", | 
| 21 |  |  |  |  |  |  | }, | 
| 22 |  |  |  |  |  |  | { route => "/$route/list", | 
| 23 |  |  |  |  |  |  | via   => ['GET'], | 
| 24 |  |  |  |  |  |  | to    => "$route#list", | 
| 25 |  |  |  |  |  |  | }, | 
| 26 |  |  |  |  |  |  | { route => "/$route/read/:id", | 
| 27 |  |  |  |  |  |  | via   => [qw(GET)], | 
| 28 |  |  |  |  |  |  | to    => "$route#read", | 
| 29 |  |  |  |  |  |  | }, | 
| 30 |  |  |  |  |  |  | { route => "/$route/create", | 
| 31 |  |  |  |  |  |  | via   => [qw(GET POST)], | 
| 32 |  |  |  |  |  |  | to    => "$route#create", | 
| 33 |  |  |  |  |  |  | over  => {authenticated => 1}, | 
| 34 |  |  |  |  |  |  | }, | 
| 35 |  |  |  |  |  |  | { route => "/$route/update/:id", | 
| 36 |  |  |  |  |  |  | via   => [qw(GET PUT)], | 
| 37 |  |  |  |  |  |  | to    => "$route#update", | 
| 38 |  |  |  |  |  |  | over  => {authenticated => 1}, | 
| 39 |  |  |  |  |  |  | }, | 
| 40 |  |  |  |  |  |  | { route => "/$route/delete/:id", | 
| 41 |  |  |  |  |  |  | via   => [qw(GET DELETE)], | 
| 42 |  |  |  |  |  |  | to    => "$route#delete", | 
| 43 |  |  |  |  |  |  | over  => {authenticated => 1}, | 
| 44 |  |  |  |  |  |  | }; | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  | return $_[0]->{routes}; | 
| 47 |  |  |  |  |  |  | }; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | sub initialise { | 
| 50 | 3 |  |  | 3 | 1 | 796 | my ($self, @args) = @_; | 
| 51 | 3 | 100 |  |  |  | 23 | return $self if $self->{_initialised}; | 
| 52 | 2 |  |  |  |  | 12 | my $args = $self->args({tables => []})->args; | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | GetOptionsFromArray( | 
| 55 |  |  |  |  |  |  | \@args, | 
| 56 |  |  |  |  |  |  | 'C|controller_namespace=s' => \$args->{controller_namespace}, | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | #'d|dsn=s'                  => \$args->{dsn}, | 
| 59 |  |  |  |  |  |  | 'L|lib=s'             => \$args->{lib}, | 
| 60 |  |  |  |  |  |  | 'M|model_namespace=s' => \$args->{model_namespace}, | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | #'N|no_dsc_code'            => \$args->{no_dsc_code}, | 
| 63 |  |  |  |  |  |  | 'O|overwrite' => \$args->{overwrite}, | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | #'P|password=s'             => \$args->{password}, | 
| 66 |  |  |  |  |  |  | 'T|templates_root=s' => \$args->{templates_root}, | 
| 67 |  |  |  |  |  |  | 't|tables=s@'        => \$args->{tables}, | 
| 68 |  |  |  |  |  |  | 'H|home_dir=s'       => \$args->{home_dir}, | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | #'U|user=s'                 => \$args->{user}, | 
| 71 | 2 |  |  |  |  | 27 | ); | 
| 72 |  |  |  |  |  |  |  | 
| 73 | 2 |  |  |  |  | 1371 | @{$args->{tables}} = split(/\,/, join(',', @{$args->{tables}})); | 
|  | 2 |  |  |  |  | 6 |  | 
|  | 2 |  |  |  |  | 6 |  | 
| 74 | 2 | 50 |  |  |  | 2 | Carp::croak $self->usage unless scalar @{$args->{tables}}; | 
|  | 2 |  |  |  |  | 8 |  | 
| 75 | 2 |  |  |  |  | 14 | my $app = $self->app; | 
| 76 | 2 |  | 33 |  |  | 19 | $args->{controller_namespace} //= $app->routes->namespaces->[0]; | 
| 77 |  |  |  |  |  |  | $args->{model_namespace} //= | 
| 78 | 4 | 100 |  | 4 |  | 45 | (first { ref($_) eq 'HASH' and $_->{name} eq 'DSC' } @{$app->config('plugins')}) | 
|  | 2 |  |  |  |  | 15 |  | 
| 79 | 2 |  | 33 |  |  | 31 | ->{config}{namespace}; | 
| 80 | 2 |  | 33 |  |  | 9 | $args->{home_dir}       //= $app->home; | 
| 81 | 2 |  | 66 |  |  | 12 | $args->{lib}            //= catdir($args->{home_dir}, 'lib'); | 
| 82 | 2 |  | 33 |  |  | 5 | $args->{templates_root} //= $app->renderer->paths->[0]; | 
| 83 | 2 |  |  |  |  | 4 | $self->{_initialised} = 1; | 
| 84 | 2 |  |  |  |  | 7 | return $self; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub run { | 
| 88 | 2 |  |  | 2 | 1 | 1801 | my ($self) = shift->initialise(@_); | 
| 89 | 2 |  |  |  |  | 9 | my $args   = $self->args; | 
| 90 | 2 |  |  |  |  | 13 | my $app    = $self->app; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 2 |  |  |  |  | 8 | foreach my $t (@{$args->{tables}}) { | 
|  | 2 |  |  |  |  | 7 |  | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | # Controllers | 
| 95 | 2 |  |  |  |  | 8 | my $class_name = camelize($t); | 
| 96 | 2 |  |  |  |  | 42 | $args->{class} = $args->{controller_namespace} . '::' . $class_name; | 
| 97 | 2 |  |  |  |  | 8 | my $c_file = catfile($args->{lib}, class_to_path($args->{class})); | 
| 98 | 2 |  |  |  |  | 29 | $args->{t} = lc $t; | 
| 99 | 2 |  |  |  |  | 30 | $self->render_to_file('class', $c_file, $args); | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | # Templates | 
| 102 | 2 |  |  |  |  | 17290 | my $template_dir  = decamelize($class_name); | 
| 103 | 2 |  |  |  |  | 49 | my $template_root = $args->{templates_root}; | 
| 104 | 2 |  |  |  |  | 15 | my $t_file        = catfile($template_root, $template_dir, 'list.html.ep'); | 
| 105 | 2 |  |  |  |  | 10 | $self->render_to_file('list_template', $t_file, $args); | 
| 106 | 2 |  |  |  |  | 4768 | $t_file = catfile($template_root, $template_dir, 'create.html.ep'); | 
| 107 | 2 |  |  |  |  | 13 | $self->render_to_file('create_template', $t_file, $args); | 
| 108 | 2 |  |  |  |  | 2727 | $t_file = catfile($template_root, $template_dir, 'read.html.ep'); | 
| 109 | 2 |  |  |  |  | 12 | $self->render_to_file('read_template', $t_file, $args); | 
| 110 | 2 |  |  |  |  | 3273 | $t_file = catfile($template_root, $template_dir, 'delete.html.ep'); | 
| 111 | 2 |  |  |  |  | 30 | $self->render_to_file('delete_template', $t_file, $args); | 
| 112 |  |  |  |  |  |  | }    # end foreach tables | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 2 |  |  |  |  | 2828 | return $self; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | 1; | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | =pod | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | =encoding utf8 | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | =head1 NAME | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | Ado::Command::generate::crud - Generates MVC set of files | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | Usage: | 
| 132 |  |  |  |  |  |  | #on the command-line | 
| 133 |  |  |  |  |  |  | # for one or more tables. | 
| 134 |  |  |  |  |  |  | $ bin/ado generate crud --tables='news,articles' | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | #programatically | 
| 137 |  |  |  |  |  |  | use Ado::Command::generate::crud; | 
| 138 |  |  |  |  |  |  | my $v = Ado::Command::generate::crud->new; | 
| 139 |  |  |  |  |  |  | $v->run(-t => 'news,articles'); | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | B | 
| 144 |  |  |  |  |  |  | The generated code is not even expected to work properly.> | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | L generates directory structure for | 
| 147 |  |  |  |  |  |  | a fully functional | 
| 148 |  |  |  |  |  |  | L | 
| 149 |  |  |  |  |  |  | set of files, based on existing tables in the database. | 
| 150 |  |  |  |  |  |  | You only need to create the tables. The Model (M) classes are generated on the fly | 
| 151 |  |  |  |  |  |  | from the tables when the controller classes are loaded by L for the first time. | 
| 152 |  |  |  |  |  |  | You can dump them to disk if you want using the C script that | 
| 153 |  |  |  |  |  |  | comes with L. You may decide to use only L | 
| 154 |  |  |  |  |  |  | via the C<$c-Edbix> helper or L via C<$c-Edbix-Edbh>. | 
| 155 |  |  |  |  |  |  | That's up to you. | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | This tool's purpose is to promote | 
| 158 |  |  |  |  |  |  | L | 
| 159 |  |  |  |  |  |  | by generating the boilerplate code for controllers (C) | 
| 160 |  |  |  |  |  |  | and help programmers new to L and L to quickly create | 
| 161 |  |  |  |  |  |  | well structured, fully functional applications. | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | In the generated actions you will find I code | 
| 164 |  |  |  |  |  |  | for reading, creating, updating and deleting records from the tables you | 
| 165 |  |  |  |  |  |  | specified on the command-line. | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | The generated code is just boilerplate to give you a jump start, so you can | 
| 168 |  |  |  |  |  |  | concentrate on writing your business-specific code. It is assumed that you will | 
| 169 |  |  |  |  |  |  | modify the generated code to suit your specific needs. | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | =head1 OPTIONS | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | Below are the options this command accepts, described in L notation. | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | =head2 C|controller_namespace=s | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | Optional. The namespace for the controller classes to be generated. | 
| 178 |  |  |  |  |  |  | Defaults to  Croutes-Enamespaces-E[0]>, usually | 
| 179 |  |  |  |  |  |  | L. If you decide to use another namespace for the controllers, | 
| 180 |  |  |  |  |  |  | do not forget to add it to the list Croutes-Enamespaces> | 
| 181 |  |  |  |  |  |  | in C or your plugin configuration file. | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =head2 H|home_dir=s | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | Defaults to C<$ENV{MOJO_HOME}> (which is Ado home directory). | 
| 186 |  |  |  |  |  |  | Used to set the root directory to which the files | 
| 187 |  |  |  |  |  |  | will be dumped when L. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head2 L|lib=s | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | Defaults to C relative to the C<--home_dir> directory. | 
| 192 |  |  |  |  |  |  | If you installed L in some custom path and you wish to generate your controllers into | 
| 193 |  |  |  |  |  |  | e.g. C, use this option. Do not forget to add this | 
| 194 |  |  |  |  |  |  | directory to C<$ENV{PERL5LIB}>, so the classes can be found and loaded. | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =head2 M|model_namespace=s | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Optional. The namespace for the model classes to be generated. | 
| 199 |  |  |  |  |  |  | Defaults to L. If you wish however to use another namespace | 
| 200 |  |  |  |  |  |  | for another database, you will have to add another item for | 
| 201 |  |  |  |  |  |  | L to the list of loaded plugins in C | 
| 202 |  |  |  |  |  |  | or in your plugin configuration. Yes, multiple database connections/schemas | 
| 203 |  |  |  |  |  |  | are supported. | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | =head2 T|templates_root=s | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | Defaults to Crenderer-Epaths-E[0]>. This is usually | 
| 208 |  |  |  |  |  |  | C directory. If you want to use another directory, | 
| 209 |  |  |  |  |  |  | do not forget to add it to the Crenderer-Epaths> list | 
| 210 |  |  |  |  |  |  | in your configuration file. | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | =head2 t|tables=s@ | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | Mandatory. List of tables separated by commas for which controllers should be generated. | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | =head1 ATTRIBUTES | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | L inherits all attributes from | 
| 219 |  |  |  |  |  |  | L and implements the following new ones. | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | =head2 description | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | my $description = $command->description; | 
| 224 |  |  |  |  |  |  | $command        = $command->description('Foo!'); | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | Short description of this command, used for the command list. | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | =head2 routes | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | $self->routtes(); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | Returns an ARRAY reference containing routes, prepared after C<$self-Eargs-E{tables}>. | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | Altough L already has defined generic routes for CRUD, | 
| 235 |  |  |  |  |  |  | this attribute contains more specific routes, that will secure the C, | 
| 236 |  |  |  |  |  |  | C and C actions, so they are available only to an | 
| 237 |  |  |  |  |  |  | authenticated user. This attribute is used for generating routes in | 
| 238 |  |  |  |  |  |  | L. | 
| 239 |  |  |  |  |  |  | After generating a plugin you should end up with a | 
| 240 |  |  |  |  |  |  | L | 
| 241 |  |  |  |  |  |  | service. The generated code uses | 
| 242 |  |  |  |  |  |  | L. For details see | 
| 243 |  |  |  |  |  |  | L. | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | =head2 usage | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | my $usage = $command->usage; | 
| 248 |  |  |  |  |  |  | $command  = $command->usage('Foo!'); | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | Usage information for this command, used for the help screen. | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | =head1 METHODS | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | L inherits all methods from | 
| 255 |  |  |  |  |  |  | L and implements the following new ones. | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | =head2 initialise | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | sub run { | 
| 260 |  |  |  |  |  |  | my ($self) = shift->initialise(@_); | 
| 261 |  |  |  |  |  |  | #... | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | Parses arguments and prepares the command to be run. Calling this method for the second time has no effect. | 
| 265 |  |  |  |  |  |  | Returns C<$self>. | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | =head2 run | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | Ado::Command::generate::crud->new(app=>$app)->run(@ARGV); | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | Run this command. | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | L, | 
| 276 |  |  |  |  |  |  | L, | 
| 277 |  |  |  |  |  |  | L, L, | 
| 278 |  |  |  |  |  |  | L, L, | 
| 279 |  |  |  |  |  |  | L L, | 
| 280 |  |  |  |  |  |  | L, L | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | =head1 AUTHOR | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | Красимир Беров (Krasimir Berov) | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | Copyright 2014 Красимир Беров (Krasimir Berov). | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | This program is free software, you can redistribute it and/or | 
| 291 |  |  |  |  |  |  | modify it under the terms of the | 
| 292 |  |  |  |  |  |  | GNU Lesser General Public License v3 (LGPL-3.0). | 
| 293 |  |  |  |  |  |  | You may copy, distribute and modify the software provided that | 
| 294 |  |  |  |  |  |  | modifications are open source. However, software that includes | 
| 295 |  |  |  |  |  |  | the license may release under a different license. | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | See http://opensource.org/licenses/lgpl-3.0.html for more information. | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | =cut | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | __DATA__ |