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

Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
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
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
🐬 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
 
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
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
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
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
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
 
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
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
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
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 

Recently uploaded (20)

Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
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...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
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
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
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 ...
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
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
 
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
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 

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 :-)