Arquivo

Arquivo da Categoria ‘Ruby’

Desenvolvimento com Rails, mongoid, rspec e cucumber

12, agosto, 2010 Sem comentários

Nesse rápido post irei demonstrar como iniciar o desenvolvimento de uma aplicação com o Rails 2.3.8, utilizando como banco de dados NoSql o Mongodb.
Para conexão com o banco, utilizaremos a gem Mongoid. Também iremos configurar para testes o Rspec e o Cucumber. Considerando a parti daqui que você tem todas as gems citadas anteriormente instaladas no seu ambiente.
Primeiro começaremos criando uma aplicação Rails, nada de novo aqui.

rails app_teste

Com isso iremos gerar toda a estrutura da aplicação. Agora vamos configurar as gems utilizadas, em ‘config/enviroment.rb’, adicionaremos dentro do bloco ‘Rails::Initializer.run’ a gem do mongoid:

Rails::Initializer.run do |config|
  config.gem 'mongoid'
  config.time_zone = 'UTC'
end

Com a gem do Mongoid, não precisaremos mais do ActiveRecord, para remover de sua aplicação basta adicionar dentro do mesmo bloco que você configurou o a gem, a seguinte linha ‘config.frameworks -= [:active_record]‘. Deixando meu enviroment.rb assim:

Rails::Initializer.run do |config|
  config.frameworks -= [:active_record]
  config.gem 'mongoid'
  config.time_zone = 'UTC'
end

Quando utilizavamos o ActiveRecord, por padrão ele procurava um arquivo YAML chamado database.yml no qual continha as informações sobre o meu banco. No caso do mongoid, podemos continuar com esse mesmo arquivo, ou criar outro (no meu caso eu prefiro criar um chamado mongoid.yml).
Abaixo segue o meu arquivo yml:

base: &base
  adapter: mongodb
  # em tempo de execucao vamos adicionar o "-#{Rails.env}" ao nomo do banco
  database: "feedse"
# Se estiver rodando o mongodb em outro host,
#vc deveria autenticar-se
  #host: host.mongodb.com
  #username: your-username
  #password: your-password

# use as proximas linhas para colocar algo especifico a cada ambiente
development:
  <<: *base

test:
  <<: *base

production:
  <<: *base

Feito isso, agora precisamos que nossa aplicação leia esse arquivo e configure nossa conexão com o banco. Para isso, criaremos um arquivo chamado ‘mongo.rb’ (ou qualquer outro nome que você queira) dentro do diretório config/initializers.
mongo.rb

require 'yaml'

mongo_config = YAML::load(File.read(File.join(Rails.root, 'config/mongoid.yml')))[Rails.env]

Mongoid.configure do |config|
  config.from_hash(mongo_config)
end

Com isso, já temos tudo pronto em relação ao nosso banco.

Criaremos então, um model chamado ‘Content’. Lembrando que como não utilizamos mais o ActiveRecord, nossa classe não deverá herdar ActiveRecord::Base.

Abaixo segue a implementação da nossa classe:

class Content
  include Mongoid::Document
  field :title, :type => String

  validates_presence_of :title
  validates_uniqueness_of :title
end

Para que nossa classe seja persistida em nossa banco, precisamos incluir o include Mongoid::Document. Precisamos também definir os campos que ele vai ter, no nosso caso somente o campo ‘title’. A definição básica dos campos segue a seguinte estrutura ‘field , :type => ‘. Para mais informações consulte a documentação do mongoid.

Utilizando o RSpec

Vamos gerar a estrutura do rspec, como faríamos em qualquer outro projeto Rails.

script/generate rspec

Logo após gerar a estrutura, abra o arquivo spec_helper.rb dentro do diretório spec recém gerado, e dentro do bloco Spec::Runner.configure comente as seguinte linhas:

  config.use_transactional_fixtures = true
  config.use_instantiated_fixtures  = false
  config.fixture_path = RAILS_ROOT + '/spec/fixtures/'

