SlideShare uma empresa Scribd logo
1 de 45
Baixar para ler offline
Una historia de DSLs
     en Ruby




      Leo Soto M.
      Continuum
      Lenguajes Dinámicos, Enero 2013

Saturday, February 9, 13
DSLs?




Saturday, February 9, 13
Muchas libs Ruby son en el fondo, DSLs




Saturday, February 9, 13
Capistrano




namespace :deploy do
  task :start, :roles => :app, :except => { :no_release => true } do
    run "sudo monit start miapp_unicorn"
    run "sudo monit -g resque start all"
  end
end




Saturday, February 9, 13
RSpec



describe Math do
  describe "#pow" do
    it "computes the n-th-power of the receiver" do
      3.pow(3).should == 27
    end
  end
end




Saturday, February 9, 13
Bundler
gem       'rails', '3.2.11'
gem       'pg'
gem       'jquery-rails'
gem       "unicorn", ">= 4.3.1"
gem       "haml", ">= 3.1.7"
gem       "devise", ">= 2.1.2"
gem       "simple_form", ">= 2.0.4"
gem       "figaro", ">= 0.5.0"
gem       "foreigner", ">= 1.3.0"


group          :assets do
  gem          'sass-rails',   '~> 3.2.3'
  gem          'compass-rails'
  gem          'coffee-rails', '~> 3.2.1'
  gem          'uglifier', '>= 1.0.3'
end




Saturday, February 9, 13
Sinatra




require 'sinatra'

get '/hi' do
  "Hello World!"
end




Saturday, February 9, 13
Routing en Rails




Saturday, February 9, 13
Modelos en Rails




Saturday, February 9, 13
Casi todo en Rails




Saturday, February 9, 13
Lo que en J__A “resuelven” con X_L
                     en Ruby se suele hacer con DSLs internos




Saturday, February 9, 13
Enter Pharmmd Rules




Saturday, February 9, 13
age	
  >=	
  18	
  and	
  (drug_cat(“XYZ”)	
  or	
  
                    	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  drug_cat(“ABC”))
                    and	
  dosage(“PARACETAMOL”)	
  >	
  1000




Saturday, February 9, 13
Solución:




Saturday, February 9, 13
# patient_dsl.rb
class PatientDSL

   attr_reader :patient

   def initialize(patient)
     @patient = patient
   end

   delegate :age, :allergies, :gender, :labs, :medications,
            :prescriptions, :providers, :visits, :survey, :to => :patient

   def drug_cat(name)
     (1..6).to_a.reverse.detect do |level|
       medication = medications.detect do |m|
         m.send("lvl#{level}conceptname") == name
       end and break medication
     end
   end

   def dosage(name)
     drug(name).try(:dosage) || 0
   end

   # Many, many more methods

end




Saturday, February 9, 13
PatientDSL.new(patient).instance_eval(rule)




Saturday, February 9, 13
Recuerden, las reglas las ingresas usuarios, en una gran
           caja de texto en la interfaz web del sistema

        ¿Que problemas pueden ocurrir con la solución hasta
                            ahora?




Saturday, February 9, 13
age	
  >=	
  18	
  and	
  patient.destroy!




Saturday, February 9, 13
Patient.destroy_all




Saturday, February 9, 13
system(“rm	
  -­‐rf	
  /”)




Saturday, February 9, 13
PatientDSL.new(patient).instance_eval(rule)




Saturday, February 9, 13
“Cuando me equivoco en tipear la regla
              me aparece un error ‘No Method no se cuantito’”




Saturday, February 9, 13
Solución, versión 2:




Saturday, February 9, 13
# pharmmd_dsl.treetop

grammar PharmmdDsl
  rule expression
    spaces? boolean_expression spaces?
  end

    rule boolean_expression
      boolean_term (spaces logical_binary_operator spaces boolean_term)*
    end

    rule logical_binary_operator
      "and" /
      "&&" /
      "or" /
      "||"
    end

    rule boolean_term
      ("not " / "!") spaces? boolean_expression /
      (numeric_value spaces? boolean_operator spaces? numeric_value) /
      ("(" boolean_expression ")") /
      function_call
    end

    # ...
Saturday, February 9, 13
# pharmmd_dsl.treetop (cont.)

    rule boolean_operator
      ">=" /
      "<=" /
      ">" /
      "<" /
      "==" /
      "!="
    end

  rule function_call
    function_name:([a-zA-Z_] [a-zA-Z_0-9]*) arguments:("(" argument
("," spaces? argument)* ")")? <FunctionNode>
  end

    rule argument
      string /
      date /
      numeric_value
    end




Saturday, February 9, 13
# pharmmd_dsl.treetop (cont.)

    rule numeric_value
      function_call /
      number /
      "(" spaces? numeric_value spaces? ")"
    end

    rule number
      float /
      integer
    end

    rule integer
      "-"? digits
    end

    rule float
      "-"? (digits)? "." digits
    end

    rule digits
      [0-9]+
    end


Saturday, February 9, 13
# pharmmd_dsl.treetop (cont.)

    rule spaces
      [sn]+
    end

    rule string
     ['"] [^'"]* ['"]
    end

    rule date
      [0-9]+ "." time_unit "s"? ".ago"
    end

    rule time_unit
      "day" /
      "month" /
      "year"
    end

end




Saturday, February 9, 13
Por cierto, el lenguaje de gramáticas de
                    treetop es un DSL “externo”




Saturday, February 9, 13
Treetop hace el parsing extremadamente natural.

                           ¡Sigamos el proceso a mano!

                           (si es que tenemos pizarra a mano)




Saturday, February 9, 13
age	
  >=	
  18	
  and	
  (drug_cat(“XYZ”)	
  or	
  
                    	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  drug_cat(“ABC”))
                    and	
  dosage(“PARACETAMOL”)	
  >	
  1000




Saturday, February 9, 13
¿Pero qué ganamos?




Saturday, February 9, 13
age	
  >=	
  18	
  and	
  patient.destroy	
  #	
  invalido
      Patient.destroy_all	
  #	
  invalido
      system(“rm	
  -­‐rf	
  /”)	
  #	
  valido!




Saturday, February 9, 13
eval(“Patient.destroy_all”)	
  #	
  oops




Saturday, February 9, 13
¡Rara vez la validación sintáctica es suficiente!




“Saltarina casa llovió perros perrunos”
                             (¡Español sintácticamente válido!)




Saturday, February 9, 13
rule function_call
      function_name:([a-zA-Z_] [a-zA-Z_0-9]*)
      arguments:("(" argument ("," spaces? argument)* ")")?
      <FunctionNode>
    end




Saturday, February 9, 13
require 'treetop'; require 'pharmmd_dsl'

class FunctionNode < Treetop::Runtime::SyntaxNode; end

class PharmmdDslValidator
  attr_accessor :dsl, :errors

    def initialize(dsl)
      @dsl = dsl; @errors = []
    end

    def valid_dsl?
      parser = PharmmdDslParser.new
      parse_tree = parser.parse(@dsl)
      if parse_tree.nil?
        errors << "You have a syntax error: #{parser.failure_reason}"
      else
        validate_functions(parse_tree)
      end
      errors.empty?
    end
    def valid_functions
      @valid_functions ||=
        (PatientDSL.instance_methods - Object.instance_methods)
    end
Saturday, February 9, 13
# (cont.)

   def validate_functions(parse_tree)
     element = parse_tree
     if element.is_a? FunctionNode
       name = element.function_name.text_value
       unless valid_functions.include? name
         errors << ("Function name #{element.text_value} is not a
 valid function call")
       end
     end
     if element.elements
       parse_tree.elements.each do |element|
         validate_functions(element)
       end
     end
   end
 end




Saturday, February 9, 13
age	
  >=	
  18	
  and	
  patient.destroy	
  #	
  invalido
      Patient.destroy_all	
  #	
  invalido
      system(“rm	
  -­‐rf	
  /”)	
  #	
  invalido!




Saturday, February 9, 13
Errores amigables:
                           “Se esperaba ‘(‘ en linea X, columna Y”

                           “La función ‘system’ no es válida”




Saturday, February 9, 13
Y mucho más: ¡Solucion v3!
          (Sólo una mirada rápida, que se nos acaba el tiempo)




Saturday, February 9, 13
¡DSL híbrido!




Saturday, February 9, 13
quantity("lipitor") > 10 or drug("vicodin")

                 treetop, parse trees
                                                                  (PharmdDSLValidator)
External
  DSL


                                         quantity("lipitor") > 10 or drug("vicodin")


                                                                 (PharmdDSLPreProcessor)

                                        (quantity("lipitor") > 10).or(drug("vicodin"))
                 ruby objs/metodos




                                                                  (PharmdDSL)
Internal
  DSL




                                        (NumericExpr(20, ["Lipitor 50mg"]) > 10).or(
                                          BooleanExpr(true, ["Vicodin 20mg"]))



Saturday, February 9, 13
quantity("lipitor") > 10 or drug("vicodin")

                 treetop, parse trees
                                                                  (PharmdDSLValidator)
External
  DSL


                                         quantity("lipitor") > 10 or drug("vicodin")


                                                                 (PharmdDSLPreProcessor)

                                        (quantity("lipitor") > 10).or(drug("vicodin"))
                 ruby objs/metodos




                                                                 (DenominatorQuery)
Internal
  DSL




                                        (OrCondition(
                                          Condition('lipitor', {'$gt' => '10'}),
                                          Condition('vicodin', {'$exists' => true}))



Saturday, February 9, 13
...y mas
               MongoDB no tenía OR en esa época,
      por lo que optimizabamos el árbol de expresiones para
               dejar los ORs lo mas “arriba” posible.

                                          Ejemplo:

                                 ((X	
  or	
  Y)	
  and	
  (Z)

                                                      (Condition#optimize)

                           ((X	
  and	
  Z)	
  or	
  (Y	
  and	
  Z))
Saturday, February 9, 13
Conclusión
                     • Parsing, árboles de expresiones,
                           compiladores, etc no fue tiempo perdido en
                           la U :)
                     • Pero siempre hay tradeoffs
                     • Probar un DSL interno primero. La solución
                           más simple que podría funcionar
                     • Luego un DSL externo, treetop lo hace fácil
                     • Finalmente un híbrido, si no queda otra
Saturday, February 9, 13

Mais conteúdo relacionado

Semelhante a Una historia de ds ls en ruby

Ruby new wheels_condensed
Ruby new wheels_condensedRuby new wheels_condensed
Ruby new wheels_condensed
osake
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
Rebecca Murphey
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
Kang-min Liu
 

Semelhante a Una historia de ds ls en ruby (20)

Data Types Master
Data Types MasterData Types Master
Data Types Master
 
Standing on the shoulders of giants with JRuby
Standing on the shoulders of giants with JRubyStanding on the shoulders of giants with JRuby
Standing on the shoulders of giants with JRuby
 
Ruby new wheels_condensed
Ruby new wheels_condensedRuby new wheels_condensed
Ruby new wheels_condensed
 
Perl Sucks - and what to do about it
Perl Sucks - and what to do about itPerl Sucks - and what to do about it
Perl Sucks - and what to do about it
 
Mulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development ToolkitMulberry: A Mobile App Development Toolkit
Mulberry: A Mobile App Development Toolkit
 
Redis for the Everyday Developer
Redis for the Everyday DeveloperRedis for the Everyday Developer
Redis for the Everyday Developer
 
Innovative Specifications for Better Performance Logging and Monitoring
Innovative Specifications for Better Performance Logging and MonitoringInnovative Specifications for Better Performance Logging and Monitoring
Innovative Specifications for Better Performance Logging and Monitoring
 
Regexp Master
Regexp MasterRegexp Master
Regexp Master
 
CoffeeScript
CoffeeScriptCoffeeScript
CoffeeScript
 
What did you miss in Java from 9-13?
What did you miss in Java from 9-13?What did you miss in Java from 9-13?
What did you miss in Java from 9-13?
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
 
Hidden treasures of Ruby
Hidden treasures of RubyHidden treasures of Ruby
Hidden treasures of Ruby
 
An introduction to Raku
An introduction to RakuAn introduction to Raku
An introduction to Raku
 
Master perl io_2011
Master perl io_2011Master perl io_2011
Master perl io_2011
 
Hacking with ruby2ruby
Hacking with ruby2rubyHacking with ruby2ruby
Hacking with ruby2ruby
 
Stored Procedures and MUMPS for DivConq
 Stored Procedures and  MUMPS for DivConq  Stored Procedures and  MUMPS for DivConq
Stored Procedures and MUMPS for DivConq
 
Redis & ZeroMQ: How to scale your application
Redis & ZeroMQ: How to scale your applicationRedis & ZeroMQ: How to scale your application
Redis & ZeroMQ: How to scale your application
 
Defensive programming in Javascript and Node.js
Defensive programming in Javascript and Node.jsDefensive programming in Javascript and Node.js
Defensive programming in Javascript and Node.js
 
Rails Sojourn: One Man's Journey - Wicked Good Ruby Conference 2013
Rails Sojourn: One Man's Journey - Wicked Good Ruby Conference 2013Rails Sojourn: One Man's Journey - Wicked Good Ruby Conference 2013
Rails Sojourn: One Man's Journey - Wicked Good Ruby Conference 2013
 
Dig1108 Lesson 3
Dig1108 Lesson 3Dig1108 Lesson 3
Dig1108 Lesson 3
 

Mais de Leonardo Soto

El arte oscuro de estimar v3
El arte oscuro de estimar v3El arte oscuro de estimar v3
El arte oscuro de estimar v3
Leonardo Soto
 
Mapas en la web con Cloudmade
Mapas en la web con CloudmadeMapas en la web con Cloudmade
Mapas en la web con Cloudmade
Leonardo Soto
 
Decent exposure: Controladores sin @ivars
Decent exposure: Controladores sin @ivarsDecent exposure: Controladores sin @ivars
Decent exposure: Controladores sin @ivars
Leonardo Soto
 
Sounds.gd lighting talk (RubyConf Uruguay)
Sounds.gd lighting talk (RubyConf Uruguay)Sounds.gd lighting talk (RubyConf Uruguay)
Sounds.gd lighting talk (RubyConf Uruguay)
Leonardo Soto
 
Un tour por Java, Scala, Python, Ruby y Javascript
Un tour por Java, Scala, Python, Ruby y JavascriptUn tour por Java, Scala, Python, Ruby y Javascript
Un tour por Java, Scala, Python, Ruby y Javascript
Leonardo Soto
 
Lo que odiamos de la agilidad
Lo que odiamos de la agilidadLo que odiamos de la agilidad
Lo que odiamos de la agilidad
Leonardo Soto
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)
Leonardo Soto
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)
Leonardo Soto
 

Mais de Leonardo Soto (20)

El arte oscuro de estimar v3
El arte oscuro de estimar v3El arte oscuro de estimar v3
El arte oscuro de estimar v3
 
Caching tips
Caching tipsCaching tips
Caching tips
 
Dos Años de Rails
Dos Años de RailsDos Años de Rails
Dos Años de Rails
 
Mapas en la web con Cloudmade
Mapas en la web con CloudmadeMapas en la web con Cloudmade
Mapas en la web con Cloudmade
 
Startechconf
StartechconfStartechconf
Startechconf
 
RabbitMQ
RabbitMQRabbitMQ
RabbitMQ
 
Decent exposure: Controladores sin @ivars
Decent exposure: Controladores sin @ivarsDecent exposure: Controladores sin @ivars
Decent exposure: Controladores sin @ivars
 
The Hashrocket Way
The Hashrocket WayThe Hashrocket Way
The Hashrocket Way
 
Sounds.gd lighting talk (RubyConf Uruguay)
Sounds.gd lighting talk (RubyConf Uruguay)Sounds.gd lighting talk (RubyConf Uruguay)
Sounds.gd lighting talk (RubyConf Uruguay)
 
Un tour por Java, Scala, Python, Ruby y Javascript
Un tour por Java, Scala, Python, Ruby y JavascriptUn tour por Java, Scala, Python, Ruby y Javascript
Un tour por Java, Scala, Python, Ruby y Javascript
 
Lo que odiamos de la agilidad
Lo que odiamos de la agilidadLo que odiamos de la agilidad
Lo que odiamos de la agilidad
 
Oss
OssOss
Oss
 
App Engine
App EngineApp Engine
App Engine
 
Introducción a Git
Introducción a GitIntroducción a Git
Introducción a Git
 
Tres Gemas De Ruby
Tres Gemas De RubyTres Gemas De Ruby
Tres Gemas De Ruby
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)
 
Django on Jython, PyCon 2009
Django on Jython, PyCon 2009Django on Jython, PyCon 2009
Django on Jython, PyCon 2009
 
Participando Summer of Code
Participando Summer of CodeParticipando Summer of Code
Participando Summer of Code
 
Django, el framework web para perfeccionistas con deadlines
Django, el framework web para perfeccionistas con deadlinesDjango, el framework web para perfeccionistas con deadlines
Django, el framework web para perfeccionistas con deadlines
 

Último

Último (20)

MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
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
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 

Una historia de ds ls en ruby

  • 1. Una historia de DSLs en Ruby Leo Soto M. Continuum Lenguajes Dinámicos, Enero 2013 Saturday, February 9, 13
  • 3. Muchas libs Ruby son en el fondo, DSLs Saturday, February 9, 13
  • 4. Capistrano namespace :deploy do task :start, :roles => :app, :except => { :no_release => true } do run "sudo monit start miapp_unicorn" run "sudo monit -g resque start all" end end Saturday, February 9, 13
  • 5. RSpec describe Math do describe "#pow" do it "computes the n-th-power of the receiver" do 3.pow(3).should == 27 end end end Saturday, February 9, 13
  • 6. Bundler gem 'rails', '3.2.11' gem 'pg' gem 'jquery-rails' gem "unicorn", ">= 4.3.1" gem "haml", ">= 3.1.7" gem "devise", ">= 2.1.2" gem "simple_form", ">= 2.0.4" gem "figaro", ">= 0.5.0" gem "foreigner", ">= 1.3.0" group :assets do gem 'sass-rails', '~> 3.2.3' gem 'compass-rails' gem 'coffee-rails', '~> 3.2.1' gem 'uglifier', '>= 1.0.3' end Saturday, February 9, 13
  • 7. Sinatra require 'sinatra' get '/hi' do "Hello World!" end Saturday, February 9, 13
  • 8. Routing en Rails Saturday, February 9, 13
  • 9. Modelos en Rails Saturday, February 9, 13
  • 10. Casi todo en Rails Saturday, February 9, 13
  • 11. Lo que en J__A “resuelven” con X_L en Ruby se suele hacer con DSLs internos Saturday, February 9, 13
  • 13. age  >=  18  and  (drug_cat(“XYZ”)  or                                drug_cat(“ABC”)) and  dosage(“PARACETAMOL”)  >  1000 Saturday, February 9, 13
  • 15. # patient_dsl.rb class PatientDSL attr_reader :patient def initialize(patient) @patient = patient end delegate :age, :allergies, :gender, :labs, :medications, :prescriptions, :providers, :visits, :survey, :to => :patient def drug_cat(name) (1..6).to_a.reverse.detect do |level| medication = medications.detect do |m| m.send("lvl#{level}conceptname") == name end and break medication end end def dosage(name) drug(name).try(:dosage) || 0 end # Many, many more methods end Saturday, February 9, 13
  • 17. Recuerden, las reglas las ingresas usuarios, en una gran caja de texto en la interfaz web del sistema ¿Que problemas pueden ocurrir con la solución hasta ahora? Saturday, February 9, 13
  • 18. age  >=  18  and  patient.destroy! Saturday, February 9, 13
  • 22. “Cuando me equivoco en tipear la regla me aparece un error ‘No Method no se cuantito’” Saturday, February 9, 13
  • 24. # pharmmd_dsl.treetop grammar PharmmdDsl rule expression spaces? boolean_expression spaces? end rule boolean_expression boolean_term (spaces logical_binary_operator spaces boolean_term)* end rule logical_binary_operator "and" / "&&" / "or" / "||" end rule boolean_term ("not " / "!") spaces? boolean_expression / (numeric_value spaces? boolean_operator spaces? numeric_value) / ("(" boolean_expression ")") / function_call end # ... Saturday, February 9, 13
  • 25. # pharmmd_dsl.treetop (cont.) rule boolean_operator ">=" / "<=" / ">" / "<" / "==" / "!=" end rule function_call function_name:([a-zA-Z_] [a-zA-Z_0-9]*) arguments:("(" argument ("," spaces? argument)* ")")? <FunctionNode> end rule argument string / date / numeric_value end Saturday, February 9, 13
  • 26. # pharmmd_dsl.treetop (cont.) rule numeric_value function_call / number / "(" spaces? numeric_value spaces? ")" end rule number float / integer end rule integer "-"? digits end rule float "-"? (digits)? "." digits end rule digits [0-9]+ end Saturday, February 9, 13
  • 27. # pharmmd_dsl.treetop (cont.) rule spaces [sn]+ end rule string ['"] [^'"]* ['"] end rule date [0-9]+ "." time_unit "s"? ".ago" end rule time_unit "day" / "month" / "year" end end Saturday, February 9, 13
  • 28. Por cierto, el lenguaje de gramáticas de treetop es un DSL “externo” Saturday, February 9, 13
  • 29. Treetop hace el parsing extremadamente natural. ¡Sigamos el proceso a mano! (si es que tenemos pizarra a mano) Saturday, February 9, 13
  • 30. age  >=  18  and  (drug_cat(“XYZ”)  or                                drug_cat(“ABC”)) and  dosage(“PARACETAMOL”)  >  1000 Saturday, February 9, 13
  • 32. age  >=  18  and  patient.destroy  #  invalido Patient.destroy_all  #  invalido system(“rm  -­‐rf  /”)  #  valido! Saturday, February 9, 13
  • 34. ¡Rara vez la validación sintáctica es suficiente! “Saltarina casa llovió perros perrunos” (¡Español sintácticamente válido!) Saturday, February 9, 13
  • 35. rule function_call function_name:([a-zA-Z_] [a-zA-Z_0-9]*) arguments:("(" argument ("," spaces? argument)* ")")? <FunctionNode> end Saturday, February 9, 13
  • 36. require 'treetop'; require 'pharmmd_dsl' class FunctionNode < Treetop::Runtime::SyntaxNode; end class PharmmdDslValidator attr_accessor :dsl, :errors def initialize(dsl) @dsl = dsl; @errors = [] end def valid_dsl? parser = PharmmdDslParser.new parse_tree = parser.parse(@dsl) if parse_tree.nil? errors << "You have a syntax error: #{parser.failure_reason}" else validate_functions(parse_tree) end errors.empty? end def valid_functions @valid_functions ||= (PatientDSL.instance_methods - Object.instance_methods) end Saturday, February 9, 13
  • 37. # (cont.) def validate_functions(parse_tree) element = parse_tree if element.is_a? FunctionNode name = element.function_name.text_value unless valid_functions.include? name errors << ("Function name #{element.text_value} is not a valid function call") end end if element.elements parse_tree.elements.each do |element| validate_functions(element) end end end end Saturday, February 9, 13
  • 38. age  >=  18  and  patient.destroy  #  invalido Patient.destroy_all  #  invalido system(“rm  -­‐rf  /”)  #  invalido! Saturday, February 9, 13
  • 39. Errores amigables: “Se esperaba ‘(‘ en linea X, columna Y” “La función ‘system’ no es válida” Saturday, February 9, 13
  • 40. Y mucho más: ¡Solucion v3! (Sólo una mirada rápida, que se nos acaba el tiempo) Saturday, February 9, 13
  • 42. quantity("lipitor") > 10 or drug("vicodin") treetop, parse trees (PharmdDSLValidator) External DSL quantity("lipitor") > 10 or drug("vicodin") (PharmdDSLPreProcessor) (quantity("lipitor") > 10).or(drug("vicodin")) ruby objs/metodos (PharmdDSL) Internal DSL (NumericExpr(20, ["Lipitor 50mg"]) > 10).or( BooleanExpr(true, ["Vicodin 20mg"])) Saturday, February 9, 13
  • 43. quantity("lipitor") > 10 or drug("vicodin") treetop, parse trees (PharmdDSLValidator) External DSL quantity("lipitor") > 10 or drug("vicodin") (PharmdDSLPreProcessor) (quantity("lipitor") > 10).or(drug("vicodin")) ruby objs/metodos (DenominatorQuery) Internal DSL (OrCondition( Condition('lipitor', {'$gt' => '10'}), Condition('vicodin', {'$exists' => true})) Saturday, February 9, 13
  • 44. ...y mas MongoDB no tenía OR en esa época, por lo que optimizabamos el árbol de expresiones para dejar los ORs lo mas “arriba” posible. Ejemplo: ((X  or  Y)  and  (Z) (Condition#optimize) ((X  and  Z)  or  (Y  and  Z)) Saturday, February 9, 13
  • 45. Conclusión • Parsing, árboles de expresiones, compiladores, etc no fue tiempo perdido en la U :) • Pero siempre hay tradeoffs • Probar un DSL interno primero. La solución más simple que podría funcionar • Luego un DSL externo, treetop lo hace fácil • Finalmente un híbrido, si no queda otra Saturday, February 9, 13