Posts de July, 2010

[Alexandre Martins] TDD: Listen to the tests… they tell smells in your code!

Wednesday, July 28th, 2010

These days, reading the Goos book, by Steve Freeman and Nat Pryce, it reminded me of a project I worked on a while ago. It was a one year old system, poorly tested, integrating to a handful of other systems, and the code-base… well I prefer not to remember. Despite this scenario, I joined the team to help them implement some new functionalities.

I remember sometimes it was difficult to write tests, the classes were tightly coupled, with no clear responsibilities, several attributes, bloated constructors, etc. And despite our best effort, working around the bits that were preventing us from writing the tests, we felt we were getting down the wrong road, trying to do it in such a crappy code-base. As a result some of our tests were massive! A bunch of lines of mocks, stubs, and expectations, making it impossible to understand their purpose.

What have I learned?

Reading one of the book chapters I learned that the same qualities that makes an object easy to test also makes the code responsive to change. In my situation, the tests were telling me how clumsy the code was and how difficult it would be to extend it.

I also learned that when we come across a functionality that is difficult to test, asking ourselves how to test it is not enough, we also have to ask why is it difficult to test, and check whether it’s an opportunity to improve our code. The trick is to do it driven by tests, so we can get rapid feedback on code’s internal qualities and on whether it’s doing what it’s supposed to do.

So they introduced a variation for the well-known TDD cycle— Write a failing test” => “Make the test pass” => “Refactor. As described on the figure below (extracted from the book), if we’re finding it hard to write the next failing test for our application, we should look again at the design of the production code and often refactor it before moving on, until we get to the point that we can write tests that reads well.

Extracted from Growing Object-Oriented Software, Guided By Tests— Steve Freeman and Nat Pryce

An Example of a Smell Tests Might Be Telling You

Reference data rather than behavior

When applying “Tell Don’t Ask” or “Law of Demeter” consistently, we end up with a coding style where we tend to pass behavior into the system instead of pulling values up through the stack. So picking up the famous Paperboy example, before refactoring the code applying the “Law of Demeter” the code and test would look something along the lines of the snippet showed below.

class Paperboy
  def collect_money(customer, due_amount)
    if due_amount > customer.wallet.cash
      raise InsuficientFundsError
    else
      customer.wallet.cash -= due_amount
      @total_collected += due_amount
    end
  end
end
it "should collect money from customer" do
  customer = Customer.new :wallet => Wallet.new(:amount => 200)
  paperboy = Paperboy.new
  paperboy.total_collected.should == 0
  paperboy.collect_money(customer, 50)
  customer.wallet.cash.should == 150
  paperboy.total_collected.should == 50
end

We can easily see that the test is telling us it knows too much detail about Customer class implementation. We can see its internals, which objects it’s related to, and even worse, we’re also exposing implementation details of its peers. So it’s clear for me that it needs some design improvement. My main goal here is to hide Customer implementation details from the users of the Paperboy class. Which means that I don’t wanna see anything but Customer and Money classes referenced on the test!

class Paperboy
  def collect_money(customer, due_amount)
    @collected_amount += customer.pay(due_amount)
  end
end
it "should collect money from customer" do
  customer = Customer.new :total_cash => 200
  paperboy = Paperboy.new
  paperboy.total_collected.should == 0
  paperboy.collect_money(customer, 50)
  customer.total_cash.should == 150
  paperboy.total_collected.should == 50
end

The method customer.pay(due_amount) wraps all the implementation detail up behind a single call. The client of paperboy no longer needs to know anything about the types in the chain. We’ve reduced the risk that a design change might cause ripples in remote parts of the codebase.

As well as hiding information, there’s a more subtle benefit from “Tell, Don’t Ask.” It forces us to make explicit and so name the interactions between objects, rather than leaving them implicit in the chain of getters. The shorter version above is much clearer about what it’s for, not just how it happens to be implemented.

All the logic necessary to collect the money is inside the Customer object, so it doesn’t have to expose its state to its peers.

Now it’s safer to continue writing new failing tests to our objects.
Remember, listen to the tests!

[Andrews Medina] Listando branchs remotos e locais com no git

Wednesday, July 28th, 2010

Isso é mais uma anotação, uma dica do que um post.

Mas, para listar branchs remotos e locais no git é só usar o parâmetro ‘-a’ no comando branch:

git branch -a

[Bernardo Heynemann] Dream Team – Part III – Processes

