[Jifty-commit] r3709 - talks/talks

jifty-commit at lists.jifty.org jifty-commit at lists.jifty.org
Sun Jul 22 16:22:48 EDT 2007


Author: jesse
Date: Sun Jul 22 16:22:48 2007
New Revision: 3709

Added:
   talks/talks/oscon.dsls.2007.txt

Log:
oscon dsls slides

Added: talks/talks/oscon.dsls.2007.txt
==============================================================================
--- (empty file)
+++ talks/talks/oscon.dsls.2007.txt	Sun Jul 22 16:22:48 2007
@@ -0,0 +1,957 @@
+#title Jesse Vincent - Best Practical
+Domain Specific Languages in Perl
+---
+#title A bit about DSLs
+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
+---
+SQL
+---
+Regexes
+---
+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?
+---
+#title How did I get here?
+Taxi
+---
+Airplane
+---
+MAX
+---
+Ok, How'd I really get here?
+---
+All started at OSCON 2005
+---
+DHH demonstrated Rails migrations
+---
+They looked very sexy
+---
+I was very jealous
+---
+"You can't do this in any other language"
+---
+Never say that to a Perl Hacker
+---
+#title Agenda
+We've made some DSLs
+---
+One for declaring database schema
+---
+(I thought Rails did more. But I never read the manual)
+---
+(It does a lot more than Rails migrations)
+---
+One for web templating
+---
+(Yes, another web templating system)
+---
+(Hopefully, this one will provide some closure)
+---
+(You'll see)
+---
+One for dispatching HTTP requests
+---
+One for making web testing easier
+---
+(It's VERY beta)
+---
+(Perfect for Web 2.0)
+---
+One for writing these slides
+---
+(If we have time)
+---
+#title
+Jifty::DBI::Schema
+---
+#title Jifty::DBI::Schema - The design process
+Delarative Syntax for an Object Relational Mapper
+---
+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' 
+};
+---
+<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;
+    
+    db_table bla bla bla;
+    field bla;
+    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__);
+
+$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 evily).
+---
+<obra> we could tie @ISA
+
+<obra> ...I'm kidding
+---
+<obra> we could tie the symbol table
+
+<obra> ...It just doesn't work
+---
+#mode perl
+use base 'Jifty::DBI::Record';
+Jifty::DBI::Record->___from_code();
+
+db_table 'addresses';
+
+field {
+    called 'name'; # ? 
+}
+--- 
+<glasser> is 
+	
+  "has_type 'string'" 
+
+definitely better than 
+
+  "type => 'string'"  
+
+in your book?
+---
+<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.
+---
+<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.
+---
+<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 of classes as strings :)
+---
+<obra> shit! it actually works!
+---
+What we had left:
+---
+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';
+};
+
+field employee_id => {
+    refers_to_a Sample::Employee;
+}
+---
+#mode perl
+
+# This is ugly and verbose
+
+package Sample::Employee;
+
+use base qw/Jifty::DBI::Record/;
+
+__PACKAGE__->db_table 'employees';
+
+__PACKAGE__->field name => has_type 'varchar';
+
+__PACKAGE__->field dexterity =>  { has_type 'integer'};
+
+---
+#title 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,
+  label is 'Score';
+
+column time_zone =>
+  label is 'Time zone',
+  since '0.0.12',
+  default is 'America/New_York',
+  valid are formatted_timezones();
+};
+---
+We implemented the DSL twice
+---
+#title 
+Take 1
+---
+#title Take 1
+Jifty::DBI::Schema
+---
+#title Take 1: Jifty::DBI::Schema
+Our first DSL in Perl
+---
+We beat the parser into submission with a few tricks
+---
+Injection of functions
+---
+We saw that a moment ago
+---
+Clever function prototypes
+---
+Let's have a look at that
+---
+#mode perl
+# The syntax we wanted
+
+score => type is 'int',
+  is immutable,
+  default is '0',
+  render as 'text',
+  label is 'Score',
+  since is '0.0.7';
+---
+#mode perl
+# perl -MO=Deparse parses that as:
+
+'is'->type('int', 
+  'immutable'->is, 
+   'is'->default('0', 
+      'as'->render('text', 
+        'is'->label('Score', 
+          'is'->since('0.0.7')))));
+---
+How can we fix that?
+---
+Prototype hacking!
+---
+#mode perl
+sub is ($) { return shift };
+sub as ($) { return shift };
+sub since ($) { }
+sub type ($) { }
+sub render ($) {}
+sub label ($) {}
+sub default ($) {}
+---
+#mode perl
+
+# Now, perl -MO=Deparse parses that as:
+
+ type(is('int')), 
+ is('immutable'), 
+ default(is('0')), 
+ render(as('text')), 
+ label(is('Score')), 
+ since(is('0.0.7'));
+---
+Downsides
+---
+Limited flexibility
+---
+Needs new functions for every attribute
+---
+#title Take 2
+#`mpg123  ~/katamari.mp3`
+Object::Declare
+---
+#title Take 2: Object::Declare
+Katamari for Code
+---
+#mode perl
+use Jifty::DBI::Record schema { 
+
+ column score => type is 'int',
+  is immutable,
+  render as 'text',
+  default is '0',
+  label is 'Score',
+  since is '0.0.7';
+};
+---
+# mode perl
+# perl -MO=Deparse parses that as:
+
+'is'->type('int', 
+  'immutable'->is, 
+   'is'->default('0', 
+      'as'->render('text', 
+	'is'->label('Score', 
+	  'is'->since('0.0.7')))));
+---
+Why fight the parser?
+---
+Perl gives us all the rope we need
+---
+UNIVERSAL:: and ::AUTOLOAD
+---
+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 hands the result off to a method of your choice
+---
+it removes its magic symbols
+---
+...and then your program gets control back
+---
+#title Jifty::DBI::Schema - end
+That's Jifty::DBI::Schema.
+---
+#title
+Template::Declare
+---
+#title Template::Declare
+A pure Perl Templating Language
+---
+Perl has two big templating camps
+---
+Template::Toolkit
+---
+A DSL for templates
+---
+It's NOT Perl
+---
+Why should I learn a new language for templates?
+---
+HTML::Mason is closer.
+---
+There's Perl inside.
+---
+<mason><is><still> 
+<full><of> 
+<angle><brackets>
+---
+</brackets></angle> 
+</of></full> 
+</still></is></mason> 
+---
+<html><is><for> 
+<jedi><knights>
+---
+What it looks like
+---
+#mode perl
+template '/pages/mypage.html' => sub {
+    html {
+        head {};
+        body {
+            h1 {'Hey, this is text'};
+            }
+        }
+};
+---
+#mode perl
+template choices => page {
+    h1 { 'My Choices' }
+    dl {
+        my $choices = Doxory::Model::ChoiceCollection->new;
+        $choices->limit(
+            column => 'asked_by',
+            value  => Jifty->web->current_user->id,
+        );
+        while (my $c = $choices->next) {
+            dt { $c->name, ' (', $c->asked_by->name, ')' }
+            dd { b { $c->a } em { 'vs' } b { $c->b } }
+        }
+    }
+};
+---
+But! 
+---
+Content! Templates! 
+---
+Design! Code!
+---
+OMGWTFBBQ!? THAT'S WRONG!
+---
+The person who told you it's wrong was lying to you.
+---
+We're Perl hackers
+---
+Why are we writing our templates in another language?
+---
+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 CODING tools to work with them
+---
+#mode perl
+#title Refactoring
+
+template 'mypage.html' => page {
+ h1 { 'Two choices' };
+ div { attr { class => 'item' };
+       h2 { 'Item 1'};
+  };
+ div { attr { class => 'item' };
+       h2 { 'Item 2'};
+  };
+}; 
+---
+#title Refactoring
+#mode perl
+
+template 'mypage.html' => page {
+ h1 { 'Two choices' };
+ for ("Item 1", "Item 2") { item($_); }
+};
+
+sub item {
+    my $content = shift;
+    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)
+---
+#title 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 store templates per package 
+---
+(Don't want to mix things together)
+---
+Basically, we need a symbol table
+---
+It's Perl
+---
+We HAVE a symbol table
+---
+But URLs have characters that are illegal in sub names. :/
+---
+Wrong
+---
+Perl doesn't care
+---
+(The parser cares....but we don't need to parse this)
+---
+#mode perl
+no strict 'refs';
+*{ $class . '::' . $subname } = $coderef;
+---
+That just works.
+---
+Even if your sub is named './\\foo#title <>'
+---
+But how do you call it?
+---
+#mode perl
+# 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.
+---
+#title Object Oriented Templates
+Subclassing
+---
+It just works
+---
+We're using Perl's symbol table
+---
+(Template::Declare's show() passes the class for you)
+---
+Mixins
+---
+#mode perl
+alias Some::Template::Library under '/path';
+---
+That's it.
+---
+#title Closures
+Now, about that syntax.
+---
+HTML tags take blocks of content
+---
+Our tag methods take blocks of Perl
+---
+They return closures when you want them to
+---
+They run and output their content when you want them to
+---
+#mode perl
+sub h1 (&;$) {
+  my $code = shift;
+  
+  ...
+
+  if (defined wantarray) {
+    return sub { ...closure around $code...};
+  } else {
+    # Actually do our work, run $code and return the output
+    return $code->();
+  }
+}
+---
+We install methods for every HTML tag
+---
+(Except 'tr'. Anybody know why?)
+---
+#mode perl
+use CGI ();
+install_tag($_) 
+  for ( @CGI::EXPORT_TAGS{
+	qw/:html2 :html3 :html4 
+	   :netscape :form/}
+);
+---
+#title 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 MUST come first
+---
+:/
+---
+Can anybody help me?
+---
+#title Template::Declare - end
+That's Template::Declare
+---
+...almost.
+---
+My partner, CL, is from Taiwan.
+---
+You should know something about Taiwanese Perl Hackers
+---
+They're scary
+---
+Good scary
+---
+Google Gears came out
+---
+CL got jealous
+---
+He wanted offline apps with Jifty
+---
+That means a Javascript Templating Engine
+---
+Actually, no.
+---
+We can introspect compiled templates...
+---
+...using an improved B::Deparse.
+---
+That gets us an OP tree...
+---
+...which we can use to generate Javascript
+---
+We can already do T::D tags, map, grep and for
+---
+Ping me afterward if you want to help out.
+---
+#title
+Jifty::Dispatcher
+---
+#title Jifty::Dispatcher
+Routing your HTTP requests
+---
+I come from HTML::Mason
+---
+dhandlers
+---
+autohandlers
+---
+Logic in your web pages
+---
+Ick
+---
+Doesn't have to be that way
+---
+Jifty::Dispatcher
+---
+Centralizes your page logic
+---
+Features
+---
+3 stages
+---
+Before
+---
+Runs before action or rendering
+---
+#mode perl
+ # Backwards compat for the bizarre 
+ # offchance anyone kept this URL
+ # around.
+
+ before '/legal/accept_eula' => redirect '/accept_eula';
+---
+On 
+---
+Runs right before page is rendered
+---
+Can control page to be rendered
+---
+#mode perl
+ on qr'^/task/([A-Za-z0-9]+)/(.*)' => run {
+    my $id = $1;
+    my $task = BTDT::Model::Task->new();
+    $task->load($id);
+
+    set task => $task;
+    set id => $id;
+    show "/task/$2";
+ };
+
+---
+After
+---
+Designed for cleanup code
+---
+Only ever found one use
+---
+#mode perl
+ after '/news/' => run {
+    if ( Jifty->web->current_user->id ) {
+        my $now  = DateTime->now;
+        my $user = BTDT::Model::User->new();
+        $user->load( Jifty->web->current_user->id );
+        $user->set_last_visit( DateTime->now);
+    }
+ };
+
+
+---
+Shell-style globbing
+---
+#mode perl
+ before '*' => run {
+    if ($ENV{'HTTP_USER_AGENT'} =~ /(?:hiptop|Blazer|iPhone
+                                    SonyEricsson|Symbian|
+                                    NetFront|UP.Browser/iox )  {
+        set mobile_ua => '1';
+    } else {
+        set mobile_ua => '0';
+
+    }
+ };
+---
+Perl Regexes
+---
+#mode perl
+ on qr'^/(?:|index.html)$' => run {
+    if (get 'mobile_ua') {
+        redirect '/mobile/';
+    } else {
+        redirect '/todo/';
+    }
+ };
+---
+HTTP VERBS
+---
+#mode perl
+before GET '*' => run { Jifty->api->deny(qr/./);
+                        Jifty->api->allow('TaskSearch');
+};
+
+---
+SSL adjectives
+---
+#mode perl
+before HTTPS $RESTRICTED_PAGES => run {
+    redirect '/login' if not Jifty->web->current_user->id;
+}
+---
+How it's built
+---
+
+show off the mapping tables and how it builds up the dispatch table
+
+
+---
+#title 
+Test::WWW::Declare
+---
+#title Test::WWW::Declare
+A language for testing web applications
+---
+In early development
+---
+It might change
+---
+Web test scripts are UGLY
+---
+Test::WWW::Declare is PRETTY
+---
+Simple, declarative web testing
+---
+Easy to read
+---
+Easy to write
+---
+Looks more like what users do
+---
+#mode perl
+# Test::WWW::Mechanize
+
+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
+
+# Test::WWW::Declare
+
+session "search" => run {
+    flow "google searches work" => check {
+        get 'http://google.com/ncr'; 
+        fill form 'f' => { 
+		q   => 'Squeamish ossifrage' };
+        click button 'Google Search';
+    }
+};
+---
+Regular tests keep running on failure
+---
+Makes no sense when a failure means you lose context
+---
+Every 'check' block aborts on failure
+---
+Abort means 'failing test'
+---
+Every 'session' has a cookie jar 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;
+    };
+};
+---
+What's the weird syntax?
+---
+# mode perl
+content should match qr{RT Essentials}i;
+---
+#mode perl
+content should match qr{RT Essentials}i;
+
+#   vs
+
+ok($req->content =~ /RT Essentials/i);
+---
+# mode perl
+# How do we make this valid perl?
+
+content should match qr{RT Essentials}i;
+---
+Prototypes
+---
+#mode perl
+sub match ($) {
+    return shift;
+}   
+---
+#mode perl
+sub should ($) {
+    my $item = shift;
+    return $item;
+}
+---
+#mode perl
+sub content ($) { 
+    my $regex = shift;
+    unless ( mech()->content =~ /$regex/ ) {
+        die "Content did not match $regex";
+    }
+}
+---
+#title Test::WWW::Declare - end
+That's Test::WWW::Declare
+---
+#title Conclusion
+Creating DSLs is lots of fun
+---
+Creating DSLs can be a lot of work
+---
+Creating DSLs helps you learn Perl internals
+---
+Creating DSLs helps find bugs in Perl
+---
+DSLs can make coding more fun
+---
+Thanks
+---


More information about the Jifty-commit mailing list