I've watched the RailsCasts #281, and Ryan Bates is talking about the Foreman gem. At the first time I did not mind for the project, this is interesting, but I don't see how it's can be useful for me. Then I go back to the project when I'm working. I open my iTerm, an tab for mongod, and tab for spork, and a tab for guard, and another to run the generators.
After I open all this tabs, Foreman gem cames to my mind. Well, I gave to Foreman a opportunity, and I loved it.
If you think this scenario is the same of you, take a look at Foreman, and give to it a chance.
You can add the gem to your Gemfile or install it by your own hand. To work foreman only needs a file called Procfile. in this manifest file, you name your process and tell to foreman what command they should use, and above a simple Procfile for the scenario:
Very simple file, I've a process called mond, wich executes "sudo mongod" and starts the mongodb database, You'll see in the console, something like that "mongod.1 | started with pid XXX", every log from mongod will appear in the console.
To start all this process type "foreman start", Now I've only one tab running all my development processes.
I the weekend I write a simple app based on the Backbone.js demo the Todo App, this app is very simple, and has only one model. In my first studies I'm tried to make this todo model related to an todo list, such as the 37Signals' application Tada List, here you have a main list, and you add your tasks inside this list. But I fail with that, I can't understand Backbone itself, and I'm trying to do JQuery things and Rails Things on the backbone, and the app is not what I'm trying to do. You can checkout the source code of this fail (but working) app here: https://github.com/fellix/todo-list
Well, I forget that app, and I passed to study more about the backbone, I take a look inside the router, written the backbone-galley in a rails app, and that give me more details about the backbone. With that visions I tried to write the todo-list app again, but for now, I using Rails 3.1.rc5 and coffeescript, I have no problems with coffeescript insted of JavaScript, and I don't know why many people hates coffeescript.
Going back to the app, the only view that was served by the rails is the home/index.html.haml, wich have only the div wich is used for render the templates, all of you can see is rendered for backbone.
Bah, esse me surgiu esses tempos, e resolvi fazer alguns testes, eu armazeno um objeto em uma sessão com o rails, mas eu não tinha intenção de armazenar essa bagaça na base, somente em memória. sem ideias fui eu pela internet buscar como fazer isso. O Rails 3 modularizou boa parte do rails, um refactoring grande, mas que facilitou muita coisa, como o teste abaixo, por exemplo:
class Person
attr_accessor :name, :age
end
Ta tio, e se quiser usar as validações e outras coisas do rails? vou ter que fazer tudo no braço? Bom é claro que não né, que puta perca de tempo isso. Veja a adaptação abaixo:
class Person
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :age
validates :name, :age, :presence => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
vejam só que maravilha, agora vc pode fazer coisas como essas (irb):
Haml é uma linguagem de marcação, para renderizar templates HTML, baseada em tags e identação. Sempre gostei muito de usar o HAML nas minhas aplicações Rails 2, pela simplicidade e facilidade na criação. Você se acostuma a identar ao invés de procurar aonde a tag deve ser fechada. O Rails 3 foi modularizado para facilitar a customização. Porém a gem haml, responsável por renderizar os arquivos HAML, não possui generators embutidos para o Rails, procurando na internet encontrei o projeto rails3_haml_scaffold_generator que faz exatamente isso, mas vc precisa executar alguns passos, e eu odeio coisas repetitivas, a primeira coisa que pensei foi procurar se havia alguma gem parecida, e não encontrei.
Resolvi arregaçar as mangas, e fiz minha primeira gem, no caso eu simplesmente movi para o diretório os arquivos como se fossem um gem, e ajustei os diretórios com a ajuda do Jeweller.
O uso dessa gem é bem simples, ela nem tem reação com a gem haml, pois ela consiste somente nos generators.
Adicione ao seu Gemfile as seguintes linhas
gem haml
gem haml_rails
use o bundle install ou uso o gem install para instala-las.
depois só executar o comando
rails g haml_rails:install
ele vai editar seu application.rb e adicionar o haml como seu template engine. vc pode apagar caso prefira usar o ERB.
Estive estudando o Rails 3 (Atualmente 3.0.1 para o rails), e também estudando um pouco de NoSQL, com o MongoDB, ainda pretendo me dedicar um pouco para aprender sobre o Redis, mas por enquanto estou vendo o Rails. Gosto de usar para autenticação o Devise, um projeto brasileiro, que se tornou bem famoso pelo mundo Rails. Para ORM gostei do Mongoid, e ainda mais pelo devise ter suporte ao mongoid ao invez do ActiveRecord.
Iniciando
Sobre o mongoid não há muito o que falar, a documentação presente no site é completa, e funciona muito bem, vale lembrar que arquitetura de um banco relacional é bem diferente de um NoSQL, e algumas definições já padrão na nossa cabeça devem ser desfeitas.
O RSpec-Rails vem configurado para rodar sobre o ActiveRecord e quando vc executar um rails g rspec:install ira gerar toda a estrutura padrão do rspec, incluindo a pasta spec que contem os testes. Primeiramente vamos editar o arquivo spec/spec_helper.rb
RSpec.configure do |config|
require 'database_cleaner'
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.orm = "mongoid"
end
config.before(:each) do
DatabaseCleaner.clean
end
config.mock_with :rspec
config.include Devise::TestHelpers, :type => :controller # Essa linha adiciona os helpers do Devise para o RSpec.
end
A adição da gem DatabaseCleaner (que deve estar declarada no Gemfile), serve para limpar os resultados dos testes apos a execução, como podem reparar o uso de fixtures é desabilidado para o Mongo, somente para o ActiveRecord, sem o uso de fixtures os testes são mantidos na base, o que não é interessante.
Testando os controllers
Criei um modelo simples com o devise, apesar da documentação do devise indicar a criação de um modelo Admin para esses casos, preferia adotar o uso de um flag para poder indicar se o usuário é um administrador, acho isso um tanto mais simples.
Adicionei também no application_controller.rb um metodo filtro que indica se o usuário é um adminsitrador
protected
def admin_user!
if authenticate_user!
unless current_user.admin
flash[:warning] = "Acesso Negado"
redirect_to root_url
end
end
end
reparem que usei o filtro authenticated_user! que é do devise pra indicar que o usuário está logado no sistema.
meu controller admin_controller.rb
class AdminController < ApplicationController
before_filter :admin_user!
def index
end
end
é um controller bem simples, mas para garantirmos a segurança do aplicativo, e se algum dia nosso filtro precisar ser ajustado precisamos garantir que o acesso a esse controller seja restrito sempre, e quem alterar deve ter noção da quebra do teste.
Em alguns dos meus testes, normalmente em fixtures, eu pego um hash que representa o codigo BCrypt da senha (padrão do Devise), mas como aqui no mongo não tem fixutes, parti pra outro ponto.
require 'spec_helper'
describe AdminController do
describe "GET 'index'" do
def do_action_with_user(admin)
@user = User.create!(:name => "Test", :email => "test@test.com",
:password => "testet",
:password_confirmation => "testet",
:admin => admin)
sign_in @user
get 'index'
end
#1
it "should not be successful when no user was logged in" do
get 'index'
flash[:alert].should_not be_nil
response.should_not be_success
end
#2
it "should not be successful when no admin user was logged in" do
do_action_with_user(false)
flash[:warning].should_not be_nil
response.should_not be_success
end
#3
it "should be successful when admin user was logged in" do
do_action_with_user(true)
response.should be_success
end
end
end
bem simples, um metodo dry pra fazer o registro do usuario e usar o metodo auxiliar do devise para o login (sign_in):
o teste 1 é um teste para acesso sem login, no caso uma chamada a action index do controller admin sem um usuário logado.
teste 2 teste para um usuário que não é administrador, repare que testo se o flash esta preenchido
teste 3 testa a ação de sucesso, no caso o acesso de um usuário administrador
Inicialmente esse teste se fez suficiente para dizer que agora a classe está segura.
Outras notas
Tive alguns problemas no começo do uso do RSpec 2 em views, e vou listar aqui alguns deles:
assign :name, "Teste"
ao invez de
assigns[:name] = "Teste"
Outra mudança é que em views você não deve mais usar o response, mas usar o rendered.
Também deixou de existir o metodo have_tag, para verificar o conteudo de uma tag, veja abaixo:
Mais uma poderosa batalha a ser travada, ahauhhuauha, estou cá eu com meus botões estudando rails, e resolvo por na mensagem de alert de erro os erros que ocorreram quando o objeto foi salvo meu snipet do rjs é algo semelhante a isso (Rails 2.3.4 e JQuery)
update.rjs
if flash[:error] page << "alert('#{flash[:error]}')"
Quando no controller havia uma única mensagem (Erro ao atualizar dados), esse snipet funcionava muito bem, muitos vão dizer que é mais facil usar so page.alert, sim é mais facil, enfim, o código tava assim e pronto ;)
Qual a solução encontrata para mostrar os erros? Bom percorrer o array de erros e mostrar cada um em uma linha não é? Coisa facil fui lá eu
controller.rb
def update #Preenche o objeto if @objeto.save #Não interessa else msg = "Erro ao atualizar dados \n" @objeto.errors.full_messages.each do |error| msg += "#{error.to_s}\n" end flash[:error] = msg respond_to end
Muito simples né, e adivinha que me apareceu no alert?
"Erro ao atualizar dados \n Data não pode ser vazio\n"
sim os "\n" apareceram no alert se mudar para o aparece no meio o , eu tava quase querendo joga pela janela o PC, eis que eu resolvo recorrer ao nosso grande oraculo e encontrei um post de um caboclo falando a solução, pois ele passou por esse mesmo problema, e se vcs olharem o post do blog foi la em 2007 :(, só fazem 3 anos ahauuhauhah, resumindo, o que deve ser feito é criar um metodo na classe helper
helper.rb
def get_alert_error_message msg = "Erro ao atualizar dados \n" @objeto.errors.full_messages.each do |error| msg += "#{error.to_s}\n" end return msg end
Reparem o código é exatamente igual
update.rjs
if @error page.alert "#{get_alert_error_message}" end
E acreditem, exatamente o mesmo código, funcionou da forma que eu queria :), pra quem não consegue imaginar, a variavel @error foi criada para indicar quando ocorreu um erro no RJS, para imprimir esse alerta só se ocorreu um erro.
PS: Esse post além de compartilhar essa informação, é pra eu me lembrar futuramente disso ai.
Acredito que todo o desenvolvedor tenha alguns problemas com CSS, ou de esquecer um maldito campo, e ele ficar sem o layout, muitas vezes, todos os campos são renderizados de maneira diferente, de acordo com a definição do designer, ou da equipe. Normalmente o saco, é por exemplo em rails criar um form da seguinte maneira
PS: Esse código foi escrito em HAML. é bem simples modifica-lo para ERB. O que vemos é que existem dois campos com a tag :class, que indica a classe do componente para o Rails renderizar na tela. Eis que nessa tela imagine mais uns 10 text_field, e outras páginas de cadastro com quantidades semelhantes de campos. Ai o desgraçado do design resolve mudar o layout (não me levem a mal designer, muitas vezes vocês nos dão raiva mesmo, :D), e para mander compatibilidade ele cria outras tags, e la veio no minimo meio dia de serviço para ajustar esses campos ai para o nova classe (por ex: btext_field).
Mas vamos relembrar um conceito simples do Ruby as classes abertas. Nessa situação é um modulo (module), que gerencia os forms, o querido ActionView::Helpers::FormTagHelper. Ele é o responsavel por renderizar esses objetos. agora basta simplesmente você alterar esse modulo, veja como fazer (editei no meu environment.rb, como ele não é recarregado tem que para o servidor e iniciar novamente, vocês podem fazer esse código em qualquer lugar)
module ActionView module Helpers module FormTagHelper def text_field(object_name, method, options={}) super object_name, method, options.merge(:class => :btext_field) end def text_area(object_name, method, options={}) super object_name, method, options.merge(:class => :btext_area) end def check_box(object_name, method, options={}, checked_value="1", unchecked_value = "0") super object_name, method, options.merge(:class => :bcheck_box), checked_value, unchecked_value end end end end
Caso não conheça o metodo merge dos hash do ruby, seria interessante conhece-lo, dessa maneira, não existe mais a necessidade de definir o atributo :class em cada text_field, essa alteração coloca automáticamente essa informação disponivel. Existem coisa que o ruby faz que surpreendem :D.
Estava eu pensando esses dias, se posso executar código java via Jruby por que não o contrário? Quais as minhas chances de usar Jruby dentro do Java?
Então la fui eu ao oraculo, e começei a buscar, ouvi falar de um JSR XXX (preencha com o número que quiser, nunca me lembro desses números, que pra mim são irrelevantes), que define o Java ScriptEngine, que nada mais é do que usar scripts dentro do Java, no Java 6, já vem com superte a Rhino (JavaScript), embutido, mas exemplos usando JavaScript tem de monte, vou lhes mostrar como instanciar uma classe via JRuby e chamar alguns metodos dessa classe no java. Não vou dizer que isso vai ficar bom, mas enfim, quem sabe melhorem futuramente essas engines, Mesmo assim você não precisa massacrar seu codigo java para migra-lo para JRuby, pode simplemente adaptar o codigo do JRuby no Java. Vamos ao exemplo, primeiramente vejamos quais nossas engines disponiveis na nossa versão.
public static void main(String[] args) throws Exception{
ScriptEngineManager manager = new ScriptEngineManager();
for(ScriptEngineFactory factory : manager.getEngineFactories()){
System.out.println(factory.getEngineName());
}
}
}
A saida deve ser Mozilla Rhino, lembre-se java 6, eu não tenho bem certeza, mas acho que o ScriptEngine está disponivel na versão 5, porém sem nenhuma engine disponivel.
E ai tio e agora como que nos faz?
Vamos adicionar o Jruby, no site do projeto baixem os script-engines, com isso temos as libs de diversas linguagens suportadas que podemos utilizar, para nos interessa, o que esta dentro da pasta jruby/build, o jruby-engine.jar deve ser adicionado ao seu classpath, ainda existem outro jar a ser adicionado, se você olhar outros tutoriais, ele vai passar diversos jar que não estão mais no $JRUBY_HOME/lib, ignore :), adicione ao classpath do seu projeto somente o jruby.jar, e agora sim. Se executar o programinha de antes deve aparecer agora
Mozilla Rhino
Jruby Engine
Vamos para o nosso querido Hello World (eu sei, eu sei, todos vocês amam o Hello World (: )
public static void main(String[] args) throws Exception{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");//Buscamos a Engine do Jruby
engine.eval("puts 'Hello World'");//Codigo JRuby
}
}
ao executar temos a mesma saida do Println, não é magico? Heeheh, um puts executado via java. Sei exatamente o que a maioria pensou, vou ter que por todo o codigo Jruby num String? A reposta é não :), a não ser que você queira fazer isso, mas eu não aconselho.
Existem outros metodos para recuperar as engines, e um deles é por extensão, poderiamos ter usado o metodo getEngineByExtension e passar como parametro o “rb”, e teriamos o Jruby.
Vamos criar então uma classe bem bunito um arquivo .rb
class Teste
attr_accessor :name, :age
def set_name(name)
self.name = name
end
def set_age(age)
self.age = age
end
def toString
self.name+" "+self.age.to_s
end
end
Lembrando que o toString sobrescreve o metodo toString do java sem problemas algum, os metodos set_ foram definidos para seguir as convensões do ruby, e isso vai tornar nosso codigo java feio, e não digam que não avisei. Vamos criar uma pequena interface
public interface Teste {
String name();
void set_name(String name);
Integer age();
void set_age(Integer age);
}
notem o estilo que foi declarado os metodos set_ na interface tbm, deve haver alguma maneira de fazer isso automatico, se alguem souber me avisa :)
E agora tio? Vamos brincar um pouco de ScriptEngine?
public static void main(String[] args) throws Exception{
ScriptEngineManager manager = new ScriptEngineManager();
for(ScriptEngineFactory factory : manager.getEngineFactories()){
System.out.println(factory.getEngineName());
}
ScriptEngine engine = manager.getEngineByName("jruby");
engine.eval(new FileReader("src/com/rollingwithcode/testejruby/teste.rb"));//arquivo .rb
Object instance = engine.eval("@teste = Teste.new");//crio a instancia
Invocable inv = (Invocable) engine;
Teste teste = inv.getInterface(instance, Teste.class); //converto a classe em uma Interface Java
teste.set_name("Rafael");
teste.set_age(21);
System.out.println(teste.toString());
System.out.println(teste.name());
}
E adivinhem a saida:
Rafael 21
Rafael
Tudo isso só pra misturar um pouco de Ruby com java, credo, parece coisa de loco :)
vou investigar mais algo, e ver se existem maneiras de melhorar isso.