[Jifty-commit] r6053 - in jifty/trunk/plugins/Tag: . lib lib/Jifty lib/Jifty/Plugin lib/Jifty/Plugin/Tag lib/Jifty/Plugin/Tag/Mixin lib/Jifty/Plugin/Tag/Model t t/TestApp-Plugin-Tag t/TestApp-Plugin-Tag/bin t/TestApp-Plugin-Tag/etc t/TestApp-Plugin-Tag/lib t/TestApp-Plugin-Tag/lib/TestApp/Plugin t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model t/TestApp-Plugin-Tag/t

Jifty commits jifty-commit at lists.jifty.org
Mon Dec 1 02:20:34 EST 2008


Author: ruz
Date: Mon Dec  1 02:20:34 2008
New Revision: 6053

Added:
   jifty/trunk/plugins/Tag/
   jifty/trunk/plugins/Tag/META.yml
   jifty/trunk/plugins/Tag/Makefile.PL
   jifty/trunk/plugins/Tag/lib/
   jifty/trunk/plugins/Tag/lib/Jifty/
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag.pm
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/Collection.pm
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/Model.pm
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Model/
   jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Model/Tag.pm
   jifty/trunk/plugins/Tag/t/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/Makefile.PL
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/bin/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/bin/jifty   (contents, props changed)
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/etc/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/etc/config.yml
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/Node.pm
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/NodeCollection.pm
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/t/
   jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/t/00-basics.t

Log:
* commit alpha version of Tag plugin

Added: jifty/trunk/plugins/Tag/META.yml
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/META.yml	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,18 @@
+---
+abstract: ~
+author:
+  - 'Ruslan Zakirov <Ruslan.Zakirov at gmail.com>'
+distribution_type: module
+generated_by: 'Module::Install version 0.77'
+license: unknown
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: Jifty-Plugin-Tag
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  perl: 5.8.0
+version: undef

