[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