Um dos problemas que enfrentei logo quando comecei a mexer com Rspec junto com MongoDb, foi que o rpspec não limpa o banco de testes após executar os mesmos. Logo isso deverá ser feito na mão, para isso adicionaremos dentro do bloco Spec::Runner.configure em o seguinte código:

  config.after(:each) do
    Mongoid.database.collections.each do |collection|
      unless collection.name =~ /^system\./
        collection.remove
      end
    end
  end

Deixando o nosso spec_helper.rb assim:

ENV["RAILS_ENV"] ||= 'test'
require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment'))
require 'spec/autorun'
require 'spec/rails'

# in ./support/ and its subdirectories.
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}

Spec::Runner.configure do |config|

  #config.use_transactional_fixtures = true
  #config.use_instantiated_fixtures  = false
  #config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
  #clean database mongodb after tests
  config.after(:each) do
    Mongoid.database.collections.each do |collection|
      unless collection.name =~ /^system\./
        collection.remove
      end
    end
  end

end

Nesse caso o Rspec irá limpar o banco depois de cada teste ‘each’, mas você pode efetuar a limpeza somente depois de todos os testes terem rodado, nesse caso ao invés de ‘:each’ como parâmetro, utilizaremos ‘:suite’, e caso você queira executar alguma coisa antes dos testes, utilize o ‘config.before’, que funciona exatamente da mesma maneira do ‘config.after’, com a diferença de ser executado antes(before) dos testes.

Obs: Se você está utilizando também o cucumber e está com problemas em relação a limpeza do banco após os testes, a solução é simples.
Crie o arquivo ‘clean_mongo.rb’ (ou qualquer outro nome) dentro de features/support e adicione nele o código abaixo:

After do |scenario|
  Mongoid.database.collections.each do |collection|
    unless collection.name =~ /^system\./
      collection.remove
    end
  end
end

E com isso temos nossos testes funcionando com o mongodb.

Pesquisa avançada com MongoMapper, simulando um “like %”

3, agosto, 2010 Sem comentários

Esse post é uma “continuação” do anterior, estarei demonstrando como fazer uma query no “like %” no MongoDb, utilizando a gem MongoMapper

Como no post anterior, eu tenho o model “Person”:

class Person
  include MongoMapper::Document
  key :name, String
  key :middle_initial, String
end

E eu preciso encontrar todos os registros (no caso do Mongodb, documentos) que contenham o nome “Silva” em ‘name’, podendo ter vários Silva’s, como: “João da Silva”, “Maria Silva” e etc…

A solução, mais uma vez, é simples:

Person.all("$where" => "function() { return this.name.match(/"Silva"/i)"})

Pesquisa avançada com Mongoid, simulando um “like %”

1, agosto, 2010 Sem comentários

Comecei semana passada a utilizar o Mongodb (utilizando a biblioteca mongoid) em um novo projeto que estou desenvolvendo. Embora a documentação do Mongoid seja excelente, tive problemas para utilizar uma query com o ‘like %%’ de pesquisa como eu utilizava em bancos de dados relacionais.
A solução que eu encontrei foi utilizar uma Regex no parâmetro de busca.
Imagine que eu tenho um model chamado Person, como o abaixo:

class Person
  include Mongoid::Document
  field :name
  field :middle_initial
end

E eu preciso encontrar todos os registros (no caso do Mongodb, documentos) que contenham o nome “Silva” em ‘name’, podendo ter vários Silva’s, como: “João da Silva”, “Maria Silva” e etc…

A solução é simples:

Person.find(:all, :conditions => {:name => Regexp.new("Silva")}).entries

Então será retornado um array contendo todos os documentos com o termo “Silva” no nome.

Categories: Mongodb, NoSql, Rails, Ruby Tags: , , , ,

Iniciando no desenvolvimento de aplicações Desktop com Qt e Ruby

26, julho, 2010 2 comentários