Added: jifty/trunk/plugins/Tag/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/Makefile.PL	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,8 @@
+use inc::Module::Install 0.46;
+
+name        'Jifty-Plugin-Tag';
+all_from    'lib/Jifty/Plugin/Tag.pm';
+
+tests(join ' ', qw(t/*.t t/*/*.t t/*/*/*.t t/*/*/*/*.t));
+
+WriteAll();

Added: jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag.pm	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,45 @@
+use 5.008;
+use utf8;
+use strict;
+use warnings;
+
+package Jifty::Plugin::Tag;
+use base qw/Jifty::Plugin/;
+
+=head1 NAME
+
+Jifty::Plugin::Tag - simple implementation of Tag model for Jifty apps
+
+=head1 DESCRIPTION
+
+This plugin in its early stage. You can try it at your own risk.
+
+=head1 METHODS
+
+=head2 init
+
+Called during initialization.
+
+=cut
+
+sub init {
+    my $self = shift;
+
+    Jifty::DBI::Record->add_trigger(
+        name     => "after_delete",
+        callback => sub {
+            my $record = shift;
+            if (my $method = $record->can('delete_all_tags')) {
+                $method->($record);
+            }
+        },
+    );
+}
+
+=head1 AUTHOR
+
+Ruslan Zakirov E<lt>Ruslan.Zakirov at gmail.comE<gt>
+
+=cut
+
+1;

Added: jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/Collection.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/Collection.pm	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,77 @@
+use 5.008;
+use utf8;
+use strict;
+use warnings;
+
+package Jifty::Plugin::Tag::Mixin::Collection;
+
+use base qw(Exporter);
+
+our @EXPORT = qw(limit_by_tag);
+
+sub import {
+    my $self = shift;
+    my $caller = caller;
+    $self->export_to_level(1,undef);
+}
+
+my $tags_table = sub {
+    return Jifty->app_class('Model', 'Tag')->table;
+};
+
+sub limit_by_tag {
+    my $self  = shift;
+    my $value = shift;
+    my %opt   = @_;
+
+    $value = '' unless defined $value;
+    my $not = $value =~ s/^!//;
+
+    my $alias = $self->join(
+        type    => 'LEFT',
+        column1 => 'id',
+        table2  => $tags_table->(),
+        column2 => 'record',
+    );
+    $self->limit(
+        leftjoin => $alias,
+        alias => $alias,
+        column => 'model',
+        value => ($self->record_class =~ /([^:]+)$/)[0],
+    );
+
+    if ( length $value ) {
+        unless ( $not ) {
+            $self->limit(
+                %opt,
+                alias => $alias,
+                column => 'value',
+                value => $value,
+            );
+        } else {
+            $self->limit(
+                leftjoin => $alias,
+                alias => $alias,
+                column => 'value',
+                value => $value,
+            );
+            $self->limit(
+                %opt,
+                alias => $alias,
+                column => 'record',
+                operator => 'IS',
+                value => 'NULL',
+            );
+        }
+    } else {
+        $self->limit(
+            %opt,
+            alias => $alias,
+            column => 'record',
+            operator => $not? 'IS NOT': 'IS',
+            value => 'NULL',
+        );
+    }
+}
+
+1;

Added: jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/Model.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Mixin/Model.pm	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,77 @@
+use 5.008;
+use utf8;
+use strict;
+use warnings;
+
+package Jifty::Plugin::Tag::Mixin::Model;
+use base 'Jifty::DBI::Record::Plugin';
+
+use Jifty::DBI::Schema;
+use Jifty::Plugin::Tag::Record schema {};
+
+our @EXPORT = qw(tags has_tag add_tag delete_tag delete_all_tags);
+
+sub tags {
+    my $self = shift;
+
+    my $res = Jifty->app_class('Model', 'TagCollection')->new;
+    my ($model) = (ref($self) =~ /([^:]+)$/);
+    $res->limit( column => 'model', value => $model, case_sensetive => 0 );
+    $res->limit( column => 'record', value => $self->id );
+    return $res;
+}
+
+sub add_tag {
+    my $self = shift;
+    my $value = shift;
+    my %opt = @_;
+
+    if ( !$opt{'no_check'} && $self->has_tag($value) ) {
+        return (0, _("Record already tagged with '%1'", $value))
+            unless $opt{'exist_ok'};
+        return (1, _('Added a tag'));
+    }
+    my $tag = Jifty->app_class('Model', 'Tag')->new;
+    return $tag->create( record => $self, value => $value );
+}
+
+sub delete_tag {
+    my $self = shift;
+    my $value = shift;
+    my %opt = @_;
+
+    my $tag = $self->has_tag( $value );
+    unless ( $tag ) {
+        return (0, _("Record has no tag '%1'", $value))
+            unless $opt{'not_exist_ok'};
+        return (1, _("Deleted a tag"));
+    }
+    return $tag->delete;
+}
+
+sub delete_all_tags {
+    my $self = shift;
+
+    my $tags = $self->tags;
+    while ( my $t = $tags->next ) {
+        my ($s, $msg) = $t->delete;
+        return ($s, $msg) unless $s;
+    }
+    return (1, _('Deleted all tags of the record'));
+}
+
+sub has_tag {
+    my $self = shift;
+    my $value = shift;
+
+    my $res = Jifty->app_class('Model', 'Tag')->new;
+    $res->load_by_cols(
+        model  => (ref($self) =~ /([^:]+)$/)[0],
+        record => $self->id,
+        value  => $value,
+    );
+    return undef unless $res->id;
+    return $res;
+}
+
+1;

Added: jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Model/Tag.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/lib/Jifty/Plugin/Tag/Model/Tag.pm	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,97 @@
+use 5.008;
+use utf8;
+use strict;
+use warnings;
+
+package Jifty::Plugin::Tag::Model::Tag;
+use Jifty::DBI::Schema;
+
+use Scalar::Util qw(blessed);
+
+=head1 NAME
+
+Jifty::Plugin::Tag::Model::Tag - tags attached to anything
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+This model is the repository for all comments in your application, if you use the L<Jifty::Plugin::Comment> plugin.
+
+=head1 SCHEMA
+
+=cut
+
+use Jifty::Record schema {
+    column model =>
+        type is 'varchar(32)',
+        label is _('Model name'),
+        is mandatory,
+        ;
+    column record =>
+        type is 'int',
+        label is _('ID'),
+        is mandatory,
+        ;
+    column value =>
+        type is 'varchar(32)',
+        label is _('Value'),
+        is mandatory,
+        ;
+};
+
+sub create {
+    my $self = shift;
+    my %args = @_;
+    if ( my $class = blessed $args{'record'} ) {
+        $args{'model'} = ($class =~ /([^:]+)$/)[0];
+        $args{'record'} = $args{'record'}->id;
+    }
+    return $self->SUPER::create( %args );
+}
+
+sub canonicalize_value {
+    my ($self, $value) = @_;
+    return $value unless defined $value;
+
+    $value =~ s/^\s+//;
+    $value =~ s/\s+$//;
+    $value =~ s/\s/ /g;
+    return $value;
+}
+
+sub validate_value {
+    my ($self, $value, $create_args) = @_;
+
+    return (0, _('Characters [!,"] are not allowed in tags'))
+        if $value =~ /[!,"]/;
+
+    return 1;
+}
+
+sub record {
+    my $self = shift;
+    return Jifty->app_class('Model', $self->model)->load( $self->_value('record', @_) );
+}
+
+sub record_id {
+    return (shift)->_value('record', @_);
+}
+
+sub used_by {
+    my $self = shift;
+    my %opt = @_;
+
+    my $this_model   = $self->model;
+    my $target_model = $opt{'model'} || $this_model;
+
+    my $res = Jifty->app_class('Model', $target_model.'Collection')->new;
+    $res->limit( column => 'id', operator => '!=', value => $self->record_id )
+        if $this_model eq $target_model && !$opt{'include_this'};
+    my $alias = $res->join( column1 => 'id', table2 => $self->table, column2 => 'record' );
+    $res->limit( leftjoin=> $alias, alias => $alias, column => 'model', value => $target_model );
+    $res->limit( alias => $alias, column => 'value', value => $self->value );
+    return $res;
+}
+
+1;

Added: jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/Makefile.PL	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,7 @@
+use inc::Module::Install;
+
+name        'TestApp::Plugin::Tag';
+version     '0.01';
+requires    'Jifty' => '0.80913';
+
+WriteAll;

Added: jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/bin/jifty
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/bin/jifty	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,16 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use UNIVERSAL::require;
+
+BEGIN {
+    Jifty::Util->require or die $UNIVERSAL::require::ERROR;
+    my $root = Jifty::Util->app_root;
+    unshift @INC, "$root/lib" if ($root);
+}
+
+use Jifty;
+use Jifty::Script;
+
+local $SIG{INT} = sub { warn "Stopped\n"; exit; };
+Jifty::Script->dispatch();

Added: jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/etc/config.yml
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/etc/config.yml	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,77 @@
+--- 
+framework: 
+  AdminMode: 1
+  ApplicationClass: TestApp::Plugin::Tag
+  ApplicationName: TestApp-Plugin-Tag
+  ApplicationUUID: 9D293C06-BD00-11DD-96D9-F645A48C7FB7
+  ConfigFileVersion: 4
+  Database: 
+    AutoUpgrade: 1
+    CheckSchema: 1
+    Database: testapp_plugin_tag
+    Driver: SQLite
+    Host: localhost
+    Password: ''
+    RecordBaseClass: Jifty::DBI::Record::Cachable
+    User: ''
+    Version: 0.0.1
+  DevelMode: 1
+  L10N: 
+    PoDir: share/po
+  LogLevel: INFO
+  Mailer: Sendmail
+  MailerArgs: []
+
+  Plugins: 
+    - 
+      LetMe: {}
+
+    - 
+      SkeletonApp: {}
+
+    - 
+      REST: {}
+
+    - 
+      Halo: {}
+
+    - 
+      ErrorTemplates: {}
+
+    - 
+      OnlineDocs: {}
+
+    - 
+      CompressedCSSandJS: {}
+
+    - 
+      AdminUI: {}
+
+    - 
+      Tag: {}
+
+  PubSub: 
+    Backend: Memcached
+    Enable: ~
+  SkipAccessControl: 0
+  TemplateClass: TestApp::Plugin::Tag::View
+  View: 
+    FallbackHandler: Jifty::View::Mason::Handler
+    Handlers: 
+      - Jifty::View::Static::Handler
+      - Jifty::View::Declare::Handler
+      - Jifty::View::Mason::Handler
+  Web: 
+    BaseURL: http://localhost
+    DataDir: var/mason
+    Globals: []
+
+    MasonConfig: 
+      autoflush: 0
+      default_escape_flags: h
+      error_format: text
+      error_mode: fatal
+    Port: 8888
+    ServeStaticFiles: 1
+    StaticRoot: share/web/static
+    TemplateRoot: share/web/templates

Added: jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/Node.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/Node.pm	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::Tag::Model::Node;
+use Jifty::DBI::Schema;
+
+use TestApp::Plugin::Tag::Record schema {
+    column type =>
+        type is 'varchar(32)',
+        is mandatory,
+        valid are qw(memo article news),
+    ;
+    column subject =>
+        type is 'varchar(255)',
+    ;
+};
+
+use Jifty::Plugin::Tag::Mixin::Model;
+
+# Your model-specific methods go here.
+
+1;

Added: jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/NodeCollection.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/lib/TestApp/Plugin/Tag/Model/NodeCollection.pm	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+
+package TestApp::Plugin::Tag::Model::NodeCollection;
+use base 'Jifty::Collection';
+
+use Jifty::Plugin::Tag::Mixin::Collection;
+
+1;

Added: jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/t/00-basics.t
==============================================================================
--- (empty file)
+++ jifty/trunk/plugins/Tag/t/TestApp-Plugin-Tag/t/00-basics.t	Mon Dec  1 02:20:34 2008
@@ -0,0 +1,161 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use Jifty::Test::Dist tests => 12;
+
+sub flush_nodes {
+    my $nodes = Jifty->app_class('Model', 'NodeCollection')->new;
+    $nodes->unlimit;
+    $_->delete foreach @$nodes;
+}
+
+{
+    my $node = Jifty->app_class('Model', 'Node')->new;
+    my ($ok, $msg) = $node->create(
+        type    => 'memo',
+        subject => 'Cake',
+    );
+    ok $ok, $msg;
+
+    ok !$node->has_tag('tag'), 'has no tag';
+
+    ($ok, $msg) = $node->add_tag('tag');
+    ok $ok, $msg;
+    ($ok, $msg) = $node->add_tag('tag');
+    ok !$ok, $msg;
+    ($ok, $msg) = $node->add_tag('tag', exist_ok => 1);
+    ok $ok, $msg;
+
+    ok $node->has_tag('tag'), 'has tag';
+
+    ($ok, $msg) = $node->delete_tag('tag');
+    ok $ok, $msg;
+    ($ok, $msg) = $node->delete_tag('tag');
+    ok !$ok, $msg;
+    ($ok, $msg) = $node->delete_tag('tag', not_exist_ok => 1);
+    ok $ok, $msg;
+
+    ok !$node->has_tag('tag'), 'has no tag';
+}
+
+{
+    my $node = Jifty->app_class('Model', 'Node')->new;
+    my ($ok, $msg) = $node->create(
+        type    => 'memo',
+        subject => 'Cake',
+    );
+    ok $ok, $msg;
+
+    ($ok, $msg) = $node->add_tag('tag');
+    ok $ok, $msg;
+
+    my $nid = $node->id;
+
+    ($ok, $msg) = $node->delete;
+    ok $ok, $msg;
+
+    my $tags = Jifty->app_class('Model', 'TagCollection')->new;
+    $tags->limit( column => 'model', value => 'Node' );
+    $tags->limit( column => 'record', value => $nid );
+    is $tags->count, 0, "node is deleted => no tags";
+}
+
+{
+    my $node = Jifty->app_class('Model', 'Node')->new;
+    my ($ok, $msg) = $node->create(
+        type    => 'memo',
+        subject => 'Cake',
+    );
+    ok $ok, $msg;
+
+    ($ok, $msg) = $node->add_tag('tag');
+    ok $ok, $msg;
+
+    my $tag = $node->has_tag('tag');
+    ok $tag, "has tag";
+    isa_ok $tag, Jifty->app_class('Model', 'Tag');
+    is $tag->value, 'tag', 'correct value';
+    is $tag->model, 'Node', 'correct model';
+    is $tag->record_id, $node->id, 'correct record id';
+
+    my $record = $tag->record;
+    isa_ok $record, Jifty->app_class('Model', 'Node');
+    is $record->id, $node->id, 'correct value';
+}
+
+flush_nodes();
+
+{
+    my %test_nodes = (
+        '-' => [],
+        'a' => ['a'],
+        'b' => ['b'],
+        'c' => ['c'],
+        'ab' => ['a', 'b'],
+        'ac' => ['a', 'c'],
+        'bc' => ['b', 'c'],
+        'abc' => ['a', 'b', 'c'],
+    );
+
+    while ( my ($s, $t) = each %test_nodes ) {
+        my $node = Jifty->app_class('Model', 'Node')->new;
+        my ($ok, $msg) = $node->create(
+            type    => 'memo',
+            subject => $s,
+        );
+        ok $ok, $msg;
+        foreach ( @$t ) {
+            my ($ok, $msg) = $node->add_tag($_);
+            ok $ok, $msg;
+        }
+    }
+}
+
+{
+    my $test = sub {
+        my $node = Jifty->app_class('Model', 'Node')->load_by_cols( subject => shift);
+        ok $node, 'loaded a node';
+
+        my $tag = $node->has_tag(shift);
+        ok $tag, 'has tag';
+
+        my ($expect, $expect_count) = ('', 0);
+        $expect = join ' ', map { $expect_count++; $_ } sort split /\s+/, pop;
+
+        my $nodes = $tag->used_by(@_);
+        is $nodes->count, $expect_count, 'correct count';
+        my $str = join ' ', sort map $_->subject, @$nodes;
+        is $str, $expect, 'correct list' or diag "wrong query: ". $nodes->build_select_query;
+    };
+
+    $test->('a', 'a', 'ab ac abc');
+    $test->('a', 'a', include_this => 1, 'a ab ac abc');
+    # XXX: need additional models to test 
+}
+
+{
+    my $test = sub {
+        my ($expect, $expect_count) = ('', 0);
+        $expect = join ' ', map { $expect_count++; $_ } sort split /\s+/, pop;
+
+        my $nodes = Jifty->app_class('Model', 'NodeCollection')->new;
+        if ( ref $_[0] ) {
+            $nodes->limit_by_tag(@$_) foreach @_;
+        } else {
+            $nodes->limit_by_tag(@_);
+        }
+        is $nodes->count, $expect_count, 'correct count';
+        my $str = join ' ', sort map $_->subject, @$nodes;
+        is $str, $expect, 'correct list' or diag "wrong query: ". $nodes->build_select_query;
+    };
+
+    $test->('', '-');
+    $test->('!', 'a b c ab ac bc abc');
+    $test->('a', 'a ab ac abc');
+    $test->('!a', '- b c bc');
+    $test->(['a'], ['b'], 'ab abc');
+    $test->(['!a'], ['b'], 'b bc');
+    $test->(['a'], ['b'], ['c'], 'abc');
+    $test->(['!a'], ['b'], ['c'], 'bc');
+}
+


More information about the Jifty-commit mailing list