SlideShare une entreprise Scribd logo
1  sur  40
Télécharger pour lire hors ligne
My
First
Ruby
by Murray Steele (aged 31 1/2)
Me in 2010
Passport photo:    Employed by:



                  Codes mostly in:
Me in 2003
Passport photo:    Employed by:



                  Codes mostly in:
I FORSAKE JAVA! I RENOUNCE
C++! ALL HAIL the Mighty
RUBY!!

Riddle:
2.upto(100) { |num|
   print "ruby is #{num} times
   better that java or c++"
}


Work it out...


.... if you dare.
L LED
   N CE
C A
I am all dressed up ready to code
me some Ruby. YEAH!!!
You don’t even know Ruby, I’ll
make sure you do it right.
http://www.flickr.com/photos/ajstarks/4196202909/
@ -24,8 +24,8 @@ class Config < Hash
         }
     end

-      def initialize(name)
-          self[Config::Name] = name
+      def initialize(name=nil)
+          self[Config::Name] = name if name != nil
       end

 end
public Config(String name) {
  if (name != null) {
    this.put(Config.NAME, name)
  }
}

public Config() {
  super(null);
}


              def initialize(name=nil)
                self[Config::Name] = name↩
              if name != nil
              end
For frequent
strong language
FUCKNUT
def handleMessage(message)
  sender = @users.findByEmail(message.header.from[0])

  if (sender == nil)
    return
  end

  message.header.delete('From')
  message.header['From'] = sender.preferredFromAddress

  processSubject(message) # modify the subject
  addListHeaders(message) # add the list headers
  archive(message)        # archive the mail
  processAttachments(message) # modify attachments
  sendToMembers(message) # send it out.
end
def processSubject(message)
  sub = message.header.subject.dup
  prefix = "[" + @config.listHeaders["List"] + "]"
  i = sub.index(prefix)
  sub.slice!(i..(i+prefix.length)) if (i != nil)
  found = 0
  re_str = "re: "
  i = sub.downcase().index(re_str)
  while (i != nil)
    sub.slice!(i,re_str.length)
    i = sub.downcase().index(re_str)
    found = 1
  end
  prefix = "Re: "+prefix if (found == 1)
  message.header.subject = prefix + " " + sub
end
def processSubject(message)
  sub = message.header.subject.dup
  prefix = "[" + @config.listHeaders["List"] + "]"

  sub.gsub!(/#{Regexp.escape(prefix)}/,'')

  if sub =~ /re: /i
    sub.gsub!(/re: /i, '')
    prefix = "Re: "+prefix
  end

  message.header.subject = prefix + " " + sub
end
# this is a loosely extended Hash with some little
# things to make it's use more convenient
class ListConfiguration < Hash
# this method is a bit overkill, but by caching
# the actual list header object instead of having
# to get it from the main config hash each time
# we should save some overhead. the ListHeaders
# part is the most frequently accessed, because
# it contains a lot of varied list info.
def listHeaders
  if @listHeaders.id != self[ListHeaders].id
    puts "recaching"
    @listHeaders = self[ListHeaders]
  end
  @listHeaders
end
@config[ListHeaders]

def listHeaders
  self[ListHeaders]
end
def listHeaders
  if @listHeaders.id != self[ListHeaders].id
    puts "recaching"
    @listHeaders = self[ListHeaders]
  end
  @listHeaders
end

  # these just resolve to unique strings,
  # used as keys within the config hash
  Name = "name"
  ListHeaders = "listheaders"

  # this is the root directory
  BaseDir = "basedir"
log("looking at "#{part.header.media_type}"")
if !TypesToKeep.include?(part.header.media_type)
  md = name_regex.match(part.header["Content-Type"])
  filename = "null"
  if md != nil
    filename =
       newUniqueFile(AttachmentDir, md[3]) { |f|
         f.write(part.decode())
       }
    saveAttachmentMetaData(filename, message)
  else
    log("error getting filename from
"#{part.header["Content-Type"]}"")
  end
  size = File.size(File.join(
             base_dir, @config[AttachmentDir], filename
          ))
def handleMessage(message)
  sender = @users.findByEmail(message.header.from[0])

  if (sender == nil)
    return
  end

  message.header.delete('From')
  message.header['From'] = sender.preferredFromAddress

  processSubject(message) # modify the subject
  addListHeaders(message) # add the list headers
  archive(message)        # archive the mail
  processAttachments(message) # modify attachments
  sendToMembers(message) # send it out.
end
#!/home/room206/bin/narf
fucknut_home = '/home/room206/fucknut'
$: << fucknut_home # add our library path
to the search path

require 'web'
AddHandler cgi-script .rb

RewriteEngine on

RewriteRule ^(.*)/archives/(.*)$ /handler.rb?↩
listname=$1&mode=archive&$2
listname = Web['listname']
if listname != nil and listname != ""
  # main processing when we have a listname param
else
     lists = Config.possible
     lists = lists.collect { |i|
          {'name' => i, 'value' => i}
     }
     if lists != []
          Web.print_template("what_list.html",
{'listname' => listname, 'listnamevalues' =>
lists})
     else
          Web.print_template("generalerror.html",
{'error' => "No lists defined, get a better
admin"})
     end
end
Web.flush
<html>
    <head>
        <title>Login to {$listname}</title>
        <link href="/arse.css" rel="stylesheet"
type="text/css">
    </head>
    <body>
        <h1>Error during login to {$listname}</h1>
        <p>{$error}</p>
        <narf:form formname="tryagain" method="POST">
            <narf:hidden name="listname">
            <input type="submit" value="Try Again">
        </narf:form>
    </body>
</html>
class LoginHandler
  LoginTemplate = "login.html"
  LogoutTemplate = "login.html"
  LoginCommand = "login"
  LogoutCommand = "logout"

  def showLogin()
    @web.print_template( *loginTemplateArgs() )
  end

  def loginTemplateArgs()
    [LoginTemplate, {"listname" => @web['listname'],
                     "cmd" => LoginCommand}]
  end
end
def showLogin()
  @web.print_template(LoginTemplate,
                      loginTemplateParams() )
end

def loginTemplateParams()
  {"listname" => @web['listname'],
    "cmd" => LoginCommand}
end
def showLogin()
  @web.print_template(LoginTemplate,
                      {"listname" => @web['listname'],
                       "cmd" => LoginCommand}
end
errors = ""
commalist = @web['addresses'].gsub(/n/, ",")
list = RMail::Address.parse(commalist)
if list.empty?
  errors += "<br><h2>no valid email addresses given in
list</h2>"
end
user.addresses = list

sendTo = RMail::Address.parse(@web['sendto'])[0]
sendToString = @web['sendto']
if nil == sendTo
  errors += "<br><h2>Send To Address is not valid</h2>"
  sendToString = "INVALID: " + sendToString
else
  sendToString = sendTo.format.to_s
  user.sendTo = sendTo
end
/archives/<year>/<month>/<message_id>.msg
if @config[StartYear] < lastyear.year
  lastyear = "<a href=
"#{baseurl}year=#{lastyear.year}&month=#{lastyear
.month}">&lt;&lt;</a>"
elsif @config[StartYear] == lastyear.year
  if @config[StartMonth] <= lastyear.month
     lastyear = "<a href=
"#{baseurl}year=#{lastyear.year}&month=#{lastyear
.month}">&lt;&lt;</a>"
  else
     lastyear = "&lt;&lt;"
  end
else
  lastyear = "&lt;&lt;"
end
The End

Contenu connexe

Tendances

Introduction to DBIx::Lite - Kyoto.pm tech talk #2
Introduction to DBIx::Lite - Kyoto.pm tech talk #2Introduction to DBIx::Lite - Kyoto.pm tech talk #2
Introduction to DBIx::Lite - Kyoto.pm tech talk #2Hiroshi Shibamura
 
(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and ProfitOlaf Alders
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Kris Wallsmith
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 WorldFabien Potencier
 
Functional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures editionFunctional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures editionosfameron
 
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodPhp 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodJeremy Kendall
 
Not Really PHP by the book
Not Really PHP by the bookNot Really PHP by the book
Not Really PHP by the bookRyan Kilfedder
 
Functional pe(a)rls: Huey's zipper
Functional pe(a)rls: Huey's zipperFunctional pe(a)rls: Huey's zipper
Functional pe(a)rls: Huey's zipperosfameron
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB jhchabran
 
Groovy as a scripting language
Groovy as a scripting languageGroovy as a scripting language
Groovy as a scripting languageJenn Strater
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPJeremy Kendall
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPJeremy Kendall
 
Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6Andrew Shitov
 
Bash and regular expressions
Bash and regular expressionsBash and regular expressions
Bash and regular expressionsplarsen67
 
WordCamp Portland 2018: PHP for WordPress
WordCamp Portland 2018: PHP for WordPressWordCamp Portland 2018: PHP for WordPress
WordCamp Portland 2018: PHP for WordPressAlena Holligan
 

Tendances (20)

Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3
 
Introduction to DBIx::Lite - Kyoto.pm tech talk #2
Introduction to DBIx::Lite - Kyoto.pm tech talk #2Introduction to DBIx::Lite - Kyoto.pm tech talk #2
Introduction to DBIx::Lite - Kyoto.pm tech talk #2
 
(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Functional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures editionFunctional Pe(a)rls - the Purely Functional Datastructures edition
Functional Pe(a)rls - the Purely Functional Datastructures edition
 
wget.pl
wget.plwget.pl
wget.pl
 
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodPhp 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the Good
 
Dades i operadors
Dades i operadorsDades i operadors
Dades i operadors
 
Cpsh sh
Cpsh shCpsh sh
Cpsh sh
 
Not Really PHP by the book
Not Really PHP by the bookNot Really PHP by the book
Not Really PHP by the book
 
Functional pe(a)rls: Huey's zipper
Functional pe(a)rls: Huey's zipperFunctional pe(a)rls: Huey's zipper
Functional pe(a)rls: Huey's zipper
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB
 
Groovy as a scripting language
Groovy as a scripting languageGroovy as a scripting language
Groovy as a scripting language
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6Text in search queries with examples in Perl 6
Text in search queries with examples in Perl 6
 
My shell
My shellMy shell
My shell
 
Bash and regular expressions
Bash and regular expressionsBash and regular expressions
Bash and regular expressions
 
WordCamp Portland 2018: PHP for WordPress
WordCamp Portland 2018: PHP for WordPressWordCamp Portland 2018: PHP for WordPress
WordCamp Portland 2018: PHP for WordPress
 

En vedette

Care For The Community
Care For The CommunityCare For The Community
Care For The CommunityMurray Steele
 
Wild & Weird Ideas: An Overview of Ruby 1.9
Wild & Weird Ideas: An Overview of Ruby 1.9Wild & Weird Ideas: An Overview of Ruby 1.9
Wild & Weird Ideas: An Overview of Ruby 1.9Murray Steele
 
Some Rough Fibrous Material
Some Rough Fibrous MaterialSome Rough Fibrous Material
Some Rough Fibrous MaterialMurray Steele
 
WOOD AND ITS DERIVATES
WOOD AND ITS DERIVATESWOOD AND ITS DERIVATES
WOOD AND ITS DERIVATESIES Consaburum
 
W504+Asbestos+and+Other+Fibres
W504+Asbestos+and+Other+FibresW504+Asbestos+and+Other+Fibres
W504+Asbestos+and+Other+FibresOHLearning.com
 
Simulation in Design - Dive into ANSYS simulation
Simulation in Design -  Dive into ANSYS simulationSimulation in Design -  Dive into ANSYS simulation
Simulation in Design - Dive into ANSYS simulationDerek Sweeney
 

En vedette (7)

Care For The Community
Care For The CommunityCare For The Community
Care For The Community
 
Wild & Weird Ideas: An Overview of Ruby 1.9
Wild & Weird Ideas: An Overview of Ruby 1.9Wild & Weird Ideas: An Overview of Ruby 1.9
Wild & Weird Ideas: An Overview of Ruby 1.9
 
Some Rough Fibrous Material
Some Rough Fibrous MaterialSome Rough Fibrous Material
Some Rough Fibrous Material
 
Effective Scala @ Jfokus
Effective Scala @ JfokusEffective Scala @ Jfokus
Effective Scala @ Jfokus
 
WOOD AND ITS DERIVATES
WOOD AND ITS DERIVATESWOOD AND ITS DERIVATES
WOOD AND ITS DERIVATES
 
W504+Asbestos+and+Other+Fibres
W504+Asbestos+and+Other+FibresW504+Asbestos+and+Other+Fibres
W504+Asbestos+and+Other+Fibres
 
Simulation in Design - Dive into ANSYS simulation
Simulation in Design -  Dive into ANSYS simulationSimulation in Design -  Dive into ANSYS simulation
Simulation in Design - Dive into ANSYS simulation
 

Similaire à My First Ruby Programming Experience by Murray Steele (aged 31 1/2

Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosEdgar Suarez
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a bossgsterndale
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1Jano Suchal
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairMark
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record.toster
 
Ex[1].3 php db connectivity
Ex[1].3 php db connectivityEx[1].3 php db connectivity
Ex[1].3 php db connectivityMouli Chandira
 
Ruby Language - A quick tour
Ruby Language - A quick tourRuby Language - A quick tour
Ruby Language - A quick touraztack
 
Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Workhorse Computing
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v RubyJano Suchal
 
RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”apostlion
 
第49回Php勉強会@関東 Datasource
第49回Php勉強会@関東 Datasource第49回Php勉強会@関東 Datasource
第49回Php勉強会@関東 DatasourceKaz Watanabe
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShellGaetano Causio
 
Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Wen-Tien Chang
 
Where's My SQL? Designing Databases with ActiveRecord Migrations
Where's My SQL? Designing Databases with ActiveRecord MigrationsWhere's My SQL? Designing Databases with ActiveRecord Migrations
Where's My SQL? Designing Databases with ActiveRecord MigrationsEleanor McHugh
 

Similaire à My First Ruby Programming Experience by Murray Steele (aged 31 1/2 (20)

Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutos
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a boss
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Php 101: PDO
Php 101: PDOPhp 101: PDO
Php 101: PDO
 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record
 
PHP POWERPOINT SLIDES
PHP POWERPOINT SLIDESPHP POWERPOINT SLIDES
PHP POWERPOINT SLIDES
 
DataMapper
DataMapperDataMapper
DataMapper
 
Ex[1].3 php db connectivity
Ex[1].3 php db connectivityEx[1].3 php db connectivity
Ex[1].3 php db connectivity
 
Ruby Language - A quick tour
Ruby Language - A quick tourRuby Language - A quick tour
Ruby Language - A quick tour
 
Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v Ruby
 
RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”
 
第49回Php勉強会@関東 Datasource
第49回Php勉強会@関東 Datasource第49回Php勉強会@関東 Datasource
第49回Php勉強会@関東 Datasource
 
Magic of Ruby
Magic of RubyMagic of Ruby
Magic of Ruby
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShell
 
Python Workshop
Python  Workshop Python  Workshop
Python Workshop
 
Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Ruby 入門 第一次就上手
Ruby 入門 第一次就上手
 
Where's My SQL? Designing Databases with ActiveRecord Migrations
Where's My SQL? Designing Databases with ActiveRecord MigrationsWhere's My SQL? Designing Databases with ActiveRecord Migrations
Where's My SQL? Designing Databases with ActiveRecord Migrations
 

Dernier

A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024The Digital Insurer
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 

Dernier (20)

A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 

My First Ruby Programming Experience by Murray Steele (aged 31 1/2

  • 2. Me in 2010 Passport photo: Employed by: Codes mostly in:
  • 3. Me in 2003 Passport photo: Employed by: Codes mostly in:
  • 4. I FORSAKE JAVA! I RENOUNCE C++! ALL HAIL the Mighty RUBY!! Riddle: 2.upto(100) { |num| print "ruby is #{num} times better that java or c++" } Work it out... .... if you dare.
  • 5.
  • 6.
  • 7. L LED N CE C A
  • 8. I am all dressed up ready to code me some Ruby. YEAH!!!
  • 9. You don’t even know Ruby, I’ll make sure you do it right.
  • 11. @ -24,8 +24,8 @@ class Config < Hash } end - def initialize(name) - self[Config::Name] = name + def initialize(name=nil) + self[Config::Name] = name if name != nil end end
  • 12. public Config(String name) { if (name != null) { this.put(Config.NAME, name) } } public Config() { super(null); } def initialize(name=nil) self[Config::Name] = name↩ if name != nil end
  • 15.
  • 16. def handleMessage(message) sender = @users.findByEmail(message.header.from[0]) if (sender == nil) return end message.header.delete('From') message.header['From'] = sender.preferredFromAddress processSubject(message) # modify the subject addListHeaders(message) # add the list headers archive(message) # archive the mail processAttachments(message) # modify attachments sendToMembers(message) # send it out. end
  • 17.
  • 18. def processSubject(message) sub = message.header.subject.dup prefix = "[" + @config.listHeaders["List"] + "]" i = sub.index(prefix) sub.slice!(i..(i+prefix.length)) if (i != nil) found = 0 re_str = "re: " i = sub.downcase().index(re_str) while (i != nil) sub.slice!(i,re_str.length) i = sub.downcase().index(re_str) found = 1 end prefix = "Re: "+prefix if (found == 1) message.header.subject = prefix + " " + sub end
  • 19. def processSubject(message) sub = message.header.subject.dup prefix = "[" + @config.listHeaders["List"] + "]" sub.gsub!(/#{Regexp.escape(prefix)}/,'') if sub =~ /re: /i sub.gsub!(/re: /i, '') prefix = "Re: "+prefix end message.header.subject = prefix + " " + sub end
  • 20. # this is a loosely extended Hash with some little # things to make it's use more convenient class ListConfiguration < Hash
  • 21. # this method is a bit overkill, but by caching # the actual list header object instead of having # to get it from the main config hash each time # we should save some overhead. the ListHeaders # part is the most frequently accessed, because # it contains a lot of varied list info. def listHeaders if @listHeaders.id != self[ListHeaders].id puts "recaching" @listHeaders = self[ListHeaders] end @listHeaders end
  • 22. @config[ListHeaders] def listHeaders self[ListHeaders] end
  • 23. def listHeaders if @listHeaders.id != self[ListHeaders].id puts "recaching" @listHeaders = self[ListHeaders] end @listHeaders end # these just resolve to unique strings, # used as keys within the config hash Name = "name" ListHeaders = "listheaders" # this is the root directory BaseDir = "basedir"
  • 24. log("looking at "#{part.header.media_type}"") if !TypesToKeep.include?(part.header.media_type) md = name_regex.match(part.header["Content-Type"]) filename = "null" if md != nil filename = newUniqueFile(AttachmentDir, md[3]) { |f| f.write(part.decode()) } saveAttachmentMetaData(filename, message) else log("error getting filename from "#{part.header["Content-Type"]}"") end size = File.size(File.join( base_dir, @config[AttachmentDir], filename ))
  • 25. def handleMessage(message) sender = @users.findByEmail(message.header.from[0]) if (sender == nil) return end message.header.delete('From') message.header['From'] = sender.preferredFromAddress processSubject(message) # modify the subject addListHeaders(message) # add the list headers archive(message) # archive the mail processAttachments(message) # modify attachments sendToMembers(message) # send it out. end
  • 26.
  • 27.
  • 28.
  • 29. #!/home/room206/bin/narf fucknut_home = '/home/room206/fucknut' $: << fucknut_home # add our library path to the search path require 'web'
  • 30. AddHandler cgi-script .rb RewriteEngine on RewriteRule ^(.*)/archives/(.*)$ /handler.rb?↩ listname=$1&mode=archive&$2
  • 31. listname = Web['listname'] if listname != nil and listname != "" # main processing when we have a listname param else lists = Config.possible lists = lists.collect { |i| {'name' => i, 'value' => i} } if lists != [] Web.print_template("what_list.html", {'listname' => listname, 'listnamevalues' => lists}) else Web.print_template("generalerror.html", {'error' => "No lists defined, get a better admin"}) end end Web.flush
  • 32. <html> <head> <title>Login to {$listname}</title> <link href="/arse.css" rel="stylesheet" type="text/css"> </head> <body> <h1>Error during login to {$listname}</h1> <p>{$error}</p> <narf:form formname="tryagain" method="POST"> <narf:hidden name="listname"> <input type="submit" value="Try Again"> </narf:form> </body> </html>
  • 33. class LoginHandler LoginTemplate = "login.html" LogoutTemplate = "login.html" LoginCommand = "login" LogoutCommand = "logout" def showLogin() @web.print_template( *loginTemplateArgs() ) end def loginTemplateArgs() [LoginTemplate, {"listname" => @web['listname'], "cmd" => LoginCommand}] end end
  • 34. def showLogin() @web.print_template(LoginTemplate, loginTemplateParams() ) end def loginTemplateParams() {"listname" => @web['listname'], "cmd" => LoginCommand} end
  • 35. def showLogin() @web.print_template(LoginTemplate, {"listname" => @web['listname'], "cmd" => LoginCommand} end
  • 36.
  • 37. errors = "" commalist = @web['addresses'].gsub(/n/, ",") list = RMail::Address.parse(commalist) if list.empty? errors += "<br><h2>no valid email addresses given in list</h2>" end user.addresses = list sendTo = RMail::Address.parse(@web['sendto'])[0] sendToString = @web['sendto'] if nil == sendTo errors += "<br><h2>Send To Address is not valid</h2>" sendToString = "INVALID: " + sendToString else sendToString = sendTo.format.to_s user.sendTo = sendTo end
  • 39. if @config[StartYear] < lastyear.year lastyear = "<a href= "#{baseurl}year=#{lastyear.year}&month=#{lastyear .month}">&lt;&lt;</a>" elsif @config[StartYear] == lastyear.year if @config[StartMonth] <= lastyear.month lastyear = "<a href= "#{baseurl}year=#{lastyear.year}&month=#{lastyear .month}">&lt;&lt;</a>" else lastyear = "&lt;&lt;" end else lastyear = "&lt;&lt;" end

Notes de l'éditeur

  1. wont&amp;#x2019; all be about me - but enough to say what kind of programmer i am will focus on code I&apos;m going to talk to you about the first ruby script I ever wrote. It&apos;s called *MY* first ruby, but I promise it won&apos;t all be about me. There&apos;ll be a little bit of personal history, just so you can get an idea of what sort of programmer I was at the time, but most of the talk will focus the script itself.
  2. me now. but, my first code was written in 2003 so.... First the personal history which I promise won&apos;t take up much time. This is me now. But I wrote this script in 2003. not 2010. So we have to go back in time. So step into my time portal and come with me to the late summer of 2003.... [doodley doo] [doodley doo]
  3. 2003. different. just leaving apr. hired as java, did c++, vb, python as well. not very oo python - gui based + tuple heavy no ruby, just a tase for dynamic via python still a java - starting at empower (sms place) in a few mnths -- Cast your mind back. Things were different then. Summers were longer, the sun was brighter, I had more hair and ruby 1.8.0 (the grandfather of the version you all know and love) had only just been released that August. I was in the dying stages of my first job out of university. I was interviewed to work primarily as a Java programmer; but I ended up doing a load of C++, a bit of VB and some Python GUI development. It was a great company, but the management decided to move the company offices to Cambridge, which I felt meant I was living the wrong way around; you commute *into* London, not out of it. I&apos;d never done any Ruby programming, but I did have a taste for dynamic languages having done a lot of (not very OO) Python programming (we used tuples a lot). I still considered myself a Java programmer, not least because I&apos;d just accepted a job at a Java-based SMS gateway company.
  4. James Adam. PhD not job. toyed java + python. settled on ruby 2001. wouldn&amp;#x2019;t stop going on about it. actual quote from mailing list. ----- I heard of Ruby because my friend, James Adam (pictured), had been doing a PhD instead of working and while he flirted with Java and Python for a couple of early installments of his thesis code, he finally settled in about 2001 on Ruby for the majority of his code, and wouldn&apos;t stop going on about it to anyone that would listen. The prime vehicle for James&apos; evangelism of Ruby was to send messages a mailing list that our university classmates had set up to keep in touch after we graduated.
  5. mailing list on egroups. swanky for 2000. (files, polls, etc...) *click* bought by yahoo. got rid of the yellow. started dropping mails or taking days to deliver them. *click* write our own. James said &amp;#x201C;ruby&amp;#x201D;. for no reason we agreed. -- This mailing list was originally running on egroups. Which was fine. It hosted files and polls and had a web interface. It was pretty sweet for the year 2000. *click* Then they got bought by Yahoo! and it became Yahoo! Groups! Basically the same service, but it had a red and blue logo instead of a purple one, and yellow was banished from the UI. However, and here&apos;s where we finally get to the ruby script, Yahoo! Groups! started dropping emails intermittently, or taking several days for emails to show up. As you can imagine, this sort of delay in the insane ramblings of a group of 20-somethings debating the merits of their first jobs during the dot-com era was TOO. MUCH. TO. BEAR! *click* So despite thousands of available choices, and even a single click install of mailman on our shared hosting, we decided to write our own replacement. James somehow convinced us that we should write it in ruby even though he was the only one who knew any ruby. James knocked up a simple test script as a proof of concept and we decided to go ahead.
  6. mailing list on egroups. swanky for 2000. (files, polls, etc...) *click* bought by yahoo. got rid of the yellow. started dropping mails or taking days to deliver them. *click* write our own. James said &amp;#x201C;ruby&amp;#x201D;. for no reason we agreed. -- This mailing list was originally running on egroups. Which was fine. It hosted files and polls and had a web interface. It was pretty sweet for the year 2000. *click* Then they got bought by Yahoo! and it became Yahoo! Groups! Basically the same service, but it had a red and blue logo instead of a purple one, and yellow was banished from the UI. However, and here&apos;s where we finally get to the ruby script, Yahoo! Groups! started dropping emails intermittently, or taking several days for emails to show up. As you can imagine, this sort of delay in the insane ramblings of a group of 20-somethings debating the merits of their first jobs during the dot-com era was TOO. MUCH. TO. BEAR! *click* So despite thousands of available choices, and even a single click install of mailman on our shared hosting, we decided to write our own replacement. James somehow convinced us that we should write it in ruby even though he was the only one who knew any ruby. James knocked up a simple test script as a proof of concept and we decided to go ahead.
  7. mailing list on egroups. swanky for 2000. (files, polls, etc...) *click* bought by yahoo. got rid of the yellow. started dropping mails or taking days to deliver them. *click* write our own. James said &amp;#x201C;ruby&amp;#x201D;. for no reason we agreed. -- This mailing list was originally running on egroups. Which was fine. It hosted files and polls and had a web interface. It was pretty sweet for the year 2000. *click* Then they got bought by Yahoo! and it became Yahoo! Groups! Basically the same service, but it had a red and blue logo instead of a purple one, and yellow was banished from the UI. However, and here&apos;s where we finally get to the ruby script, Yahoo! Groups! started dropping emails intermittently, or taking several days for emails to show up. As you can imagine, this sort of delay in the insane ramblings of a group of 20-somethings debating the merits of their first jobs during the dot-com era was TOO. MUCH. TO. BEAR! *click* So despite thousands of available choices, and even a single click install of mailman on our shared hosting, we decided to write our own replacement. James somehow convinced us that we should write it in ruby even though he was the only one who knew any ruby. James knocked up a simple test script as a proof of concept and we decided to go ahead.
  8. Thus, my first ruby. but, James wrote initial prototype and James and I got together 13th Sept 2003 to write it *click* -- And so, this talk is called &amp;#x201C;My First Ruby&amp;#x201D;, and while it&amp;#x2019;s true that it&amp;#x2019;s my first ruby. It&amp;#x2019;s not like I wrote it alone. After James wrote the prototype we both had a free weeken on the 13th September, 2003 and knocked up the initial version together. *click*
  9. Not all my own work. Had guidance. Much like most of you. github, peers, lrug, colleagues. pair programming. let other people look at your code. My first ruby script was written under the guidance of someone who had been using it for a couple of years. This probably isn&apos;t that different to many of you though. Any code you&apos;ve written has probably had the benefit of other people at your job working on the same project, if you&apos;re lucky you&apos;ve even been pair programming with them. Even if it&apos;s code you&apos;ve written at home, chances are you&apos;ve put it up on github and have the chance of thousands of rubyists looking at it. Don&apos;t be scared of letting other people look at your code.
  10. aside: Chris Lowis - blog post blog.chrislowis.co.uk rails apps to read read code = learn from the author. read code = teach the author. read code = good. -- Speaking of reading code, Chris Lowis, LRUG&apos;s resident podcaster, recently wrote a great blog post about open source rails projects and what you can learn from reading the source of them. I think it goes both ways, if you read other people&apos;s code you learn a lot, if you let other people read your code, they can learn, but so can you when they critique it, or suggest patches for edge-cases you didn&apos;t cover or even a neat re-factoring. It&apos;s also a nice confidence boost when you read some code for say, gemcutter, and you notice something you think could be improved. Anyway, aside over.
  11. my first commit. (dated lunchtime on sat - so probably not first ruby - ethernet cable). CVS! why this change? empty constructor for YAML? Just error checking? But, could just be excitement at ruby. Compare with: -- So. Here&amp;#x2019;s the first code I commited to the project. This probably isn&apos;t the actual first bit of ruby I ever wrote. As I already mentioned, James had hacked up a prototype and the first commit to our source control was timed at around lunchtime on the Saturday. It&amp;#x2019;s 7 years ago so while I can&amp;#x2019;t remember exactly what we did that day, and I do recall spending a lot of the morning hunting around for a spare ethernet cable. I&apos;m pretty sure we did some hacking on the code before we decided that CVS might be a good idea. Anyway, I think there&apos;s plenty in here that&apos;s worth talking about. Why did I add this &quot;empty constructor&quot;. The commit message says it&apos;s to allow YAML to make the object good. I&apos;m not sure I know what that means, and on the face of it, it looks like I&apos;m just stamping some error checking on here. I suspect however I was just experimenting with all the fun new things that Ruby can do.
  12. Java programmer in 2003. vs Ruby. (return arrow to indicate line break) LESS CODE! if statement as modifier (love it!) (also, less {} syntax) be fair - Java syntax not as bad as now (generics, annotations, etc...) (finally, the current version of this code no modifier, unnecessary) first committed ruby -&gt; onto system as a whole -- To compare what this felt like to me in 2003 it&apos;d help to compare the final code to how I&apos;d do the same thing in Java. (I think, my Java is rusty). This was pretty amazing! So much less code! First, there&apos;s the fact that by allowing default values in method signatures I can get rid of that entire 2nd constructor. Then there&apos;s using the if statement as a statement modifier, by placing it at the end. I don&apos;t know why, but I&apos;m a massive fan of this format, and I think it&apos;s one of the reasons that I get ruby. It just reads so neatly. Finally, there&apos;s a lack of extraneous syntax. But, you didn&apos;t come here to listen to a talk about why ruby syntax is better than java&apos;s. And to be fair to Java in 2003, the syntax is nothing like the mess it is now with generics and annotations. I think having shown my first &quot;committed&quot; ruby, it&apos;s time to talk about the system as a whole rather than go through it and pick holes in every commit of mine.
  13. breif warning. named after favourite insult. possibly placeholder. have to say it + show it on screen a few times. I give you.... -- Now I have to warn those of a sensitive nature, for reasons best left unexplored we decided to call our new mailing list software after a favourite insult from our university days. And I&amp;#x2019;m not going to be able to avoid saying it or showing it on screen, so I have to warn you; * click *
  14. Fucknut. what is it? less good version of mailman. (someone keeps asking on twitter, mailinglist = python? why?) 2 parts - mail processor + web front end -- We called it Fucknut Fucknut is a mailing list with an attached web front-end for viewing the archived messages and attachments and managing your user account. Basically, it&apos;s a less accomplished version of mailman.
  15. talk about mail processor. mostly based on james initial prototype. procmail -&gt; unix tool .procmailrc -&gt; regexp match run a script (+ stop, or continue) kinda like routes.rb pipes mail via STDIN to script. ours match on TO or CC addr for list, run slim handler script for that list. handler sets up env, parses mail using Rmail (not tmail or mail) (at the time, tmail not as good apparently) yaml::syck (now default?) for config + users db net::smtp sending mail get&amp;#x2019;s mail as rmail obj does some stuff to it archives it sends to users. simple. lets look at some code. -- The main component of fucknut is the part that processes mail, and this is also the oldest part of it as it&apos;s based on an organic growth out of james&apos; inital prototype. It starts with a .procmailrc file. For those that don&apos;t know, procmail is a UNIX tool that you can get to run against every mail that is delivered to your shell account and the .procmailrc file controls it. You can think of it like a rails routes.rb file for mail (except it doesn&apos;t use ruby or have a nice dsl). [ SHOW .procmailrc HERE ] You define a regexp to match against some part of the incoming mail and if it matches you can decide what to do, for example forward the mail to /dev/null, or invoke a script on it (it passes the mail in via STDIN). You also decide if you want to stop processing or continue to see if it matches other rules. For fucknut we have a rule that matches against the TO (or CC) address, and if it matches the list address we ask procmail to invoke a list handler script for that list. These handler scripts are slim wrappers that set up the environment for the mailing list processor and then pass it the mail as a ruby object. We use Rmail for this (not Tmail or Mail which you may be more famlilar with)). This mail part of fucknut also uses YAML::Syck (which I think is now default in 1.8.x so you&amp;#x2019;re just using YAML now) to deal with some configuration stuff and Net::Smtp to send out email. We&amp;#x2019;ll cover this in detail later, but having received an email it stores it in an archive db and then sends it on to the other users on the list.
  16. the main method of the script. route of a mail object through the system Just talk, briefly, about each method. now, some bits of the system in detail. -- This is the main method from the mail processing script and it described the route that the mail takes through the system. First thing it does it make sure that the sender of the mail is one of the users. Because we&apos;re all digital natives a user is allowed to have several email addresses attached to their account and can post from any of them. (@users is our list of users) If it&apos;s not a valid user the mail is discarded. If it is a valid user we continue processing it. The next thing we do is set the from address to the user&apos;s preferred posting address. I might send email from my work account, but I don&apos;t want people using that account to mail me (and this was important at the time because my work email address changed about 4 times as the company underwent furious rebranding every few months at the whim of our VCs) so we tell fucknut to make it seem like all my mail comes from my personal account. We then massage the subject of the mail to add our list identifier and keep &amp;#x201C;re:s&amp;#x201D; down to a minimum Then we do various things to the headers, mostly required of us by the 12 hundred RFCs that there are about mailing lists. Then we process any attachments to save them to a separate data store. And, because we wrote this in 2003 when many people were still on dialup, remove any attachments over a certain limit. Then we archive the message to our database (for which read dump the raw mail to disk). Finally, we go through the complete user list and send the mail out to all the users. Including the sender. And that&amp;#x2019;s it. That&amp;#x2019;s Fucknut at a glance. Now I&amp;#x2019;ve described the system I&amp;#x2019;ll go over some of the code that I think is particularly terrible.
  17. first code show = subject massaging. first an aside. asked about the whole idea of new list = not much response. asked about position of listname + re:&amp;#x2019;s in subj = 3 days of furious debate don&amp;#x2019;t bikeshed if you don&amp;#x2019;t have to. just do. -- Originally we were just going to add the list name in square brackets to the start of the mail, but then we realised we had to do something to prevent various mail clients messing up the re: re: re: re: stuff. After a tortuous requirements gathering [ SHOW SCREEN SHOT OF re: DISCUSSION ] thread we decided to settle on [list name] &lt;original subject&gt; and Re: [list name] &lt;original subject without any re:&gt;. As you can might be able to see, this took 3 days and 23 messages to argue about and decide to do the thing we were going to do anyway. A further argument, if you ever needed it, for not starting a bikeshed discussion if you can possibly get away with it. There was nothing else about this app that involved this level of debate. *click*
  18. code for adding listname in [] + removing all re: and adding one at start if any found painful. use regex, not slice. Something I was doing 2-3 years later when working in ruby professionally. (Jon Lim) Probably java. strings = immutable, work with stringbuffers to avoid memory regexp new / bad. -- This code is pretty bad. I&apos;m massaging a string, I should be using regexp here. I don&apos;t necessarily agree with using regexp for everything but clearly doing all this string manipulation here it would be much better done with regexp. It wasn&apos;t until a few months into doing ruby professionally (2005-ish I think) that Jon Lim (who I was working with at the time) asked my why I kept using .slice and [] all the time instead of .gsub with a simple regexp. I think this was a hangover from my Java days where regexp&apos;s were (percieved?) as slow and crappy, and strings were immutable so you did everything with stringbuffers.
  19. I&amp;#x2019;m pretty sure, that even despite using regexps, this is easier to read and understand what&amp;#x2019;s going on.
  20. Next thing - add headers from list config. object extended from Hash? Why? more convenient? huh? hash is pretty convenient. should just have a hash inside + delegate the specific bits of api we want. as an aside, I made this same mistake at uni. first project with java. clearly not learned. The next thing we do is add list headers. We store those as part of the system config and have this object ListConfiguration, &amp;#x201C;an extended Hash with some little things to make it&amp;#x2019;s use more convenient&amp;#x201D;. As a comment at the top. You know what, I&apos;ve never *ever* used a hash in ruby and thought, &quot;Gee, I wish this was more convenient&quot;, I can&apos;t say I even really think that the new 1.9 hash syntax is that much better. It also shouldn&apos;t extend Hash, it should contain a Hash and delegate the bits of the Hash API that I want and then provide it&apos;s own methods where I want more convience.
  21. one of those methods. clearly premature optimisation. hash is yaml loaded. maybe misunderstood? still. don&amp;#x2019;t optimize if you don&amp;#x2019;t have too. also, what would be more convenient is this: -- Clearly there&amp;#x2019;s some insane premature optimisation going on here. Maybe I misunderstood yaml backed Hashes and though it would always be hitting the YAML file. Even if I did, don&amp;#x2019;t optimize until you have to. Given all that, you know what would be more convenient than having to write that method in the first place: *click*
  22. we mostly assigned instances of ListConfiguration to a @config variable when we use it. Or, if I wanted to save 1 char typing whenever I accessed the list headers. *click* But, really, the first thing is better, I&amp;#x2019;ve genuinely no idea what was going on here.
  23. go back to existing method. using constants as keys? all defined at top of config module *click* why? again, java hangover? &amp;#x201C;magic&amp;#x201D; strings create only once as public final static String. but ruby == symbols? distinct lack of symbols in here, probably didn&amp;#x2019;t know about &amp;#x2018;em or maybe, want error if typo? constant missing vs. nil down the road? -- The final weird thing about this ListConfiguration object is, if we look back at that listHeaders method you&apos;ll notice, we don&apos;t use symbols or strings as the keys into the hash. We use Constants, which are defined at the top of the Config module. For example: *click* Why? I don&apos;t know. I just doesn&apos;t make any sense, until you remember my Java routes where these sorts of &quot;magic&quot; strings would be defined as public static Strings because you&apos;d only want to create that object once (woe betide the Java programmer in the early 2000&apos;s who went around creating more objects than he strictly needed to). Thing is, Ruby has symbols which save one char on typing a string and are more idiomatic. I can only assume I didn&apos;t know about symbols when I wrote this. I probably wanted some comfort that I wasn&apos;t spelling a config key wrong and causing a nil error so shied away from using strings. Using Constants means I&apos;d get a runtime error saying there&apos;s a missing constant instead of a nil error somewhere down the line.
  24. fragment from attachment processing. message is multipart, do this on each part. code actually not bad. seen most work as we see new messages and clients (richer + richer). loads of edgecases. &amp;#x2018;cept a bug in it I fixed 2 weeks ago. multipart/alternative means nesting multipart messages within parts. so the multipart/alternative part doesn&amp;#x2019;t have a filename no filename log error then *CONTINUE TO USE* highlights 2 things: 1. TDD should have caught it? 2. real world data hates you. -- The next bit of code to talk about is this, it&amp;#x2019;s a chunk of the attachment processing method. We get here if the message is multipart, and this fragment is run on each part. we extract and store all the attachments, but we also remove them entirely if they are over a certain size This code, is actually not too shabby. It&apos;s quite long because there are loads of edge-cases. And over the years, this is the part of the code that&apos;s seen a lot of changes. Turns out multipart mime messages are hard and you can nest things and it gets weird. Whatever your naive approach is, it&apos;s going to crumble as people send richer and richer messages from more and more esoteric mail clients. In fact I had to fix a bug in it only a month ago, someone&apos;s mail client started sending nested multipart messages with multipart/alternative. What you&amp;#x2019;re looking at is what the code used to look like. Some of you may already have noticed the bug. If the part of the mail we&apos;re dealing with didn&apos;t have a filename (such as multipart/alternative which is effectively nesting another set of parts), then our code logs the error but then blindly continued on assuming that it has a filename it can do something with. It never came to bite us until someone sent an multipart/alternative message. This is something I hope would have been fixed by TDD. With TDD I&apos;d probably have mocked the logging or file.size calls and built the function up slowly. But, I may not have caught it with TDD because I might never have thought to try out a nested multipart file. The other thing it shows is that the real world will always conspire to break your code. If I had tests though I probably would have been able to feed the mail that broke it into the test suite and find out what tests suddenly failed.
  25. I&apos;ve covered a few of the little refactorings I&apos;d do to individual methods, but I think the whole thing could do with an overhaul. It&apos;s pretty much one class and it does everything. I think something like this: *click*
  26. it&amp;#x2019;s a pipeline. one thing shouldn&amp;#x2019;t have all those responsibilities. each thing takes a mail message and passes it on. like rack, but for mail. Even have simple API like Rack. test in isolation. add new ones. etc... -- might work. It&apos;s really a pipeline and, like Rack, it might make sense to have a chain of smaller classes linked together, they all take in a mail message and do something to or with it and then pass it on. That way everything can be tested properly in isolation and it reduces the coupling between things.
  27. that&amp;#x2019;s the mail. now for the webhead. 2003 -&gt; web dev in ruby a pain. little libs, nothing great. also, no gems or (not sure, rubyforge). used raa (hangs off ruby-lang.org, so pretty official) no hosting, just pointers to stuff. all browser, except, raa-install (quite new) would scrap (api?) and install (no deps) most libs used ruby setup.rb so was some standard there. not sure why gems came along. probably interesting story there. aside: some gem hate right now. not really a problem. it replaced this, so if something replaces it, whatevs. -- That&apos;s the first part of fucknut covered, so now we&apos;re onto the 2nd part, the web front-end. 2003 was a dark time for web development in ruby. There were lots of little libraries, but nothing as comprehensive as Rails, certainly not the rails we have now, but not even the rails we got in 2005. The thing that may surprise you is that we didn&apos;t even have gems back in 2003. The first release of gems was in March 2004. To find ruby stuff we scoured something called the Ruby Application Archive which was a website similar to what rubyforge is now, where you could list ruby projects and categorise them. Except it did no hosting, you just pointed the links to where the data was. On top of this, someone wrote a program called raa-install, which would go and find projects on RAA, download them and install them. At this point most libraries, if they had any installation, used the ruby setup.rb incantations, and raa-install would run those for you too. It didn&apos;t do dependency graph information, but that&apos;s because this info wasn&apos;t on RAA. The thing that&apos;s not clear to me looking back, is why gems and rubyforge came along when there was already this in place. I&apos;ve not looked into it nor looked at the code. There&apos;s probably an interesting story there. So, as an aside, I know there&apos;s a bit of hate for gems right now, mostly about issues over dependencies and keeping applications and system stuff independent. I wouldn&apos;t worry about it though, gems is not the first ruby code distribution and management system that&apos;s existed, so maybe it won&apos;t be the last. If it&apos;s served it&apos;s purpose maybe it is time to move on; be that bundler, rip or something else.
  28. chose narf. sound -nve, but was good at time. also 0.3.4 went on to 0.7.3 by 2005 before death (rails?) docs imply good things for future versions (i&amp;#x2019;ve never used) came with testing framework (we didn&amp;#x2019;t use) surprising to me looking back as I assumed rails was the first to put it front-and-center but narf does it too. -- Anyway, after a couple of false starts we settled on something called narf, which appeared to be the most high-level thing at the time. Now, remember this is me talking about narf as it was in September 2003. It was version 0.3.4 then, and it got up to 0.7.3 before development appeared to stop in 2005, and some of the docs imply it was headed in a direction that would abstract things further.
  29. weird == not just lib, is executable too don&amp;#x2019;t use ruby, use narf. (actually docs say just to help with funnelling errors back up cgi to webserver) also, loadpath massaging, probably not many in the room have done this. -- So, the first thing that seems weird to me, is that narf isn&apos;t just a library that you include into your scripts. It also comes with an executable. As it turns out this executable is just to redirect stderr into the cgi environment properly and redirect any exceptions and errors back into the cgi environment. But this fact is buried as an aside in the docs. It&apos;s weird.
  30. first missing - no routes not high level. abstracts CGI only. run as cgi script (can do fastcgi if you build/compile/install that too) or via mod_ruby (original, not passenger - flawed though) want nice urls or routing, do it with webserver config. this is ours. it&amp;#x2019;s a pain so we didn&amp;#x2019;t do it much. (think early versions of rails did this too) -- There&apos;s no nice abstraction of routes, but then that&apos;s not surprising, it&apos;s not a higher-level MVC framework. It&apos;s a web framework. It abstracts the first level of CGI interaction, it doesn&apos;t build on top of it to give you what rails or sinatra gives you. So, you run it as a CGI script, although you could have used mod_ruby (no, not passenger, the mod_ruby that existed ages ago that no-body used). And if you&apos;ve installed ruby_fastcgi you can run it as fastcgi. So, it&apos;s very bare bones, if you want fancy urls you have to write them yourselves using RewriteRule directives. This is ours: We clearly got bored, as there are a few more URLs in the webhead other than looking at the archives, but we clearly couldn&amp;#x2019;t be bothered with making them pretty. &amp;#x2018;cos we&amp;#x2019;d have to write them in this non-expressive regex format. That said, I&apos;m pretty sure early versions of rails asked you to do the same thing. I could be wrong though. Already it should be clear we are working at a lower level of abstraction. We&apos;re close to the metal here.
  31. fragment of main cgi handler. my naive stylings (4 spaces, collect not map) + a lot of narf api narf gives Web object. Web[] = params Web.print_template = render templates Web.flush = send it to the browser -- Having asked apache to invoke your script, you require the narf libraries and this gives you a Web object. This is what you interact with to communicate with the webserver. This is a fragment of our main cgi script Apart from showing off my naive ruby stylings (4 space indent! collect instead of map!) this explains a lot of the narf api. Web[] to get params that were sent with the request, and Web.print_template to invoke some template processing on a file providing a list of variables and Web.flush to send everything back to the webserver.This is what that template looks like:
  32. an example narf template 2 ways of rendering data {} -&gt; moustache style eval expressions hash param to print_template -&gt; $vars for {}&amp;#x2019;s &lt;narf:&gt; tags for more complex stuff this kind of thing comes around alot view code == code (erb style) view code == markup (this style) only radius does this for rails (I think). seen it alot in other langs though. some narf tags emit things (&lt;narf:foreach&gt;) for use in {} probably could write own for more complex views. that&amp;#x2019;s narf. now back to fucknut (and my code). -- As you can see here, there are 2 ways of rendering data in these templates. The first is that, moustache style, it&amp;#x2019;ll evalutate and render the results of any expressions within {} braces. The key/values in the hash you provide to the print_template are available as $vars for evaluation. Much like the :locals hash when rendering a rails partial. The other way of interacting is to use these &lt;narf:&gt; prefixed tags. Think of them like rails helpers, except instead of looking like code, they look like html. The web community swings back and forth on this sort of thing every so often, should the code in our views look like code (front-enders keep your hands off!) or should it look like markup (front-enders get stuck in!). I can think of only one templating engine for rails (radius, comes with radiant) that does it this way though. Some of the narf tags would emit things, like &lt;narf:foreach&gt; so you could use what that emits in the curly braces. But as far as I can tell, it was just expressions, no logic. If you wanted logic you used the narftags. Anyway, that&apos;s a whistlestop tour of narf as it was. To be honest it&apos;s clearly early days and some of the things littered in the documentation suggest it was pointed in the right direction (it came with a testing framework and the docs suggested building the app test first using that framework).
  33. fragment of the login handler. showLogin is render action for the login command. not logged in show this. strings as constants, for no reason. abstracted .print_template into calling + args. can&amp;#x2019;t say why. never call .loginTemplateArgs elsewhere. probably just excitement about splat. maybe this makes sense: *click* -- showLogin is effectively the render action for the &quot;login&quot; command. If the user isn&apos;t logged in we want to show them the login page, we&apos;ll call .showLogin on the LoginHandler. So, what&apos;s going on here? Again I&apos;ve defined strings as constants when they really didn&apos;t need to be, it&apos;s all internal. I&apos;ve also, for no reason abstracted the call to print_template out into calling it and the args I&apos;d want to pass to it, I think it was just excitement at using the splat operator. Nowhere in the code do I ever call loginTemplateArgs except in this method, and I can&apos;t think where I&apos;d want to given that the showLogin method is simply a pass through to print_template. The only way this might make sense were if it was like this: *click*
  34. if show login ui in other page + want params for it? but I don&amp;#x2019;t. pointless complication before I need it. should be: *click* -- Maybe somewhere else (and I use this form for other handlers and their &quot;actions&quot;) I might want to render a template that shows a fragment of the login ui, and so grabbing the params that it needs from the Login Handler might make sense. But I don&apos;t. This just makes it more complex, and it should really be: *click*
  35. It&amp;#x2019;s just simpler.
  36. talked about loginhandler. narf not mvc. no controller framework. we rolled our own (ish). main handler looks at &amp;#x2018;cmd&amp;#x2019; param and forwards to one of: login, user, archive. similar .new takes Web object .do method looks at &amp;#x2018;cmd&amp;#x2019; to work what todo. e.g. user -&gt; &amp;#x2018;update&amp;#x2019; changes current user via POST params archive -&gt; &amp;#x2018;showmsg&amp;#x2019; looks for and displays single message login -&gt; &amp;#x2018;logout&amp;#x2019; terminates session differences User + Archive .do is render endpoint. LoginHandler .do isn&amp;#x2019;t an endpoint. it returns session .showLogin or .showLogout does render could make them same, but I didn&amp;#x2019;t. have something else deal with is user logged in. we .do loginhandler at the start to get session, then look at &amp;#x2018;cmd&amp;#x2019; framework = rules. go it alone = inconsistency without thought. finally, surpised by how much code == routing. something not seen in rails (it does it for you). -- So. You&apos;ll have noticed that I talk about LoginHandler. Narf doesn&apos;t give you any controller framework, so we came up with our own. There are 3 handlers: LoginHandler (deals with login), UserUpdater (deals with letting the user manipulate their details from the user database) and ArchiveDisplayer (deals with showing archived messages). They all have the same constructor: takes the Web object and the path to the fucknut root. They all have a .do() method. This do method looks at the &apos;cmd&apos; param of the Web object and acts accordingly, for example if the &apos;cmd&apos; is &apos;showmsg&apos; in the ArchiveDisplayer, it finds the requested msg and displays it. If the &apos;cmd&apos; is &apos;update&apos; in the UserUpdater it fetches the current user&apos;s details and updates them based on POST&apos;ed params. That&apos;s where consistency ends though. LoginHandler returns a session object (our own wrapper to a couple of methods on Web) and has other methods for rendering templates like showLogin above. For the other 2 calling .do is effectively an action endpoint and that handler will do everything from then on. It&apos;s clear that Login and the other 2 aren&apos;t really the same sort of thing, and yet I&apos;ve made them look the same, or, the fact that I was using them differently meant I should have realised that they were different things, or that I was doing something else wrong. A LoginHandler could easily have acted like the other 2 (where .do() is a render endpoint) if I&apos;d had some other object that deals with is the user logged in or not. Frameworks give you rules and consistency, when you go it alone without much thought you end up with messes like this. Also, looking at the code for the main handler script and these other &apos;cmd&apos; handlers I&apos;m amazed at how much plumbing has gone into *my* code to determine what to do based on the params, as opposed to actually doing it. With a higher level abstraction (like rails or sinatra routing) I just get on with saying &apos;this url =&gt; this code gets run&apos;.
  37. fragment from when &amp;#x2018;update&amp;#x2019; cmd all code in .do method! not even refactored out to method for update or show 2010 me reels! not 2003 though + more: view code! model code! - data conversion - data validation ugh! should have a model for this! we even have a User class. but it doesn&amp;#x2019;t abstract this. if you save it with bad data, you have bad data. -- Let&amp;#x2019;s look at some code in one of those handlers. UserUpdater. So, this is some of the code that is run when the &amp;#x2018;update&amp;#x2019; command is sent to the handler. This code is directly inside the UserUpdater.do method. It&amp;#x2019;s not even refactored out into it&amp;#x2019;s own method! My mind reels at this nowadays, but clearly not in 2003. We&apos;ve got: view code - I&apos;m building html fragments that later I send to a template. model code - data conversion - converting params from strings into objects data validation - checking that the data isn&apos;t nil or invalid Clearly there&apos;s the shock that I&apos;ve had to hand code all this, then there&apos;s the shock that it&apos;s all there mixed up in one method and finally that although we have a User class it didn&apos;t even cross our minds to keep this logic inside there. There&apos;s nothing inside that User object that deals with validation. If I didn&apos;t check that the sentTo mail address wasn&apos;t valid here, it would be saved by default by the User when I asked it to later in the method.
  38. finally, archive displayer. wrote this in jan 2004 (week between jobs) bit better. more refactored. but it&amp;#x2019;s pretty simple provides web view of a directory structure on disk same flaws as other code: view code, bad styling, etc... but some code could be a view helper in a rails app (actually, one bug surfaced recently Contract of Ruby&amp;#x2019;s Dir.open is that the files come in disk order, but on a 64bit server (with ruby 1.8.4 at least) random order... still need to fix that) -- Finally, I want to show some code from the Archive Displayer. The code in here isn&apos;t actually so bad. Maybe it&apos;s because I wrote this in January 2004 in the week I had off between jobs. I was clearly more learned, or maybe it&apos;s because one you get past the web stuff, what the code is doing is fairly workmanlike. It just has to go through our disk based archive structure: archive/&lt;year&gt;/&lt;momnth&gt;/message_id.msg And display them, it has some of the flaws already discussed in that everything happens in one class even when it probably could be decomposed more, it has a mix of view code and model code. But actually, looking back at it, although the framework is unfamiliar, some of the methods look not too far from what I&apos;d write as view helpers today. Apart from this one: *click*
  39. fragment for page navigation this gets link for going to the previous year. nothing *so* bad about that although, using the same var for a date + the output string is odd except: repeats this *exact* code for getting prev month, next month, next year. exactly same logic, just different vars! prolog example form uni &quot;Surely we put this sort of copy and paste behind us in CS1001?&quot; apparently not. -- That doesn&apos;t look *too* bad, until you look at the rest of the method (this is just a fragment). That last if statement (which gets the navigation link or placeholder for taking you from the page you are on to the page for the previous year) is repeated almost in it&apos;s entirety to get the navigation links for the previous month, the next month and the next year. I remember during my final year in university doing a prolog exercise and getting a good mark for it because somehow I&apos;d managed to bend my mind into making it do a reasonable job at playing noughts and crosses (for a partial board) without using all the memory on the planet. However one of the negative remarks was for a section of code where I&apos;d repeated some lines without abstracting them into another method call. &quot;Surely we put this sort of copy and paste behind us in CS1001?&quot; Apparently almost 4 years later I was still doing it.
  40. before I go: why get up and show off your code. as I said, good to show code, you learn, we learn. hopefully inspired you to do so: either: not embarrassed it&amp;#x2019;s better than this scared I&amp;#x2019;ll do a series on every bit of ruby I&amp;#x2019;ve ever written cheerz -- So that&apos;s my first ruby. And before I go, I just want to explain why I gave this talk. Mostly it&apos;s because I hope that after having me come up here and show you this code from 7 years ago, and how pretty bad it is more people will want to get up and show off their code in future meetings. Either, as I&apos;ve intended, by showing that everyone writes bad code and you needn&apos;t be worried about it. Or, unintentionally, because you&apos;re worried that if you don&apos;t I&apos;ll turn this into a series of lectures where I talk you through every piece of ruby code I&apos;ve ever written. Thanks.