Monday, July 26th, 2010

The Value

As explained in the previous post, the first value outlined in the iNews team is that processes serve the sole purpose of being challenged and improved.

The Discussion

The team gathered at a meeting room. At the meeting there were John Miller, Susan Lawrence, Jane Collins, Jake Preston, Christian Fields, Joseph Ross and me.

I was invited just to listen, no talking allowed. I was in charge of time keeping so they would respect the time box for the meeting (the only thing I was allowed to say).

The first one to speak was John:

John: Hi guys! It’s a pleasure to be working with such a distinct and diverse team.

We have a very ambitious and interesting project ahead of us. We are in charge of changing the way people think about mobile news.

But before we set out to do just that, I’d like to discuss with you our team values.

All the other team members looked puzzle. Susan quickly replied:

Susan: I don’t get your meaning, John. I thought we were supposed to use scrum like the rest of the company.

If we are to use it as a process then we already know our values: the twelve principles in the agile manifesto.

What exactly do you mean by “our values”?

Every pair of eyes in the room turned to John. Tension in the air (ok, that’s just me being dramatic):

John: Very well observed. The issue here is that those are the agile manifesto principles. Not OUR principles.

We might end up with exactly the same set of principles, but then they’ll be our principles as well.

If we get to that, I’m sure we’ll live by those and in every decision you’ll abide by them.

A sense of shared understanding filled the room. “That makes just so much sense”, I thought.

Jane still looked uneasy, though. John, as a good leader sensed that and asked her what was worrying her.

Jane: Well, as a designer I’m not as used as you guys to a formal process. I’m very used to change, though.

It’s very clear to me that change is a competitive advantage.

John: I see, Jane. It is a good thing that you mentioned the process.

I really believe that processes are guides to help us interact and they serve no other purpose than to be challenged and replaced.

Christian: Coming from an open-source background, I’m very used to challenging and replacing “processes” of all kinds: contribution, releases, licensing.

Usually you get the processes from some other well-known successful project and start adapting them to your project.

Joseph: I like that discussion a lot. I come from a ScrumMaster position in the company from the early days of scrum.

A lot of failed Scrum implementations come from the fact that people get the practices without understanding the principles.

This leads to process paralysis. They won’t change anything in the process even if it gets in their way just because some book says they *HAVE* to do it this way.

John: What do you think now, Susan?

Susan: I couldn’t agree more with you guys.

I never thought that people failed to understand that processes are mutating things.

I always tried to adapt processes to fill my needs.

Jake: Even though I come from a very different background, what you said makes perfect sense.

I do share the concern with Jane, though. What if the current process does not really include us in the loop?

John: That’s exactly why we are suggesting that the process must be challenged and replaced with a better fit.

If ANYONE in the team feels some practice is not worth doing and have ANY better idea we should at least give it a try.

Best case scenario, we get best at doing what we have to do. Worst case, we learn. Looks like a win-win situation to me.

At this point I’m just amazed at how easily people with such different backgrounds connected over an ideal of delivering more.

Joseph: Well, I can confidently say that we have one of our values defined.

Processes are guides that should and will be challenged any time anyone thinks of a better way of doing some practice (or even not doing it at all).

There are no fool ideas or experiments. There are no reprimands to suggesting improvements by anyone in the team.

Does that sum it in an understandable way?

Everyone nods in agreement.

John: Ok, so we abide by this value at all times until we find it to be a poor fit to our team and change it. Ok?

Again happy nods in agreement.

Me: Awesome! Done with 5 mins to spare!

Susan: Yeah, and since I’m hungry, what do you guys say of a trip to Starbucks?

All: YAY!

Hmmm… Caffeine addicted, are we?!

They go happily to Starbucks and then come back to discuss next value.

Conclusion

This value may seem counter-intuitive at first. If I keep challenging a process that means it’s not good enough. What’s the point of having a process if it’s not good enough?

The team’s answer to that is that the process is just the current best practices for delivering value and as there’s no such thing as THE best way to deliver software, they are supposed to be continuously challenged, experimented with, improved and replaced.

They do not believe that any of them know exactly the best way of doing anything, yet they uphold the ideal that together, through iterative refinement they can keep improving the way they do things.

This is the reason for this value. The process is not what’s important. Delivering business value is. The process is just the means to do that.

[Bernardo Heynemann] Dream Team – Part II – The Team’s Values