Esse post é o inicio de uma série que estou preparando sobre desenvolvimento desktop com Ruby. Para quem não sabe o Qt é um framework para desenvolvimento de aplicações Desktop muito utilizado pelo comunidade OpenSource. E já provou sua eficácia em grandes projetos. Foi desenvolvido originalmente pela empresa e mais tarde adquirido pela Nokia. Nesse exemplo, será desenvolvido uma simples aplicação “Hello World!” e no futuro aprofundaremos mais no assunto.

require 'Qt4'

app = Qt::Application.new(ARGV)

hello = Qt::PushButton.new('Hello World!')
hello.resize(100, 30)
hello.show()

app.exec()
Categories: QT, Ruby Tags: ,

Utilizando o is_paranoid

13, junho, 2010 2 comentários

O is_paranoid é uma gem que faz com que os registros do banco não sejam apagados através do método destroy. Ao chamar o método destroy nosso objeto é apenas marcado como apagado e pode ser restaurado posteriormente, bem como listado através de método fornecidos pelo is_paranoid.
Sua utilização é bem simples, primeiro instale a gem:

gem install is_paranoid

Para que funcione em sua aplicação Rails, adicione no enviroment.rb , dentro do bloco de inicialização:

config.gem "is_paranoid"

Deixando mais ou menos assim:

Rails::Initializer.run do |config|
  # ...
  config.gem "is_paranoid"
end

Agora, nos models que vão utilizar dos recursos do is_paranoid, adicione logo após a declaração da classe ‘is_paranoid’

Exemplo:

class Student < ActiveRecord::Base
  is_paranoid
end

E para finalizar, crie uma migrate que adicione na tabela que vai utilizar o is_paranoid, o campo ‘deleted_at’, do tipo ‘timestamp’.

Exemplo:

class AddIsparanoidInStudent < ActiveRecord::Migration
  def self.up
    add_column :students, :deleted_at, :timestamp
  end

  def self.down
    remove_column :students, :deleted_at
  end
end

Pronto! Se quiser saber mais sobre o is_paranoid, consulte a documentação oficial no site do projeto.

Categories: Dicas, Rails, Ruby Tags: , ,

Remember password com restful_authentication

10, abril, 2010 2 comentários

O restful_authentication é um plugin de autenticação para aplicativos ruby on rails, com excelentes recursos, super fácil de configurar. Enfim, não vou falar muito sobre ele, pois não é o propósito desse tutorial. Irei assumir que você já tem o restful_authentication funcionando e quer apenas adicionar a funcionalidade de enviar um código de reset password via email para o usuário que esqueceu sua senha. Ao acessar o link com esse código o sistema irá exibir uma tela de troca de senha, lembrando que esse código deverá ser único e poderá ser usado somente uma vez. Então vamos lá:

Primeiro iremos gerar uma migration que adiciona na tabela o usuário a coluna ‘reset_password_code’.

script/generate migration reset_password_code

Agora editarei a migration gerada, deixando da seguinte forma:

class ResetPasswordCode < ActiveRecord::Migration
  def self.up
    add_column :users, :reset_password_code, :string
  end

  def self.down
    remove_column :users, :reset_password_code
  end
end

em seguida, ‘rake db:migration’ e estaremos pronto para começar!

Na classe UsersController irei criar um método protected que irá gerar um código para ser enviado ao usuário.

protected
  def random_code( len = 40 )
    chars = (("a".."z").to_a + ("1".."9").to_a )- %w(i o 0 1 l 0)
    newpass = Array.new(len, '').collect{chars[rand(chars.size)]}.join
  end

Agora criarei o método que chamará a view com o campo para o usuário colocar seu email:

  def change_reset_password
  end

Agora criarei a view que receberá o endereço de email do usuário e chamará o método “reset_password” passando o email:

<p>
    <%= form_tag (:controller => 'users', :action => 'reset_password' ) %>

    <p>
        <%= label_tag 'Email' %>
        <br/>
        <%= text_field_tag (:email) %>
    </p>
    <p>
        <%= submit_tag 'Enviar' %>
    </p>
    <%= flash[:alert] %>
