SlideShare a Scribd company logo
1 of 46
Download to read offline
the
magic
 of
ruby
gabriele lana
gabriele.lana@cleancode.it
  twitter: @gabrielelana
once upon
a time there
    was a
 developer
  working
 for a big
  company
in the beginning...



                   it’s very
                   easy, we
                      need
                  something
                  quick and
                    dirty...
in the beginning...



                1.   login
                2. go to a
                     page
                3. scrap a
                   number
                4. that’s it!!!
ok!
enter mechanize + nokogiri

require "mechanize"

agent = Mechanize.new do | agent |
  agent.user_agent_alias = "Linux Mozilla"
end

agent.get("http://example.com/login") do | login_page |

  result_page = login_page.form_with(:name => "login") do | login_form |
    login["username"] = username
    login["password"] = password
  end.submit

  result_page.search("//table[starts-with(@class,'boundaries')]").map do | option_table |
    { "name" => option_table.search("./caption/child::text()")
      "credits" => option_table.search("./descendant::td[position()=3]/child::text()")
    }
  end

end
and then...


                good, but
               we’d like to
              extract more
              informations
               from a few
                different
                  pages
enter commander



      describe arguments

command :is_registered do | command |
  command.syntax = "is_registered --username TELEPHONE_NUMBER [ --without-cache ]"
  command.description = "Check if user is registered"
  command.option "-u", "--username TELEPHONE_NUMBER", String, "user's telephone number"
  command.option "-n", "--without-cache", "bypass user's profile informations cache"

  command.when_called do | arguments, options |
    options.default :username => "", :without_cache => false
    ok(is_registered(options.username, options.without_cache))
  end
end

       extract code into functions
use page object pattern




def is_registered(username)
  browse do | agent, configuration |
    LoginPage.new(
      agent.get(configuration["login_page_url"])
    ).is_registered?(username)
  end
end
use page object pattern
class LoginPage < PageToScrub

  def is_registered?(username)
    begin
      login(username, "fake password")
    rescue WrongPassword
      true
    rescue NotRegistered, WrongUsername, WrongArea
      false
    end
  end

  def login(username, password)
    check_page(
      use_element(:login_form) do | login |
        login["username"] = username
        login["password"] = password
      end.submit
    )
  end

  def login_form
    @page.form_with(:name => "login")
  end
use page object pattern
  class LoginPage < PageToScrub

    def is_registered?(username)
      begin
        login(username, "fake password")
      rescue WrongPassword
        true
      rescue NotRegistered, WrongUsername, WrongArea
        false
      end
    end                                          useful   abstractions
    def login(username, password)
      check_page(
        use_element(:login_form) do | login |
          login["username"] = username
          login["password"] = password
        end.submit
      )
    end

    def login_form
      @page.form_with(:name => "login")
    end
use page object pattern


 class PageToScrub

   ...

   def use_element(element_name)
     element = self.send(element_name)
     raise MalformedPage.new(@page, "unable to locate #{element_name}") if (
       element.nil? || (element.empty? rescue true)
     )
     return yield(element) if block_given?
     element
   end

   ...

 end
after a while...



   ...few pages
       my A@@
  45 pages and
  93 different
     pieces of
        data
i need to
feel more
confident
with this...
rspec is your friend :-)


describe "is_registered" do

  context "XXX3760593" do

      it "should be a consumer registered" do
        result = command(:is_registered, :username => "XXX3760593")
        result.should_not be_an_error
        result["area"].should == "consumer"
        result["registered"].should == true
      end

  end

end
and then...



              obviously
              not all the
              requests
              can be live
               on our
               systems
enter the cache

def browse
  begin
    cache = CommandCache.new(database_path)
    configuration = YAML::load(File.open(configuration_path))
    agent = Mechanize.new do | agent |
      agent.user_agent_alias = "Linux Mozilla"
    end
    yield(agent, configuration, cache)
  rescue Mechanize::ResponseCodeError => error
    failure(LoadPageError.new(error))
  rescue Timeout::Error
    failure(TimeoutPageError.new)
  rescue ScrubError => error
    failure(error)
  rescue => error
    failure(UnknownError.new(error.to_s))
  ensure
    cache.close!
  end
end
enter the cache


                                        single line change


def is_registered(username, without_cache)
  browse do | agent, configuration, cache |
    cache.command([ username, "is_registered" ]) do
      LoginPage.new(
        agent.get(configuration["login_page_url"])
      ).is_registered?(username)
    end
  end
end
enter the cache


class CommandCache

  def initialize(database_path)
    @database = create_database(database_path)
  end
                                 better ask
  def command(keys)           forgiveness than
    begin                        permission
      from_cache(keys)
    rescue NotInCache => e
      raise e if not block_given?
      to_cache(keys, yield)
    end
  end

end
and then...


              our systems
               cannot take
              more than 25
              concurrent
               requests...
              make sure of
                   it!!!
@!#$$@&#
     ...
maybe we can
use a proxy
     ...
god bless mechanize

def browse
  begin
    cache = CommandCache.new(database_path)
    configuration = YAML::load(File.open(configuration_path))
    proxy = configuration["proxy"]
    agent = Mechanize.new do | agent |
      agent.user_agent_alias = "Linux Mozilla"
      agent.set_proxy(proxy["host"], proxy["port"]) if proxy
    end
    yield(agent, configuration, cache)
  rescue Mechanize::ResponseCodeError => error            single   line change
    failure(LoadPageError.new(error))
  rescue Timeout::Error
    failure(TimeoutPageError.new)
  rescue ScrubError => error
    failure(error)
  rescue => error
    failure(UnknownError.new(error.to_s))
  ensure
    cache.close!
  end
end
and then...



               well, you know,
               we have a lot of
               users, so when
                proxy says is
               overloaded you
              must retry a few
              times before give
                      up
@!#$$@&#
god bless ruby
class Mechanize

  alias real_fetch_page fetch_page

  def fetch_page(params)
    ...
    attempts = 0
    begin
      attempts += 1
      real_fetch_page(params)
                                                            look at      this line!!!
    rescue Net::HTTPServerException => error
      if is_overloaded?(error)
        sleep wait_for_seconds and retry if attempts < retry_for_times
        raise SystemError.new("SystemOverloaded")
      end
      raise error
    end
  end

  def is_overloaded?(error)
    error.response.code == "403"
  end

end
we can also test it :-)
class WEBrick::HTTPResponse

  def serve(content)
    self.body = content
    self["Content-Length"] = content.length
  end

  def overloaded
    serve("<html><body>squid</body></html>")
    self.status = 403
  end

end



proxy = WEBrick::HTTPProxyServer.new(
  :Port => 2200,
  :ProxyContentHandler => Proc.new do | request, response |
    response.overloaded
  end
)

trap("INT") { proxy.shutdown }
proxy.start
finally ;-)




                 well... i
              guess we can
               release it...
the unexpected



                 but... wait...
                   our i.t.
                 department
                  said that
                 sometimes it
                   crashes
the unexpected




             you need to
               fix it by
              tomorrow
                   !!!
@!#$$@&#
@!#$$@&#
@!#$$@&#
@!#$$@&#
@!#$$@&#
@!#$$@&#
If you want something done, do it yourself
how to transform a command line program
into a web application

class ScrubsHandler < Mongrel::HttpHandler

  def process(request, response)
    command = request.params["PATH_INFO"].tr("/", "")
    elements = Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"])
    parameters = elements.inject([]) do | parameters, parameter |
      name, value = parameter
      parameters << if value.nil?
        "--#{name}"
      else
        "--#{name}='#{value}'"
      end
    end.join(" ")
                                               almost a single line change
    response.start(200) do | head, out |
      head["Content-Type"] = "application/json"
      out.write(scrubs.execute(command, parameters))
    end
  end

  ...
end
can this be true ?!?!?




                  well... i
               guess we can
                release it...
after a while...


                       all the
                     requests
                      are live!!!
                   our systems
                    are melting
                    down!!! fix
                     it!!! now!!!
@!#$$@&#
@!#$$@&#
@!#$$@&#
@!#$$@&#
@!#$$@&#
@!#$$@&#
change the cache implementation
use the file system luke...

def expire(keys, result = nil)
  FileUtils.rm path(keys), :force => true
  result.merge({ "from_cache" => false }) unless result.nil?
end

def expire_after(keys, seconds, result = nil)
  expire(keys, result) if (from_cache(keys)["cached_at"] + seconds) <= now rescue nil
end

def from_cache(keys)
  cache_file_path = path(keys)
  raise NotInCache.new(keys) unless File.exists?(cache_file_path)
  JSON.parse(File.read(cache_file_path)).merge({ "from_cache" => true })
end

def to_cache(keys, result)
  result = result.merge({ "cached_at" => now })
  File.write(path(keys), JSON.generate(result))
  result.merge({ "from_cache" => false })
end
after a while...




                     are you
                   handling the
                   maintenance
                   page right?
maintenance page detection


class PageToScrub

  def initialize(page)
    @page = page
    check_page_errors
    check_for_maintenance
  end

   def check_for_maintenance
    @page.search("//td[@class='txtbig']").each do | node |
      if extract_text_from(node.search("./descendant::text()")) =~
          /^.+?area.+?clienti.+?non.+?disponibile.+?stiamo.+?lavorando/im
        raise OnMaintenancePage.new(@page, "??? is on maintenance")
      end
    end
  end

  ...

end
after few days




              good job
             gabriele, it’s
               working
             beyond our
             expectations
after few days


               tell me,
                these
             “robots” of
             yours can be
               used to
              check our
               systems
yes!




 ...
in the end...



  • almost all self care’s
      features are replicated
  •   ~500.000 unique users/day
  •   ~12.000.000 requests/day
  •   ~4gb of cached data
  •   specs are used to
      monitoring the entire
      system
but in the beginning was...



                 it’s very
                 easy, we
                    need
                something
                quick and
                  dirty...
that is for
me the ruby
  magic :-)
questions?
gabriele lana
gabriele.lana@cleancode.it
  twitter: @gabrielelana

More Related Content

What's hot

RubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxRubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxDr Nic Williams
 
WordPress for developers - phpday 2011
WordPress for developers -  phpday 2011WordPress for developers -  phpday 2011
WordPress for developers - phpday 2011Maurizio Pelizzone
 
Rails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power StackRails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power StackDavid Copeland
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuerydeimos
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwrdeimos
 
Transformando os pepinos do cliente no código de testes da sua aplicação
Transformando os pepinos do cliente no código de testes da sua aplicaçãoTransformando os pepinos do cliente no código de testes da sua aplicação
Transformando os pepinos do cliente no código de testes da sua aplicaçãoRodrigo Urubatan
 
Lecture8 php page control by okello erick
Lecture8 php page control by okello erickLecture8 php page control by okello erick
Lecture8 php page control by okello erickokelloerick
 
JavaScript
JavaScriptJavaScript
JavaScriptSunil OS
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosEdgar Suarez
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I thinkWim Godden
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 MinutesAzim Kurt
 
Write Less Do More
Write Less Do MoreWrite Less Do More
Write Less Do MoreRemy Sharp
 
Web2py Code Lab
Web2py Code LabWeb2py Code Lab
Web2py Code LabColin Su
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Remy Sharp
 

What's hot (20)

RubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxRubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
 
CakePHP workshop
CakePHP workshopCakePHP workshop
CakePHP workshop
 
WordPress for developers - phpday 2011
WordPress for developers -  phpday 2011WordPress for developers -  phpday 2011
WordPress for developers - phpday 2011
 
Rails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power StackRails, Postgres, Angular, and Bootstrap: The Power Stack
Rails, Postgres, Angular, and Bootstrap: The Power Stack
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
 
CakeFest 2013 keynote
CakeFest 2013 keynoteCakeFest 2013 keynote
CakeFest 2013 keynote
 
Transformando os pepinos do cliente no código de testes da sua aplicação
Transformando os pepinos do cliente no código de testes da sua aplicaçãoTransformando os pepinos do cliente no código de testes da sua aplicação
Transformando os pepinos do cliente no código de testes da sua aplicação
 
Lecture8 php page control by okello erick
Lecture8 php page control by okello erickLecture8 php page control by okello erick
Lecture8 php page control by okello erick
 
JavaScript
JavaScriptJavaScript
JavaScript
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutos
 
My app is secure... I think
My app is secure... I thinkMy app is secure... I think
My app is secure... I think
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
jQuery
jQueryjQuery
jQuery
 
Write Less Do More
Write Less Do MoreWrite Less Do More
Write Less Do More
 
Web2py Code Lab
Web2py Code LabWeb2py Code Lab
Web2py Code Lab
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
jQuery Essentials
jQuery EssentialsjQuery Essentials
jQuery Essentials
 
Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)
 

Viewers also liked

Minimum Viable Product
Minimum Viable ProductMinimum Viable Product
Minimum Viable ProductGabriele Lana
 
Parse Everything With Elixir
Parse Everything With ElixirParse Everything With Elixir
Parse Everything With ElixirGabriele Lana
 
Resource Oriented Design
Resource Oriented DesignResource Oriented Design
Resource Oriented DesignGabriele Lana
 
Professional Programmer
Professional ProgrammerProfessional Programmer
Professional ProgrammerGabriele Lana
 
Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013Gabriele Lana
 
Agileday Coderetreat 2013
Agileday Coderetreat 2013Agileday Coderetreat 2013
Agileday Coderetreat 2013Gabriele Lana
 
Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)Gabriele Lana
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to NodejsGabriele Lana
 
It is not supposed to fly but it does
It is not supposed to fly but it doesIt is not supposed to fly but it does
It is not supposed to fly but it doesGabriele Lana
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with ExamplesGabriele Lana
 

Viewers also liked (18)

Why couchdb is cool
Why couchdb is coolWhy couchdb is cool
Why couchdb is cool
 
Minimum Viable Product
Minimum Viable ProductMinimum Viable Product
Minimum Viable Product
 
Nosql
NosqlNosql
Nosql
 
Parse Everything With Elixir
Parse Everything With ElixirParse Everything With Elixir
Parse Everything With Elixir
 
Resource Oriented Design
Resource Oriented DesignResource Oriented Design
Resource Oriented Design
 
MongoDB With Style
MongoDB With StyleMongoDB With Style
MongoDB With Style
 
Beyond Phoenix
Beyond PhoenixBeyond Phoenix
Beyond Phoenix
 
API Over HTTP
API Over HTTPAPI Over HTTP
API Over HTTP
 
Professional Programmer
Professional ProgrammerProfessional Programmer
Professional Programmer
 
Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013
 
Agileday Coderetreat 2013
Agileday Coderetreat 2013Agileday Coderetreat 2013
Agileday Coderetreat 2013
 
coderetreat
coderetreatcoderetreat
coderetreat
 
Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)
 
CouchDB Vs MongoDB
CouchDB Vs MongoDBCouchDB Vs MongoDB
CouchDB Vs MongoDB
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to Nodejs
 
The Magic Of Elixir
The Magic Of ElixirThe Magic Of Elixir
The Magic Of Elixir
 
It is not supposed to fly but it does
It is not supposed to fly but it doesIt is not supposed to fly but it does
It is not supposed to fly but it does
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with Examples
 

Similar to Magic of Ruby

Phoenix for laravel developers
Phoenix for laravel developersPhoenix for laravel developers
Phoenix for laravel developersLuiz Messias
 
Python magicmethods
Python magicmethodsPython magicmethods
Python magicmethodsdreampuf
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v RubyJano Suchal
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to CeleryIdan Gazit
 
Acceptance Testing with Webrat
Acceptance Testing with WebratAcceptance Testing with Webrat
Acceptance Testing with WebratLuismi Cavallé
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a bossgsterndale
 
Django - Know Your Namespace: Middleware
Django - Know Your Namespace: MiddlewareDjango - Know Your Namespace: Middleware
Django - Know Your Namespace: Middlewarehowiworkdaily
 
Ruby on Rails ステップアップ講座 - 大場寧子
Ruby on Rails ステップアップ講座 - 大場寧子Ruby on Rails ステップアップ講座 - 大場寧子
Ruby on Rails ステップアップ講座 - 大場寧子Yasuko Ohba
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksNate Abele
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportBen Scofield
 
PyCon 2010 SQLAlchemy tutorial
PyCon 2010 SQLAlchemy tutorialPyCon 2010 SQLAlchemy tutorial
PyCon 2010 SQLAlchemy tutorialjbellis
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is AwesomeAstrails
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairMark
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+ConFoo
 
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby DeveloperVenturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby DeveloperJon Kruger
 

Similar to Magic of Ruby (20)

Phoenix for laravel developers
Phoenix for laravel developersPhoenix for laravel developers
Phoenix for laravel developers
 
Python magicmethods
Python magicmethodsPython magicmethods
Python magicmethods
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v Ruby
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to Celery
 
Practical Celery
Practical CeleryPractical Celery
Practical Celery
 
Acceptance Testing with Webrat
Acceptance Testing with WebratAcceptance Testing with Webrat
Acceptance Testing with Webrat
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a boss
 
Django Celery
Django Celery Django Celery
Django Celery
 
Django - Know Your Namespace: Middleware
Django - Know Your Namespace: MiddlewareDjango - Know Your Namespace: Middleware
Django - Know Your Namespace: Middleware
 
Ruby on Rails ステップアップ講座 - 大場寧子
Ruby on Rails ステップアップ講座 - 大場寧子Ruby on Rails ステップアップ講座 - 大場寧子
Ruby on Rails ステップアップ講座 - 大場寧子
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
DataMapper
DataMapperDataMapper
DataMapper
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
PyCon 2010 SQLAlchemy tutorial
PyCon 2010 SQLAlchemy tutorialPyCon 2010 SQLAlchemy tutorial
PyCon 2010 SQLAlchemy tutorial
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is Awesome
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
 
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby DeveloperVenturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
 

More from Gabriele Lana

Microservice Architectures
Microservice ArchitecturesMicroservice Architectures
Microservice ArchitecturesGabriele Lana
 
Professional Programmer 2018
Professional Programmer 2018Professional Programmer 2018
Professional Programmer 2018Gabriele Lana
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartGabriele Lana
 
Erlang: the language and the platform
Erlang: the language and the platformErlang: the language and the platform
Erlang: the language and the platformGabriele Lana
 
Resource Oriented Architectures
Resource Oriented ArchitecturesResource Oriented Architectures
Resource Oriented ArchitecturesGabriele Lana
 
Sustainable Agile Development
Sustainable Agile DevelopmentSustainable Agile Development
Sustainable Agile DevelopmentGabriele Lana
 
Introduction to Erlang
Introduction to ErlangIntroduction to Erlang
Introduction to ErlangGabriele Lana
 

More from Gabriele Lana (8)

Microservice Architectures
Microservice ArchitecturesMicroservice Architectures
Microservice Architectures
 
Professional Programmer 2018
Professional Programmer 2018Professional Programmer 2018
Professional Programmer 2018
 
ProgrammingKatas
ProgrammingKatasProgrammingKatas
ProgrammingKatas
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing Part
 
Erlang: the language and the platform
Erlang: the language and the platformErlang: the language and the platform
Erlang: the language and the platform
 
Resource Oriented Architectures
Resource Oriented ArchitecturesResource Oriented Architectures
Resource Oriented Architectures
 
Sustainable Agile Development
Sustainable Agile DevelopmentSustainable Agile Development
Sustainable Agile Development
 
Introduction to Erlang
Introduction to ErlangIntroduction to Erlang
Introduction to Erlang
 

Recently uploaded

Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 

Recently uploaded (20)

Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 

Magic of Ruby

  • 3. once upon a time there was a developer working for a big company
  • 4. in the beginning... it’s very easy, we need something quick and dirty...
  • 5. in the beginning... 1. login 2. go to a page 3. scrap a number 4. that’s it!!!
  • 6. ok!
  • 7. enter mechanize + nokogiri require "mechanize" agent = Mechanize.new do | agent | agent.user_agent_alias = "Linux Mozilla" end agent.get("http://example.com/login") do | login_page | result_page = login_page.form_with(:name => "login") do | login_form | login["username"] = username login["password"] = password end.submit result_page.search("//table[starts-with(@class,'boundaries')]").map do | option_table | { "name" => option_table.search("./caption/child::text()") "credits" => option_table.search("./descendant::td[position()=3]/child::text()") } end end
  • 8. and then... good, but we’d like to extract more informations from a few different pages
  • 9. enter commander describe arguments command :is_registered do | command | command.syntax = "is_registered --username TELEPHONE_NUMBER [ --without-cache ]" command.description = "Check if user is registered" command.option "-u", "--username TELEPHONE_NUMBER", String, "user's telephone number" command.option "-n", "--without-cache", "bypass user's profile informations cache" command.when_called do | arguments, options | options.default :username => "", :without_cache => false ok(is_registered(options.username, options.without_cache)) end end extract code into functions
  • 10. use page object pattern def is_registered(username) browse do | agent, configuration | LoginPage.new( agent.get(configuration["login_page_url"]) ).is_registered?(username) end end
  • 11. use page object pattern class LoginPage < PageToScrub def is_registered?(username) begin login(username, "fake password") rescue WrongPassword true rescue NotRegistered, WrongUsername, WrongArea false end end def login(username, password) check_page( use_element(:login_form) do | login | login["username"] = username login["password"] = password end.submit ) end def login_form @page.form_with(:name => "login") end
  • 12. use page object pattern class LoginPage < PageToScrub def is_registered?(username) begin login(username, "fake password") rescue WrongPassword true rescue NotRegistered, WrongUsername, WrongArea false end end useful abstractions def login(username, password) check_page( use_element(:login_form) do | login | login["username"] = username login["password"] = password end.submit ) end def login_form @page.form_with(:name => "login") end
  • 13. use page object pattern class PageToScrub ... def use_element(element_name) element = self.send(element_name) raise MalformedPage.new(@page, "unable to locate #{element_name}") if ( element.nil? || (element.empty? rescue true) ) return yield(element) if block_given? element end ... end
  • 14. after a while... ...few pages my A@@ 45 pages and 93 different pieces of data
  • 15. i need to feel more confident with this...
  • 16. rspec is your friend :-) describe "is_registered" do context "XXX3760593" do it "should be a consumer registered" do result = command(:is_registered, :username => "XXX3760593") result.should_not be_an_error result["area"].should == "consumer" result["registered"].should == true end end end
  • 17. and then... obviously not all the requests can be live on our systems
  • 18. enter the cache def browse begin cache = CommandCache.new(database_path) configuration = YAML::load(File.open(configuration_path)) agent = Mechanize.new do | agent | agent.user_agent_alias = "Linux Mozilla" end yield(agent, configuration, cache) rescue Mechanize::ResponseCodeError => error failure(LoadPageError.new(error)) rescue Timeout::Error failure(TimeoutPageError.new) rescue ScrubError => error failure(error) rescue => error failure(UnknownError.new(error.to_s)) ensure cache.close! end end
  • 19. enter the cache single line change def is_registered(username, without_cache) browse do | agent, configuration, cache | cache.command([ username, "is_registered" ]) do LoginPage.new( agent.get(configuration["login_page_url"]) ).is_registered?(username) end end end
  • 20. enter the cache class CommandCache def initialize(database_path) @database = create_database(database_path) end better ask def command(keys) forgiveness than begin permission from_cache(keys) rescue NotInCache => e raise e if not block_given? to_cache(keys, yield) end end end
  • 21. and then... our systems cannot take more than 25 concurrent requests... make sure of it!!!
  • 22. @!#$$@&# ... maybe we can use a proxy ...
  • 23. god bless mechanize def browse begin cache = CommandCache.new(database_path) configuration = YAML::load(File.open(configuration_path)) proxy = configuration["proxy"] agent = Mechanize.new do | agent | agent.user_agent_alias = "Linux Mozilla" agent.set_proxy(proxy["host"], proxy["port"]) if proxy end yield(agent, configuration, cache) rescue Mechanize::ResponseCodeError => error single line change failure(LoadPageError.new(error)) rescue Timeout::Error failure(TimeoutPageError.new) rescue ScrubError => error failure(error) rescue => error failure(UnknownError.new(error.to_s)) ensure cache.close! end end
  • 24. and then... well, you know, we have a lot of users, so when proxy says is overloaded you must retry a few times before give up
  • 26. god bless ruby class Mechanize alias real_fetch_page fetch_page def fetch_page(params) ... attempts = 0 begin attempts += 1 real_fetch_page(params) look at this line!!! rescue Net::HTTPServerException => error if is_overloaded?(error) sleep wait_for_seconds and retry if attempts < retry_for_times raise SystemError.new("SystemOverloaded") end raise error end end def is_overloaded?(error) error.response.code == "403" end end
  • 27. we can also test it :-) class WEBrick::HTTPResponse def serve(content) self.body = content self["Content-Length"] = content.length end def overloaded serve("<html><body>squid</body></html>") self.status = 403 end end proxy = WEBrick::HTTPProxyServer.new( :Port => 2200, :ProxyContentHandler => Proc.new do | request, response | response.overloaded end ) trap("INT") { proxy.shutdown } proxy.start
  • 28. finally ;-) well... i guess we can release it...
  • 29. the unexpected but... wait... our i.t. department said that sometimes it crashes
  • 30. the unexpected you need to fix it by tomorrow !!!
  • 32. If you want something done, do it yourself how to transform a command line program into a web application class ScrubsHandler < Mongrel::HttpHandler def process(request, response) command = request.params["PATH_INFO"].tr("/", "") elements = Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) parameters = elements.inject([]) do | parameters, parameter | name, value = parameter parameters << if value.nil? "--#{name}" else "--#{name}='#{value}'" end end.join(" ") almost a single line change response.start(200) do | head, out | head["Content-Type"] = "application/json" out.write(scrubs.execute(command, parameters)) end end ... end
  • 33. can this be true ?!?!? well... i guess we can release it...
  • 34. after a while... all the requests are live!!! our systems are melting down!!! fix it!!! now!!!
  • 36. change the cache implementation use the file system luke... def expire(keys, result = nil) FileUtils.rm path(keys), :force => true result.merge({ "from_cache" => false }) unless result.nil? end def expire_after(keys, seconds, result = nil) expire(keys, result) if (from_cache(keys)["cached_at"] + seconds) <= now rescue nil end def from_cache(keys) cache_file_path = path(keys) raise NotInCache.new(keys) unless File.exists?(cache_file_path) JSON.parse(File.read(cache_file_path)).merge({ "from_cache" => true }) end def to_cache(keys, result) result = result.merge({ "cached_at" => now }) File.write(path(keys), JSON.generate(result)) result.merge({ "from_cache" => false }) end
  • 37. after a while... are you handling the maintenance page right?
  • 38. maintenance page detection class PageToScrub def initialize(page) @page = page check_page_errors check_for_maintenance end def check_for_maintenance @page.search("//td[@class='txtbig']").each do | node | if extract_text_from(node.search("./descendant::text()")) =~ /^.+?area.+?clienti.+?non.+?disponibile.+?stiamo.+?lavorando/im raise OnMaintenancePage.new(@page, "??? is on maintenance") end end end ... end
  • 39. after few days good job gabriele, it’s working beyond our expectations
  • 40. after few days tell me, these “robots” of yours can be used to check our systems
  • 42. in the end... • almost all self care’s features are replicated • ~500.000 unique users/day • ~12.000.000 requests/day • ~4gb of cached data • specs are used to monitoring the entire system
  • 43. but in the beginning was... it’s very easy, we need something quick and dirty...
  • 44. that is for me the ruby magic :-)