Sunday, July 25th, 2010

Introduction

In the last part of the ACME’s iNews saga, John, Susan, Jane, Jake, Christian and Joseph set out to start the product.

They decided that, in order to consistently deliver and provide value to the company, they needed to get their team’s values straight. What do they believed in as a group? What principles and ideals would they uphold and work by? So they got together and after some brainstorming they came to the following Team Values Statement.

Team Values

The following values are what the entire team considers fundamental and is committed to upholding.

  1. Every process exists for the sole purpose of being challenged and improved;
  2. The best time to solve any problem or defect is now;
  3. Code only has value in production. Unreleased code is waste;
  4. Respect and Fun, with responsibility;
  5. We own the product!;
  6. Automate everything you possibly can, except people;
  7. Tranparency above everything. No questions can be invalid or taboo.

Mission Statement

They finish their value statement with their mission statement:

  • These are our values and we commit to them and to one another.
  • We also recognize that our purpose is to create value for the organization.

Conclusion

These values are not something the team “agreed” on, or just accepted. This is a set of common values that they share and will uphold above everything.

There’s a big difference in agreeing with or accepting something and sharing a value with someone. The fundamental difference is commitment. People are committed to their values and this team is committed to the values above.

I’ll explain in detail in further posts every single one of the values they outlined as they discussed (I was invited to the discussion).

[Guilherme Garnier] Criando e compartilhando componentes Facelets

Friday, July 23rd, 2010

Atualmente estou trabalhando em alguns projetos que possuem diversas características em comum. Para facilitar o reaproveitamento de código, criamos um módulo à parte, uma espécie de framework, com todo o código comum aos projetos, e modularizamos os projetos usando Maven.

Quando chegamos na camada de apresentação, percebemos que não estávamos reaproveitando código como nas demais camadas. Pelo contrário, os XHTMLs de várias telas eram bastante parecidos, e estávamos basicamente copiando e colando quando surgia uma tela nova. Inclusive dentro de um mesmo XHTML, muita parte do código era copiada, pois vários elementos se repetiam (ex: elementos de formulário, com um label e um campo de texto ao lado). Decidimos então criar componentes Facelets.

A criação de componentes Facelets é muito simples, basta seguir os passos abaixo:

  1. Criar o componente. Como exemplo, criei um chamado itemFormulario, que exibe um label, um campo de texto e as mensagens de erro correspondentes:
  2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:t="http://myfaces.apache.org/tomahawk"
        xmlns:c="http://java.sun.com/jstl/core"
        xmlns:fn="http://java.sun.com/jsp/jstl/functions">
    
    <ui:component>
        <h:outputLabel for="#{id}" value="#{label}" />
        <h:inputText id="#{id}" value="#{value}" />
        <h:message for="#{id}" />
    </ui:component>
    </html>
    

    Esse arquivo será salvo em /WEB-INF/facelets/ com o nome itemFormulario.xhtml. Neste exemplo, o componente utiliza os parâmetros id, label e value.

  3. Criar um arquivo de taglib para registrar os componentes criados. Este arquivo, que vou chamar de projeto.taglib.xml, será criado no diretório /WEB-INF/facelets do projeto, e será como no exemplo abaixo:
  4. <?xml version="1.0"?>
    <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
    <facelet-taglib>
        <namespace>http://exemplo.com.br/jsf</namespace>
        <tag>
            <tag-name>itemFormulario</tag-name>
            <source>itemFormulario.xhtml</source>
        </tag>
    </facelet-taglib>
    

    Neste exemplo, registrei o componente itemFormulario. Sempre que criar um novo componente, ele deverá ser registrado neste arquivo, através de uma nova tag <tag>.

  5. Registrar a biblioteca de taglib no projeto, adicionando o trecho abaixo ao arquivo web.xml:
  6. <context-param>
        <param-name>facelets.LIBRARIES</param-name>
        <param-value>/WEB-INF/facelets/projeto.taglib.xml</param-value>
    </context-param>
    

Desta forma, os componentes declarados no arquivo de taglib poderão ser usados no seu projeto, como neste exemplo:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
 xmlns:ui="http://java.sun.com/jsf/facelets"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:t="http://myfaces.apache.org/tomahawk"
 xmlns:c="http://java.sun.com/jstl/core"
 xmlns:fn="http://java.sun.com/jsp/jstl/functions"
 xmlns:custom="http://exemplo.com.br/jsf">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>
    </title>
</head>
<body>
    <custom:itemFormulario id="username" label="Digite seu login:" value="#{loginController.username}" />
</body>
</html>

A partir deste ponto, precisamos compartilhar estes componentes entre os diferentes projetos. Para isso, seguimos os passos abaixo:

  1. Mover o conteúdo do diretório /WEB-INF/facelets (arquivo de taglib e componentes criados) para o módulo compartilhado. Colocar estes arquivos na raiz do diretório /META-INF
  2. Atualizar no arquivo web.xml o trecho que registra a taglib:
  3. <context-param>
        <param-name>facelets.LIBRARIES</param-name>
        <param-value>/META-INF/projeto.taglib.xml</param-value>
    </context-param>
    

    Se for utilizar mais de uma taglib, declare-as separadas por “;” no trecho acima.

  4. Repetir o passo anterior para cada projeto que irá utilizar os componentes criados

Nas versões atuais do Facelets, a declaração da taglib no arquivo web.xml é desnecessária caso este arquivo esteja na raiz do diretório /META-INF. Os arquivos de componentes poderão ficar em outro diretório (ex: /META-INF/facelets), bastanto atualizar o arquivo de taglib de acordo com o diretório escolhido:

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
<facelet-taglib>
    <namespace>http://exemplo.com.br/jsf</namespace>
    <tag>
        <tag-name>itemFormulario</tag-name>
        <source>/META-INF/facelets/itemFormulario.xhtml</source>
    </tag>
</facelet-taglib>

Há ainda um passo opcional, que é a criação de um arquivo TLD (taglib descriptor) para que a IDE possa validar os componentes que você criou. Um arquivo TLD tem o seguinte formato:

<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>Componentes</short-name>
    <uri>http://exemplo.com.br/jsf</uri>
    <display-name>Minha biblioteca de componentes</display-name>
    <tag>
        <name>itemFormulario</name>
        <tag-class />
        <body-content>empty</body-content>
        <description>
            Cria um item de formulário com label, campo de texto e mensagens de erro.
        </description>
        <attribute>
            <name>id</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <description>
                Identificação do componente
            </description>
        </attribute>
        <attribute>
            <name>label</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <description>
                Texto exibido no label do componente
            </description>
        </attribute>
        <attribute>
            <name>value</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
            <type>java.lang.String</type>
            <description>
                Valor associado ao inputText do componente
            </description>
        </attribute>
    </tag>
</taglib>

Salve esse arquivo com o nome projeto.taglib.tld, no mesmo diretório do arquivo projeto.taglib.xml. Agora, ao abrir um arquivo XHTML que esteja usando o componente itemFormulario, a IDE exibirá os erros de validação (ex: um atributo marcado como required não foi definido). Com este arquivo criado, ao abrir um XHTML no Eclipse usando o editor de JSP, você terá também o recurso de autocomplete (Control + espaço), que exibirá todos os atributos do componente, assim como a descrição de cada um.

Referências:

Posts relacionados:



[Bernardo Heynemann] Dream Team – Part I – The People

Friday, July 23rd, 2010

Introduction

Tonight I was wondering if I could describe a dream team. I’m not talking about dream team members, since I’ve only worked with very smart and committed people for this last year and a half in globo.com. I’m talking about a fictitious team where people would apply everything I believe a dream team would: profound knowledge, scientific method for problem solving, set-based design, continuous improvement, continuous deployment, iterative discovery, design and development, and some other things.

As is very well put by Mary and Tom Poppendieck in their book, it’s never the workers faults that create bugs, delivery rescheduling or customer dissatisfaction. It’s ALWAYS the system. So the main goal for this team is to make the system as mistake-proof as they possibly can, and then some. Through these posts I’ll try to describe the people, the process and its improvements and a product they are developing.

The Product

The team will be doing an iPhone/iPad app to deliver news to clients of a news agency. It’s creatively named iNews.

The Team

Even though this is a fictional team, I want to describe them (even with pictures – all Creative Commons) so I can translate that PEOPLE are the goal, the main thing. These are the people set to do a FANTASTIC product for my fictitious company, ACME Software.

http://www.flickr.com/photos/ian_munroe/4174136961/ John Miller

John Miller is the tech lead for iNews. He used to work for a very large news agency and has more than 10 years of experience developing software.

John has a lot of experience with agile practices and lean software development. ACME’s board of directors expect him to lead the team to deliver a surprising, efficient and competitive product.

John has experience with static and dynamic languages, yet no experience with iPhone development. He’s eager to learn all about it, though. He wonders what kinds of architectural issues the iPhone/iPad development model poses.

 

http://www.flickr.com/photos/lara604/2369412952/ Susan Lawrence

Susan Lawrence is a senior engineer with the company. She is very knowledgeable of Scrum and in previous projects for ACME she loved the methodology. She feels that something was missing, though. There were some issues identified by the team and yet the team did little to solve them.

She has no experience with mobile development but is very eager to learn as much as she can. User experience is a subject that she cares deeply and is looking forward to working with Jane Collins.

She is the author of Stinks, the Continuous Integration server being used by some teams at ACME, so you can tell that she cares a lot about this practice.

 

http://www.flickr.com/photos/dichohecho/2556568314/ Jane Collins

Jane Collins is the user experience designer for iNews. She has a success track record with the company on several previous products.

She used to decide on  her own what the user experience was supposed to be. This has proven to be the ineffective way of doing this, since the team always had issues implementing what she designed.

In her last project she tried a more tight integration with developers and they provided invaluable insight into what the users might value and what they wouldn’t.

She’s looking forward to the challenges of designing a consistent user experience for such distinct devices as the iPhone and iPad.

 

 

Jake Prestonhttp://www.flickr.com/photos/titlap/3936883765/

Jake Preston used to be an expert at front-end software development. Tired of this separation of front-end and back-end developers, he decided he would be as knowledgeable on back-end development as everyone else in the company (if not more).

Jake has been studying software engineering practices in general and learning more in every project he´s in. The iNews product has several interesting challenges for him. Among them, the different presentation requirements and capabilities of the devices.

He’s very concerned with how can interface automated testing be done in such devices, as well.

 

 

 

http://www.flickr.com/photos/greggoconnell/252976245/Christian Fields

Christian Fields has just joined ACME. He never worked with news agencies, but has a very good track record with open source projects, being a contributor to large and very large projects.

He has a very strong culture of sharing and contributing. He expects to be able to apply this knowledge to iNews, since collaboration with customers and to some other teams will not only be required, but key to success.

He values second to none automated testing and versioning, being so used to rejecting patches that do not provide automated tests and releasing early and often to gather feedback of the community.

He is VERY excited to be working with a team of smart people in a very promising product.

 

http://www.flickr.com/photos/flechtnerby/4817094778/Joseph Ross

Joseph Ross is the project manager. His role is to remove any impediments that stop the team from fulfilling the values in their value statement (next part).

He has a very strong process background being a certified scrum master and PMP. He thinks methodologies are guides whose only purpose is to be improved and replaced with the new improved process.

He has been in the company for more than 10 years, thus he knows virtually every employee and knows exactly how ACME and its people operate, as well as what the company’s values are.

Conclusion

These are the people who, together, will succeed or fail in delivering an innovative news content delivery application for the iPhone/iPad platform.

More about what their values are in the next post.

[Emerson Macedo] RVM com Passenger e múltiplas instâncias HTTPD

Monday, July 19th, 2010

No artigo anterior, falei sobre sobre RVM (Ruby Version Manager), explicando sobre instalação, configuração e seu funcionamento interno. Porém, não mencionei nada sobre seu uso com o Phusion Passenger (A.K.A mod_rails, mod_rack), e algumas pessoas me perguntaram sobre isso. Eu não abordei essa parte no artigo anterior por duas razões: A primeira é porque já era madrugada e o sono já me dominava :) e a segunda porque o artigo iria ficar muito grande e o conteúdo inicial já era suficiente para montar um ambiente local na máquina do desenvolvedor.

Esse artigo basea-se no uso do Apache 2.x e no Sistema Operacional OSX. Porém, as configurações em SOs POSIX se assemelham, portanto usuários do Ubuntu e afins não deverão ter nenhum problema.

Instalando o Passenger em uma Rubie/Gemset

Se você já usa RVM ou seguiu os passos do artigo anterior, já deve ter tudo instalado e configurado e já possui uma Rubie com ou sem Gemsets (não faz diferença ter Gemsets para esse artigo). Caso contrário, leia novamente.

Abra o terminal e entre na sua Rubie/Gemset, instale a gem do passenger e o módulo do apache (ou nginx):

$ rvm use rubie@gemset #ex: rvm use 1.8.7@minhaapp
$ gem install passenger
$ rvmsudo passenger-install-apache2-module

Muita atenção nesse momento. A instalação do módulo do apache (ou nginx) deve ser feita usando rvmsudo. Sem isso não funcionou comigo e acho que com ninguém vai funcionar :)

Feito isso, no fim da instalação do módulo ele vai mandar você colocar aquelas 3 linhas de configuração do Passenger nas suas configs do apache. Eu sugiro criar um arquivo passenger.conf ou similar e colar esse código dentro desse arquivo, pra não misturar com as configs básicas do Servidor Http. Se você estiver usando Mac, as configurações do apache provavelmente estarão em /etc/apache2 e existe um diretório other, onde colocamos as configurações adicionais, como esta que acabamos de criar para o passenger.

Agora precisamos ajustar um detalhe nessas configurações do Passenger. Quando instalamos o Passenger e módulo do apache em uma RVM, a terceira linha da configuração (PassengerRuby) que a instalação sugere, não aponta para o diretório correto, e não funciona. É necessário fazer uma modificação e vou explicar.

Quando instalamos a RMV, ele cria um diretório chamado ~/.rvm/bin, onde ficam todos os executáveis das rubies (ruby, irb, gem, ri, rdoc). A instalação do Passenger não entende isso e aponta para uma estrutura diferente, formada da seguinte maneira: ~/.rvm/rubies/{Rubie}/bin/ruby, e isso não funciona. Portanto, precisamos alterar essa linha para que fique da segunte forma:

$ PassengerRuby ~/.rvm/bin/{Rubie}@{Gemset}

A documentação sugere uma forma diferente dessa, nos indicando executar rvm {Rubie@Gemset} –passenger e substituir essa terceira linha da segunte forma:

$ PassengerRuby ~/.rvm/bin/passenger_ruby

O que isso faz é criar um link symbólico de passenger_ruby apontando para o {Rubie}@{Gemset} que você quer. Eu acho isso desnecessário, principalmente porque você pode querer usar o passenger com várias Rubies diferentes, que é o que vou abordar mais adiante. Portanto, eu prefiro ficar com a primeira opção.

Agora basta configurar o VirtualHost (presumo que você já sabiba fazer isso) e reiniciar o Apache. Nesse momento tudo já deve estar funcionando corretamente e você deve estar conseguindo ver sua aplicação funcionando. Não se esqueça de configurar o /etc/hosts (é bastante comum ver gente capotando porque esqueceu essa parte, rs).

Múltiplas apps em Rubies diferentes

Segundo a documentação do Passenger, a diretiva PassengerRuby deve ser usada apenas uma vez, nos limitando a ter nossas aplicações em uma Rubie/Gemset única. Uma maneira comum de trabalhar com essa limitação é instalar o Ruby Enterprise Edition (i.e. Ruby EE) ou qualquer versão de Ruby e instalar todas as gems de todos os projetos nessa Rubie. A partir daí, basta criar um VirtualHost para cada Aplicação. Essa era a forma que trabalhavamos sem RVM. Com RVM a idéia e sair dessa limitação, podendo por exemplo rodar uma app usando Ruby 1.8.7 com Rails 2.3 e outra com Ruby 1.9.2 e Rails3. Isso inclusive num ambiente de produção, isolando bem as apps e evitando conflitos. Vamos portanto resolver o problema de outra maneira.

Criando uma nova configuração para o Apache

Talvez você não saiba, mas o diretório /etc/apache2 não é o servidor Apache HTTPD propriamente dito, mas as configurações que esse servidor usa quando você sobe a instância. Por padrão, o apachectl procura as configurações nesse diretório, mas é perfeitamente possível indicar o path do arquivo de configuração que você quer usar, e é baseado nisto que vamos usar uma alternativa melhor.

Antes de qualquer coisa, vamos trocar alguns parâmetros na configuração default do Apache, para podermos rodar mais de uma instância sem nenhum tipo de problema acidental. Por padrão, nas configurações em /etc/apache2/httpd.conf tem um Listen definito para *:80. Vamos mudar isso:

Listen 127.0.0.1:80

Mudemos também quaisquer configurações de VirtualHost existentes que estejam usando *:80:

VirtualHost 127.0.0.1:80

Agora chegou a hora de clonar essas configurações para uma nova. Esse trabalho é um pouco chato, portanto resolvi criar um Shell Script para isso chamado osx-clone-apache.sh que pode ser baixado aqui e está no meu gist. Já testei no Snow Leopard e Leopard. Se alguém quiser fazer um para ubuntu e outros SOs é bem vindo e eu coloco como update no artigo.

Baixe o Script e execute a seguinte linha no terminal:

$ sudo ./osx-clone-apache.sh http-clone 127.0.0.1:80 127.0.0.2:80

Onde http-clone é o nome da nova configuração que será criada dentro de /etc, 127.0.0.1:80 é o Listen que está configurado em /etc/apache2 e 127.0.0.2:80 é o novo Listen. Essa nova configuração não leva nada do diretório other nem do passenger_pane_vhosts se você estiver usando o Passenger Preference Pane.

Agora que temos uma nova config do apache apontando para 127.0.0.2:80 precisamos configurar nossa interface de rede para passar a responder a esse ip:

$ ifconfig lo0 alias 127.0.0.2/32

Se não me engano, no Ubuntu isso não é necessário, mas no Mac eu não conheço outra forma de fazer isso. Se alguém souber é bem vindo.

Nesse ponto basta repetir os mesmos procedimentos de configuração de Passenger para novas Rubies e Gemsets para essa nova configuração do apache que acabamos de criar. Mais uma vez não esqueça de ajustar suas configurações no /etc/hosts.

O último passo é subir uma instância do apache usando as novas configurações criadas.

$ sudo apachectl -f /etc/{novaconfig}/httpd.conf -k start

Pronto, você já tem 2 instâncias do Apache rodando versões de Ruby/Rails diferentes na mesma máquina.

Conclusão

O RVM (Ruby Version Manager) trouxe uma nova perspectiva no gerênciamento de ambientes Ruby/Rails. O Deploy de Aplicações com versões de Ruby e Rails usando Phusion Passenger numa mesma máquina se torna trivial, desde que se saiba o que está fazendo. As configurações apresentadas neste artigo podem (e acho que devem) ser usadas também em ambiente de produção, pois a SANDBOX criada é muito mais simples de ser gerênciada do que aquela mistura habitual de VirtualHosts, Gems e afins.

Mãos a obra !!!

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

[Andrews Medina] novo post do andrews

Saturday, July 17th, 2010

quero so ver como vai ser

[Fabio M Costa] Domain redirect keeping the URL path with .htaccess

Friday, July 16th, 2010

I had a site hosted at a domain and the client asked me to change the domain. But it’s a very bad idea to simply change a site’s address without thinking about all the users who have bookmarked the old site. It means losing all of them. So I thought that i could do a redirect from the old domain to the new one. This redirect should be using HTTP status code 301 to identify that the site has moved permanently. There are many ways to do such a thing. But one took my attention. Why not use Apache .htaccess file associated with mod_rewrite? Most of the hosting services supports this. Firstly I tried this:

RewriteEngine On
RewriteBase /
Redirect 301 / http://mynewdomain.com/

This way, all requests to the root of my old domain get redirected to the root of my new domain. But what if I try yo access some path inside my old domain? It would raise a 404 error because it does not have the content anymore. I want that if the user access http://myolddomain.com/path/ it redirects him to http://mynewdomain.com/path/. So I ended up with this:

RewriteEngine On
RewriteBase /
RedirectMatch 301 (.*)$ http://mynewdomain.com$1

[Fabio M Costa] Domain redirect keeping the URL path with .htaccess

Friday, July 16th, 2010

I had a site hosted at a domain and the client asked me to change the domain. But it’s a very bad idea to simply change a site’s address without thinking about all the users who have bookmarked the old site. It means losing all of them. So I thought that i could do a redirect from the old domain to the new one. This redirect should be using HTTP status code 301 to identify that the site has moved permanently. There are many ways to do such a thing. But one took my attention. Why not use Apache .htaccess file associated with mod_rewrite? Most of the hosting services supports this. Firstly I tried this:

RewriteEngine On
RewriteBase /
Redirect 301 / http://mynewdomain.com/

This way, all requests to the root of my old domain get redirected to the root of my new domain. But what if I try yo access some path inside my old domain? It would raise a 404 error because it does not have the content anymore. I want that if the user access http://myolddomain.com/path/ it redirects him to http://mynewdomain.com/path/. So I ended up with this:

RewriteEngine On
RewriteBase /
RedirectMatch 301 (.*)$ http://mynewdomain.com$1