</p>

Já método reset_password, irá receber o email, procurar o usuário com esse email, gerar o código de reset password, e chamar o UserMailer para enviar o email com o código.

  def reset_password
    user = User.find_by_email(params[:email])
    unless user.nil?
      user.reset_password_code = random_code
      if user.save!
        UserMailer.deliver_new_password(user)
        flash[:done] = "Verifique seu email para trocar a senha"
        redirect_to ('/')
      else
        flash[:error] = "Erro ao efetuar trocar de senha"
        render :action => 'change_reset_password'
      end
    else
      flash[:error]  = "Email Inválido"
      render :action => 'change_reset_password'
    end
  end

Vejam que estou chamando “UserMailer.deliver_new_password(user)” e passando o usuário como parâmetro. Na classe ‘UserMailer’ devo criar o método ‘new_password’ que será usado para enviar o email.
Meu método new_password deverá ficar assim:

  def new_password(user)
    setup_email(user)
    @subject    += 'Mudança de senha'
    @body[:url]  = "#{$SERVER_CONTENT_BASE}/password/#{user.reset_password_code}"
  end

Agora criarei a view para esse email, dentro de ‘app/views/user_mailer’ com o nome de ‘new_password.html.erb’

Olá <%=h @user.login %> alguém solicitou a troca de senha para essa conta.
Se deseja realmente trocar sua senha, clique no link abaixo:
<%=h @url %>

Agora de volta ao UsersController, criarei o método que recebe o reset_code e se ele estiver correto será jogado em um campo hidden na view de troca de senha.

  def code_password_reset
    user = User.find_by_reset_password_code(params[:reset_code])
    if user.nil?
      flash[:error]  = "Código inválido"
      redirect_to ('/')
    else
      @reset_code = params[:reset_code]
      render :action => "new_password"
    end
  end

Criarei a view de troca de senha, chamada ‘new_password’

<p>
    <%= form_tag (:controller => 'users', :action => 'reset_password_update', :method => 'put' ) %>
    <p>
        <%= label_tag 'Nova Senha' %>
        <br/>
        <%= password_field_tag (:password) %>
    </p>
    <p>
        <%= label_tag 'Confirmação' %>
        <br/>
        <%= password_field_tag (:password_confirmation) %>
    </p>
    <%= hidden_field_tag(:reset_code, @reset_code) %>
    <p>
        <%= submit_tag 'Enviar' %>
    </p>
	<%= flash[:alert]%>
</p>

E agora o método que de fato executa a troca de senha:

  def reset_password_update
    @user = User.find_by_reset_password_code(params[:reset_code]) unless params[:reset_code].blank?
    if ((params[:password] == params[:password_confirmation]) && !params[:password_confirmation].blank?)
       @user.password_confirmation = params[:password_confirmation]
       @user.password = params[:password]
       @user.reset_password_code = nil
      if @user.save!
        flash[:done] = "Senha atualizada com sucesso!"
        redirect_to ("/")
      else
        flash[:alert] = "Falha ao atualizar senha."
        render :action => 'new_password'
      end
    else
      flash[:alert] = "Senhas diferentes!"
      render :action => 'new_password'
   end
  end

Para finalizar iremos configurar as rotas;

  map.resources :users, :collection => { :change_reset_password => :get,
                                         :new_password => :get, :reset_password_update => :post,
                                         :code_password_reset => :get, :reset_password => :get}
  map.connect 'password/:reset_code', :controller => 'users', :action => 'code_password_reset'

E assim chegamos ao fim desse tutorial.

Teste em ruby utilizando o RSpec

5, março, 2010 Sem comentários

Essa semana comecei a utilizar o rspec para realizar testes na aplicação que estou trabalhando e estou gostando muito, pra não dizer que estou viciado. O Rspec torna o trabalho de escrever testes divertido, rápido e prático.
Abaixo segue o link de alguns excelentes artigos que tive a chance de ler sobre rspec:

