[Jifty-commit] r3081 - in jifty/trunk: .
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Tue Apr 3 02:25:59 EDT 2007
Author: jesse
Date: Tue Apr 3 02:25:58 2007
New Revision: 3081
Added:
jifty/trunk/doc/talks/present-slides
Modified:
jifty/trunk/ (props changed)
jifty/trunk/doc/talks/yapc.asia.2007.txt
Log:
r54413 at dhcp207: jesse | 2007-04-03 15:25:32 +0900
* Next slides draft (and the tool to render them)
Added: jifty/trunk/doc/talks/present-slides
==============================================================================
--- (empty file)
+++ jifty/trunk/doc/talks/present-slides Tue Apr 3 02:25:58 2007
@@ -0,0 +1,95 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use Term::ANSIScreen qw/:color :cursor :screen :keyboard/;
+use Term::ReadKey;
+use Text::Autoformat;
+
+my $file = shift @ARGV;
+my $handle;
+open( $handle, "<$file" ) || die $!;
+
+my $datadata = join( '', <$handle> );
+
+my @slides = split( /^----?\s*$/mi, $datadata );
+my $counter = 0;
+my $slides_played = {};
+ my $title;
+while ( $counter <= $#slides ) {
+ my $mode = 'text';
+ my ( $cols, $rows, undef, undef ) = GetTerminalSize();
+ my $slide = $slides[$counter];
+ my $console = Term::ANSIScreen->new;
+ $console->Cls;
+ $console->Cursor(1,1);
+ if ( $slide =~ s/^!\!\s*?(.*?)$//gm ) {
+ $title = $1;
+ }
+ if ($title) {
+ my $start = int ( ( $cols / 2 ) - ( length($title) / 2 ) );
+ $console->Cursor($start,0);
+ chomp $title;
+ print "$title\n";
+ }
+
+ if ($slide =~ s/#\s*`(.*?)`//m) {
+ #my $cmd = $1;
+ #if(!$slides_played->{$counter} && ($slides_played->{$counter} = fork() )) {
+ #`$cmd>/dev/null 2>/dev/null`;
+ #}
+ }
+ if ( $slide =~ s/#\s*mode.*?perl.*?$//gms ) {
+ $mode = 'perl';
+ }
+ if ( $mode eq 'text' and $slide ) {
+ $slide = autoformat $slide, { left => 1, right => ($cols), all => 1 };
+ }
+ elsif ($mode eq 'perl') {
+ my $tidycols = $cols - 2; # squeeze for display
+ open my $out , ">/tmp/output.$$" || die $!;
+ print $out $slide || die $!;
+ close $out || die $!;
+ $slide = ` cat /tmp/output.$$ | source-highlight -s perl -f esc`;
+ #$slide = ` cat /tmp/output.$$ | perltidy -q -l $tidycols| source-highlight -s perl -f esc`;
+ }
+
+ if ( $slide =~ /(\S+)\s*\n\s*(\S+)/m ) {
+
+ print $slide;
+ } else {
+ chomp $slide;
+ $slide =~ s/(?:\n|\r)//g;
+ my $left = int ( ( $cols / 2 ) - ( length($slide) / 2 ) );
+ if ($left < 0 ){
+ $left = 0;
+ }
+ $console->Cursor(
+ $left,
+ int($rows/2)-1,
+
+);
+ print $slide."\n";
+ }
+
+
+ $console->Cursor( 0, ( $rows - 1 ) );
+ print "$counter/" . $#slides;
+ ReadMode 4;
+ my $key = ReadKey(0);
+ ReadMode 0;
+ if ( $key eq 'q' ) {
+ exit;
+ }
+ if ( $key =~ /^(?: |\n|n)/ ) {
+ $counter++;
+ } elsif ( $key eq 'r' ) {
+ next;
+ } else {
+ $counter--;
+ if ( $counter < 0 ) {
+ $counter = 0;
+ }
+ }
+}
+
Modified: jifty/trunk/doc/talks/yapc.asia.2007.txt
==============================================================================
--- jifty/trunk/doc/talks/yapc.asia.2007.txt (original)
+++ jifty/trunk/doc/talks/yapc.asia.2007.txt Tue Apr 3 02:25:58 2007
@@ -1,44 +1,88 @@
-* What are Domain Specific Languages
-
-* How did I get here?
-
- - Airplane, then Subway
-
- - All started at OSCON 2005
- - DHH demonstrated Rails migrations
- - Looked very sexy
- - Was very jealous
- - "You can't do this in any other language"
- - Never tell that to a Perl Hacker
-
- - Started sketching Jifty::DBI columns
-
- - Started with searchbuilder
- - It was a big hash
- - It was a big mess
- - It was ugly
-
- - Spent about a month playing with syntaxes.
-
-
+!!Jesse Vincent - Best Practical
+Domain Specific Languages in Perl
---
-
+!!DSLs in Perl
+DSLs are 'little languages' for specific programming tasks
+---
+DSLs are easier to read
+---
+DSLs are more expressive
+---
+DSLs let you optimize your code for coding
+---
+Mostly, I'm going to talk about "Englishy" DSLs
+---
+Not all DSLs are Englishy
+---
+- Excel Macros
+- XML config files
+- XSL-T
+- GraphViz
+---
+...but I've been on an Englishy DSL kick
+---
+DSLs can be implemented in your 'host' language
+---
+(These get called "internal" DSLs)
+---
+DSLs can be implemented outside your 'host' langauge
+---
+(External DSLs)
+---
+Everything I'm going to talk about is Pure Perl (Internal)
+---
+The Ruby community is big on DSLs
+---
+You can make DSLs in Perl, too.
+---
+(but it does take more work in Perl)
+---
+!!How did I get here?
+- Airplane
+- Narita Express
+- Subway
+---
+- All started at OSCON 2005
+- DHH demonstrated Rails migrations
+- Looked very sexy
+- Was very jealous
+- "You can't do this in any other language"
+---
+Never say that to a Perl Hacker
+---
+!!Jifty::DBI::Schema - The design process
+- Started sketching Jifty::DBI columns
+- Started with DBIx::SearchBuilder
+- Columns were defined as a hash
+- Hashes are ugly
+---
+We spent about a month playing with syntax.
+---
+Our first goal was "feels right"
+---
+Our second goal was "we can implement this"
+---
+I'm going to show you some of our design process
+---
+(It's a mix of code and IRC)
+---
+#mode perl
$x = Jifty::DBI::SchemaBuilder->new;
$x->define_blablalb
$x->bla bla
-
-
---
-
+#mode perl
our db_table 'addresses';
-our field name => { has_type 'varchar'; has_default 'frank' };
-
-# (by the way, i'm pretty sure we don't get to do the sub-at-t-end thing
-# either... I tried lots of hacky ways to get it working and failed.)
-
-# yeah, I think we're going to end up having a pseudo-sub that's really a hash behind the scenes
+our field name => {
+ has_type 'varchar';
+ has_default 'frank'
+};
+---
+<chmrr> (by the way, i'm pretty sure we don't get to do the sub-at-the-end thing either... I tried lots of hacky ways to get it working and failed.)
+<glasser> yeah, I think we're going to end up having a pseudo-sub that's really a hash behind the scenes
---
+#mode perl
{
my $s = Jifty::DBI::SG->import_functions;
@@ -47,27 +91,33 @@
field bar;
} # $s.DESTROY gets called and unimports db_table/field/...
---
-
+(This was astonishingly close to what we do today.)
+---
+#mode perl
my $schema = Jifty::DBI::RecordSchema->new;
-$schema->for_class(__PACKAGE__); #just riffing
+$schema->for_class(__PACKAGE__);
-$schema->field name => { has_type 'varchar'; has_default 'Frank'}
+$schema->field name => {
+ has_type 'varchar';
+ has_default 'Frank'
+}
---
+#mode perl
+
BEGIN { @ISA = 'Jifty::DBI::Record' }
use Jifty::DBI::Record; # but this sucks!
use base qw/Jifty::DBI::Record/;
-__PACKAGE__->schema_version (0.0001) # or some other method that
-# does two thing evilly.
+__PACKAGE__->schema_version (0.0001)
+# (or some other method that does two thing evily).
---
+<obra> we could tie @ISA
-# we could tie @ISA
-
-# I'm kidding
+<obra> ...I'm kidding
---
-
+#mode perl
use base 'Jifty::DBI::Record';
Jifty::DBI::Record->___from_code();
@@ -75,59 +125,67 @@
field {
called 'name'; # ?
----
- # but yeah, falls into the "works" category"
- # and
- has_type 'string'
- # is definitely better than
- type => 'string'
- # in your book?
----
- # how would you do:
-
- refers_to_many RT::Tickets by 'owner';
+}
+---
+<glasser> is
+
+ "has_type 'string'"
+
+definitely better than
+
+ "type => 'string'"
+
+in your book?
---
-
- # hmm. i thought about this before. we can do like simon and
- refers_to_many "RT::Tickets by owner";
- # but I don't really like that. parsing is lame.
+<obra> how would you do:
+
+ refers_to_many RT::Tickets by 'owner';
+
+<Robrt> hmm. i thought about this before. we can do like simon and
+
+ refers_to_many "RT::Tickets by owner";
+
+<Robrt> but I don't really like that. parsing is lame.
+
+<Robrt> I'm *pretty* sure that we can't get the line you've written to compile.
---
- # I'm *pretty* sure that we can't get the line you've written to compile.
+<obra> I've got a bad perl5 idea for you. Robert claims it's impossible
+
+<obra> I'm trying to make the syntax "refers_to_many 'BTDT::Model::Tasks' by 'owner';" valid perl5 syntax.
---
- # oh no, autrijus gave me the one line I needed.
-
- # don't forget that RT::Tickets is a class/package.
-
- # shit! it actually works!!!
+<audreyt> well, that may be true but you don't want that.
+
+<audreyt> refers_to_many BTDT::Model::Tasks by 'owner'
+
+<audreyt> is more readable and easily implemented.
+
+<audreyt> sub by ($) { by => @_ }
+
+<audreyt> done!
+
+<audreyt> stop thinking classes as strings :)
---
-
- # the idea is that it just returns a key, val pair. so it doesn't matter.
-
- # well, right, but refers_to_many is being called in RT::Tickets
- # instead of in the current package. but that's ok.
----
- 23:46 <obra> I've got a bad perl5 idea for you. Robert claims it's impossible
-23:47 <obra> I'm trying to make the syntax "refers_to_many 'BTDT::Model::Tasks' by 'owner';" valid perl5 syntax.
----
-03:57 <autrijus> well, that may be true but you don't want that.
-03:57 <autrijus> refers_to_many BTDT::Model::Tasks by 'owner'
-03:57 <autrijus> is more readable and easily implemented.
-03:58 <autrijus> sub by ($) { by => @_ }
-03:58 <autrijus> done!
-03:58 <autrijus> stop thinking classes as strings :)
+<obra> shit! it actually works!
---
- # so, now we're just still on the
+
+What we had left:
- field foo => sub {}; issue
- # let's see what the hash syntax looks like with my weird keys.
-
+the field foo => sub {}; issue
---
+#mode perl
+
+ # We wanted something that acted like this
+ # But without the ugly 'sub' keyword
field email => sub {
has_type 'varchar';
has_default 'Frank';
};
+---
+#mode perl
+# We cculd do this, but it used a hash
+# not a block
field phone => {
has_type 'varchar';
@@ -137,6 +195,10 @@
refers_to_a Sample::Employee;
}
---
+#mode perl
+
+# This is ugly and verbose
+
package Sample::Employee;
use base qw/Jifty::DBI::Record/;
@@ -147,61 +209,418 @@
__PACKAGE__->field dexterity => { has_type 'integer'};
-1;
---
-
- - Do we have notes from these?
+!!In the end...
+We ended up with Jifty::DBI columns
+---
+#mode perl
+use Jifty::DBI::Record schema {
+column
+ auth_token => type is 'text',
+ render as 'Unrendered';
+
+column score => type is 'int',
+ is immutable,
+ default is '0',
+ label is 'Score',
+ since is '0.0.7';
+
+column time_zone =>
+ label is 'Time zone',
+ since '0.0.12',
+ default is 'America/New_York',
+ valid are formatted_timezones();
+};
+---
+Implemented it twice
+---
+!!
+Take 1
+---
+!!Take 1:
+Jifty::DBI::Schema
+---
+Our first DSL in Perl
+---
+We beat the parser into submission using:
+- Clever function prototypes
+- Injection of functions
+---
+#mode perl
+score => type is 'int',
+ is immutable,
+ default is '0',
+ render as 'text',
+ label is 'Score',
+ since is '0.0.7';
+---
+Prototype hacking
+---
+#mode perl
+sub is ($) { return shift };
+sub as ($) { return shift };
+sub since ($) { }
+sub type ($) { }
+sub render ($) {}
+sub label ($) {}
+sub default ($) {}
+---
+#mode perl
+score => type is 'int',
+ is immutable,
+ default is '0',
+ render as 'text',
+ label is 'Score',
+ since is '0.0.7';
- - Ended up with Jifty::DBI columns
+# parses to:
+ type(is('int')), is('immutable'), default(is('0')), render(as('text')), label(is('Score')), since(is('0.0.7'));
+---
+TODO: function injection
+---
+!!Take 2:
+Object::Declare
+---
+#`mpg123 ~/katamari.mp3`
+Katamari for Code
+---
+#mode perl
+use Jifty::DBI::Record schema {
-* Object::Declare
- - Sample usage
- - Explanation of the sample usage's meaning
- - Comparison with traditional code
- - Why we like it
- - Tricks we use to make it go
- - Katamari Damacy Video
- - Localized symbols
- - copula
-
- - Explanation of how it works
-* Template::Declare
- - Sample usage
- - Explanation of the sample usage's meaning
- - Comparison with traditional code
- - Why we like it
- - perlish
- - refactorable
- - templates with class (inheritance)
- - readable
- - can balance parens
- - if it doesn't have matched tags, it doesn't compile
+ column score => type is 'int',
+ is immutable,
+ render as 'text',
+ default is '0',
+ label is 'Score',
+ since is '0.0.7';
+};
- - Tricks we use to make it go
-
- - Our own method dispatch and inheritance tree
- - $self hacking
- - throwing insane things into the symbol table
- - generating "tags" that take closures
- - buffers
- - Explanation of how it works
+# parses as:
+
+'is'->type('int',
+ 'immutable'->is,
+ 'is'->default('0',
+ 'as'->render('text',
+ 'is'->label('Score',
+ 'is'->since('0.0.7')))));
+---
+What actually happens at compile time:
+
+- The 'schema' function in our baseclass takes a code block
+- ...and returns a closure
+- Jifty::DBI::Record::import takes over:
+- it takes the closure
+- it installs some methods...
+- ...is::AUTOLOAD and UNIVERSAL::is and as::AUTOLOAD
+- it runs the closure
+- it removes its magic symbols
+
+---
+Template::Declare
+---
+!!Template::Declare
+Sample usage
+---
+#mode perl
+template '/pages/mypage.html' => sub {
+ html {
+ head {};
+ body {
+ h1 {'Hey, this is text'};
+ }
+ }
+};
+---
+But!
+Content! Templates!
+Design! Code!
+---
+OMGWTF!? THAT'S WRONG!
+---
+The person who told you it's wrong was lying to you.
+---
+We're perl hackers
+---
+Why are we putting a minilanguage in our templates?
+---
+This is not 1997
+---
+It's 2007.
+---
+People use CSS for design now.
+---
+Programmers still have to make templates
+---
+Templates run like CODE
+---
+Because they ARE code
+---
+Let's use our PROGRAMMING tools to work with them.
+---
+#mode perl
+!!Refactoring
+
+template 'mypage.html' => page {
+ h1 { 'Two choices' };
+ div { attr { class => 'item' };
+ h2 { 'Item 1'};
+ };
+ div { attr { class => 'item' };
+ h2 { 'Item 2'};
+ };
+};
+---
+!!Refactoring
+#mode perl
+
+template 'mypage.html' => page {
+ h1 { 'Two choices' };
+ for ("Item 1", "Item 2") { item($_); }
+};
+
+sub item {
+ my $content = shift;
+ return
+ div { attr { class => 'item' };
+ h2 {$content};
+ };
+
+}
+---
+We can refactor templates!
+---
+Have you ever tried to refactor HTML?
+---
+Our HTML is magically valid.
+(Syntax errors are...Syntax Errors)
+---
+Inheritance
+---
+Mixins
+---
+Tricks we use
+---
+!!Stashing our templates
+#mode perl
+template '/foo/index.html' => sub {... };
+---
+'sub template' takes a name and a coderef.
+---
+But where do we put these?
+---
+We need a global stash.
+---
+It needs to be per package
+(Don't want to mix things together)
+---
+Basically, we need a symbol table.
+---
+It's Perl.
+---
+We have THE symbol table.
+---
+But you can have characters in URLS you can't have in sub names. Oh no!
+---
+Actually, Perl doesn't care.
+---
+#mode perl
+ no strict 'refs';
+ *{ $class . '::' . $subname } = $coderef;}
+---
+That just works.
+---
+Even if your subroutine is named './\\foo!!<>'
+---
+But how do you call it?
+---
+# perldoc UNIVERSAL
+
+CLASS->can( METHOD )
+"can" checks if the object or class has a method called "METHOD".
+If it does then a reference to the sub is returned.
+---
+!!Closures
+Now, about that syntax.
+---
+HTML tags take blocks
+of content.
+---
+Our tag methods take
+blocks. (Of perl)
+---
+#mode perl
+sub h1 (&;$) {
+ my $code = shift;
+
+ ...
+
+ if (defined wantarray) {
+ return $closure_around_$code;
+ } else {
+ # Actually do our work and run $code
+ }
+}
+---
+!!Not everything is roses
+(Here's where it all goes wrong)
+---
+HTML Attributes
+---
+# mode perl
+# What we've got:
+
+div {
+ attr { id => 'my-div'};
+ ...
+};
+
+# and
+
+with ( id => 'my-div'), div {
+...
+};
+---
+# mode perl
+# What I think I'd like:
+div ( id => 'my-div' ), {
+...
+}
+---
+So, what's the big problem?
+---
+Just change the prototype.
+---
+In Perl, the (&) in a prototype
+may ONLY come first.
+---
+ORZ
+---
+Not covering:
+
+- Our own method dispatch and inheritance tree
+- $self hacking
+- buffers
-* Test::WWW::Declare
- - Sample usage
+---
+Test::WWW::Declare
+---
+!!Test::WWW::Declare
+Web test scripts are UGLY
+---
+- Simple, declarative web testing
+- Easy to read
+- Easy to write
+- Looks more like what users do
+---
+#mode perl
+my $server=Jifty::Test->make_server;
+isa_ok($server, 'Jifty::Server');
+my $URL = $server->started_ok;
+my $mech = Jifty::Test::WWW::Mechanize->new;
+$mech->get_html_ok($URL);
+like($mech->uri, qr{splash}, 'Redirected to splash page');
+---
+The insides are great
+---
+The syntax ain't
+---
+We built on Test::More and WWW::Mechanize
+---
+#mode perl
+session "check logins" => run {
+ flow "basic connectivity" => check {
+ get 'http://fsck.com';
+ content should match qr{fsck.com};
+ click href qr{book};
+ content should match qr{RT Essentials}i;
+ };
+};
+---
+In early development
+---
+- Sample usage
- Explanation of the sample usage's meaning
- Comparison with traditional code
- Why we like it
- - Tricks we use to make it go
+---
+- Tricks we use to make it go
+---
+
+session "check logins" => run {
+ flow "basic connectivity" => check {
+ get 'http://fsck.com';
+ content should match qr{fsck.com};
+ click href qr{book};
+ content should match qr{RT Essentials}i;
+
+
+ };
+};
+
+---
+Why do we make this valid syntax?
+ content should match qr{RT Essentials}i;
+---
+Readability
+---
+Understandability
+---
+It feels English-y.
+---
+
+content should match qr{RT Essentials}i;
+
+ vs
+
+ok($req->content =~ /RT Essentials/i);
+
+---
+
+How do we make this valid perl?
+ content should match qr{RT Essentials}i;
+---
+
- Prototypes
- - eval
- - custom test functions
- - Explanation of how it works
-
+---
+sub match ($) {
+ return shift;
+}
+---
+sub should ($) {
+ my $item = shift;
+ return $item;
+}
+---
+sub content ($) {
+ my $regex = shift;
+ unless ( mech()->content =~ /$regex/ ) {
+ die "Content did not match $regex";
+ }
+}
+---
+ - eval, the (&) prototype and custom test functions
+---
+ check {
+ # do stuff that won't fail
+ };
+---
* Other interesting usages of DSLs in Perl
+
+
+---
+Problems with DSLs
+---
+Debugging can get harder
+---
+Editors can get confused
+---
+Hackers can get confused
+---
+
More information about the Jifty-commit
mailing list