
Mais um post muito atrazado, desta vez levei mais de 3 meses para continuar falando de scala, mas como comentei no post anterior foram 3 meses corridos, então voltando ao histórico, este é o nono post da série sobre Scala, já falamos de o básico da linguagem scala, logo depois Orientação a objetos em scala, um pouco sobre closures, depois compreensão de listas e mapas, depois Programação Funcional em Scala, depois tipos parametrizados, depois pattern matching e case classes e por último sobre Atores e concorrência em scala. Agora vamos revisar alguns conceitos de orientação a objetos e vamos ver alguns recursos interessantes da linguagem scala.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | abstract class Animal { def breathe : Unit; } class Dog extends Animal { def breathe { println("Dogs breath!"); } } class Person(val fullName : String, val age : Int) extends Animal { def breathe = println("Dogs breath!"); override def toString = "[Person name:" + fullName + "]" } class Student(fullName : String, age : Int) extends Person(fullName, age) { } |
Mas os recursos OO de scala não ficam só nisto, podemos utilizar classes internas como no exemplo simples abaixo:
samples/044_inner_class.scala
1 2 3 4 5 6 7 8 9 10 11 12 | class Book { class Chapter(name:String,content:String) { override def toString() = "<chapter name=\"" + name + "\">" + content + "</chapter>" } var chapters: List[Chapter] = Nil def newChapter(name:String, content:String) = { val chap = new Chapter(name,content) chapters = chap :: chapters chap } override def toString() = "<book>" + chapters.foldRight("")((memo,it) => memo + it.toString ) + "</book>" } |
Este exemplo é bem simples para que não fiquem dúvidas, é possível criar classes aninhadas, mas diferente do Java, a classe Chapter não esta presa a classe Book, e sim a instância de livro que a criou, parecido com a relação entre os objetos de uma classe e o objeto da classe exterior em Java, podemos ver uma continuação do exemplo anterior para demonstrar melhor este conceito:
samples/045_inner_class.scala
1 2 3 4 5 6 7 8 9 | val b = new Book var c1 : b.Chapter = b.newChapter("Preface",""" This is a really beautiful book With only one chapter """) var c2 : b.Chapter = b.newChapter("Other Title",""" Just kidding no book can have only one chapter """) |
Mas ainda continuamos com exemplos simples. Em scala eu tenho herança e polimorfismo, e posso usar isto em métodos, métodos que mudam o tipo de retorno como por mágica dependendo dos parâmetros recebidos, como no exemplo abaixo, que o método pode retornar um List[Int] ou List[String] de acordo com os parâmetros passados.
samples/046_polimorfic_methods.scala
1 2 3 4 5 | def dup[T](x: T, n: Int): List[T] = if (n == 0) Nil else x :: dup(x, n - 1) println(dup[Int](3, 4)) println(dup("three", 3)) |
Neste exemplo é fácil de verificar o que esta acontecendo, pelo menos quando executamos o código, e a sintaxe para isto também é simples, o tipo variável é definido entre colchetes na declaração do método, como pode ser visto na linha 1, depois disto o tipo pode ser referenciado em qualquer parte do código dentro do mesmo método.
Outro recurso bastante interessante que também já vimos em outros exemplos, é a composição de classes, que funciona bem diferente das classes abertas do Ruby, mas é bem parecida com a composição de classes com modulos no Ruby, o recurso para composição de classes em Scala é o uso de “traits”, vamos dar uma nova olhada em um dos exemplos de traits.
samples/010_traits.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | trait Flyer { def fly = println("I can fly!") } trait Jumper { def jump = println("I can jump!") } trait Walker { def walk = println("I walk too!") } class Animal { def breathe = println("Dogs breath!") } class Dog extends Animal with Jumper { } class Bird extends Animal with Flyer with Jumper{ } class Airplane extends Flyer { } object TraitsApp extends Application { val dog = new Dog with Walker val bird = new Bird val plane = new Airplane dog.breathe dog.jump dog.walk bird.breathe bird.jump bird.fly plane.fly } |
É possível definir parte do comportamento esperado em um grupo de classes utilizando traits, mas é possível adicionar traits também no momento em que uma classe é instanciada, como pode ser visto no exemplo abaixo:
samples/047_traits.scala
1 2 3 4 5 6 7 8 | trait ToXML { def toXML = "<xml>" + toString() + "</xml>" } class TraitTest(name:String) { override def toString = "--" + name } var t = new TraitTest("Name Test") with ToXML println(t.toXML) |
E ainda temos as views, que são uma forma de criar conversões automáticas de tipos, em scala este recurso é chamado de “views”, a idéia é ter um método definido que sabe como converter um objeto em outro, e quando o compilador precisa explicitamente do segundo tipo, ou um método do segundo tipo é chamado no primeiro, o método de conversão é chamado automaticamente.
Isto parece confuso, então vamos ver um exemplo simples de como isto funciona para facilitar as explicações:
samples/048_views.scala
1 2 3 4 5 6 | final class RichChar(c: Char) { def isDigit: Boolean = Character.isDigit(c) // isLetter, isWhitespace, etc. } implicit def charWrapper(c: Char) = new RichChar(c) println('0'.isDigit) |
Na linha 6, quando o método isDigit é chamado na literal de um caracter, o compilador verifica todas as possíveis conversões daquele objeto que possuam um método isDigit, verifica todos os métodos declarados como “implicit”, que recebam o objeto como parâmetro para fazer a conversão, e chama o método implicitamente para converter o objeto e chamar o método na classe que tem o método declarado.
Se existir algum conflito no escopo, por exemplo duas possíveis conversões, um erro sera gerado, neste caso a conversão precisara ser feita manualmente.
E acredito agora que estamos prontos para voltar a falar de programação funcional em scala, já conhecemos o suficiente para escrever código Java em scala, mas a idéia de se aprender uma nova linguagem é aprender nvoas formas de se resolver problemas, então nos próximos posts vamos entrar um pouco mais no mundo funcional da linguagem, vamos entender as implementações e pelo menos conseguiremos lêr muitos dos códigos complicados em scala que vemos por ai …
E por último, se você leu até aqui, talvez você queira me seguir no twitter ou assinar o feed do blog.

Bom, este post atrazou bastante, mas vamos ver se volto a periodicidade padrão. Só para não perder o histórico, este é o oitavo post da série sobre Scala, já falamos de o básico da linguagem scala, logo depois Orientação a objetos em scala, um pouco sobre closures, depois compreensão de listas e mapas, depois Programação Funcional em Scala e na semana passada sobre tipos parametrizados e por último falamos de pattern matching e case classes. Mas deixando de lenga lenga, hoje vamos falar de Actors, que são a solução Scala para programação concorrente.
Vamos ver um exemplo simples para começar a entender como criar Actors e depois tentarei mostrar uma situação para tirar proveito deles.
samples/040_actor.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import scala.actors.Actor import scala.actors.Actor._ case object Ping case object Pong case object Stop class Ping(count: Int, pong: Actor) extends Actor { def act() { var pingsLeft = count - 1 pong ! Ping while (true) { receive { case Pong => if (pingsLeft % 1000 == 0) Console.println("Ping: pong") if (pingsLeft > 0) { pong ! Ping pingsLeft -= 1 } else { Console.println("Ping: stop") pong ! Stop exit() } } } } } class Pong extends Actor { def act() { var pongCount = 0 while (true) { receive { case Ping => if (pongCount % 1000 == 0) Console.println("Pong: ping "+pongCount) sender ! Pong pongCount = pongCount + 1 case Stop => Console.println("Pong: stop") exit() } } } } val pong = new Pong val ping = new Ping(100000, pong) ping.start pong.start Thread.sleep(5000) |
Agora um pouco de explicação sobre o código:
samples/041_actor.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import scala.actors.Actor import scala.actors.Actor._ case object Ping case object Pong case object Stop class Ping(count: Int, pong: Actor) extends Actor { def act() { var pingsLeft = count - 1 pong ! Ping loop { react { case Pong => if (pingsLeft % 1000 == 0) Console.println("Ping: pong") if (pingsLeft > 0) { pong ! Ping pingsLeft -= 1 } else { Console.println("Ping: stop") pong ! Stop exit() } } } } } class Pong extends Actor { def act() { var pongCount = 0 loop { react { case Ping => if (pongCount % 1000 == 0) Console.println("Pong: ping "+pongCount) sender ! Pong pongCount = pongCount + 1 case Stop => Console.println("Pong: stop") exit() } } } } val pong = new Pong val ping = new Ping(100000, pong) ping.start pong.start Thread.sleep(5000) |
Uma coisa interessante sobre o “react” é que ele pode ser encadeado, ou seja é possível escrever lógicas mais complexas com diversas mensagens para completar a atuação de um ator.
Com isto podemos tentar montar um exemplo um pouco mais interessante, simulando uma peça de teatro, bom, na verdade não vai ser nada tão complexo, vão ser apenas 3 atores brincando de telefone sem fio para que possamos ver mais um recurso interessante dos atores, veja o exemplo asseguir e logo depois vou explicar os pontos importantes do código:
samples/042_actor.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import scala.actors.Actor import scala.actors.Actor._ class Messenger(mediator : Actor) extends Actor { def act() { mediator ! Some("Hello") loop { react { case Some(x) => { Console.println(x) mediator ! None } case None => exit() } } } } class Mediator(destiny : Actor) extends Actor { def act() { loop{ react { case Some(x) => { destiny ! Some(x) reply { receive { case x: Option[_] => x } } } case None => { destiny ! None sender ! None exit() } } } } } class Destiny extends Actor { def act() { while(true) { receive { case Some(x) => { println(x) Thread.sleep(100) sender ! Some("World") } case None => exit() } } } } val destiny = new Destiny val mediator = new Mediator(destiny) val messenger = new Messenger(mediator) destiny.start mediator.start messenger.start Thread.sleep(10000) |
Os pontos importantes neste exemplo são os seguintes:
E por último, vamos dar uma olhada em outras formas de mandar mensagens para Actors e como iteragir com eles de fora do código de um Actor.
samples/043_actors.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import scala.actors.Actor import scala.actors.Actor._ class PingPongPlayer() extends Actor { def act() { loop{ react { case x:String => { println("Ping") reply("Pong") } case _ => exit() } } } } println("Start") val ppp = new PingPongPlayer ppp.start val resp = (ppp !! "Ping") println(resp()) val resp1 = (ppp !? "Ping") println(resp1) val resp2 = (ppp !? (100,"Ping")) println(resp2) ppp ! None |
Os operadores utilizados para enviar as mensagens desta vez foram “!!” e “!?”.
Bom, acho que por hoje era isto, não consegui pensar em uma aplicação simples para montar o exemplo, mas acho que apenas estes exemplos já ajudam bastante.
Se você leu até aqui, gostaria de deixar umas pergunta para que sejam respondidas nos comentários:
1) Baseado em sua experiência, quais vantagens você acha que os atores trazem sobre a implementação de thread padrão, como por exemplo de Java ou C#?
2) Você costuma escrever códigos pensando em execução concorrente? Quais técnicas você usa para garantir que seu código é thread safe?
E por último, se você leu até aqui, talvez você queira me seguir no twitter ou assinar o feed do blog.
Tags: artigo, concorrencia, funcional, scala

Este é o sétimo post da série sobre Scala, o primeiro foi o básico da linguagem scala, logo depois Orientação a objetos em scala, um pouco sobre closures, depois compreensão de listas e mapas, depois Programação Funcional em Scala e na semana passada sobre tipos parametrizados. Agora vamos falar um pouco de operadores condicionais, pattern matching, type matching e outras possíveis implementações para programação condicional.
Como eu disse que começaria neste artigo com exemplos mais complexos do que os pedaços de código dos artigos anteriores, a minha proposta para o primeiro exemplo é implementar uma calculadora, provavelmente de mais de uma forma para mostrar várias formas de fazer as mesmas operações, lembrem-se que este vai ser um exemplo simples.
Vamos começar vendo como funciona o pattern matching, este seria o equivalente a um case na linguagem scala, mas um pouco mais flexível do que um case do java, vamos ver no método calculadora abaixo.
samples/034_calculator.scala
1 2 3 4 5 6 7 8 | def calculate(value1 : Int, value2 : Int, operation : String) : Int = operation match { case "+" => value1 + value2 case "-" => value1 - value2 case "*" => value1 * value2 case "/" => value1 / value2 case _ => -1 } println(calculate(20, 25, "+")) |
Como eu comentei, o match pode ser utilizado exatamente como o switch/case do java ou do C++, mas ele é um pouco mais flexível do que isto, vamos implementar a mesma calculadora agora, utilizando type matching, e utilizando classes para implementar cada uma das operações da calculadora. Mas antes perceba o “_” na última posição do case, que vai funcionar como uma condição default, o que não for capturado em nenhuma das outras condições cai ali.
Agora vamos a um exemplo com case classes, como são chamadas em Scala.
samples/035_calculator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | abstract class Operation case class Addition(value1: Int, value2:Int) extends Operation case class Subtraction(value1: Int, value2:Int) extends Operation case class Multiplication(value1: Int, value2:Int) extends Operation case class Division(value1: Int, value2:Int) extends Operation def calculate(op : Operation) : Int = op match { case Addition(x,y) => x + y case Subtraction(a,b) => a - b case Multiplication(c,d) => c * d case Division(e,f) => e / f case _ => -1 } println(calculate(Addition(30,45))) |
Os case classes são úteis apenas no case de pattern matching, as variáveis de instância podem ser utilizadas diratamente no corpo do case como pode ser visto. Esta é uma boa opção para simular uma enumeração, já que as case classes de uma hierarquia tem que ser obrigatoriamente definidas no mesmo arquivo fonte. Esta é uma estrutura bastante utilizada em programação funcional, mas para quem esta acostumado com programação orientada a objetos esta parece uma abordagem um pouco pobre, por não possuir métodos definidos dentro das classes.
Como scala é uma linguagem mista, ou seja alem de funcional é também orientada a objetos, podemos utilizar o pattern matching para fazer type matching, como podemos ver no terceiro exemplo de calculadora abaixo.
samples/036_calculator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | abstract class Operation class Addition(value1: Int, value2:Int) extends Operation { def add() = value1 + value2 } class Subtraction(value1: Int, value2:Int) extends Operation { def sub() = value1 - value2 } class Multiplication(value1: Int, value2:Int) extends Operation { def mult() = value1 * value2 } class Division(value1: Int, value2:Int) extends Operation { def div() = value1 / value2 } def calculate(op : Operation) : Int = op match { case a:Addition => a.add() case s:Subtraction => s.sub() case m:Multiplication => m.mult() case d:Division => d.div() case _ => -1 } println(calculate(new Addition(30,45))) |
Como podemos ver neste exemplo, fazer type matching é bastante simples, claro que poderiamos ter declarado métodos nas case classes e chamado estes métodos, mas esta não é a ideia das mesmas.
As case classes tem a fantagem de poder ser instanciadas sem a utilização do “new” mas tem limitações de todas as classes de uma hierarquia precisarem ser definidas no mesmo arquivo, a idéia delas é serem utilizada como um tipo de enumeração.
Alem do pattern matching, scala ainda tem suporte aos operadores condicionais padrão da maioria das linguagens imperativas, como o if, else, operadores de loop como o do, while, mas o código funcional sempre dapreferencia para a utilização do pattern matching, como no exemplo abaixo.
samples/037_calculator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | abstract class Operation { def calculate() : Int; } class Addition(value1: Int, value2:Int) extends Operation { def calculate() = value1 + value2 } class Subtraction(value1: Int, value2:Int) extends Operation { def calculate() = value1 - value2 } class Multiplication(value1: Int, value2:Int) extends Operation { def calculate() = value1 * value2 } class Division(value1: Int, value2:Int) extends Operation { def calculate() = value1 / value2 } def calculate(op : Operation) : Int = op match { case null => throw new Exception("The parameter should not be null") case _ => op.calculate() } println(calculate(new Addition(30,45))) |
Neste exemplo o pattern matching esta sendo utilizado para verificar se o objeto é nulo ou não, a diferença em linhas de código é insignificante, mas a forma de pensar é em diferente de uma linguagem imperativa, no caso do if por exemplo. Em linguagens funcionais, assim como em algumas de paradigma misto como o Scala e o Python, é bastante comum a utilização de mapas para tomar decisões sobre qual caminho seguir, ou qual código executar, e é exatamente isto que o pattern matching é, uma forma especializada de mapa utilizado para tomar decisões, inclusive em diversos exemplos encontrados na internet, são misturados tipos de matching diferentes no mesmo bloco mostrando a flexibilidade da ferramenta.
Bom, a calculadora criada até agora não me parece uma aplicação mais complexa do que os métodos soltos que estavamos criando até agora, então vamos fazer exatamente a mesma calculadora, mas com um pouco de código swing, utilizando o pacote java.swing, hoje não vou detalhar muito os recursos, apenas comentar o código do exemplo, mas se vocês se interessarem, deixem comentários que escrevo um artigo só sobre o wrapper scala.swing.
Então vamos ao código, a minha idéia é ter uma calculadora com dois campos de entrada de números, um botão para cada uma das 4 operações básicas, e um label para o resultado.
O layout vai ser simples, os dois campos de entrada numérica estarão bem no topo da tela, logo abaixo o label para o resultado, e abaixo disto os botões, o usuário deve preencher os dois campos numéricos, e quando um botão de uma das operações for precionado, o resultado da operação devera aparecer no label.
Vamos ver o código necessário para isto, primeiro apenas para o layout, depois eu adiciono a lógica na tela para fazer ela funcionar.
samples/038_calculator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import swing._ object Calculator extends SimpleSwingApplication { def newTextField = new TextField { text = "0" columns = 5 } var input1 = newTextField var input2 = newTextField var resultLabel = new Label { text = "0" } var buttons = new GridPanel(2,2) { List("Add", "Sub", "Mult", "Div") foreach { contents += new Button(_) } } def top = new MainFrame { title = "Calculator" contents = new GridPanel(4,1){ List(input1, input2, resultLabel, buttons) foreach {contents += _ } } } } |
Este é um exemplo simples dos wrappers scala para Swing, a aplicação básica é criada utilizando-se o objeto SimpleSwingApplication.
A classe SimpleSwingApplication faz o bootstrap do swing, e facilita o desenvolvimento, ela precisa implementar um método de nome “top” que retorne um MainFrame, este main frame vai ser a tela principal da aplicação.
As classes de objetos principais do SWING tem wrappers com contrução facilitada e propriedades mais amigáveis para a utilização com o Scala.
Agora vamos dar uma olhada em como fazer os objetos, principalmente os botões tratarem os eventos, vamos ver o exemplo de código e depois eu explico ele.
samples/039_calculator.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | import swing._ import swing.event._ abstract class Operation { def calculate() : Int; } class Addition(value1: Int, value2:Int) extends Operation { def calculate() = value1 + value2 } class Subtraction(value1: Int, value2:Int) extends Operation { def calculate() = value1 - value2 } class Multiplication(value1: Int, value2:Int) extends Operation { def calculate() = value1 * value2 } class Division(value1: Int, value2:Int) extends Operation { def calculate() = value1 / value2 } object Calculator extends SimpleSwingApplication { def newTextField = new TextField { text = "0" columns = 5 } var input1 = newTextField var input2 = newTextField var resultLabel = new Label { text = "0" } def calculate(op : Operation) : Unit = op match { case null => throw new Exception("The parameter should not be null") case _ => resultLabel.text = "" + op.calculate() } var buttons = new GridPanel(2,2) { List(Action("Add"){ calculate(new Addition(input1.text.toInt,input2.text.toInt)) }, Action("Sub"){ calculate(new Subtraction(input1.text.toInt,input2.text.toInt)) }, Action("Mult"){ calculate(new Multiplication(input1.text.toInt,input2.text.toInt)) }, Action("Div"){ calculate(new Division(input1.text.toInt,input2.text.toInt)) }) foreach { contents += new Button(_)} } listenTo(input1.keys,input2.keys) reactions += { case KeyPressed (source, key, modifiers, location) => println("Clicked " + key) } def top = new MainFrame { title = "Calculator" contents = new GridPanel(4,1){ List(input1, input2, resultLabel, buttons) foreach {contents += _ } } } } |
Neste exemplo temos duas formas de “ouvir” eventos do SWING em scala, uma delas é no caso dos botões, que só interessa a ação do botão, disparada em um clique ou com o enter, utilizei a classe Action para criar o botão, e a segunda forma mais flexível é a combinação do método “listenTo” com a propriedade “reactions”.
No “listenTo” informamos que queremos ouvir os eventos dos objetos listados, e em reactions adicionamos blocos de código que serão utilizados para processar os eventos, neste caso utilizamos a case class disponível para o evento “KeyPressed” para logar quais teclas foram precionadas nos campos de entrada de texto.
Claro que este exemplo de calculadora não é nenhum exemplo de código perfeito, mas eu queria apenas misturar um pouco de swing com um dos exemplos de código deste artigo, acho que não foi uma boa forma de mostrar uma aplicação mais complexa, mas a partir do próximo post vamos falar de actors em scala, e como eles podem te ajudar, isto e um pouco de programação web em um framework escrito em scala.
Se vocês quiserem um artigo mais detalhado sobre os wrappers de scala para programação com SWING é só falar nos comentários que eu escrevo um ASAP.
Por enquanto, espero que a utilização do match e das case classes tenha ficado clara. Se não ficou, se tiverem dúvidas, sugestões ou criticas, por favor deixe seu comentário.
Tags: case classes, functional, match, programming, scala

Este é o sexto post da série sobre Scala, o primeiro foi o básico da linguagem scala, logo depois Orientação a objetos em scala, um pouco sobre closures, depois compreensão de listas e mapas, e na semana passada sobre Programação Funcional em Scala. Outra coisa bastante comum em todo código Scala que vejo por ai são Type Parameters, que em Scala podem assumir mais de uma forma, e também precisamos entender como funcionam upper e lower bounds, ou limites superiores e inferiores.
Vamos começar com alguns exemplos, e vamos analisar os códigos para tentar deixar claro como isto funciona e como podemos utilizar este recurso bastante poderoso a nosso favor.
(este código não é executável, é apenas um exemplo)
samples/022_typeparamsbasic.scala
1 2 3 4 5 6 7 8 9 | //Declaração parcial da classe Array em Scala class Array[A] { def apply(index: Int): A def update(index: Int, elem: A) } val x = new Array[String]() val y: Array[Any] = x y(0) = new Rational(1, 2) // Açucar sintatico para y.update(0, new Rational(1, 2)) |
Neste exemplo, podemos ver um exemplo simples da declaração de uma classe parametrizável, e também podemos ver um problema gráve gerado por esta parametrização, o código das linhas 7 e 8, é válido, já que String é subclasse de Any, mas como não existe nenhuma verificação em tempo de execução, teriamos problemas na linha 9, onde um objeto diferente de string seria colocado no Array, o java resolve este problema com uma verificação em tempo de execução, o que fere um pouco a idéia de tipagem estática da linguagem, em scala é possível mudar a declaração da classe para melhorar um pouco esta verificação, adicionando um pouco de variância na declaração da classe e do método …
(este código não é executável, é apenas um exemplo)
samples/032_typeparams2.scala
1 2 3 4 5 | //Declaração parcial da classe Array em Scala class Array[A+] { def apply(index: Int): A def [B <: A]update(index: Int, elem: B) } |
A notação A+ diz que o tipo A só deve ser utilizado em posições covariantes, e na nova assinatura do método update, que agora é um método polimorfico, não é mais utilizado o tipo A, agora utilizamos o tipo B, que é definido na própria assinatura do método, como sendo um sub tipo de A, assim resolvendo o problema de inserir objetos que não são strings, o método só vai aceitar strings ou seus sub tipos.
Scala possui as seguintes notações para variância e limites superiores e inferiores de tipos:
| Notação | Explicação |
|---|---|
| A+ | A só pode ser utilizado em posições de covariância |
| A- | A só pode ser utilizado em posiçÕes de contravariância |
| B <: A | B é menor ou igual a A, ou seja, B é A ou um de seus subtipos |
| B >: A | B é maior ou igual a A, ou seja, B é A ou um super tipo deste |
| B <: A >: C | B é um subtipo de A que é um super tipo de C |
Isto torna as coisas bastante flexíveis para definição de tipos e de parâmetros para métodos, permitindo que recebamos apenas o que sabemos como utilizar.
Mas tenham cuidado com os métodos polimorficos, eles são poderosos mas podem deixar o código um tanto quanto difícil de ser lido.
Vamso dar uma olhada em um exemplo um pouco mais complexo agora, retirado da documentação da linguagem e explicar os detalhes dele.
samples/033_typeparams3.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Stack[+A] { def push[B >: A](elem: B): Stack[B] = new Stack[B] { override def top: B = elem override def pop: Stack[B] = Stack.this override def toString() = elem.toString() + " " + Stack.this.toString() } def top: A = error("no element on stack") def pop: Stack[A] = error("no element on stack") override def toString() = "" } var s = new Stack[Any]().push("hello"); s = s.push(new Object()) s = s.push(7) Console.println(s) |
Na linha 2, definimos que o parâmetro do método push vai ser qualquer super tipo do tipo inicial, basicamente isto faz o método aceitar qualquer coisa, por que a declaração vai até Any, neste método é criada uma nova classe, descendente de Stack, que sobre escreve os métodos “top”, “pop” e “toString”.
Na linha 13 criamos uma nova instância de Stack[Any], em que o método push vai ser declarado como push(Any), já que não existe um super tipo de Any, e push(Any) vai aceitar qualquer valor como parâmetro.
Bom, não sei se neste artigo ajudei ou atrapalhei, mas se tiverem dúvidas, criticas ou sugestões, como sempre é só deixar um comentário que vou tentar responder o mais rápido possível.
Tags: covariance, lower bounds, metodos polimorficos, scala, upper bounds
A imagem no início deste post é uma comparação entre os ciclios básicos de desenvolvimento de TDD e BDD, se você prestar um pouco de atenção, vai ver que os dois são bem semelhantes, utilizei a mesma imagem base do ciclo do BDD que eu fiz para o post da semana passada, e fiz uma nova para o TDD, se ignorarmos as fazes de executar os testes, que na imagem do BDD estão implicitas, elas existem no ciclo, só não estão ali para aumentar o foco nas outras etapas, os dois processos são praticamente os mesmos, vamos colocar em uma tabela para facilitar a comparação.
| Fase | TDD | BDD |
|---|---|---|
| 1 | Escrever um teste | Escrever uma especificação do comportamento esperado |
| 2 | Executar os testes e ver eles falhando | Executar as especificações e ver elas falhando |
| 3 | Escrever código suficiente para que o teste passe | Escrever código suficiente para implementar o comportamento esperado/fazer a especificação passar |
| 4 | Executar os testes e ver eles passando | Executar as especificações e ver elas passando |
| 5 | Refatorar o código | Refatorar o código |
As fases são exatamente as mesmas, a diferença é que em TDD se fala em testes, enquanto em BDD se fala em comportamento, então vamos ver algumas semelhanças e fazer algumas traduções para ver que as coisas são mais iguais do que parecem.
Uma especificação do comportamento esperado, em BDD, pode ser simplesmente um teste JUnit/RUnit/NUnit, validando se um método de um objeto se comporta da forma esperada.
Um teste no TDD, pode ser um teste JUnit/RUnit/NUnit, verificando se um método de um objeto, dado um conunto de parâmetros gera um output esperado, ou seja, se ele se comporta de forma esperada.
Escrever um teste ou especificação quer dizer praticamente a mesma coisa, são duas formas de dizer que vamos escrever um teste automatizado para validar o que um método deve fazer.
Ah, então as doias coisas são iguais? TDD e BDD são a mesma coisa?
Não, são bem diferentes até, são abordagens diferentes para solucionar o mesmo problema, o BDD evoluiu do TDD, ele não tenta criar nada novo, tenta facilitar as coisas para a maior parte dos desenvolvedores.
Um ótimo desenvolvedor, trabalhando com TDD, na prática esta testando o comportamento dos métodos, das classes e do sistema como um tudo, um desenvolvedor ruim, trabalhando com TDD, esta na prática testando como os métodos foram implementados.
Já com BDD as coisas são diferentes, tanto o bom desenvolvedor quando o ruim, vão escrever testes para validar o comportamento dos métodos, classes e do sistema como um todo, por uma simples mudança de abordagem e nomenclatura das coisas. Pelo menos foi o que aconteceu comigo.
Por muito tempo, sempre que eu trabalhava com TDD, eu cometia diversos erros, e tinha muitas dúvidas, como por exemplo:
Vou falar um pouquinho sobre cada uma destas dúvidas, e como o BDD me ajudou.
Qualquer desenvolvedor iniciante tem dúvidas parecidas com esta, em algum momento da vida começamos a utilizar nomenclaturas vindas de Design Patterns, depois alguns tem sorte de ouvir falar de DDD (Domain Driven Design), e os mais inteligentes começam a utilizar DDD e começam a colocar nas classes e métodos nomes relacionados com os problemas de negócio que estes estão ajudando a resolver, ou as entidades do mundo real que as classes e métodos representam,
Isto não tem diretamente a ver com TDD, mas eu já havia ouvido falar de DDD, até achei que utilizava, mas só comecei a entender realmente DDD e a usar quando utilizei BDD em um ou dois projetos, por que o BDD reforça que temos que utilizar uma linguagem única na analise, design e implementação do sistema.
Claro que é possível contornar isto, mas também é possível utilizar o MS Word para escrever código, mas não é por isto que você vai querer fazer isto.
Eu tinha um problema que descobri depois ser bastante comum, vamos pensar na seguinte situação hipotética:
Tenho uma classe de nome Cliente e um método de nome criar, preciso escrever testes para este método.
Seguindo TDD como um principiante, vou olhar alguns tutoriais, e vou fazer mais ou menos isto:
Criar uma classe chamada ClienteTest, nesta classe vou criar um método testCriar, e depois disto dentro deste método vou popular um cliente, e chamar o método criar, depois vou usar o método list ou get para verificar se o cliente foi criado.
Até ai sem problemas, mas eu testei se o método cria dois clientes com o mesmo nome ou CPF? testei outra validações?
Vou colocar todos estes testes dentro do mesmo método testCriar? Se a resposta for não, como vou chamar o outro método que esta testando também o método criar?
Com BDD a abordagem de um iniciante que leu um tutorial na internet seria diferente, ele iria criar uma classe ClienteSpec, e criar o método shouldCreate e neste método iria popular um cliente e chamar o método create, e depois usar o método list ou get pata verificar se o cliente foi criado.
Mas qual a diferença?
No momento de verificar se o método cria dois clientes com o mesmo nome ou CPF, seguindo o padrão BDD que este iniciante aprendeu em um tutorial, ele vai criar um método shouldNotCreateWithDuplicatedName e outro shouldNotCreateWithDuplicatedCPF, já sabendo o quanto deve testar em cada um dos métodos, e qual deve ser o nome do método de testes, os nomes dos métodos de testes em BDD devem seguir o padrão:
should
Apenas a idéia de trocar o verbo test por should já ajuda quem esta começando a pensar melhor em qual deve ser o conteúdo daquele teste, e principalmente, ajuda a entender que se o comportamento mudou o teste não é mais válido, já que esta testando um comportamento inválido, diminuindo o trauma de apagar testes inválidos, que muita gente tem por ai.
Até onde devo testar é mais dificil de responder, o ideal seria testar todas as possibilidades de erro, mas isto raramente é possível, e pensar em comportamento em vez de testes me ajuda a pensar sobre o que vale mais a pena testar, e o que é mais importante ser testado.
Ai alguem vai dizer que o cliente é quem deve priorizar o que deve ser feito ou não, e eu vou dizer que com BDD, utilizando uma linguagem única em todas as fases do desenvolvimento, fica mais fácil até mesmo de explicar o que esta sendo feito naquele momento.
Nenhum cliente vai querer saber quais métodos do sistema estão sendo testados, eles apenas assumem que nós desenvolvedores testamos todo e qualquer poblema possível, mas algumas vezes precisamos da ajuda do cliente para entender o que ele quer, e algumas vezes eles querem saber como o dinheiro deles esta sendo gasto, e isto é mais comum do que muitos desenvolvedores iniciantes pensam.
É ilusão achar que com BDD um cliente vai escrever as especificações em qualquer que seja a linguagem para nós, mas utilizando no código a mesma linguagem do dominio do cliente, e testando o comportamento, podemos dizer para ele que estamos testando se o sistema não vai cadastrar usuários com nomes duplicados, que ele vai entender muito mais fácil do que se dissermos que estamos testando o método User.create.
(Lembre-se de que este foi um exemplo simplório e de baixissimo nível, uma conversa com este conteúdo raramente vai acontecer em um ambiente saudável)
Em diversas situações não é possível utilizar BDD, muitas vezes não é possível utilizar DDD, que é um requisito para BDD, muitas vezes não existem especificações claras antes de se trabalhar no código, e na maior parte das vezes o projeto já estava utilizando TDD e não tem por que isto ser alterado.
Mas depois que aprendi os conceitos de BDD, não tenho mais as mesmas dúvidas, eu sei até onde testar, por que o que deve ser testado é o comportamento do método, não como ele foi implementado, eu sei quanto testar por que cada método de teste testa um comportamento diferente, eu sei como conversar com o cliente por que eu aprendi a mapear responsábilidades do sistema para a linguagem do dominio do cliente, mesmo quando o código não esta escrito desta forma.
Ou seja, a forma de pensar do BDD me ajudou a ser um desenvolvedor melhor.
Bom, esta pergunta é um pouco mais fácil de responder.
Há alguns anos atraz, eu fui em Punta Del Este a trabalho, e no caminho de volta passei em uma lancheria perto da rodoviaria, por que eu estava com fome.
Olhei o cardapio, e tentei pedir um sanduiche e uma coca cola com todo o meu portunhol, claro que a garçonete não entendeu lhufas do que eu disse, ai ela pediu para que eu falasse em portugues, que ela responderia em espanhol, e assim nos entendemos melhor e consegui o que eu queria, meu sanduiche e minha coca cola.
Conseguimos nos entender enquanto eu falava portugues, e ela falava espanhol, mas eu garanto que teria sido muito mais fácil se ambos falassemos a mesma lingua.
E este é o objetivo das ferramentas especificas para BDD, facilitar que exista uma linguagem única no dominio do problema do cliente, na analise do problema, na especificação de requisitos e na implementação do sistema.
Claro que as ferramentas não são perfeitas, e a maior parte deles se foca mais em testes de aceitação apenas, em vez de permitir que as especificações sejam escritas para toda e qualque parte do sistema, com a granularidade necessária, mas mesmo assim, o objetivo delas continua sendo permitir que seja utilizada uma linguagem única durante todo o ciclo de desenvolvimento como manda o BDD.
Bom, acho que por hoje era isto, espero que tenha ficado claro por que eu gosto tanto de BDD, e por que estou escrevendo tanto sobre ferramentas feitas para BDD, não acho que BDD seja a bala de prata e solução para todos os problemas, mas da mesma forma que aprender uma nova linguagem de programação te faz um desenvolvedor melhor, aprender outros paradigmas e técnicas também faz.
Ainda mais no caso do BDD, que sendo uma “metodologia ágil quase completa”, inclui:
Aprender tudo isto, com certeza faz de qualquer um um desenvolvedor melhor.
Mas este é o meu entendimento sobre BDD e sobre TDD, pode ser que eu esteja errado, então como sempre, dúvidas, criticas e sugestões são bem vindos nos comentários do post.

Fluxo de trabalho Behavior Driven Development
Comecei a escrever esta coletânea de posts por que BDD realmente me ajudou a ser um desenvolvedor melhor. Eu sempre tive problemas com TDD por que eu achava os testes muito desconectados do motivo daquela classe ou método existir, depois que comecei a usar BDD eu simplesmente entendi o motivo de estar escrevendo aqueles testes, mesmo que eu estivesse escrevendo os mesmos testes que com TDD.
Na verdade eu não utilizo sempre o cucumber, não utilizo sempre o RSpec, as vezes uso Minitest ou JUnit mas a forma de pensar do BDD me ajudou a entender por que testar, o que testar e quanto testar.
E eu acho que não consegui passar esta mensagem nos posts até agora nestes posts sobre BDD e Cucumber, mas é esta a mensagem que eu quero passar, a idéia do BDD é te ajudar a responder pequenas perguntas importantes como estas:
Então para ajudar a passar esta mensagem, o que vocês me sugerem? (respostas nos comentários por favor …)
Muito obrigado pela opinião de vocês, aguardo comentários, por favor peçam para que desenvolvedores conhecidos de vocês passem por aqui e deixem a opinião deles, isto vai ser bastante importante para mim.

Em scala, instanciar listas de diversos tipos é bastante fácil, principalmente por causa dos factory methods definidos como objetos estáticos no pacote scala, vamos ver alguns exemplos.
samples/017_lists.scala
1 2 3 4 5 6 7 8 9 10 | var l = List(1,2 ,3) var l1 = 4 :: 5 :: 6 :: Nil var l2 = l1 :: l; var r1 = 1 until 10 var r2 = 1 to 10 println(l.toString) println(l1.toString) println(l2.toString) println(r1.toString) println(r2.toString) |
Podemos ver na linha 1 a utilização do factory method “List” para criar uma instância de List[Int]
Na linha 2, estamos utilizando o operador :: também chamado de “cons” para concatenar itens no início de uma linha, este operador utiliza um truque da linguagem scala, todos os operadores que começam com “:” são considerados operadores do elemento a direita, e não do elemento a esquerda como normalmente acontece, deste forma, o método foi inicialmente chamado em “Nil”, que também representa uma lista vazia, e apartir dai o método foi chamado na lista criada.
Na linha 3, utilizamos o mesmo operador para criar uma lista maior, acontece a mesma coisa que na linha anterior, mas neste ponto já é claro que estamos concatenando duas listas.
Nas linhas 4 e 5 estamos utilizando os métodos “until” e “to” da classe “Int” para criar dois objetos do tipo “Range”, coloquei estes intervalos neste exemplo por que eles compartiham muitas das propriedades de List que quero mostrar mais adiante, o método until não inclui o último elemento na lista, o método to sin.
As linhas 7 a 12 imprimem o resultado no console quando executamos o programa como um script Scala.
Podemos fazer algumas coisas interessantes com listas, olhem estes exemplos de “for” em Scala.
samples/018_for.scala
1 | for(i <- 1 to 10 ; if i % 2 == 0 ) println(i) |
O que esta sendo feito neste “for”, é atribuimos a “i” um “Range” de 1 a 10, e o for só vai executar o bloco correspondente quando o número for par, ou seja, esta linha de código imprime todos os números pares.
Para tirar mais vantagens das listas, precisamos conhecer os métodos disponíveis, vamos dar uma olhada na tabela abaixo:
| Método | O que ele faz |
|---|---|
List() |
Cria uma instância vazia de List |
Nil |
Cria uma instância vazia de List |
List("Cool", "tools", "rule") |
Cria uma nova instância de List[String] com os valores "Cool", "tools", e "rule" |
val thrill = "Will" :: "fill" :: "until" :: Nil |
Cria uma nova List[String] com os valores "Will", "fill", e "until" |
thrill(2) |
Retorna o segundo elemento (com baze zero) da lista thrill (retorna "until") |
thrill.count(s => s.length == 4) |
Conta quantas Strings na lista tem comprimento igual a 4 (retorna 2) |
thrill.drop(2) |
Retorna uma nova lista sem os dois primeiros itens |
thrill.dropRight(2) |
Retorna uma nova lista sem od sois últimos itens |
thrill.exists(s => s == "until") |
Verifica se existe na lista um elemento igual a “until” |
thrill.filter(s => s.length == 4) |
Retorna uma nova lista contendo apenas os elementos com tamanho igual a 4 |
thrill.forall(s => s.endsWith("l")) |
Verifica se todos os elementos na lista terminam com a letra “l”(retorna true) |
thrill.foreach(s => print(s)) |
Executa o método “print” para cada um dos itens da lista |
thrill.foreach(print) |
O mesmo que o anterior, mas de forma mais concisa, passa cada um dos elementos da lista como parâmetro para o método print |
thrill.head |
Retorna o primeiro elemento da lista |
thrill.init |
Retorna uma lista com todos os elementos menos o último |
thrill.isEmpty |
Verifica se a lista esta vazia |
thrill.last |
Retorna o último elemento da lista |
thrill.length |
Retorna o número de elementos da lista |
thrill.map(s => s + "y") |
Retorna uma nova lista contendo os itens resultantes da operação especificada, no caso, adicionar “y” a cada um dos itens da lista |
thrill.remove(s => s.length == 4) |
Retorna uma nova lista contendo todos da primeira, exceto os que tem o comprimento 4 |
thrill.reverse |
Retorna uma nova lista com a ordem inversa |
thrill. |
Retorna uma nova lista ordenada alfabeticamente consideando apenas a primeira letra de cada palavra |
thrill.tail |
Retorna uma nova lista contendo todos menos o primeiro elemento |
Com estes métodos, junto com os outros recursos da linguagem, podemos ver por que listas são uma das estruturas de dados mais importantes na linguagem Scala.
Mapas em scala diferentes das listas que são sempre imutáveis, podem ser mutáveis ou imutáveis, no exemplo abaixo podemos ver a utilização de um mapa mutável.
samples/019_maps.scala
1 2 3 4 5 6 7 | import scala.collection.mutable.HashMap val treasureMap = new HashMap[Int, String] treasureMap += 1 -> "Go to island." treasureMap += 2 -> "Find big X on ground." treasureMap += 3 -> "Dig." println(treasureMap(2)) |
Podemos também criar mapas imutáveis, utilizando a sintaxe simplificada da linguagem como podemos ver no exemplo abaixo.
samples/020_maps.scala
1 2 | val romanNumeral = Map(1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V") println(romanNumeral(4)) |
Mapas podem ser bastante úteis também, mas no código Scala que eu lí até hoje ví muito poucos mapas.
Uma coisa a se notar, é que o contrutor de um mapa em scala recebe uma lista de tuples de dois elementos, e que a sintaxe “KEY -> VALUE” é apenas açucar sintático para (KEY, VALUE), e que poderiamos ter feito o exemplo anterior da seguinte forma:
samples/021_maps.scala
1 2 | val romanNumeral = Map((1, "I"), (2, "II"), (3, "III"), (4, "IV"), (5, "V")) println(romanNumeral(4)) |
A classe Map também tem diversos métodos úteis, mas como eu não costumo ver muitos mapas no código, não acho que valha a pena adicionar mais uma tabela aqui neste post, mas vocês podem dar uma olhada na documentação da classe Map na API online da linguagem.
Uma das coisas mais importantes neste post é perceber que a grande maior parte dos métodos das listas recebe Closures como parâmetro, ou seja, o conhecimento adquirido até agora tem que ser utilizado em conjunto, o método List.filter recebe uma função como parâmetro, esta função por sua vez tem um parâmetro, e quando ela retorna true o elemento atual passa para a nova lista, quando retorna false o elemento é excluido da nova lista.
Outra coisa importante é que qualquer tipo de coleção ou iterator pode ser utilizado em um for, isto inclui Mapas, cada iteração do loop vai trabalhar com um Tuple.
No próximo post sobre Scala vamos voltar a falar sobre programação funcional, e logo depois sobre tipos parametrizados, upper e lower type bounds e coisas do gênero.
Como sempre, se tiver dúvidas, sugestões ou críticas, por favor deixe um comentário.
E para incentivar os comentários agora, deixo algumas perguntas:
Tags: artigo, funcional, scala, scala_for_begginers

Ok, mas por que se preocupar então em aprender outra solução? Simplesmente por que nenhuma solução é perfeita, em algum momento a combinação Capybara + WebDriver não vai atender as suas necessidades, e você vai precisar utilizar o WebDriver diretamente, ou o Watir, já que o Capybara utiliza estes, e não expões todos os recursos deles por exemplo.
Como nos outros tutoriais, vamos começar criando a estrutura básica, vamos criar um diretório de nome “cucumber_watir”, e dentro dele vamos criar um arquivo Gemfile e um Rakefile como nos exemplos abaixo:
cucumber_watir/Gemfile
1 2 3 | source 'http://rubygems.org' gem 'cucumber' gem 'watir' |
Neste Gemfile, precisamos apenas do Cucumber e do Watir, não vou falar da parte de banco de dados, acho que vou escrever um post exclusivamente sobre isto mais adiante, mas por enquanto vou colocar apenas comentários sobre qual operação aquele ponto do código deveria realizar no banco de dados.
cucumber_watir/Rakefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | require "rubygems" require "bundler/setup" Bundler.require require 'cucumber/rake/task' namespace :cucumber do Cucumber::Rake::Task.new(:ok, 'Run features that should pass') do |t| t.cucumber_opts = "features --format pretty" end Cucumber::Rake::Task.new(:wip, 'Run features that are being worked on') do |t| t.cucumber_opts = " --format pretty --tags @wip:3 --wip features" end Cucumber::Rake::Task.new(:rerun, 'Record failing features and run only them if any exist') do |t| rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" rerun_opts = rerun.to_s.strip.empty? ? "--format pretty features" : "--format pretty #{rerun}" t.cucumber_opts = "#{rerun_opts} --format rerun --out rerun.txt --strict --tags ~@wip" end desc 'Run all features' task :all => [:ok, :wip] desc 'Setup the basic directories and files' task :setup do require 'fileutils' ["features", "features/support", "features/step_definitions"].each {|f| FileUtils.mkdir_p f } File.open("features/support/env.rb","w") do |env_rb| env_rb.write <<_EOF_ require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support Before do #actions before each scenario end _EOF_ end end end desc 'Alias for cucumber:ok' task :cucumber => 'cucumber:ok' task :default => :cucumber |
Este Rakefile é o padrão que temos utilizado nos últimos posts, sem nenhuma alteração, copiado do projeto do Capybara e adicionada a task de setup do ambiente que criei para o post sobre Java.
Agora com a infra configurada, podemos finalizar o setup do projeto com os comandos abaixo, executados no console dentro do diretório cucumber_watir.
1 2 | bundle install
rake cucumber:setup |
Agora podemos começar a trabalhar, acredito que como a idéia é testar uma aplicação web qualquer, podemos utilizar o mesmo requisito de lista de tarefas utilizado nos exemplos com Rails e com Capybara, então estou copiando o mesmo arquivo .feature, o código esta abaixo:
cucumber_watir/features/task_list.feature
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | Feature: Task List
In order to remember what I need to do and what I have already completed
As a professional
I want to create a list of tasks, add tasks to the list and mark as completed the tasks I have completed
Scenario: Add task
Given I am on my task list page
And I fill the task field with "my new task"
When I click "Add"
Then I should see "my new task" on the task list
Scenario: List tasks
Given I am on my task list page
And the following tasks exists:
|title |done |
|my task |true |
|other task |false|
|one more task|false|
And I refresh the page
Then I should see the folloring tasks:
|Task |Done |
|my task |Done |
|other task |Done |
|one more task|Done |
And the line with text "my task" should have the class "done"
Scenario: Mark task as completed
Given I am on my task list page
And the following tasks exists:
|title |done |
|my task |false|
|other task |false|
|one more task|false|
And I refresh the page
When I click on "Done" at "other_task_line"
Then the line with text "other task" should have the class "done"
Scenario: Duplicate completed task
Given I am on my task list page
And the following tasks exists:
|title |done |
|my task |true |
|other task |false|
|one more task|false|
And I refresh the page
And I fill the task field with "my task"
When I click "Add"
Then I should see the folloring tasks:
|Task |Done |
|my task |Done |
|other task |Done |
|one more task|Done |
And I should see "has already been taken"
Scenario: Duplicate open task
Given I am on my task list page
And the following tasks exists:
|title |done |
|my task |true |
|other task |false|
|one more task|false|
And I refresh the page
And I fill the task field with "other task"
When I click "Add"
Then I should see the folloring tasks:
|Task |Done |
|my task |Done |
|other task |Done |
|one more task|Done |
And I should see "has already been taken" |
Se executarmos o cucumber agora, ele vai informar que não sabe como executar nenhum dos passos especificados, e vai imprimir na tela o esqueleto dos métodos que devem ser implementados em Ruby, podemos ver abaixo este esqueleto de métodos gerado pelo cucumber:
cucumber_watir/cucumber_steps.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | Given /^I am on my task list page$/ do pending # express the regexp above with the code you wish you had end Given /^I fill the task field with "([^"]*)"$/ do |arg1| pending # express the regexp above with the code you wish you had end When /^I click "([^"]*)"$/ do |arg1| pending # express the regexp above with the code you wish you had end Then /^I should see "([^"]*)" on the task list$/ do |arg1| pending # express the regexp above with the code you wish you had end Given /^the following tasks exists:$/ do |table| # table is a Cucumber::Ast::Table pending # express the regexp above with the code you wish you had end Given /^I refresh the page$/ do pending # express the regexp above with the code you wish you had end Then /^I should see the folloring tasks:$/ do |table| # table is a Cucumber::Ast::Table pending # express the regexp above with the code you wish you had end Then /^the line with text "([^"]*)" should have the class "([^"]*)"$/ do |arg1, arg2| pending # express the regexp above with the code you wish you had end When /^I click on "([^"]*)" at "([^"]*)"$/ do |arg1, arg2| pending # express the regexp above with the code you wish you had end Then /^I should see "([^"]*)"$/ do |arg1| pending # express the regexp above with the code you wish you had end |
Como podemos ver, são mais passos a implementar do que os necessários quando implementamos os testes com a aplicação Rails, a explicação para isto é que o Rails já provê alguns passos padrão já implementados no arquivo web_steps.rb, mas como estamos utilizando algo diferente do padrão do Rails temos que implementar estes passos também.
Para facilitar a implementação destes passos, vamos primeiro estudar um pouco sobre a API do Watir que vamos utilizar mais adiante.
O Watir segundo a documentação dele, roda em Windows, Linux e Mac, e consegue controlar os browsers Internet Explorer, Firefox, Safari, Chrome e Opera. Claro que nem todas as combinações entre Browser e OS são possíveis.
Trabalhando com o Watir, normalmente estaremos trabalhando com uma instância da classe Watir::Browser, então vamos começar com um overview dos métodos mais úteis desta classe:
| Código | Explicação |
|---|---|
| Começando | |
| browser = Watir::Browser.new | Abre uma nova janela do browser |
| browser = Watir::Browser.start(“http://google.com”) | Abre uma nova janela do browser no endereço especificado |
| browser.goto(“http://amazon.com”) | Abre o endereço no browser |
| browser.close | Fecha a janela do browser |
| Acessando elementos | |
| t = browser.text_field(:name, “username”) | Busca um campo de texto |
| b = browser.button(:value, “Click Here”) | Busca um boão pelo titulo |
| d = browser.select_list(:name, “month”) | busca um combo box |
| c = browser.checkbox(:name, “enabled”) | busca um checkbox |
| r = browser.radio(:name, “payment type”) | busca um radio |
| f = browser.form(:name, “address”) f = browser.form(:action, “submit”) |
Busca um formulário |
| l = browser.link(:url, “http://google.com”) l = browser.link(:href, “http://google.com”) |
Busca um link |
| td = browser.table(:name, ‘recent_records’)[2][1] | Uma tabela, depois a segunda linha primeira coluna da mesma |
| Lendo valores | |
| browser.html e.html |
O HTML interno do browser ou de qualquer elemento |
| browser.text e.text |
O texto do browser ou qualquer elemento |
| browser.title | O título da janela |
| browser.status => “Done” |
O Status do browser |
| browser.text.include? ‘llama’ | Verifica se a string especificada existe na página |
| browser.table(:id, ‘recent_records’).to_a | O conteúdo da tabela como um array |
| Manipulando Elementos | |
| b.click l.click |
Simular um clique em um botão ou link |
| t.set(“mickey mouse”) | Setar o valor de um campo texto |
| t.set(“line 1\nline2″) | SSetar o valor com mais de uma linha |
| c.set r.set |
Selecionar um checkbox ou radio |
| t.clear c.clear r.clear |
Limpar o valor de um item |
| d.select “cash” d.set “cash” |
Selecionar um item em um combo box |
| d.clearSelection | Limpar a seleção de um combo box |
| f.submit | Enviar um formulário |
Primeiro devemos editar o env.rb para adicionar o require do watir, como podemos ver abaixo.
cucumber_watir/features/support/env.rb
1 2 3 4 5 6 7 8 | require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support require 'watir' browser = Watir::Browser.new @browset = browser Before do #aqui é interessante limpar o banco de dados end |
Perceba que nas linhas 3 e 4 estamos inicializando a variável browser que sera utilizada no resto do script.
Depois, podemos passar a implementar os passos para o cucumber, vamos dar uma olhada no código do script pronto abaixo:
cucumber_watir/features/step_definitions/cucumber_steps.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | Given /^I am on my task list page$/ do browser.open('http://localhost:3000/') end Given /^I fill the task field with "([^"]*)"$/ do |value| browser.text_field(:name, 'task_field').set(value) end When /^I click "([^"]*)"$/ do |value| browser.button(:text,value).click end Then /^I should see "([^"]*)" on the task list$/ do |value| raise 'error' unless browser.table(:name,'tasks_list').text.incluse? value end Given /^the following tasks exists:$/ do |table| #create the tasks on the database end Given /^I refresh the page$/ do browser.open('http://localhost:3000/?') end Then /^I should see the folloring tasks:$/ do |table| raise 'error' unless browser.table(:name,'tasks_list').to_a == table.raw end Then /^the line with text "([^"]*)" should have the class "([^"]*)"$/ do |text, class| raise 'error' unless browser.row(:class,class).text.include? text end When /^I click on "([^"]*)" at "([^"]*)"$/ do |button, place| browser.row(:id,place).button(:value,button).click end Then /^I should see "([^"]*)"$/ do |text| raise 'error' unless browser.text.include? text end |
Podemos ver que as implementações são bem simples, mas a navegação entre os elementos da página é bem mais limitada que no Capybara.
Outra diferença bastante gritante é a falta de uma DSL como a do capybara, nada que seja muito complexo de ser criado, mas não esta pronto, mas uma vantagem é a possibilidade de interagir diretamente com o browser, ter acesso ao objeto COM que representa o Internet Explorer por exemplo, pode ser útil em algumas situações, precisei disto para automatizar uma aplicão Adobe Flex a algum tempo.
Na próxima semana vou dar uma folga ao cucumber, e vou falar um pouco do SpecFlow para testes de aplicações escritas em .NET, mas o conhecimento no Watir vai nos ajudar nisto, já que pretendo mostrar ele integrado ao WatirN.
Uma pergunta para quem leu até aqui, vocês gostaram do formato de Cheat Sheet apresentado neste post?
Como sempre, se tiverem dúvidas quanto ao post, sugestões ou críticas podem deixar um comentário que ele sera sempre bem vindo.
Falando nisto, se quiserem algum exemplo mais especifico sobre o cucumber ou sobre BDD, podem deixar um comentário ou mandar pelo formulário de contato do blog que tento responder no próximo post, ou até mesmo com um post especial para responder a pergunta.
Tags: bdd, cheatsheet, cucumber, funcional, lcucumber, Ruby, watir

Então sem mais delongas, vamos a alguns conceitos:
Closure é uma função de primeira classe com variáveis livres, ligadas ao contexto onde a closure foi criada.
Lendo esta definição, já que closure é uma função de primeira classe, quer dizer que Scala possui funções de primeira classe.
Funções de primeira classe são funções tratadas como objetos, que podem ser armazenados em estruturas de dados, ser passados como parâmetros para outras funções e podem ser o valor de retorno para outras funções.
Lendo esta definição de funções de primeira classe, ou first-class functions, uma parte desta definição me lembra da definição de Higher-Order functions, veja abaixo:
Higher-Order Functions são funções que recebem uma ou mais funções como parâmetros e/ou retornam uma função.
Juntando tudo isto, e partindo do principio que Scala suporta Closures podemos também inferir que scala suporta higher-order functions, uma caracteristica bastante comum de linguagens de programação funcionais.
Vamos começar com um exemplo, para facilitar as expicações:
samples/013_closure.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 | object ClosureApp { def printResult(func : (Int) => Any) { println(func(5)) } def main(args : Array[String]){ var i = 5 val funcParam = (j : Int) => j + i printResult(funcParam) i = 6 printResult(funcParam) } } ClosureApp.main(args) |
Na linha 2, o método printResult precisa receber como parâmetro uma função que receba um Int como parâmetro e retorne qualquer coisa.
Na linha 3 chamamos esta função passando 5 como parâmetro e passamos o resultado dela para o println.
Se você lêr novamente a definição de higher-order function, esta função definida na linha 2 é um exemplo bem simples de uma, até agora temos uma higher-order function, mas nenhuma closure.
Na linha 7, definimos uma variável “funcParam” que aponta para uma função anonima, que recebe um Int e retorna um Int.
Esta é uma função de primeira classe, estamos armazenando uma função em uma variável, que poderia ser parte de uma estrutura de dados, e na linha 8, estamos passando esta função armazenada em uma variável como parâmetro para outra função.
Mas o que define esta função anonima declarada na linha 7 como uma closure, alem de ser uma função de primeira classe, é que ela acessa uma variável local do método onde ela foi definida, dentro da closure, é armazenada uma referência para a variável definida no mesmo contexto onde esta closure foi criada, e quando a closure é executada ela lê o valor atual da variável, e não o valor que esta variável tinha quando a closure foi criada. Ou seja, mesmo sendo executada em um lugar totalmente diferente, a closure é executada no contexto onde ela foi declarada.
Isto pode parecer um pouco complexo, mas com o tempo vai ficando bem simples.
Scala pode criar closures automaticamente quando a assinatura do método pede uma closure como parâmetro, e como já vimos antes que qualquer método pode ser utilizado como um infix operator, vamos juntar estas duas informações, e re-criar o loop while do Java. Veja o exemplo abaixo:
samples/014_whileloop.scala
1 2 3 4 5 6 7 8 9 10 11 | def whileLoop(cond: => Boolean)(body: => Unit): Unit = { if (cond) { body whileLoop(cond)(body) } } var i = 10 whileLoop (i > 0) { println(i) i -= 1 } |
Na linha 1, definimos o método “whileLoop”, este método recebe dois parâmetros, o primeiro parâmetro é uma função que não tem parâmetros e retorna um valor boolean, a forma de declarar é um caso especial do que acabamos de ver sobre funções anonimas, se a função anonima não precisa de parâmetros podemos omitir os parenteses e utilizar a sintaxe “: =>
Dentro da função, se “cond” retornar true, chama body, e chama o método novamente, como ambos “cond” e “body” são closures, e tem acesso as variáveis declaradas no escopo onde foram criadas, depois de executar body uma vez, o retorno de cond pode ter sido alterado.
Se olharmos entre as linhas 7 e 11, “cond” é a expressão “(1 > 0)” e body o bloco de código que segue, ambas as closures foram passadas como parâmetro para o método whileLoop.
Podemos brincar um pouco mais com a idéia de que qualquer método pode ser utilizado como um infix ou postfix operator, e com a criação de closres dinâmicas criando um loop um pouco mais flexível, vamos analisar o seguinte exemplo:
samples/015_loop.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class LoopUnlessCond(body: => Unit) { def unless(cond: => Boolean) { body if (!cond) unless(cond) } } def loop(body: => Unit): LoopUnlessCond = new LoopUnlessCond(body) var i = 10 loop { println("i = " + i) i -= 1 } unless (i == 0) |
Entre as linhas 1 e 6, definimos a classe LoopUnlessCond que recebe uma função sem parâmetros e que retorna Unit como parâmetro no construtor.
Nesta classe, entre as linhas 2 e 5 definimos um método nesta classe, que recebe uma função sem parâmetros e que retorna Boolean como parâmetro, neste método executamos o método “body” recebido como parâmetro no construtor da classe, e logo depois, se cond retornar false, executamos o método unless novamente.
Mais adiante na linha 8 definimos um método chamado “loop” que recebe como parâmetro uma função sem parâmetros que retorna Unit, este método retorna uma nova instância da classe “LoopUnlessCond” que acabamos de criar, e passa o método recebido como parâmetro para o contrutor desta classe.
Na linha 11, chamamos o método loop, passando a closure definida nas linhas 12 e 13. Como o retorno do método “loop” é uma instância da classe “LoopUnlessCond”, na linha 14 podemos chamar o método unless definido nesta classe, passando mais uma closure como parâmetro para este método.
Juntando a possibilidade de utilizar qualquer método como um infix ou postfix operator, e a criação atomática de closures, podemos ter bastante flexibilidade na criação de pequenas DLSs, e como estas são apenas classes e métodos Scala, podemos empacotalas em bibliotecas e distribuílas com bastante facilidade.
Vamos dar uma olhada no último exemplo deste post:
samples/016_loop.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | object ExceptionHolder { class BreakException extends Exception {} val breakException = new BreakException } def break = throw ExceptionHolder.breakException def loop(body: => Unit): Unit = { try{ body loop(body) }catch{ case e: ExceptionHolder.BreakException => return } } var i = 10 loop { if(i == 0) break println("i = " + i) i -= 1 } |
Entre as linhas 1 e 4, criamos um objeto com uma única variável armazenando uma instância de uma excessão (BreakException).
Na linha 5 criamos um método “break” que a única responsabilidade é disparar uma excessão do tipo “BreakException” utilizando aquela variável do objeto definido.
Entre as linhas 7 e 14 criamos um método “loop” que recebe um bloco de código que retorna Unit como parâmetro, este método fica chamando o bloco até que uma excessão do tipo BreakException seja disparada.
E para terminar, entre as linhas 17 e 21 utilizamos esta nova construção da linguagem que acabamos de criar.
Como podemos ver neste post, a combinação de closures e métodos como operadores é mais do que o suficiente para criarmos novas construções para a linguagem, e como estas novas construções não passam se classes e métodos Scala, podemos compartilha-los entre projetos como bibliotecas “.jar” por exemplo.
Por enquanto estamos criando construções bastante simples, no próximo artigo vamos estudar um pouco sobre tipos genéricos em Scala, depois vamos estudar um pouco sobre programação funcional, mas em um dos próximos artigos vamos voltar a estudar sobre a criação de DSLs mais complexas em Scala.
Espero que agora vocês estejam começando a gostar um pouco mais da linguagem.
Como sempre, se tiverem dúvidas, comentários, sugestões ou criticas, por favor deixem nos comentários.
Tags: artigo, funcional, scala, scala_for_begginers