http://blog.wolfman.com/articles/2007/7/6/rspec-testing-views-for-escaped-html
http://www.elevatedrails.com/articles/2007/09/10/testing-controllers-with-rspec/
http://simplesideias.com.br/usando-o-rspec-para-testar-sua-aplicacao-rails-modelos/
http://simplesideias.com.br/7-coisas-que-voce-precisa-conhecer-no-rspec/
http://blog.davidchelimsky.net/2007/05/14/an-introduction-to-rspec-part-i/

Categories: Rails, Ruby Tags: ,

Paginação no Rails usando o will_paginate

20, fevereiro, 2010 Sem comentários

Uma maneira fácil de se trabalhar com paginação no Ruby on Rails é utilizar o plugin will_paginate. Nesse breve artigo irei demonstrar como instalar e utilizar em sua aplicação.
Primeiro instalaremos o plugin:

gem install will_paginate

Com o plugin instalado, podemos começar a configura-lo na aplicação. Para isso abra o arquivo environment.rb dentro do diretório config e adicione no final do arquivo a seguinte linha:

require "will_paginate"

Com isso, sua aplicação só irá subir se o plugin will_paginate estiver presente.
Já no controlador devo usar o método paginate para efetuar a busca pelos registros no banco. No meu caso, tenho uma entidade chamada ‘noticia’ e no método ‘index’ do seu controlador, pelo padrão o rails faz uma busca por todos os registros no banco.

  def index
    @noticias = Noticia.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render : xml => @noticias }
    end

E no caso eu irei deixa-lo assim:

  def index
    @noticias = Noticia.paginate :page => params[:page], : order => 'created_at DESC', :per_page => 10

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render : xml => @noticias }
    end
  end

Onde o “:order =>” irá definir a ordem de exibição dos registros.
E o “:per_page =>” a quantidade de registros por página.

E pra terminar, abra a view e adicione logo abaixo do término da tabela:

<%= will_paginate @noticias %>
Categories: Rails, Ruby Tags: , , ,

Ruby! Ruby! Ruby! Ruby e … Ruby!

24, outubro, 2008 2 comentários

Bom, como muitos de vocês já sabem sou um grande fã da linguagem Ruby, e por isso estou postando dois excelentes artigos.

O primeiro é para quem está iniciando no mundo Ruby, serve como introdução sobre os conceitos da linguagem.

O segundo é sobre o uso de simbolos, que é uma das grandes sacadas do Ruby (não conheço nenhuma outra linguagem com algo parecido, se alguém conhecer posta ai) e ao mesmo tempo um dos assuntos que gera mais dificuldade (eu mesmo tive dificuldade).

Artigo 1:

http://ruby-br.org/tutoriais/?t=4&p=0

Artigo 2:

http://www.akitaonrails.com/2007/11/26/ruby-symbols

Bom eu sei que é meio off-topic, mas ainda em tempo recomendo a todos a leitura do artigo “Como eu desistir de USD 300k da Microsoft para trabalhar full-time no Github” no qual o Tom Preston-Werner conta como ele desistiu do emprego dos sonhos de muitos programadores para trabalhar em um projeto pessoal.

http://www.akitaonrails.com/2008/10/21/off-topic-tradu-o-como-eu-desisti-de-usd-300k-da-microsoft-para-trabalhar-full-time-no-github

Categories: Ruby Tags:

JBoss On Rails?

20, outubro, 2008 3 comentários

Se é boato ou verdade eu não sei, o fato é: o rails vem crescendo de maneira assustadora e é claro que grandes empresas como RedHat, SUN e etc… não iam ficar de fora disso.

Não é atoa que em 2006 a SUN contratou o Charles Nutter e Tom Enebo para colocar pra frente o projeto JRuby, o que deve permitir codigos Ruby serem executados na JVM.

Agora é esperar para ver.

Fonte:

http://blog.seatecnologia.com.br/articles/2008/10/20/jboss-on-rails


Categories: Java, Ruby Tags: