[Jifty-commit] r3858 - in jifty/trunk: . lib lib/Jifty lib/Jifty/Action/Record

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Sun Aug 12 16:23:47 EDT 2007


Author: sterling
Date: Sun Aug 12 16:23:46 2007
New Revision: 3858

Modified:
   jifty/trunk/   (props changed)
   jifty/trunk/lib/Jifty.pm
   jifty/trunk/lib/Jifty/API.pm
   jifty/trunk/lib/Jifty/Action/Autocomplete.pm
   jifty/trunk/lib/Jifty/Action/Record.pm
   jifty/trunk/lib/Jifty/Action/Record/Create.pm
   jifty/trunk/lib/Jifty/Action/Record/Delete.pm
   jifty/trunk/lib/Jifty/Action/Record/Search.pm
   jifty/trunk/lib/Jifty/Action/Record/Update.pm
   jifty/trunk/lib/Jifty/Action/Redirect.pm

Log:
 r8534 at riddle:  andrew | 2007-08-12 15:13:21 -0500
 Improving some of the Pod, code comments, and minor perl tidying.


Modified: jifty/trunk/lib/Jifty.pm
==============================================================================
--- jifty/trunk/lib/Jifty.pm	(original)
+++ jifty/trunk/lib/Jifty.pm	Sun Aug 12 16:23:46 2007
@@ -143,13 +143,13 @@
 sub new {
     my $ignored_class = shift;
 
+    # Setup the defaults
     my %args = (
         no_handle        => 0,
         logger_component => undef,
         @_
     );
 
-
     # Add the appliation's library path
     push @INC, File::Spec->catdir(Jifty::Util->app_root, "lib");
 
@@ -158,10 +158,12 @@
     # which is likely Record::Cachable or Record::Memcached
     @Jifty::Record::ISA = grep { $_ ne 'Jifty::DBI::Record' } @Jifty::Record::ISA;
 
+    # Configure the base class used by Jifty models
     my $record_base_class = Jifty->config->framework('Database')->{'RecordBaseClass'};
     Jifty::Util->require( $record_base_class );
     push @Jifty::Record::ISA, $record_base_class unless $record_base_class eq 'Jifty::Record';
 
+    # Logger turn on
     Jifty->logger( Jifty::Logger->new( $args{'logger_component'} ) );
 
     # Set up plugins
@@ -169,19 +171,30 @@
     my @plugins_to_load = @{Jifty->config->framework('Plugins')};
     my $app_plugin = Jifty->app_class('Plugin');
     for (my $i = 0; my $plugin = $plugins_to_load[$i]; $i++) {
+
+        # Prepare to learn the plugin class name
         my $plugin_name = (keys %{$plugin})[0];
         my $class;
+
+        # Is the plugin name a fully-qualified class name?
         if ($plugin_name =~ /^(?:Jifty::Plugin|$app_plugin)::/) {
             # app-specific plugins use fully qualified names, Jifty plugins may
             $class = $plugin_name; 
         }
+
         # otherwise, assume it's a short name, qualify it
         else {
             $class = "Jifty::Plugin::".$plugin_name;
         }
+
+        # Load the plugin options
         my %options = %{ $plugin->{ $plugin_name } };
+
+        # Load the plugin code
         Jifty::Util->require($class);
         Jifty::ClassLoader->new(base => $class)->require;
+
+        # Initialize the plugin and mark the prerequisites for loading too
         my $plugin_obj = $class->new(%options);
         push @plugins, $plugin_obj;
         foreach my $name ($plugin_obj->prereq_plugins) {
@@ -190,6 +203,7 @@
         }
     }
 
+    # All plugins loaded, save them for later reference
     Jifty->plugins(@plugins);
 
     # Now that we have the config set up and loaded plugins,
@@ -201,9 +215,11 @@
         base => Jifty->app_class,
     );
 
+    # Save the class loader for later reference
     Jifty->class_loader($class_loader);
     $class_loader->require;
 
+    # Configure the request handler and action API handler
     Jifty->handler(Jifty::Handler->new());
     Jifty->api(Jifty::API->new());
 
@@ -217,9 +233,9 @@
     # application specific for startup
     my $app = Jifty->app_class;
     
+    # Run the App::start() method if it exists for app-specific initialization
     $app->start()
         if $app->can('start');
-    
 }
 
 # Explicitly destroy the classloader; if this happens during global
@@ -431,15 +447,20 @@
     my $self = shift;
     my %args = (no_handle =>0,
                 @_);
+
+    # Don't setup the database connection if we're told not to
     unless ( $args{'no_handle'}
         or Jifty->config->framework('SkipDatabase')
         or not Jifty->config->framework('Database') )
     {
 
+        # Load the app's database handle and connect
         my $handle_class = Jifty->app_class("Handle");
         Jifty::Util->require( $handle_class );
         Jifty->handle( $handle_class->new );
         Jifty->handle->connect();
+
+        # Make sure the app version matches the database version
         Jifty->handle->check_schema_version()
             unless $args{'no_version_check'};
     }

Modified: jifty/trunk/lib/Jifty/API.pm
==============================================================================
--- jifty/trunk/lib/Jifty/API.pm	(original)
+++ jifty/trunk/lib/Jifty/API.pm	Sun Aug 12 16:23:46 2007
@@ -57,8 +57,10 @@
     my $class = shift;
     my $self  = bless {}, $class;
 
+    # Setup the basic allow/deny rules
     $self->reset;
 
+    # Find all the actions for the API reference (available at _actions)
     Jifty::Module::Pluggable->import(
         search_path => [
             Jifty->app_class("Action"),
@@ -85,12 +87,15 @@
     my $self   = shift;
     my $action = shift;
 
+    # Get the application class name
     my $base_path = Jifty->app_class;
 
+    # Return the class now if it's already fully qualified
     return $action
         if ($action =~ /^Jifty::/
         or $action =~ /^\Q$base_path\E::/);
 
+    # Otherwise qualify it
     return $base_path . "::Action::" . $action;
 }
 
@@ -109,6 +114,7 @@
     # Set up defaults
     my $app_actions = Jifty->app_class("Action");
 
+    # These are the default action limits
     $self->action_limits(
         [   { deny => 1, restriction => qr/.*/ },
             {   allow       => 1,
@@ -180,6 +186,7 @@
     my $polarity     = shift;
     my @restrictions = @_;
 
+    # Check the sanity of the polarity
     die "Polarity must be 'allow' or 'deny'"
         unless $polarity eq "allow"
         or $polarity     eq "deny";

Modified: jifty/trunk/lib/Jifty/Action/Autocomplete.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Autocomplete.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Autocomplete.pm	Sun Aug 12 16:23:46 2007
@@ -59,12 +59,15 @@
 sub take_action {
     my $self = shift;
 
+    # Load the arguments
     my $moniker = $self->argument_value('moniker');
     my $argument = $self->argument_value('argument');
 
+    # Load the action associated with the moniker
     my $request_action = Jifty->web->request->action($moniker);
     my $action = Jifty->web->new_action_from_request($request_action);
 
+    # Call the autocompleter for that action and argument and set the result
     my @completions = $action->_autocomplete_argument($argument);
     $self->result->content->{completions} = \@completions;
 

Modified: jifty/trunk/lib/Jifty/Action/Record.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Record.pm	Sun Aug 12 16:23:46 2007
@@ -69,26 +69,31 @@
     );
     my $self = $class->SUPER::new(%args);
 
-
+    # Load the associated record class just in case it hasn't been already
     my $record_class = $self->record_class;
     Jifty::Util->require($record_class);
 
+    # Die if we were given a record that wasn't a record
     if (ref $args{'record'} && !$args{'record'}->isa($record_class)) {
         Carp::confess($args{'record'}." isn't a $record_class");
     }
 
-
-
-    # Set up record
+    # If the record class is a record, use that
     if ( ref $record_class ) {
         $self->record($record_class);
         $self->argument_value( $_, $self->record->$_ )
             for @{ $self->record->_primary_keys };
-    } elsif ( ref $args{record} and $args{record}->isa($record_class) ) {
+    } 
+
+    # Otherwise, try to use the record passed to the constructor
+    elsif ( ref $args{record} and $args{record}->isa($record_class) ) {
         $self->record( $args{record} );
         $self->argument_value( $_, $self->record->$_ )
             for @{ $self->record->_primary_keys };
-    } else {
+    } 
+    
+    # Otherwise, try to use the arguments to load the record
+    else {
 
         # We could leave out the explicit current user, but it'd have
         # a slight negative performance implications
@@ -101,6 +106,7 @@
         }
         $self->record->load_by_primary_keys(%given_pks) if %given_pks;
     }
+
     return $self;
 }
 
@@ -132,195 +138,270 @@
 sub arguments {
     my $self = shift;
 
+    # Don't do this twice, it's too expensive
     return $self->_cached_arguments if $self->_cached_arguments;
 
-        my $field_info = {};
+    # Get ready to rumble
+    my $field_info = {};
+    my @fields = $self->possible_fields;
+
+    # we use a while here because we may be modifying the fields on the fly.
+    while ( my $field = shift @fields ) {
+        my $info = {};
+        my $column;
+
+        # The field is a column object, adjust to that
+        if ( ref $field ) {
+            $column = $field;
+            $field  = $column->name;
+        } 
+        
+        # Otherwise, we need to load the column info
+        else {
+            # Load teh column object and the record's current value
+            $column = $self->record->column($field);
+            my $current_value = $self->record->$field;
+
+            # If the current value is actually a pointer to
+            # another object, turn it into an ID
+            $current_value = $current_value->id
+                if blessed($current_value)
+                and $current_value->isa('Jifty::Record');
 
-        my @fields = $self->possible_fields;
-
-        # we use a while here because we may be modifying the fields
-        # on the fly.
-        while ( my $field = shift @fields ) {
-            my $info = {};
-            my $column;
-            if ( ref $field ) {
-                $column = $field;
-                $field  = $column->name;
-            } else {
-                $column = $self->record->column($field);
-                my $current_value = $self->record->$field;
-
-                # If the current value is actually a pointer to
-                # another object, dereference it
-                $current_value = $current_value->id
-                    if blessed($current_value)
-                    and $current_value->isa('Jifty::Record');
-                $info->{default_value} = $current_value if $self->record->id;
-            }
+            # The record's current value becomes the widget's default value
+            $info->{default_value} = $current_value if $self->record->id;
+        }
 
-            # 
-            #  if($field =~ /^(.*)_id$/ && $self->record->column($1)) {
-            #    $column = $self->record->column($1);
-            #}
-
-            ##################
-            my $render_as = $column->render_as;
-            $render_as = defined $render_as ? lc($render_as) : '';
-
-            if ( defined (my $valid_values = $column->valid_values)) {
-                $info->{valid_values} = $valid_values;
-                $info->{render_as}    = 'Select';
-            } elsif ( defined $column->type && $column->type =~ /^bool/i ) {
-                $info->{render_as} = 'Checkbox';
-            } elsif ( $render_as eq 'password' )
-            {
-                my $same = sub {
-                    my ( $self, $value ) = @_;
-                    if ( $value ne $self->argument_value($field) ) {
-                        return $self->validation_error(
-                            ($field.'_confirm') => _("The passwords you typed didn't match each other")
-                        );
-                    } else {
-                        return $self->validation_ok( $field . '_confirm' );
-                    }
-                };
+        # 
+        #  if($field =~ /^(.*)_id$/ && $self->record->column($1)) {
+        #    $column = $self->record->column($1);
+        #}
+
+    #########
+
+        # Canonicalize the render_as setting for the column
+        my $render_as = $column->render_as;
+        $render_as = defined $render_as ? lc($render_as) : '';
+
+        # Use a select box if we have a list of valid values
+        if ( defined (my $valid_values = $column->valid_values)) {
+            $info->{valid_values} = $valid_values;
+            $info->{render_as}    = 'Select';
+        } 
+        
+        # Use a checkbox for boolean fields
+        elsif ( defined $column->type && $column->type =~ /^bool/i ) {
+            $info->{render_as} = 'Checkbox';
+        } 
+        
+        # Add an additional _confirm field for passwords
+        elsif ( $render_as eq 'password' ) {
+
+            # Add a validator to make sure both fields match
+            my $same = sub {
+                my ( $self, $value ) = @_;
+                if ( $value ne $self->argument_value($field) ) {
+                    return $self->validation_error(
+                        ($field.'_confirm') => _("The passwords you typed didn't match each other")
+                    );
+                } else {
+                    return $self->validation_ok( $field . '_confirm' );
+                }
+            };
 
-                $field_info->{ $field . "_confirm" } = {
-                    render_as => 'Password',
-                    virtual => '1',
-                    validator => $same,
-                    sort_order => ($column->sort_order +.01),
-                    mandatory => 0
-                };
-            }
+            # Add the extra confirmation field
+            $field_info->{ $field . "_confirm" } = {
+                render_as => 'Password',
+                virtual => '1',
+                validator => $same,
+                sort_order => ($column->sort_order +.01),
+                mandatory => 0
+            };
+        }
 
-            elsif ( defined (my $refers_to = $column->refers_to) ) {
-                if ( UNIVERSAL::isa( $refers_to, 'Jifty::Record' ) ) {
-                    $info->{render_as} = $render_as || 'Select';
-                }
+        # Handle the X-to-one references
+        elsif ( defined (my $refers_to = $column->refers_to) ) {
 
-                if ( UNIVERSAL::isa( $refers_to, 'Jifty::Record' ) && $info->{render_as} eq 'Select' ) {
-                    my $collection = Jifty::Collection->new(
-                        record_class => $refers_to,
-                        current_user => $self->record->current_user
-                    );
-                    $collection->unlimit;
+            # Render as a select box unless they override
+            if ( UNIVERSAL::isa( $refers_to, 'Jifty::Record' ) ) {
+                $info->{render_as} = $render_as || 'Select';
+            }
 
-                    my $method = $refers_to->_brief_description();
+            # If it's a select box, setup the available values
+            if ( $info->{render_as} eq 'Select' ) {
 
-                    # FIXME: we should get value_from with the actualy refered by key
-                    $info->{valid_values} = [
-                        {   display_from => $refers_to->can($method) ? $method : "id",
-                            value_from => 'id',
-                            collection => $collection
-                        }
-                    ];
-                } else {
-                    # No need to generate arguments for
-                    # JDBI::Collections, as we can't do anything
-                    # useful with them yet, anyways.
-
-                    # However, if the column comes with a
-                    # "render_as", we can assume that the app
-                    # developer know what he/she is doing.
-                    # So we just render it as whatever specified.
+                # Get an unlimited collection
+                my $collection = Jifty::Collection->new(
+                    record_class => $refers_to,
+                    current_user => $self->record->current_user
+                );
+                $collection->unlimit;
+
+                # Fetch the _brief_description() method
+                my $method = $refers_to->_brief_description();
+
+                # FIXME: we should get value_from with the actualy refered by key
+
+                # Setup the list of valid values
+                $info->{valid_values} = [
+                    {   display_from => $refers_to->can($method) ? $method : "id",
+                        value_from => 'id',
+                        collection => $collection
+                    }
+                ];
+            } 
+            
+            # If the reference is X-to-many instead, skip it
+            else {
+                # No need to generate arguments for
+                # JDBI::Collections, as we can't do anything
+                # useful with them yet, anyways.
+
+                # However, if the column comes with a
+                # "render_as", we can assume that the app
+                # developer know what he/she is doing.
+                # So we just render it as whatever specified.
 
-                    next unless $render_as;
-                }
+                next unless $render_as;
             }
+        }
 
-	    #########
+    #########
 
+        # Figure out what the action's validation method would for this field
+        my $validate_method = "validate_" . $field;
 
-            # build up a validator sub if the column implements validation
-            # and we're not overriding it at the action level
-            my $validate_method = "validate_" . $field;
-
-            if ( $column->validator and not $self->can($validate_method) ) {
-                $info->{ajax_validates} = 1;
-                $info->{validator} = sub {
-                    my $self  = shift;
-                    my $value = shift;
-                    my ( $is_valid, $message )
-                        = &{ $column->validator }( $self->record, $value );
-
-                    if ($is_valid) {
-                        return $self->validation_ok($field);
-                    } else {
-                        unless ($message) {
-                            $self->log->error(
-                                qq{Schema validator for $field didn't explain why the value '$value' is invalid}
-                            );
-                        }
-                        return (
-                            $self->validation_error(
-                                $field => ($message || _("That doesn't look right, but I don't know why"))
-                            )
+        # Build up a validator sub if the column implements validation
+        # and we're not overriding it at the action level
+        if ( $column->validator and not $self->can($validate_method) ) {
+            $info->{ajax_validates} = 1;
+            $info->{validator} = sub {
+                my $self  = shift;
+                my $value = shift;
+
+                # Check the column's validator
+                my ( $is_valid, $message )
+                    = &{ $column->validator }( $self->record, $value );
+
+                # The validator reported valid, return OK
+                if ($is_valid) {
+                    return $self->validation_ok($field);
+                } 
+                
+                # Bad stuff, report an error
+                else {
+                    unless ($message) {
+                        $self->log->error(
+                            qq{Schema validator for $field didn't explain why the value '$value' is invalid}
                         );
                     }
-                };
-            }
-            my $autocomplete_method = "autocomplete_" . $field;
-
-            if ( $self->record->can($autocomplete_method) ) {
-                $info->{'autocompleter'} ||= sub {
-                    my ( $self, $value ) = @_;
-                    my %columns;
-                    $columns{$_} = $self->argument_value($_)
-                        for grep { $_ ne $field } $self->possible_fields;
-                    return $self->record->$autocomplete_method( $value,
-                        %columns );
-                };
-            }
-            elsif ($column->autocompleted) {
-                # Auto-generated autocompleter
-                $info->{'autocompleter'} ||= sub {
-                    my ( $self, $value ) = @_;
-
-                    my $collection = Jifty::Collection->new(
-                        record_class => $self->record_class,
-                        current_user => $self->record->current_user
+                    return (
+                        $self->validation_error(
+                            $field => ($message || _("That doesn't look right, but I don't know why"))
+                        )
                     );
+                }
+            };
+        }
 
-                    $collection->unlimit;
-                    $collection->rows_per_page(20);
-                    $collection->limit(column => $field, value => $value, operator => 'STARTSWITH', entry_aggregator => 'AND') if length($value);
-                    $collection->limit(column => $field, value => 'NULL', operator => 'IS NOT', entry_aggregator => 'AND');
-                    $collection->limit(column => $field, value => '', operator => '!=', entry_aggregator => 'AND');
-                    $collection->columns('id', $field);
-                    $collection->order_by(column => $field);
-                    $collection->group_by(column => $field);
-
-                    my @choices;
-                    while (my $record = $collection->next) {
-                        push @choices, $record->$field;
-                    }
-                    return @choices;
-                };
-            }
+        # What would the autocomplete method be for this column in the record
+        my $autocomplete_method = "autocomplete_" . $field;
 
-            if ( $self->record->has_canonicalizer_for_column($field) ) {
-                $info->{'ajax_canonicalizes'} = 1;
-                $info->{'canonicalizer'} ||= sub {
-                    my ( $self, $value ) = @_;
-                    return $self->record->run_canonicalization_for_column(column => $field, value => $value);
-                };
-            } elsif ( $render_as eq 'date')
-            {
-                $info->{'ajax_canonicalizes'} = 1;
-            }
+        # Set the autocompleter if the record has one
+        if ( $self->record->can($autocomplete_method) ) {
+            $info->{'autocompleter'} ||= sub {
+                my ( $self, $value ) = @_;
+                my %columns;
+                $columns{$_} = $self->argument_value($_)
+                    for grep { $_ ne $field } $self->possible_fields;
+                return $self->record->$autocomplete_method( $value,
+                    %columns );
+            };
+        }
 
-            # If we're hand-coding a render_as, hints or label, let's use it.
-            for (qw(render_as label hints max_length mandatory sort_order container)) {
+        # The column requests an automagically generated autocompleter, which
+        # is baed upon the values available in the field
+        elsif ($column->autocompleted) {
+            # Auto-generated autocompleter
+            $info->{'autocompleter'} ||= sub {
+                my ( $self, $value ) = @_;
+
+                my $collection = Jifty::Collection->new(
+                    record_class => $self->record_class,
+                    current_user => $self->record->current_user
+                );
+
+                # Return the first 20 matches...
+                $collection->unlimit;
+                $collection->rows_per_page(20);
+
+                # ...that start with the value typed...
+                if (length $value) {
+                    $collection->limit(
+                        column   => $field, 
+                        value    => $value, 
+                        operator => 'STARTSWITH', 
+                        entry_aggregator => 'AND'
+                    );
+                }
 
-                if ( defined (my $val = $column->$_) ) {
-                    $info->{$_} = $val;
+                # ...but are not NULL...
+                $collection->limit(
+                    column => $field, 
+                    value => 'NULL', 
+                    operator => 'IS NOT', 
+                    entry_aggregator => 'AND'
+                );
+
+                # ...and are not empty.
+                $collection->limit(
+                    column => $field, 
+                    value => '', 
+                    operator => '!=', 
+                    entry_aggregator => 'AND'
+                );
+
+                # Optimize the query a little bit
+                $collection->columns('id', $field);
+                $collection->order_by(column => $field);
+                $collection->group_by(column => $field);
+
+                # Set up the list of choices to return
+                my @choices;
+                while (my $record = $collection->next) {
+                    push @choices, $record->$field;
                 }
+                return @choices;
+            };
+        }
+
+        # Add a canonicalizer for the column if the record provides one
+        if ( $self->record->has_canonicalizer_for_column($field) ) {
+            $info->{'ajax_canonicalizes'} = 1;
+            $info->{'canonicalizer'} ||= sub {
+                my ( $self, $value ) = @_;
+                return $self->record->run_canonicalization_for_column(column => $field, value => $value);
+            };
+        } 
+        
+        # Otherwise, if it's a date, we have a built-in canonicalizer for that
+        elsif ( $render_as eq 'date') {
+            $info->{'ajax_canonicalizes'} = 1;
+        }
+
+        # If we're hand-coding a render_as, hints or label, let's use it.
+        for (qw(render_as label hints max_length mandatory sort_order container)) {
+
+            if ( defined (my $val = $column->$_) ) {
+                $info->{$_} = $val;
             }
-            $field_info->{$field} = $info;
         }
+        $field_info->{$field} = $info;
+    }
 
+    # After all that, use the schema { ... } params for the final bits
     if ($self->can('PARAMS')) {
+
         # User-defined declarative schema fields can override default ones here
         my $params = $self->PARAMS;
 
@@ -335,9 +416,13 @@
             }
         }
 
+        # Cache the result of merging the Jifty::Action::Record and schema
+        # parameters
         use Jifty::Param::Schema ();
         $self->_cached_arguments(Jifty::Param::Schema::merge_params($field_info, $params));
     }
+
+    # No schema { ... } block, so just use what we generated
     else {
         $self->_cached_arguments($field_info);
     }
@@ -374,6 +459,7 @@
 sub _setup_event_before_action {
     my $self = shift;
 
+    # Setup the information regarding the event for later publishing
     my $event_info = {};
     $event_info->{as_hash_before} = $self->record->as_hash;
     $event_info->{record_id} = $self->record->id;
@@ -387,10 +473,13 @@
 sub _setup_event_after_action {
     my $self = shift;
     my $event_info = shift;
+
+    # Add a few more bits about the result
     $event_info->{result} = $self->result;    
     $event_info->{timestamp} = time(); 
     $event_info->{as_hash_after} = $self->record->as_hash;
 
+    # Publish the event
     my $event_class = $event_info->{'record_class'};
     $event_class =~ s/::Model::/::Event::Model::/g;
     Jifty::Util->require($event_class);

Modified: jifty/trunk/lib/Jifty/Action/Record/Create.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Create.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Record/Create.pm	Sun Aug 12 16:23:46 2007
@@ -30,6 +30,7 @@
 sub arguments {
     my $self = shift;
     
+    # Add default values to the arguments configured by Jifty::Action::Record
     my $args = $self->SUPER::arguments;
     for my $arg (keys %{$args}) {
         my $column = $self->record->column($arg) or next;
@@ -56,13 +57,18 @@
     my $self   = shift;
     my $record = $self->record;
 
+    # Build the event to be fired later
     my $event_info = $self->_setup_event_before_action();
     
-    
     my %values;
-    # Virtual arguments aren't really ever backed by data structures. they're added by jifty for things like confirmations
+
+    # Iterate through all that are set, except for the virtual ones
     for (grep { defined $self->argument_value($_) && !$self->arguments->{$_}->{virtual} } $self->argument_names) {
+
+        # Prepare the hash to pass to create for each argument
         $values{$_} = $self->argument_value($_);
+
+        # Handle file uploads
         if (ref $values{$_} eq "Fh") { # CGI.pm's "lightweight filehandle class"
             local $/;
             my $fh = $values{$_};
@@ -70,24 +76,31 @@
             $values{$_} = scalar <$fh>;
         }
     }
+
+    # Attempt creating the record
     my $id;
     my $msg = $record->create(%values);
-    # Handle errors?
-    if (ref($msg)) { # If it's a Class::ReturnValue
+
+    # Convert Class::ReturnValue to an id and message
+    if (ref($msg)) {
         ($id,$msg) = $msg->as_array;
     }
 
+    # If ID is 0/undef, the record didn't create, so we fail
     if (! $record->id ) {
         $self->log->warn(_("Create of %1 failed: %2", ref($record), $msg));
         $self->result->error($msg || _("An error occurred.  Try again later"));
     }
 
+    # No errors! Report success
     else { 
         # Return the id that we created
         $self->result->content(id => $self->record->id);
         $self->report_success if  not $self->result->failure;
     }
-    $self->_setup_event_after_action($event_info) ;
+
+    # Publish the event, noting success or failure
+    $self->_setup_event_after_action($event_info);
 
     return ($self->record->id);
 }
@@ -105,5 +118,15 @@
     $self->result->message(_("Created"))
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Action::Record>, L<Jifty::Record>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
 
 1;

Modified: jifty/trunk/lib/Jifty/Action/Record/Delete.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Delete.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Record/Delete.pm	Sun Aug 12 16:23:46 2007
@@ -33,6 +33,7 @@
     my $self = shift;
     my $arguments = {};
 
+    # Mark the primary key for use in the constructor and not rendered
     for my $pk (@{ $self->record->_primary_keys }) {
         $arguments->{$pk}{'constructor'} = 1;
         # XXX TODO IS THERE A BETTER WAY TO NOT RENDER AN ITEM IN arguments
@@ -52,12 +53,17 @@
 sub take_action {
     my $self = shift;
 
+    # Setup the event info for later publishing
     my $event_info = $self->_setup_event_before_action();
 
+    # Delete the record and return an error if delete fails
     my ( $val, $msg ) = $self->record->delete;
     $self->result->error($msg) if not $val and $msg;
 
+    # Otherwise, we seem to have succeeded, report that
     $self->report_success if not $self->result->failure;
+
+    # Publish the event
     $self->_setup_event_after_action($event_info);
 
     return 1;
@@ -76,4 +82,15 @@
     $self->result->message(_("Deleted"))
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Action::Record>, L<Jifty::Record>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
+
 1;

Modified: jifty/trunk/lib/Jifty/Action/Record/Search.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Search.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Record/Search.pm	Sun Aug 12 16:23:46 2007
@@ -51,27 +51,31 @@
 
 sub arguments {
     my $self = shift;
+
+    # The args processing here is involved, so only calculate them once
     return $self->_cached_arguments if $self->_cached_arguments;
     
+    # Iterate through all the arguments setup by Jifty::Action::Record
     my $args = $self->SUPER::arguments;
     for my $field (keys %$args) {
         
+        # Figure out what information we know about the field
         my $info = $args->{$field};
-
         my $column = $self->record->column($field);
-        # First, modify the ``exact match'' search field (same name as
-        # the original argument)
 
+        # We don't care about validation and mandatories on search
         delete $info->{validator};
         delete $info->{mandatory};
 
-        if($info->{valid_values}) {
+        # If the column has a set of valid values, deal with those
+        if ($info->{valid_values}) {
             my $valid_values = $info->{valid_values};
 
+            # Canonicalize the valid values
             local $@;
             $info->{valid_values} = $valid_values = (eval { [ @$valid_values ] } || [$valid_values]);
 
-            # For radio display, display an "any" label as empty choices looks weird
+            # For radio display, display an "any" label (empty looks weird)
             if (lc $info->{render_as} eq 'radio') {
                 if (@$valid_values > 1) {
                     unshift @$valid_values, { display => _("(any)"), value => '' };
@@ -82,53 +86,98 @@
                     $info->{default_value} ||= $valid_values->[0];
                 }
             }
+
+            # If not radio, add a blank options
             else {
                 unshift @$valid_values, "";
             }
         }
 
+        # You can't search passwords, so remove the fields
         if(lc $info->{'render_as'} eq 'password') {
             delete $args->{$field};
             next;
         }
 
+        # Warn if we have a search field without an actual column
         warn "No column for: $field" unless($column);
         
+        # Drop out X-to-many columns from the search
         if(defined(my $refers_to = $column->refers_to)) {
             delete $args->{$field}
              if UNIVERSAL::isa($refers_to, 'Jifty::Collection');
         }
+
         # XXX TODO: What about booleans? Checkbox doesn't quite work,
         # since there are three choices: yes, no, either.
 
         # Magic _id refers_to columns
         next if($field =~ /^(.*)_id$/ && $self->record->column($1));
 
+        # Setup the field label for the comparison operator selection
         my $label = $info->{label} || $field;
+
+        # Add the "X is not" operator
         $args->{"${field}_not"} = { %$info, label => _("%1 is not", $label) };
+
+        # The operators available depend on the type
         my $type = lc($column->type);
+
+        # Add operators available for text fields
         if($type =~ /(?:text|char)/) {
+
+            # Show a text entry box (rather than a textarea)
             $info->{render_as} = 'text';
+
+            # Add the "X contains" operator
             $args->{"${field}_contains"} = { %$info, label => _("%1 contains", $label) };
+
+            # Add the "X lacks" operator (i.e., opposite of "X contains")
             $args->{"${field}_lacks"} = { %$info, label => _("%1 lacks", $label) };
-        } elsif($type =~ /(?:date|time)/) {
+        } 
+        
+        # Handle date, datetime, time, and timestamp fields
+        elsif($type =~ /(?:date|time)/) {
+
+            # Add the "X after" date/time operation
             $args->{"${field}_after"} = { %$info, label => _("%1 after", $label) };
+
+            # Add the "X before" date/time operation
             $args->{"${field}_before"} = { %$info, label => _("%1 before", $label) };
+
+            # Add the "X since" date/time operation
             $args->{"${field}_since"} = { %$info, label => _("%1 since", $label) };
+
+            # Add the "X until" date/time operation
             $args->{"${field}_until"} = { %$info, label => _("%1 until", $label) };
-        } elsif(    $type =~ /(?:int|float|double|decimal|numeric)/
+        } 
+        
+        # Handle number fields
+        elsif(    $type =~ /(?:int|float|double|decimal|numeric)/
                 && !$column->refers_to) {
+
+            # Add the "X greater than" operation
             $args->{"${field}_gt"} = { %$info, label => _("%1 greater than", $label) };
+
+            # Add the "X less than" operation
             $args->{"${field}_lt"} = { %$info, label => _("%1 less than", $label) };
+
+            # Add the "X greater than or equal to" operation
             $args->{"${field}_ge"} = { %$info, label => _("%1 greater or equal to", $label) };
+
+            # Add the "X less than or equal to" operation
             $args->{"${field}_le"} = { %$info, label => _("%1 less or equal to", $label) };
+
+            # Add the "X is whatever the heck I say it is" operation
             $args->{"${field}_dwim"} = { %$info, hints => _('!=>< allowed') };
         }
     }
 
+    # Add generic contains/lacks search boxes for all fields
     $args->{contains} = { type => 'text', label => _('Any field contains') };
     $args->{lacks} = { type => 'text', label => _('No field contains') };
 
+    # Cache the results so we don't have to do THAT again
     return $self->_cached_arguments($args);
 }
 
@@ -145,30 +194,43 @@
 sub take_action {
     my $self = shift;
 
+    # Create a generic collection for our record class
     my $collection = Jifty::Collection->new(
         record_class => $self->record_class,
         current_user => $self->record->current_user
     );
 
+    # Start with an unlimited collection
     $collection->unlimit;
 
+    # For each field, process the limits
     for my $field (grep {$self->has_argument($_)} $self->argument_names) {
+
+        # We process contains last, skip it here
         next if $field eq 'contains';
+
+        # Get the value set on the field
         my $value = $self->argument_value($field);
         
+        # Load the column this field belongs to
         my $column = $self->record->column($field);
         my $op = undef;
         
+        # A comparison or substring search rather than an exact match?
         if (!$column) {
+
             # If we don't have a column, this is a comparison or
             # substring search. Skip undef values for those, since
             # NULL makes no sense.
             next unless defined($value);
             next if $value =~ /^\s*$/;
 
+            # Decode the field_op name
             if ($field =~ m{^(.*)_([[:alpha:]]+)$}) {
                 $field = $1;
                 $op = $2;
+
+                # Convert each operator into limit operators
                 if($op eq 'not') {
                     $op = '!=';
                 } elsif($op eq 'contains') {
@@ -193,41 +255,60 @@
                         $op = '=' if $op eq '==';
                     }
                 }
-            } else {
+            } 
+            
+            # Doesn't look like a field_op, skip it
+            else {
                 next;
             }
         }
         
-        if(defined($value)) {
-            next if $value =~ /^\s*$/;
+        # Now, add the limit if we have a value set
+        if (defined($value)) {
+            next if $value =~ /^\s*$/; # skip blank values!
            
+            # Allow != and NOT LIKE to match NULL columns
             if ($op && $op =~ /^(?:!=|NOT LIKE)$/) {
-                $collection->limit( column   => $field, value    => $value, operator => $op || "=", entry_aggregator => 'OR', $op ? (case_sensitive => 0) : (),);
-                $collection->limit( column   => $field, value    => 'NULL', operator => 'IS');
-            } else { 
-
+                $collection->limit( 
+                    column   => $field, 
+                    value    => $value, 
+                    operator => $op,
+                    entry_aggregator => 'OR', 
+                    case_sensitive => 0,
+                );
+                $collection->limit( 
+                    column   => $field, 
+                    value    => 'NULL', 
+                    operator => 'IS',
+                );
+            } 
             
-            $collection->limit(
-                column   => $field,
-                value    => $value,
-                operator => $op || "=",
-                entry_aggregator => 'AND',
-                $op ? (case_sensitive => 0) : (),
-               );
-
+            # For any others, just the facts please
+            else { 
+                $collection->limit(
+                    column   => $field,
+                    value    => $value,
+                    operator => $op || "=",
+                    entry_aggregator => 'AND',
+                    $op ? (case_sensitive => 0) : (),
+                );
             } 
+        } 
 
-
-        } else {
+        # The value is not defined at all, so expect a NULL
+        else {
             $collection->limit(
                 column   => $field,
                 value    => 'NULL',
                 operator => 'IS'
-               );
+            );
         }
     }
 
+    # Handle the general contains last
     if($self->has_argument('contains')) {
+
+        # See if any column contains the text described
         my $any = $self->argument_value('contains');
         for my $col ($self->record->columns) {
             if($col->type =~ /(?:text|varchar)/) {
@@ -240,6 +321,7 @@
         }
     }
 
+    # Add the limited collection to the results
     $self->result->content(search => $collection);
     $self->result->success;
 }
@@ -248,6 +330,11 @@
 
 L<Jifty::Action::Record>, L<Jifty::Collection>
 
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
 =cut
 
 1;

Modified: jifty/trunk/lib/Jifty/Action/Record/Update.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Record/Update.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Record/Update.pm	Sun Aug 12 16:23:46 2007
@@ -33,12 +33,14 @@
     my $self = shift;
     my $arguments = $self->SUPER::arguments(@_);
 
+    # Mark read-only columns for read-only display
     for my $column ( $self->record->columns ) {
         if ( not $column->writable and $column->readable ) {
             $arguments->{$column->name}{'render_mode'} = 'read';
         }
     }
 
+    # Add the primary keys to constructors and make them mandatory
     for my $pk (@{ $self->record->_primary_keys }) {
         $arguments->{$pk}{'constructor'} = 1;
         $arguments->{$pk}{'mandatory'} = 1;
@@ -64,6 +66,7 @@
 sub _validate_arguments {
     my $self = shift;
 
+    # Only validate the arguments given
     $self->_validate_argument($_) for grep {
         $self->has_argument($_)
             or $self->arguments->{$_}->{constructor}
@@ -84,12 +87,16 @@
     my $self = shift;
     my $changed = 0;
 
+    # Prepare the event for later publishing
     my $event_info = $self->_setup_event_before_action();
 
+    # Iterate through all the possible arguments
     for my $field ( $self->argument_names ) {
+
         # Skip values that weren't submitted
         next unless $self->has_argument($field);
 
+        # Load the column object for the field
         my $column = $self->record->column($field);
 
         # Skip nonexistent fields
@@ -108,6 +115,7 @@
         next if lc $self->arguments->{$field}{render_as} eq "upload"
           and (not defined $value or not ref $value);
 
+        # Handle file uploads
         if (ref $value eq "Fh") { # CGI.pm's "lightweight filehandle class"
             local $/;
             binmode $value;
@@ -127,17 +135,21 @@
         next if (  (not defined $old or not length $old)
                     and ( not defined $value or not length $value ));
 
+        # Calculate the name of the setter and set; asplode on failure
         my $setter = "set_$field";
         my ( $val, $msg ) = $self->record->$setter( $value );
         $self->result->field_error($field, $msg)
           if not $val and $msg;
 
+        # Remember that we changed something (if we did)
         $changed = 1 if $val;
     }
 
+    # Report success if there's a change and no error, otherwise say nah-thing
     $self->report_success
       if $changed and not $self->result->failure;
 
+    # Publish the update event
     $self->_setup_event_after_action($event_info);
 
     return 1;
@@ -156,4 +168,15 @@
     $self->result->message(_("Updated"))
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Action::Record>, L<Jifty::Record>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
+
 1;

Modified: jifty/trunk/lib/Jifty/Action/Redirect.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Action/Redirect.pm	(original)
+++ jifty/trunk/lib/Jifty/Action/Redirect.pm	Sun Aug 12 16:23:46 2007
@@ -5,11 +5,26 @@
 
 Jifty::Action::Redirect - Redirect the browser
 
+=head1 SYNOPSIS
+
+  Jifty->web->new_action(
+      class => 'Redirect',
+      arguments => {
+          url => '/my/other/page',
+      },
+  )->run;
+
+=head1 DESCRIPTION
+
+Given a URL, this action forces Jifty to perform a redirect to thast URL after processing the rest of the request.
+
 =cut
 
 package Jifty::Action::Redirect;
 use base qw/Jifty::Action/;
 
+=head1 METHODS
+
 =head2 new
 
 By default, redirect actions happen as late as possible in the run
@@ -35,10 +50,9 @@
 =cut
 
 sub arguments {
-        {
-            url => { constructor => 1 },
-        }
-
+    {
+        url => { constructor => 1 },
+    }
 }
 
 =head2 take_action
@@ -52,15 +66,32 @@
 
 sub take_action {
     my $self = shift;
+
+    # Return now if the URL is not set
     return 1 unless ($self->argument_value('url'));
+
+    # Return now if the response is already sent (i.e., too late to redirect)
     return 0 unless Jifty->web->response->success;
 
+    # Find the URL to redirect to
     my $page = $self->argument_value('url');
 
+    # Set the next page and force the redirect
     Jifty->web->next_page($page);
     Jifty->web->force_redirect(1);
     return 1;
 }
 
+=head1 SEE ALSO
+
+L<Jifty::Action>, L<Jifty::Web/next_page>, L<Jity::Web/force_redirect>
+
+=head1 LICENSE
+
+Jifty is Copyright 2005-2007 Best Practical Solutions, LLC.
+Jifty is distributed under the same terms as Perl itself.
+
+=cut
+
 1;
 


More information about the Jifty-commit mailing list