Mostrando postagens com marcador Rails3. Mostrar todas as postagens
Mostrando postagens com marcador Rails3. Mostrar todas as postagens
sexta-feira, 20 de abril de 2012
Criando uma simples RESTful API usando Rails 3 e JSON
O exemplo de API utilizando JSON que vou fazer aqui é bem simples. Alguns podem se perguntar "Por que ele não faz com XML?", e eu respondo, você está no post errado, a busca certa no Sr. Google é "API XML Rails", ou algo parecido com isso. Criei esse post por que precisei fazer uns testes de criação de API, tanto para o meu trabalho, quanto para a faculdade, então compartilho minha experiência com vocês. As referências que utilizei para fazer o meu exemplo estão abaixo.
Utilizei JRuby e PostgreSQL para essa aplicação, coloque o banco e ruby que quiser, se não estiver usando JRuby não copie "jruby -S". Crie uma aplicação rails
jruby -S rails new zapi_blog -d postgresql
Verifique se o seu Gemfile está parecido com esse aqui:
source 'http://rubygems.org'
gem 'rails', '3.1.3'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'activerecord-jdbcpostgresql-adapter'
gem 'jruby-openssl'
gem 'json'
gem 'pg'
gem 'execjs'
gem 'therubyrhino'
gem 'rack-cache', :require => 'rack/cache'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
Crie um scaffold Post
jruby -s rails g scaffold Post title:string body:text
Crie e rode as migrações criadas no banco. Vamos ao controller para modificar algumas coisas, prefiro ele bem simplificado, mudo de lugar o "respond_to" que o rails gera e coloco de uma forma mais elegante utilizando "respond_with".
respond_to :html, :json
# GET /posts
# GET /posts.json
def index
@posts = Post.all
respond_with @posts
end
Inicie sua app e entre em "/posts.json" e abra uma outra aba com "/posts", elas contém o mesmo resultado, só que mostram de formas diferentes. O Rails facilita muito a vida de quem quer criar uma app, de uma forma bem simples é possível injetar os posts de fora da aplicação. Utilizando o "curl", pode ser feito um teste bem simples.
curl -d 'post[title]=Post1&post[body]=Post1' http://localhost:3000/posts.json
Nesse teste consigo injetar um post utilizando a URL do "/posts.json". Vou criar um script com ruby para criar os posts, o script irá pegar os dados de um banco de dados já criado e populado, através de JSON irá enviar os posts para a aplicação que está rodando no servidor local.
Criei os diretórios "rest_api/lib", coloquei dois aquivoes "api.rb" e "post.rb".
rest_api/lib/post.rb
class Post < ActiveRecord::Base
end
rest_api/lib/api.rb
require 'rubygems'
require 'active_record'
require 'json'
require 'net/http'
require 'uri'
ActiveRecord::Base.establish_connection(
:adapter => "postgresql",
:host => "localhost",
:username => "oquevoceusar",
:password => "suasenhaaquiounao",
:database => "umbancodedadosqualquer"
)
require 'post'
class Api
host = 'localhost:3000'
url = 'http://localhost:3000/posts.json'
uri = URI.parse(url)
post_ws = '/posts.json'
posts = Post.all
posts.each do |p|
post = {'title' => p.title, 'body' => p.body}.to_json
req = Net::HTTP::Post.new(post_ws, initheader = {'Content-Type' => 'application/json'})
req.body = post
response = Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req)}
puts "Response #{response.code} #{response.message}: #{response.body}"
end
end
É possível também passar parametros de usuário e senha, entre outras coisas de segurança por esse script e fazer a verificação na app, mas isso é um outro post. Não vou explicar em detalhes o que esse script faz, se você está olhando esse post é por que já tem uma noção boa de ruby on rails, então vá tirar suas dúvidas na API do Ruby ou do Rails, o Sr. Google também ajuda.
Referências:
https://www.socialtext.net/open/very_simple_rest_in_ruby_part_3_post_to_create_a_new_workspace
http://gavinmorrice.com/blog/posts/21-building-a-rest-api-in-rails-3
http://squarism.com/2011/04/01/how-to-write-a-ruby-rails-3-rest-api/
http://blog.project-sierra.de/archives/1788
quinta-feira, 28 de abril de 2011
Instalando Nginx e Unicorn no Rails 3
Nginx e Unicorn
O Nginx com o Unicorn é uma ótima solução para colocar seu projeto em produção. É bem fácil a instalação deles. Eu utilizo essa combinação na minha máquina para testar meus projetos que desenvolvo. Quer saber mais sobre o que os dois fazem ? Vai pesquisar no Google, eu vou mostrar só a implementação básica.
sudo apt-get install nginx
Para poder usar o Nginx e o Apache sem ter que desligar um deles é só mudar a porta padrão do Nginx. Abra o arquivo default do Nginx para mudar a porta.
sudo nano /etc/nginx/sites-available/default
Comente a linha:
#listen [::]:80 default ipv6only=on; ## listen for ipv6
Modifique a porta, eu coloco 8080:
listen 8080; ## listen for ipv4
Crie um arquivo nesta mesma página com um nome que identifique o site que está fazendo.
sudo nano sua_app
O meu arquivo está assim:
upstream app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
# for UNIX domain socket setups:
server unix:/tmp/.sock fail_timeout=0;
# for TCP setups, point these to your backend servers
# server 192.168.0.7:8080 fail_timeout=0;
# server 192.168.0.8:8080 fail_timeout=0;
# server 192.168.0.9:8080 fail_timeout=0;
}
server {
# enable one of the following if you're on Linux or FreeBSD
listen 8080 default deferred; # for Linux
# listen 80 default accept_filter=httpready; # for FreeBSD
client_max_body_size 4G;
server_name sua_app.local;
# ~2 seconds is often enough for most folks to parse HTML/CSS and
# retrieve needed images/icons/frames, connections are cheap in
# nginx so increasing this is generally safe...
keepalive_timeout 5;
# path for static files
root /home/seu_usuario/seu_caminho/sua_app/public;
# Prefer to serve static files directly from nginx to avoid unnecessary
# data copies from the application server.
#
# try_files directive appeared in in nginx 0.7.27 and has stabilized
# over time. Older versions of nginx (e.g. 0.6.x) requires
# "if (!-f $request_filename)" which was less efficient:
# http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
try_files $uri/index.html $uri.html $uri @app;
location @app {
# an HTTP header important enough to have its own Wikipedia entry:
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if and only if you use HTTPS, this helps Rack
# set the proper protocol for doing redirects:
# proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client right along so redirects
# can be set properly within the Rack application
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
# set "proxy_buffering off" *only* for Rainbows! when doing
# Comet/long-poll stuff. It's also safe to set if you're
# using only serving fast clients with Unicorn + nginx.
# Otherwise you _want_ nginx to buffer responses to slow
# clients, really.
# proxy_buffering off;
proxy_pass http://app_server;
}
# Rails error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /home/seu_usuario/seu_caminho/sua_app/public;
}
}
Os pontos importantes desse arquivo são:
# para definir a porta.
listen 8080 default deferred;
# definição do host chamado do navegador.
server_name sua_app.local;
# caminho estático da sua app.
root /home/seu_usuario/seu_caminho/sua_app/public;
Crie um link simbólico em sites-enabled:
cd /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/sua_app sua_app
Defina o nome da sua app no arquivo hosts:
sudo nano /etc/hosts
Dentro do arquivo:
127.0.0.1 sua_app.local
Start no nginx:
sudo /etc/init.d/nginx start
Teste o endereço com http://sua_app.local:8080/, vai dar uma mensagem. Vamos instalar o Unicorn. No arquivo Gemfile de sua app, adicione:
gem 'unicorn', '3.5.0'
Depois rode o 'bundle install' no terminal. Em 'config/' crie um arquivo 'unicorn.rb' e coloque o conteúdo abaixo conforme sua máquina:
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
worker_processes 2
# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
working_directory "/home/deployer/myapp.com/current"
# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
listen "/tmp/myapp.com.sock", :backlog => 64
listen 8080, :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30
# feel free to point this anywhere accessible on the filesystem
user 'deployer', 'staff'
shared_path = "/home/deployer/myapp.com/shared"
pid "#{shared_path}/pids/unicorn.pid"
stderr_path "#{shared_path}/log/unicorn.stderr.log"
stdout_path "#{shared_path}/log/unicorn.stdout.log"
Esse é o arquivo default dele encontrado aqui. Para colocar o unicorn em produção é só rodar essa linha no terminal dentro da sua app:
unicorn_rails -c config/unicorn.rb -E production -D
Para matar ele é só rodar:
kill -quit `cat tmp/pids/unicorn.pid`
É só isso, bem tranquilo é rápido, se tiver alguma dúvida comenta que eu ajudo no que puder.
Até a próxima!!
O Nginx com o Unicorn é uma ótima solução para colocar seu projeto em produção. É bem fácil a instalação deles. Eu utilizo essa combinação na minha máquina para testar meus projetos que desenvolvo. Quer saber mais sobre o que os dois fazem ? Vai pesquisar no Google, eu vou mostrar só a implementação básica.
sudo apt-get install nginx
Para poder usar o Nginx e o Apache sem ter que desligar um deles é só mudar a porta padrão do Nginx. Abra o arquivo default do Nginx para mudar a porta.
sudo nano /etc/nginx/sites-available/default
Comente a linha:
#listen [::]:80 default ipv6only=on; ## listen for ipv6
Modifique a porta, eu coloco 8080:
listen 8080; ## listen for ipv4
Crie um arquivo nesta mesma página com um nome que identifique o site que está fazendo.
sudo nano sua_app
O meu arquivo está assim:
upstream app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response (in case the Unicorn master nukes a
# single worker for timing out).
# for UNIX domain socket setups:
server unix:/tmp/.sock fail_timeout=0;
# for TCP setups, point these to your backend servers
# server 192.168.0.7:8080 fail_timeout=0;
# server 192.168.0.8:8080 fail_timeout=0;
# server 192.168.0.9:8080 fail_timeout=0;
}
server {
# enable one of the following if you're on Linux or FreeBSD
listen 8080 default deferred; # for Linux
# listen 80 default accept_filter=httpready; # for FreeBSD
client_max_body_size 4G;
server_name sua_app.local;
# ~2 seconds is often enough for most folks to parse HTML/CSS and
# retrieve needed images/icons/frames, connections are cheap in
# nginx so increasing this is generally safe...
keepalive_timeout 5;
# path for static files
root /home/seu_usuario/seu_caminho/sua_app/public;
# Prefer to serve static files directly from nginx to avoid unnecessary
# data copies from the application server.
#
# try_files directive appeared in in nginx 0.7.27 and has stabilized
# over time. Older versions of nginx (e.g. 0.6.x) requires
# "if (!-f $request_filename)" which was less efficient:
# http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
try_files $uri/index.html $uri.html $uri @app;
location @app {
# an HTTP header important enough to have its own Wikipedia entry:
# http://en.wikipedia.org/wiki/X-Forwarded-For
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# enable this if and only if you use HTTPS, this helps Rack
# set the proper protocol for doing redirects:
# proxy_set_header X-Forwarded-Proto https;
# pass the Host: header from the client right along so redirects
# can be set properly within the Rack application
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
# set "proxy_buffering off" *only* for Rainbows! when doing
# Comet/long-poll stuff. It's also safe to set if you're
# using only serving fast clients with Unicorn + nginx.
# Otherwise you _want_ nginx to buffer responses to slow
# clients, really.
# proxy_buffering off;
proxy_pass http://app_server;
}
# Rails error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /home/seu_usuario/seu_caminho/sua_app/public;
}
}
Os pontos importantes desse arquivo são:
# para definir a porta.
listen 8080 default deferred;
# definição do host chamado do navegador.
server_name sua_app.local;
# caminho estático da sua app.
root /home/seu_usuario/seu_caminho/sua_app/public;
Crie um link simbólico em sites-enabled:
cd /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/sua_app sua_app
Defina o nome da sua app no arquivo hosts:
sudo nano /etc/hosts
Dentro do arquivo:
127.0.0.1 sua_app.local
Start no nginx:
sudo /etc/init.d/nginx start
Teste o endereço com http://sua_app.local:8080/, vai dar uma mensagem. Vamos instalar o Unicorn. No arquivo Gemfile de sua app, adicione:
gem 'unicorn', '3.5.0'
Depois rode o 'bundle install' no terminal. Em 'config/' crie um arquivo 'unicorn.rb' e coloque o conteúdo abaixo conforme sua máquina:
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
worker_processes 2
# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
working_directory "/home/deployer/myapp.com/current"
# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
listen "/tmp/myapp.com.sock", :backlog => 64
listen 8080, :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30
# feel free to point this anywhere accessible on the filesystem
user 'deployer', 'staff'
shared_path = "/home/deployer/myapp.com/shared"
pid "#{shared_path}/pids/unicorn.pid"
stderr_path "#{shared_path}/log/unicorn.stderr.log"
stdout_path "#{shared_path}/log/unicorn.stdout.log"
Esse é o arquivo default dele encontrado aqui. Para colocar o unicorn em produção é só rodar essa linha no terminal dentro da sua app:
unicorn_rails -c config/unicorn.rb -E production -D
Para matar ele é só rodar:
kill -quit `cat tmp/pids/unicorn.pid`
É só isso, bem tranquilo é rápido, se tiver alguma dúvida comenta que eu ajudo no que puder.
Até a próxima!!
terça-feira, 19 de abril de 2011
Ajax no Rails 3
Era para eu falar neste post sobre teste de controller com RSpec, mas estava fazendo alguns testes com Ajax, então aproveitei o momento para falar de Ajax. Desculpe a demora por novos posts, estava em período de entrega de trabalhos e provas na faculdade, não deu tempo de preparar nada. Todos os códigos que faço aqui estão no Github(github.com/vagnerzampieri/zblog).
Pegue aquele CRUD que fizemos sobre Post, ele vai ser modificado e dará lugar a documentos com extensão '.js.erb'. A ideia é que todas as ações fiquem na index, sendo assim, ficará um carregamento mais rápido e uma página mais dinâmica.
Primeiro coloque a gem 'jquery-rails' no arquivo Gemfile da sua aplicação. A versão que estou usando está com o jquery 1.5.
gem 'jquery-rails', '0.2.7'
Logo em seguida, vá em um terminal e rode o 'bundle install'. Agora rode no terminal 'rails generate jquery:install', ele irá substtuir o prototype pelo jquery.
Primeiro, vamos começar modificando a criação de post. Ele vai ficar da seguinte forma, ao clicar no link 'New' irá carregar abaixo um formulário, ao ser preenchido e enviado irá aparecer na lista o item que foi criado.
Na index, vou modificar a tabela e colocar um id, esse id irá servir depois para identificar aonde irá adicionar na lista o post que for criado.
posts/index.html.erb:
<table id="posts">
Crie um partial que irá virar uma lista dinâmica, já que será muito utilizada no ajax:
posts/_post.html.erb:
<tr id="post_<%= post.id %>">
<td><%= post.title %></td>
<td><%= truncate post.body, :length => 20 %></td>
<td><%= post.publication.strftime "%Y-%m-%d %H:%M:%S" %></td>
<td><%= post.enabled %></td>
<td><%= link_to 'Show', post, :remote => true %></td>
<td><%= link_to 'Edit', edit_post_path(post), :remote => true %></td>
<td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete, :remote => true %></td>
</tr>
Note que coloquei um id dinâmico no 'tr', e nos liks adicionei ':remote => true' é ele que irá fazer a mágica no Ajax, coloque ele também no 'form'.
<%= simple_form_for(@post, :remote => true) do |f| %>
Na index coloque um render para esse partial.
posts/index.html.erb:
<% @posts.each do |post| %>
<%= render post%>
<% end %>
Adicione também o link 'New' um pouco abaixo:
<%= link_to 'New', '/posts/new', :remote => true %>
E uma div onde será chamado o formulário:
<div id="form">
</div>
Crie um arquivo 'new.js.erb' e dentro dele coloque o código abaixo:
$('#form').empty();
$("#form").append("<%= escape_javascript(render(:partial => "form"))%>");
Esse código é bem simples, a primeira linha vai deixar vazio a div 'form', por um simples motivo, se você não colocar essa linha sempre que clicar em um botão ele vai criar um novo elemento em vez de excluir o antigo. A segunda linha chama o partial do 'form'. Crie um arquivo 'create.js.erb' e adicione o código abaixo:
$('#posts').append('<%= escape_javascript(render(@post)) %>');
$("#new_post")[0].reset();
A primeira linha chama o partial 'post' que nós criamos e adiciona mais um elemento na lista. A segunda limpa o formulário para poder ocorrer mais um cadastro. Para finalisar, melhore o controller assim:
def create
@post = Post.new params[:post]
flash[:notice] = 'Post was successfully created.' if @post.save
respond_with @post
end
Agora o 'Show', mais acima irá notar que o link para o 'Show' já fi criado. Adicione a div abaixo na index, ela será o guia que utilizaremos para a aparição do show.
<div id="show">
</div>
Crie um arquivo 'show.js.erb' e adicione o código abaixo:
$('#show').empty();
$('#show').append('<h2>Show post</h2><p><b>Title: </b><%= @post.title %></p><p><b>Body: </b><%= @post.body %></p><p><b>Publication: </b><%= @post.publication.strftime "%Y-%m-%d %H:%M:%S" %></p><p><b>Enabled: </b><%= @post.enabled %></p>');
Como acontece no 'New' a primeira linha é para limpar se existir algo antes. A segunda irá exibir o item ao ser clicado, o append vai exibir tudo que estiver dentro dele. O próximo é o destroy, é o mais simples de todos, crie o arquivo 'destroy.js.erb' e adicione:
$('#post_<%= @post.id %>').remove();
Vai remover a linha exata que você destruir. O último é editar, é bem parecido que o criar, crie os arquivos 'edit.js.erb' e 'update.js.erb':
posts/edit.js.erb:
$('#form').empty();
$("#form").append("<%= escape_javascript(render(:partial => "form"))%>")
Mesma explicação do 'New'.
posts/update.js.erb:
$("#post_<%= @post.id %>").replaceWith("<%= escape_javascript(render(:partial => @post)) %>");
<% @post = Post.new %>
$(".simple_form").replaceWith("<%= escape_javascript(render(:partial => "form"))%>")
Primeira linha, vai adicionar o elemento criado na lista. Segundo e terceira linha, vai resetar a variável de instância para ao editar o formulário, se modifique de edição para criação. Deixe o método update no controller da forma abaixo:
def update
@post = Post.find params[:id]
flash[:notice] = 'Post was successfully updated.' if @post.update_attributes params[:post]
respond_with @post
end
Pode apagar agora os arquivos abaixo, as suas funcionalidades não serão mais necessárias já que foram migradas.
new.html.erb
edit.html.erb
show.html.erb
Chega por hoje, ainda não pensei em um próximo post, talvez um banco de imagem com mais brincadeiras com Ajax.
Até a próxima!!
Pegue aquele CRUD que fizemos sobre Post, ele vai ser modificado e dará lugar a documentos com extensão '.js.erb'. A ideia é que todas as ações fiquem na index, sendo assim, ficará um carregamento mais rápido e uma página mais dinâmica.
Primeiro coloque a gem 'jquery-rails' no arquivo Gemfile da sua aplicação. A versão que estou usando está com o jquery 1.5.
gem 'jquery-rails', '0.2.7'
Logo em seguida, vá em um terminal e rode o 'bundle install'. Agora rode no terminal 'rails generate jquery:install', ele irá substtuir o prototype pelo jquery.
Primeiro, vamos começar modificando a criação de post. Ele vai ficar da seguinte forma, ao clicar no link 'New' irá carregar abaixo um formulário, ao ser preenchido e enviado irá aparecer na lista o item que foi criado.
Na index, vou modificar a tabela e colocar um id, esse id irá servir depois para identificar aonde irá adicionar na lista o post que for criado.
posts/index.html.erb:
<table id="posts">
Crie um partial que irá virar uma lista dinâmica, já que será muito utilizada no ajax:
posts/_post.html.erb:
<tr id="post_<%= post.id %>">
<td><%= post.title %></td>
<td><%= truncate post.body, :length => 20 %></td>
<td><%= post.publication.strftime "%Y-%m-%d %H:%M:%S" %></td>
<td><%= post.enabled %></td>
<td><%= link_to 'Show', post, :remote => true %></td>
<td><%= link_to 'Edit', edit_post_path(post), :remote => true %></td>
<td><%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete, :remote => true %></td>
</tr>
Note que coloquei um id dinâmico no 'tr', e nos liks adicionei ':remote => true' é ele que irá fazer a mágica no Ajax, coloque ele também no 'form'.
<%= simple_form_for(@post, :remote => true) do |f| %>
Na index coloque um render para esse partial.
posts/index.html.erb:
<% @posts.each do |post| %>
<%= render post%>
<% end %>
Adicione também o link 'New' um pouco abaixo:
<%= link_to 'New', '/posts/new', :remote => true %>
E uma div onde será chamado o formulário:
<div id="form">
</div>
Crie um arquivo 'new.js.erb' e dentro dele coloque o código abaixo:
$('#form').empty();
$("#form").append("<%= escape_javascript(render(:partial => "form"))%>");
Esse código é bem simples, a primeira linha vai deixar vazio a div 'form', por um simples motivo, se você não colocar essa linha sempre que clicar em um botão ele vai criar um novo elemento em vez de excluir o antigo. A segunda linha chama o partial do 'form'. Crie um arquivo 'create.js.erb' e adicione o código abaixo:
$('#posts').append('<%= escape_javascript(render(@post)) %>');
$("#new_post")[0].reset();
A primeira linha chama o partial 'post' que nós criamos e adiciona mais um elemento na lista. A segunda limpa o formulário para poder ocorrer mais um cadastro. Para finalisar, melhore o controller assim:
def create
@post = Post.new params[:post]
flash[:notice] = 'Post was successfully created.' if @post.save
respond_with @post
end
Agora o 'Show', mais acima irá notar que o link para o 'Show' já fi criado. Adicione a div abaixo na index, ela será o guia que utilizaremos para a aparição do show.
<div id="show">
</div>
Crie um arquivo 'show.js.erb' e adicione o código abaixo:
$('#show').empty();
$('#show').append('<h2>Show post</h2><p><b>Title: </b><%= @post.title %></p><p><b>Body: </b><%= @post.body %></p><p><b>Publication: </b><%= @post.publication.strftime "%Y-%m-%d %H:%M:%S" %></p><p><b>Enabled: </b><%= @post.enabled %></p>');
Como acontece no 'New' a primeira linha é para limpar se existir algo antes. A segunda irá exibir o item ao ser clicado, o append vai exibir tudo que estiver dentro dele. O próximo é o destroy, é o mais simples de todos, crie o arquivo 'destroy.js.erb' e adicione:
$('#post_<%= @post.id %>').remove();
Vai remover a linha exata que você destruir. O último é editar, é bem parecido que o criar, crie os arquivos 'edit.js.erb' e 'update.js.erb':
posts/edit.js.erb:
$('#form').empty();
$("#form").append("<%= escape_javascript(render(:partial => "form"))%>")
Mesma explicação do 'New'.
posts/update.js.erb:
$("#post_<%= @post.id %>").replaceWith("<%= escape_javascript(render(:partial => @post)) %>");
<% @post = Post.new %>
$(".simple_form").replaceWith("<%= escape_javascript(render(:partial => "form"))%>")
Primeira linha, vai adicionar o elemento criado na lista. Segundo e terceira linha, vai resetar a variável de instância para ao editar o formulário, se modifique de edição para criação. Deixe o método update no controller da forma abaixo:
def update
@post = Post.find params[:id]
flash[:notice] = 'Post was successfully updated.' if @post.update_attributes params[:post]
respond_with @post
end
Pode apagar agora os arquivos abaixo, as suas funcionalidades não serão mais necessárias já que foram migradas.
new.html.erb
edit.html.erb
show.html.erb
Chega por hoje, ainda não pensei em um próximo post, talvez um banco de imagem com mais brincadeiras com Ajax.
Até a próxima!!
sexta-feira, 18 de março de 2011
Ruby on Rails do zero, Teste de model com Rspec
Continuando nosso blog(github.com/vagnerzampieri/zblog), irei de Rspec hoje para validar nossos models.
Rspec:
Vamos fazer então nossos primeiros teste, algo bem simples, vai ser teste de validação de modelo. Certifique que as gems abaixo estão no seu Gemfile:
gem 'rspec'
gem 'rspec-rails'
Se não existirem, coloque e faça um "bundle install" no terminal. Ainda no terminal instale o rspec no seu projeto fazendo um:
rails g rspec:install
Ele criará isso:
create .rspec
create spec
create spec/spec_helper.rb
Entre na pasta "spec" que ele criou, crie uma pasta chamada "models", dentro dela crie o arquivo "post_spec.rb", e adicione o conteúdo abaixo:
require 'spec_helper'
describe Post do
it "test"
end
Primeiro eu dou um "require 'spec_helper'" para ele chamar o arquivo de mesmo nome que está na pasta "spec" para que eu possa utilizar o Rspec. Eu vou testar comportamento e não método, vou usar BDD, testar comportamento e verificar se a ação que o usuário efetuou está de acordo. Para você ver alguma saída rode no terminal:
rake spec:models
Vocé irá ver uma saída em amarelo, e um contador avisando que existe 1 exemplo, e ele está pendente. Isso é só um teste para ver se o Rspec está funcionando da forma certa. A primeira coisa que testaremos é se o Post pode ser criado sem título, sempre tenha em mente, "red", "green", "refactory".
RED, vamos ver nossa ideia quebrar de propósito com esse exemplo abaixo:
describe Post do
it "should be not created without title" do
post = Post.new(:title => "", :body => "Texto qualquer em body", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
post.should be_valid
end
end
Saída:
F
Failures:
1) Post should be not created without title
Failure/Error: post.should be_valid
expected valid? to return true, got false
# ./spec/models/post_spec.rb:6:in `block (2 levels) in <top (required)>'
Finished in 0.04851 seconds
1 example, 1 failure
rake aborted!
ruby -S bundle exec rspec ./spec/models/post_spec.rb failed
Pra quem não conhece Rspec, eu descrevou uma situação quem não pode acontecer no "it", mas faço ao contrário no final "post.should be_valid", eu digo que ele "deve validar". Se você ler a saída vai ver o que estou dizendo. Para nossa espectativa funcionar troque o "should", por "should_not" e irá ver que a saída será GREEN. Faça o mesmo teste para "body", já que são os únicos que validamos no post passado em "app/models/post.rb".
describe Post do
it "should be not created without title" do
post = Post.new(:title => "", :body => "Text for body", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
post.should_not be_valid
end
it "should be not created without body" do
post = Post.new(:title => "Title", :body => "", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
post.should be_valid
end
end
Ocorreu o mesmo RED de antes, faça ele ficar GREEN e vamos agora para o REFACTORY. Esse código está meio repetitivo, vamos deixar ele mais elegante:
describe Post do
before(:each) do
@post = Post.new(:title => "Title", :body => "Text for body", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
end
it "should be not created without title" do
@post.title = nil
@post.should_not be_valid
end
it "should be not created without body" do
@post.body = nil
@post.should_not be_valid
end
end
Eu criei um "before(:each)" que criará um post, dentro de cada "it" eu passo o valor que eu quero testar como "nil", irá dar o efeito que quero. Crio um último "it" para todos os dados passem.
it 'should be created with all requirements' do
@post.should be_valid
end
Agora façam os mesmos tipos de teste com User, depois conferem se ficou igual ao que eu fiz. Lembre que o Devise faz validação de campos e que você só precisa testar os campos que exigem validação. E também não existe um certo, e sim o que você acha necessário.
describe User do
before(:each) do
@user = User.new(:name => 'User Test', :login => 'usertest', :email => 'test@test.com', :password => 'senha123', :password_confirmation => 'senha123' )
end
it 'should be not created without name' do
@user.name = nil
@user.should_not be_valid
end
it 'should be not created without login' do
@user.login = nil
@user.should_not be_valid
end
it 'should be not created without email' do
@user.email = nil
@user.should_not be_valid
end
it 'should be not created without password' do
@user.password = nil
@user.should_not be_valid
end
it 'should be not created if password and password_confirmation not equal' do
@user.password = 'senha123'
@user.password_confirmation = 'senha'
@user.password.should_not == @user.password_confirmation
end
it 'should be created with all requirements' do
@user.should be_valid
end
end
O único comportamento diferente é o penúltimo que testa se a senha do usuário está diferente.
Vamos voltar a fazer modificações no Post. Temos agora dois layouts simulando a "home" de um site e "admin", mas nossa rota não indica que "posts" faz parte de um admin, então vá no arquivo routes e modifique o path da rota, e depois é só carregar a página, veja como vai ficar abaixo.
resources :posts, :path => 'admin/posts'
Vamos adicionar autor na nossa tabela posts, já que todo post precisa de um autor, e esse autor será o usuário que estiver armazenado na nossa sessão. Primeiro crie um migração nova que adicionará o campo "author_id" na tabela "posts", será um relacionamento de um autor para muitos posts, e somente um post para um usuário. Crie a migração desta forma:
rails g migration add_field_author_id_to_posts
Abra o arquivo que foi criado e faça as modificações abaixo:
class AddFieldAuthorIdToPosts < ActiveRecord::Migration
def self.up
add_column :posts, :author_id, :integer
add_index :posts, :author_id
end
def self.down
remove_index :posts, :author_id
remove_column :posts, :author_id
end
end
Está adicionando uma nova coluna e um novo índice na tabela posts. Agora abra o model "user" e adicione a linha abaixo, na primeira linha depois da classe ter sido criada.
has_many :posts, :foreign_key => "author_id"
Está dizendo que um usuário terá muitos posts, e sua chave estrangeira é "author_id". Agora no model "post" adicione a linha abaixo, na primeira linha depois da classe ter sido criada.
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
Está dizendo que a chave estrangeira "author_id" pertence a classe "User". Como sempre o post será do usuário logado ao sistema que é um autor, a associação de autor e post não será interferida neste momento. Abra o controller "posts" e faça o código abaixo no método create.
def create
@post = Post.new params[:post]
@post.author_id = current_user.id
if @post.save
flash[:notice] = 'Post was successfully created.'
respond_with @post
else
render :action => :new
end
end
Você não viu errado, é só adicionar essa linha mesmo, antes de salvar eu adiciono o id do usuário atual no campo "author_id", esse "current_user" é um método do Devise, retorna um hash com os dados do usuário. Nas views "index" e "show" adicione a saída.
index:
<th>Author</th>
<td><%= post.author.name unless post.author.nil? %></td>
show:
<p>
<b>Author:</b>
<%= @post.author.name unless @post.author.nil? %>
</p>
Para quem não conhece esse "unless" é uma negação, só vai executar se author não for nulo.
Chega por hoje, o próximo post deve ser de Rspec, mas teste de controller.
Até a próxima!!
Rspec:
Vamos fazer então nossos primeiros teste, algo bem simples, vai ser teste de validação de modelo. Certifique que as gems abaixo estão no seu Gemfile:
gem 'rspec'
gem 'rspec-rails'
Se não existirem, coloque e faça um "bundle install" no terminal. Ainda no terminal instale o rspec no seu projeto fazendo um:
rails g rspec:install
Ele criará isso:
create .rspec
create spec
create spec/spec_helper.rb
Entre na pasta "spec" que ele criou, crie uma pasta chamada "models", dentro dela crie o arquivo "post_spec.rb", e adicione o conteúdo abaixo:
require 'spec_helper'
describe Post do
it "test"
end
Primeiro eu dou um "require 'spec_helper'" para ele chamar o arquivo de mesmo nome que está na pasta "spec" para que eu possa utilizar o Rspec. Eu vou testar comportamento e não método, vou usar BDD, testar comportamento e verificar se a ação que o usuário efetuou está de acordo. Para você ver alguma saída rode no terminal:
rake spec:models
Vocé irá ver uma saída em amarelo, e um contador avisando que existe 1 exemplo, e ele está pendente. Isso é só um teste para ver se o Rspec está funcionando da forma certa. A primeira coisa que testaremos é se o Post pode ser criado sem título, sempre tenha em mente, "red", "green", "refactory".
RED, vamos ver nossa ideia quebrar de propósito com esse exemplo abaixo:
describe Post do
it "should be not created without title" do
post = Post.new(:title => "", :body => "Texto qualquer em body", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
post.should be_valid
end
end
Saída:
F
Failures:
1) Post should be not created without title
Failure/Error: post.should be_valid
expected valid? to return true, got false
# ./spec/models/post_spec.rb:6:in `block (2 levels) in <top (required)>'
Finished in 0.04851 seconds
1 example, 1 failure
rake aborted!
ruby -S bundle exec rspec ./spec/models/post_spec.rb failed
Pra quem não conhece Rspec, eu descrevou uma situação quem não pode acontecer no "it", mas faço ao contrário no final "post.should be_valid", eu digo que ele "deve validar". Se você ler a saída vai ver o que estou dizendo. Para nossa espectativa funcionar troque o "should", por "should_not" e irá ver que a saída será GREEN. Faça o mesmo teste para "body", já que são os únicos que validamos no post passado em "app/models/post.rb".
describe Post do
it "should be not created without title" do
post = Post.new(:title => "", :body => "Text for body", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
post.should_not be_valid
end
it "should be not created without body" do
post = Post.new(:title => "Title", :body => "", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
post.should be_valid
end
end
Ocorreu o mesmo RED de antes, faça ele ficar GREEN e vamos agora para o REFACTORY. Esse código está meio repetitivo, vamos deixar ele mais elegante:
describe Post do
before(:each) do
@post = Post.new(:title => "Title", :body => "Text for body", :publication => Time.now.strftime("%Y-%m-%d %H:%M:%S"), :enabled => 1)
end
it "should be not created without title" do
@post.title = nil
@post.should_not be_valid
end
it "should be not created without body" do
@post.body = nil
@post.should_not be_valid
end
end
Eu criei um "before(:each)" que criará um post, dentro de cada "it" eu passo o valor que eu quero testar como "nil", irá dar o efeito que quero. Crio um último "it" para todos os dados passem.
it 'should be created with all requirements' do
@post.should be_valid
end
Agora façam os mesmos tipos de teste com User, depois conferem se ficou igual ao que eu fiz. Lembre que o Devise faz validação de campos e que você só precisa testar os campos que exigem validação. E também não existe um certo, e sim o que você acha necessário.
describe User do
before(:each) do
@user = User.new(:name => 'User Test', :login => 'usertest', :email => 'test@test.com', :password => 'senha123', :password_confirmation => 'senha123' )
end
it 'should be not created without name' do
@user.name = nil
@user.should_not be_valid
end
it 'should be not created without login' do
@user.login = nil
@user.should_not be_valid
end
it 'should be not created without email' do
@user.email = nil
@user.should_not be_valid
end
it 'should be not created without password' do
@user.password = nil
@user.should_not be_valid
end
it 'should be not created if password and password_confirmation not equal' do
@user.password = 'senha123'
@user.password_confirmation = 'senha'
@user.password.should_not == @user.password_confirmation
end
it 'should be created with all requirements' do
@user.should be_valid
end
end
O único comportamento diferente é o penúltimo que testa se a senha do usuário está diferente.
Vamos voltar a fazer modificações no Post. Temos agora dois layouts simulando a "home" de um site e "admin", mas nossa rota não indica que "posts" faz parte de um admin, então vá no arquivo routes e modifique o path da rota, e depois é só carregar a página, veja como vai ficar abaixo.
resources :posts, :path => 'admin/posts'
Vamos adicionar autor na nossa tabela posts, já que todo post precisa de um autor, e esse autor será o usuário que estiver armazenado na nossa sessão. Primeiro crie um migração nova que adicionará o campo "author_id" na tabela "posts", será um relacionamento de um autor para muitos posts, e somente um post para um usuário. Crie a migração desta forma:
rails g migration add_field_author_id_to_posts
Abra o arquivo que foi criado e faça as modificações abaixo:
class AddFieldAuthorIdToPosts < ActiveRecord::Migration
def self.up
add_column :posts, :author_id, :integer
add_index :posts, :author_id
end
def self.down
remove_index :posts, :author_id
remove_column :posts, :author_id
end
end
Está adicionando uma nova coluna e um novo índice na tabela posts. Agora abra o model "user" e adicione a linha abaixo, na primeira linha depois da classe ter sido criada.
has_many :posts, :foreign_key => "author_id"
Está dizendo que um usuário terá muitos posts, e sua chave estrangeira é "author_id". Agora no model "post" adicione a linha abaixo, na primeira linha depois da classe ter sido criada.
belongs_to :author, :class_name => "User", :foreign_key => "author_id"
Está dizendo que a chave estrangeira "author_id" pertence a classe "User". Como sempre o post será do usuário logado ao sistema que é um autor, a associação de autor e post não será interferida neste momento. Abra o controller "posts" e faça o código abaixo no método create.
def create
@post = Post.new params[:post]
@post.author_id = current_user.id
if @post.save
flash[:notice] = 'Post was successfully created.'
respond_with @post
else
render :action => :new
end
end
Você não viu errado, é só adicionar essa linha mesmo, antes de salvar eu adiciono o id do usuário atual no campo "author_id", esse "current_user" é um método do Devise, retorna um hash com os dados do usuário. Nas views "index" e "show" adicione a saída.
index:
<th>Author</th>
<td><%= post.author.name unless post.author.nil? %></td>
show:
<p>
<b>Author:</b>
<%= @post.author.name unless @post.author.nil? %>
</p>
Para quem não conhece esse "unless" é uma negação, só vai executar se author não for nulo.
Chega por hoje, o próximo post deve ser de Rspec, mas teste de controller.
Até a próxima!!
segunda-feira, 14 de março de 2011
Otimizando seu controller
Hoje vou voltar um pouco para o scaffold de post que fiz, vou implementar coisas novas lá, deixar ele com uma "cara" melhor. O que já foi feito:
- Scaffold de Post feito com a gem Simple Form.
- Um Controller Home que vai simular uma página do site.
- Implementação básica do Devise.
- Gravatar.
O projeto está aqui(github.com/vagnerzampieri/zblog).
Primeiro vamos definir visualmente o que seria a capa do site da administração do site, o scaffold post simula o começo de uma administração, então ele precisa ter um layout modificado. Então vamos criar um layout específico para ele.
Entre 'app/views/layouts/' e crie um arquivo chamado 'admin.html.erb', e copie o código abaixo:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>
<container>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</div>
</container>
</html>
Se já teve algum contato com HTML5 vai notar que é um layout do mesmo, o scaffold do rails já faz isso, o que tem de diferente que eu coloquei foi a tag "<container>". No 'app/views/layouts/application.html.erb' coloque esse mesmo HTMl, mas substitua o "<content>". Se você estiver se perguntando o que é "<%= yield %>" é aonde todas as telas que você criar irá entrar. Agora vamos diferenciar o layout no CSS.
Em 'public/stylesheets/scaffold.css, ajuste a primeira linha para isso:
body { background-color: #000; color: #000; }
Depois adicione isto:
th, tr, td {
border: solid 1px black;
}
container, content {
display:block;
}
container {
width: 75%;
margin: 0 auto;
background-color: #ccc;
padding: 20px 40px;
border: solid 1px black;
margin-top: 20px;
}
content {
width: 75%;
margin: 0 auto;
background-color: #493233;
padding: 20px 40px;
border: solid 1px black;
margin-top: 20px;
}
Agora no controller de 'app/controllers/posts_controller.rb', adicione na primeira linha depois que entrar dentro da classe:
layout 'admin'
Ele vai definir o layout que a página vai usar, o padrão é "application" mas eu quero o admin para "posts", então vá olhar como ficou a "home" e "posts". Eu não sou designer e uso layout escuro para não incomodar minha vista.
Se não existir conteúdo em posts, adicione alguns, depois veja como ele está listando. Entre no código da view em "app/views/posts/index.html.erb" e no controller 'app/controllers/posts_controller.rb'. O que vou fazer agora é explicar código, otimizar e trazer para a realidade de Rails3.
No método "index" faremos algumas modificações, esse método gera uma saída para a view "index.html.erb":
def index
@posts = Post.all
respond_to do |format|
format.html
format.xml { render :xml => @posts }
end
end
No Ruby a última linha de um método, ou um bloco é seu retorno, o método respond_to é um bloco que gera duas saídas, uma em html e a outra em xml. O método "index" gera uma lista de todos os posts, essa lista é pega de sua tabela "posts", ela é acessada através da consulta "Post.all", que será atribuída para uma variável chamada "@posts", que é uma variável de instância, ela irá trazer em forma de array todos os posts. O "Post" é o model relativo a tabela posts. Vamos deixar esse código com uma cara de Rails3.
Primeiro, no controller adicione a linha abaixo, logo depois de "layout 'admin'":
respond_to :html, :xml, :js
Depois modifique nosso método index para:
def index
@posts = Post.order "publication DESC"
respond_with @posts
end
Esse novo "respond_to" vai gerar os mesmos formatos que antes mais o formato "js"(javascript), será utilizado depois quando fizermos saídas com ajax, é só passar os parâmetros que quiser. Na primeira linha do método eu modifiquei para que retorne todos os posts em ordem decrescente por data. E o "respond_with" é meu complemento do método "respond_to", eu passo como parâmetro a variável de instância "@posts". Se estiver achando desconfortável a falta de parênteses quando chamo um método, é só adicionar, o espaço simples substitue os parênteses.
def index
@posts = Post.where(:enabled => 1)
respond_with(@posts)
end
Em 'app/views/posts/index.html.erb', note a nossa variável de instância "@posts", é passado um método "each" que irá acessar o array desta linha, e gerar a saída uma por uma de todos os conteúdos de dentro dele, através do seu bloco.
Se colocou em "body" texto suficiente, notou que ficou estranho o layout, vamos formatar isso usando o método "truncate".
<td><%= truncate post.body, :length => 20 %></td>
Estou passando como parâmetros, o campo "body" e um tamanho de caracteres. Vou formatar a data também:
<td><%= post.publication.strftime "%Y-%m-%d %H:%M:%S" %></td>
Nos métodos, "show", "new", "edit" e "destroy", faça a modificação do "respond_with".
Agora vamos otimizar o método create, por enquanto ele está assim:
def create
@post = Post.new params[:post]
respond_to do |format|
if @post.save
format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
format.xml { render :xml => @post, :status => :created, :location => @post }
else
format.html { render :action => "new" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end
Ele pega os dados vindos do parâmetro "params[:post]", que vem do formulário que está em "app/views/posts/_form.html.erb". Se os parâmetros estiverem certos, ele salva e avisa que foi criado com sucesso, senão ele se matem na página que está. Vamos otimizar esse código. Outra coisa que irão notar é que eu coloquei :new e não "new", simples explicação, da primeira forma eu crio um símbolo e da segunda é uma string. Usando símbolos, o consumo de memória pode ser drasticamente reduzido, uma vez que você estará usando o mesmo objeto, sem necessidade de crias várias cópias, quando estiver em um projeto grande essas pequenas diferenças fazem diferença no tempo total de processamento.
def create
@post = Post.new params[:post]
if @post.save
flash[:notice] = 'Post was successfully created.'
respond_with @post
else
render :action => :new
end
end
A ideia foi a mesma que já tinha usado, eu diminuo o código quando utilizo o "respond_with". Veja como vai ficar o "update".
def update
@post = Post.find params[:id]
if @post.update_attributes params[:post]
flash[:notice] = 'Post was successfully updated.'
respond_with @post
else
render :action => :edit
end
end
O método "update" pega o id do post em questão, armazena na variável @post, depois atualiza ele passando o parâmetro "params[:post]" que vem do formulário. Para você o "else" funcionar valide a presença dos campos "name" e "body", por exemplo. Faça em "app/models/post.rb"
validates_presence_of :title, :body
Para não deixar esse post grande vou encerrar aqui e falar sobre Rspec no próximo, iremos validar o modelo post.
Até a próxima!!
- Scaffold de Post feito com a gem Simple Form.
- Um Controller Home que vai simular uma página do site.
- Implementação básica do Devise.
- Gravatar.
O projeto está aqui(github.com/vagnerzampieri/zblog).
Primeiro vamos definir visualmente o que seria a capa do site da administração do site, o scaffold post simula o começo de uma administração, então ele precisa ter um layout modificado. Então vamos criar um layout específico para ele.
Entre 'app/views/layouts/' e crie um arquivo chamado 'admin.html.erb', e copie o código abaixo:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>
<container>
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
<%= yield %>
</div>
</container>
</html>
Se já teve algum contato com HTML5 vai notar que é um layout do mesmo, o scaffold do rails já faz isso, o que tem de diferente que eu coloquei foi a tag "<container>". No 'app/views/layouts/application.html.erb' coloque esse mesmo HTMl, mas substitua o "<content>". Se você estiver se perguntando o que é "<%= yield %>" é aonde todas as telas que você criar irá entrar. Agora vamos diferenciar o layout no CSS.
Em 'public/stylesheets/scaffold.css, ajuste a primeira linha para isso:
body { background-color: #000; color: #000; }
Depois adicione isto:
th, tr, td {
border: solid 1px black;
}
container, content {
display:block;
}
container {
width: 75%;
margin: 0 auto;
background-color: #ccc;
padding: 20px 40px;
border: solid 1px black;
margin-top: 20px;
}
content {
width: 75%;
margin: 0 auto;
background-color: #493233;
padding: 20px 40px;
border: solid 1px black;
margin-top: 20px;
}
Agora no controller de 'app/controllers/posts_controller.rb', adicione na primeira linha depois que entrar dentro da classe:
layout 'admin'
Ele vai definir o layout que a página vai usar, o padrão é "application" mas eu quero o admin para "posts", então vá olhar como ficou a "home" e "posts". Eu não sou designer e uso layout escuro para não incomodar minha vista.
Se não existir conteúdo em posts, adicione alguns, depois veja como ele está listando. Entre no código da view em "app/views/posts/index.html.erb" e no controller 'app/controllers/posts_controller.rb'. O que vou fazer agora é explicar código, otimizar e trazer para a realidade de Rails3.
No método "index" faremos algumas modificações, esse método gera uma saída para a view "index.html.erb":
def index
@posts = Post.all
respond_to do |format|
format.html
format.xml { render :xml => @posts }
end
end
No Ruby a última linha de um método, ou um bloco é seu retorno, o método respond_to é um bloco que gera duas saídas, uma em html e a outra em xml. O método "index" gera uma lista de todos os posts, essa lista é pega de sua tabela "posts", ela é acessada através da consulta "Post.all", que será atribuída para uma variável chamada "@posts", que é uma variável de instância, ela irá trazer em forma de array todos os posts. O "Post" é o model relativo a tabela posts. Vamos deixar esse código com uma cara de Rails3.
Primeiro, no controller adicione a linha abaixo, logo depois de "layout 'admin'":
respond_to :html, :xml, :js
Depois modifique nosso método index para:
def index
@posts = Post.order "publication DESC"
respond_with @posts
end
Esse novo "respond_to" vai gerar os mesmos formatos que antes mais o formato "js"(javascript), será utilizado depois quando fizermos saídas com ajax, é só passar os parâmetros que quiser. Na primeira linha do método eu modifiquei para que retorne todos os posts em ordem decrescente por data. E o "respond_with" é meu complemento do método "respond_to", eu passo como parâmetro a variável de instância "@posts". Se estiver achando desconfortável a falta de parênteses quando chamo um método, é só adicionar, o espaço simples substitue os parênteses.
def index
@posts = Post.where(:enabled => 1)
respond_with(@posts)
end
Em 'app/views/posts/index.html.erb', note a nossa variável de instância "@posts", é passado um método "each" que irá acessar o array desta linha, e gerar a saída uma por uma de todos os conteúdos de dentro dele, através do seu bloco.
Se colocou em "body" texto suficiente, notou que ficou estranho o layout, vamos formatar isso usando o método "truncate".
<td><%= truncate post.body, :length => 20 %></td>
Estou passando como parâmetros, o campo "body" e um tamanho de caracteres. Vou formatar a data também:
<td><%= post.publication.strftime "%Y-%m-%d %H:%M:%S" %></td>
Nos métodos, "show", "new", "edit" e "destroy", faça a modificação do "respond_with".
Agora vamos otimizar o método create, por enquanto ele está assim:
def create
@post = Post.new params[:post]
respond_to do |format|
if @post.save
format.html { redirect_to(@post, :notice => 'Post was successfully created.') }
format.xml { render :xml => @post, :status => :created, :location => @post }
else
format.html { render :action => "new" }
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
end
end
end
Ele pega os dados vindos do parâmetro "params[:post]", que vem do formulário que está em "app/views/posts/_form.html.erb". Se os parâmetros estiverem certos, ele salva e avisa que foi criado com sucesso, senão ele se matem na página que está. Vamos otimizar esse código. Outra coisa que irão notar é que eu coloquei :new e não "new", simples explicação, da primeira forma eu crio um símbolo e da segunda é uma string. Usando símbolos, o consumo de memória pode ser drasticamente reduzido, uma vez que você estará usando o mesmo objeto, sem necessidade de crias várias cópias, quando estiver em um projeto grande essas pequenas diferenças fazem diferença no tempo total de processamento.
def create
@post = Post.new params[:post]
if @post.save
flash[:notice] = 'Post was successfully created.'
respond_with @post
else
render :action => :new
end
end
A ideia foi a mesma que já tinha usado, eu diminuo o código quando utilizo o "respond_with". Veja como vai ficar o "update".
def update
@post = Post.find params[:id]
if @post.update_attributes params[:post]
flash[:notice] = 'Post was successfully updated.'
respond_with @post
else
render :action => :edit
end
end
O método "update" pega o id do post em questão, armazena na variável @post, depois atualiza ele passando o parâmetro "params[:post]" que vem do formulário. Para você o "else" funcionar valide a presença dos campos "name" e "body", por exemplo. Faça em "app/models/post.rb"
validates_presence_of :title, :body
Para não deixar esse post grande vou encerrar aqui e falar sobre Rspec no próximo, iremos validar o modelo post.
Até a próxima!!
quinta-feira, 10 de março de 2011
Ruby on Rails do zero, Devise
Continuação do post "Ruby on Rails do zero" no repositório github.com/vagnerzampieri/zblog tem o projeto que estamos fazendo aqui.
Devise:
Agora vamos ver como o Devise funciona, se você for dar uma olhada no post anterior irá notar que já baixamos o Devise, mas ainda não instalamos ele. A linha abaixo é a linha que fica no arquivo Gemfile que fica na raiz principal da sua aplicação.
gem 'devise', '1.1.rc2'
O Devise é um autenticador, através dele você poderá criar usuário, logar, recuperar senha, sessão de usuário aberta pelo tempo que desejar, criptografa senha, e muito mais. É uma ferramenta muito completa e muito usada no mundo Rails e não é difícil de usar, o básico dela qualquer nível de programador pode usar, até o menos experiente. Esse Devise que estou usando provavelmente já deve existir um mais recente, então use um mais recente, não acredito que vai quebrar a aplicação ou algo assim, mas se estiver com receio utilize essa versão mesmo.
Vamos dizer que não fizemos download do Devise, se estiver usando RVM coloque aquela linha que dei um pouco acima e coloque ela em Gemfile e faça um "bundle install". Note que ela será baixada para o seu projeto.
Agora vamos instalar o Devise:
rails g devise:install
Irá instalar dois arquivos na sua aplicação, o mais importante é o arquivo de configuração dele 'config/initializers/devise.rb'. É importante dar uma olhada nele, ele já é bem explicado, quando eu for fazer alguma alteração nele irei explicar o por que, mas saiba que ele existe e é muito útil. Outra coisa legal que ele instala é em 'config/locales/devise.en.yml' para dar a saída na língua que você quiser.
O Devise além de te criar os arquivos vai mostrar uma saída que pedirá que você faça 3 passos. O primeiro é configirar uma url default, isso é só para quando for desenvolvimento. A segunda é para configurar uma rota padrão, esse passo foi feito no post passado, eu cheguei a criar um controller home e coloquei a rota da forma que pede o arquivo, o Devise pede esse passo por que ele usa a rota "root" para os seus redirecionamentos. O terceiro passo são alertas para o Devise mostrar para o usuário se a ação feita foi correspondida da forma esperada ou se ocorreu algum erro. Faça os 3 passos na sua aplicação, se estiver seguindo o post passado o segundo não precisará fazer, por que já está feito.
Ao usar o Devise é habilitado um gerador, então vamos utilizar ele e ver a mágica dele.
rails g devise User
As importantes coisas que ele faz eu vou listar abaixo:
create app/models/user.rb
create db/migrate/20110307130914_devise_create_users.rb
route devise_for :users
Utilizando o model User que você colocou como opção ele cria uma série de arquivos com o nome do seu modelo, mas antes de falar sobre o que ele criou vamos logo adicionar as views do Devise na sua aplicação.
rails g devise:views
Vou começar pelo model, 'app/models/user.rb':
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
end
O Devise inclui alguns modulos que serão usados por ele. Na linha que tem 'attr_accessible' é para habilitar os campos que você for querer utilizar quando criar a tabela 'users', se colocar um campo novo e não adicionar nesta linha do model não vai funcionar. Vamos deixar usuário um pouco mais interessante, coloque como abaixo:
attr_accessible :name, :login, :enabled, :url_image, :email, :password, :password_confirmation, :remember_me
Eu adicionei 'name', 'login', 'enabled', 'url_image', os dois primeiros ta na cara. O 'enabled' é para se eu quiser ativar, ou desativar um usuário, eu não vou precisar excluir ele de vez, é só desativar. O 'url_image' é para se o usuário não quiser usar a opção do Gravatar.
Agora no arquivo 'db/migrate/20110307130914_devise_create_users.rb', ele vai ficar assim com as modificações.
class DeviseCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.string :name
t.string :login
t.boolean :enabled, :default => true
t.string :url_image
t.database_authenticatable :null => false
t.recoverable
t.rememberable
t.trackable
# t.confirmable
# t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
# t.token_authenticatable
t.timestamps
end
add_index :users, :name
add_index :users, :login, :unique => true
add_index :users, :enabled
add_index :users, :url_image
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :confirmation_token, :unique => true
# add_index :users, :unlock_token, :unique => true
end
def self.down
drop_table :users
end
end
Se você comparar linha por linha, irá notar que eu adicionaei os 4 campos e adicionei os index deles. Antes de rodar o 'rake db:migrate', vamos nas views do Devise e colocar os campos que agente adicionou.
Em 'app/views/devise/registrations/new.html.erb' e 'app/views/devise/registrations/edit.html.erb' ficará assim:
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :name %><br />
<%= f.text_field :name %></p>
<p><%= f.label :email %><br />
<%= f.text_field :email %></p>
<p><%= f.label :login %><br />
<%= f.text_field :login %></p>
<p><%= f.label :password %><br />
<%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></p>
<p><%= f.label :enabled %><br />
<%= f.check_box :enabled %></p>
<p><%= f.label :url_image %><br />
<%= f.text_field :url_image %></p>
<p><%= f.submit "Sign up" %></p>
<% end %>
<%= render :partial => "devise/shared/links" %>
Para você não ficar maluco por que não viu saída de nada até agora, roda 'rake db:create' e 'rake db:migrate' no terminal e vai em 'http://localhost:3000/users/sign_up', mas eu quero duas coisas, uma quero acessar essas rotas através da minha home que criei e quero mudar essas rotas. Primeiro vamos para 'app/home/index'.
<div id="user_nav">
<% if user_signed_in? %>
Logado como <%= current_user.email %>. Não é você?
<%= link_to 'Sair', destroy_user_session_path, :method => :delete %>
</br>
<%= link_to 'Post', posts_path %>
<% else %>
<%= link_to 'Cadastrar', new_user_registration_path %> ou <%= link_to 'Logar', new_user_session_path %>
<% end %>
</div>
Eu adicionei o código acima, ma o que ele faz, é bem simples. Se o usuário estiver logado ele cai no 'if', que vai mostrar qual email do usuário logado, dar um link para ele destruir a sessão atual dele e um link para 'Post', senão dará duas opções, uma se cadastrar e a outra se logar. Lembre o Devise só faz a autenticação, autorização tem outras formas, se colocar 'posts' na url o Devise não vai te impedir de ir para página. Crie um usuário qualquer. Ao criar irá notar que ele já se loga automaticamente, e mostra uma mensagem que foi logado com sucesso.
Vá na sua tabela users e veja como esse registro de usuário foi criado. Notará que a senha por exemplo está criptografada em 'encrypted_password', que o Devise conta quantas vezes o seu usuário se conectou ao sistema em 'sign_in_count', entre outras informações.
Outra coisa, o model user não tem validação ainda, só para os campos que o Devise criou, então vamos colocar uma validação simples, de pressença, se existe ou não dado naquele campo.
Em 'app/models/user.rb':
validates_presence_of :name, :login
Tente criar um usuário agora sem nome e login para ver o que acontece. Erro, agora tem uma pequena validação, mais a frente vou usar RSpec para testar as nossas validações de formulários.
Se você fez testes se logando viu que a opção é de logar é com email, mas se você não quiser usar email ? Em 'config/initializers/devise.rb' tem uma linha comentada:
config.authentication_keys = [ :email ]
Teste colocando login:
config.authentication_keys = [ :login ]
Em 'app/views/devise/sessions' troque o campo email por login. Vamos configurar um Range de quantos dígitos poderá ter em um password:
config.password_length = 4..20 # Coloquei de 4 à 20 para ilustração.
Leia com atenção as inúmeras codificações que poderá fazer em termos de sessão de usuário. Se quiser configurar a rota para ficar de uma maniera que vai agradar mais você, faça assim:
Em 'config/routes.rb':
devise_for :users, :path => 'usuarios', :path_names => {:sign_in => 'logar', :sign_out => 'sair', :sign_up => 'cadastrar'}
Para encerrar o post de hoje vamos usar o Gravatar. Eu ainda não falei de tudo que da pra fazer com o Devise, quando eu precisar fazer mais coisas nele vou escrever aqui.
O Gravatar é bem simples, coloque a linha abaixo no seu helper, 'app/helpers/application_helper.rb':
def avatar_url(user)
if user.url_image.present?
user.url_image
else
gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
"http://gravatar.com/avatar/#{gravatar_id}.png?s=48"
end
end
E em 'app/views/home/index.html', chame o helper passando o usuario atual como parâmetro:
Logado como <%= avatar_url(current_user) %> <%= current_user.email %>. Não é você?
O código do método é bem simples, primeiro ele verifica se existe alguma url de imagem no campo 'url_image' que você colocou anteriormente, senão ele vai utilizar o email do usuário como um id para buscar a imagem que está armazenada no Gravatar, o parâmetro 's=48' no final é para definir o tamanho que será dimensionada a imagem.
Chega por hoje, eu decidi fazer logo a autenticação por um simples motivo, todo site tem usuários que tem permissões específicas, áreas que só ele pode entrar, então é geralmente a parte em que se trabalha mais em um site. No próximo post vamos voltar a mexer no scaffold que fizemos de post.
Até a próxima!!
Devise:
Agora vamos ver como o Devise funciona, se você for dar uma olhada no post anterior irá notar que já baixamos o Devise, mas ainda não instalamos ele. A linha abaixo é a linha que fica no arquivo Gemfile que fica na raiz principal da sua aplicação.
gem 'devise', '1.1.rc2'
O Devise é um autenticador, através dele você poderá criar usuário, logar, recuperar senha, sessão de usuário aberta pelo tempo que desejar, criptografa senha, e muito mais. É uma ferramenta muito completa e muito usada no mundo Rails e não é difícil de usar, o básico dela qualquer nível de programador pode usar, até o menos experiente. Esse Devise que estou usando provavelmente já deve existir um mais recente, então use um mais recente, não acredito que vai quebrar a aplicação ou algo assim, mas se estiver com receio utilize essa versão mesmo.
Vamos dizer que não fizemos download do Devise, se estiver usando RVM coloque aquela linha que dei um pouco acima e coloque ela em Gemfile e faça um "bundle install". Note que ela será baixada para o seu projeto.
Agora vamos instalar o Devise:
rails g devise:install
Irá instalar dois arquivos na sua aplicação, o mais importante é o arquivo de configuração dele 'config/initializers/devise.rb'. É importante dar uma olhada nele, ele já é bem explicado, quando eu for fazer alguma alteração nele irei explicar o por que, mas saiba que ele existe e é muito útil. Outra coisa legal que ele instala é em 'config/locales/devise.en.yml' para dar a saída na língua que você quiser.
O Devise além de te criar os arquivos vai mostrar uma saída que pedirá que você faça 3 passos. O primeiro é configirar uma url default, isso é só para quando for desenvolvimento. A segunda é para configurar uma rota padrão, esse passo foi feito no post passado, eu cheguei a criar um controller home e coloquei a rota da forma que pede o arquivo, o Devise pede esse passo por que ele usa a rota "root" para os seus redirecionamentos. O terceiro passo são alertas para o Devise mostrar para o usuário se a ação feita foi correspondida da forma esperada ou se ocorreu algum erro. Faça os 3 passos na sua aplicação, se estiver seguindo o post passado o segundo não precisará fazer, por que já está feito.
Ao usar o Devise é habilitado um gerador, então vamos utilizar ele e ver a mágica dele.
rails g devise User
As importantes coisas que ele faz eu vou listar abaixo:
create app/models/user.rb
create db/migrate/20110307130914_devise_create_users.rb
route devise_for :users
Utilizando o model User que você colocou como opção ele cria uma série de arquivos com o nome do seu modelo, mas antes de falar sobre o que ele criou vamos logo adicionar as views do Devise na sua aplicação.
rails g devise:views
Vou começar pelo model, 'app/models/user.rb':
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
end
O Devise inclui alguns modulos que serão usados por ele. Na linha que tem 'attr_accessible' é para habilitar os campos que você for querer utilizar quando criar a tabela 'users', se colocar um campo novo e não adicionar nesta linha do model não vai funcionar. Vamos deixar usuário um pouco mais interessante, coloque como abaixo:
attr_accessible :name, :login, :enabled, :url_image, :email, :password, :password_confirmation, :remember_me
Eu adicionei 'name', 'login', 'enabled', 'url_image', os dois primeiros ta na cara. O 'enabled' é para se eu quiser ativar, ou desativar um usuário, eu não vou precisar excluir ele de vez, é só desativar. O 'url_image' é para se o usuário não quiser usar a opção do Gravatar.
Agora no arquivo 'db/migrate/20110307130914_devise_create_users.rb', ele vai ficar assim com as modificações.
class DeviseCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.string :name
t.string :login
t.boolean :enabled, :default => true
t.string :url_image
t.database_authenticatable :null => false
t.recoverable
t.rememberable
t.trackable
# t.confirmable
# t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
# t.token_authenticatable
t.timestamps
end
add_index :users, :name
add_index :users, :login, :unique => true
add_index :users, :enabled
add_index :users, :url_image
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :confirmation_token, :unique => true
# add_index :users, :unlock_token, :unique => true
end
def self.down
drop_table :users
end
end
Se você comparar linha por linha, irá notar que eu adicionaei os 4 campos e adicionei os index deles. Antes de rodar o 'rake db:migrate', vamos nas views do Devise e colocar os campos que agente adicionou.
Em 'app/views/devise/registrations/new.html.erb' e 'app/views/devise/registrations/edit.html.erb' ficará assim:
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :name %><br />
<%= f.text_field :name %></p>
<p><%= f.label :email %><br />
<%= f.text_field :email %></p>
<p><%= f.label :login %><br />
<%= f.text_field :login %></p>
<p><%= f.label :password %><br />
<%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></p>
<p><%= f.label :enabled %><br />
<%= f.check_box :enabled %></p>
<p><%= f.label :url_image %><br />
<%= f.text_field :url_image %></p>
<p><%= f.submit "Sign up" %></p>
<% end %>
<%= render :partial => "devise/shared/links" %>
Para você não ficar maluco por que não viu saída de nada até agora, roda 'rake db:create' e 'rake db:migrate' no terminal e vai em 'http://localhost:3000/users/sign_up', mas eu quero duas coisas, uma quero acessar essas rotas através da minha home que criei e quero mudar essas rotas. Primeiro vamos para 'app/home/index'.
<div id="user_nav">
<% if user_signed_in? %>
Logado como <%= current_user.email %>. Não é você?
<%= link_to 'Sair', destroy_user_session_path, :method => :delete %>
</br>
<%= link_to 'Post', posts_path %>
<% else %>
<%= link_to 'Cadastrar', new_user_registration_path %> ou <%= link_to 'Logar', new_user_session_path %>
<% end %>
</div>
Eu adicionei o código acima, ma o que ele faz, é bem simples. Se o usuário estiver logado ele cai no 'if', que vai mostrar qual email do usuário logado, dar um link para ele destruir a sessão atual dele e um link para 'Post', senão dará duas opções, uma se cadastrar e a outra se logar. Lembre o Devise só faz a autenticação, autorização tem outras formas, se colocar 'posts' na url o Devise não vai te impedir de ir para página. Crie um usuário qualquer. Ao criar irá notar que ele já se loga automaticamente, e mostra uma mensagem que foi logado com sucesso.
Vá na sua tabela users e veja como esse registro de usuário foi criado. Notará que a senha por exemplo está criptografada em 'encrypted_password', que o Devise conta quantas vezes o seu usuário se conectou ao sistema em 'sign_in_count', entre outras informações.
Outra coisa, o model user não tem validação ainda, só para os campos que o Devise criou, então vamos colocar uma validação simples, de pressença, se existe ou não dado naquele campo.
Em 'app/models/user.rb':
validates_presence_of :name, :login
Tente criar um usuário agora sem nome e login para ver o que acontece. Erro, agora tem uma pequena validação, mais a frente vou usar RSpec para testar as nossas validações de formulários.
Se você fez testes se logando viu que a opção é de logar é com email, mas se você não quiser usar email ? Em 'config/initializers/devise.rb' tem uma linha comentada:
config.authentication_keys = [ :email ]
Teste colocando login:
config.authentication_keys = [ :login ]
Em 'app/views/devise/sessions' troque o campo email por login. Vamos configurar um Range de quantos dígitos poderá ter em um password:
config.password_length = 4..20 # Coloquei de 4 à 20 para ilustração.
Leia com atenção as inúmeras codificações que poderá fazer em termos de sessão de usuário. Se quiser configurar a rota para ficar de uma maniera que vai agradar mais você, faça assim:
Em 'config/routes.rb':
devise_for :users, :path => 'usuarios', :path_names => {:sign_in => 'logar', :sign_out => 'sair', :sign_up => 'cadastrar'}
Para encerrar o post de hoje vamos usar o Gravatar. Eu ainda não falei de tudo que da pra fazer com o Devise, quando eu precisar fazer mais coisas nele vou escrever aqui.
O Gravatar é bem simples, coloque a linha abaixo no seu helper, 'app/helpers/application_helper.rb':
def avatar_url(user)
if user.url_image.present?
user.url_image
else
gravatar_id = Digest::MD5.hexdigest(user.email.downcase)
"http://gravatar.com/avatar/#{gravatar_id}.png?s=48"
end
end
E em 'app/views/home/index.html', chame o helper passando o usuario atual como parâmetro:
Logado como <%= avatar_url(current_user) %> <%= current_user.email %>. Não é você?
O código do método é bem simples, primeiro ele verifica se existe alguma url de imagem no campo 'url_image' que você colocou anteriormente, senão ele vai utilizar o email do usuário como um id para buscar a imagem que está armazenada no Gravatar, o parâmetro 's=48' no final é para definir o tamanho que será dimensionada a imagem.
Chega por hoje, eu decidi fazer logo a autenticação por um simples motivo, todo site tem usuários que tem permissões específicas, áreas que só ele pode entrar, então é geralmente a parte em que se trabalha mais em um site. No próximo post vamos voltar a mexer no scaffold que fizemos de post.
Até a próxima!!
quinta-feira, 3 de março de 2011
Ruby on Rails do zero
No decorrer dos meus posts vou trabalhar sempre na mesma aplicação que começarei neste post, vou disponibilizar ela no Github.
Essa aplicação terá de tudo que um site tem normalmente, Devise, Simple Form, Sphinx, Acts As Taggable On, Gravatar, Autocomplete, Ajax, Rspec, etc.. Vou ajudar a criar tarefas que otimizem o seu tempo, como testar suas aplicações, como fazer um bom código. Muita coisa que vou escrever aqui vocês poderam encontrar fazendo uma busca no Google, mas a diferença é que vai estar tudo em um só lugar e explicado em vários posts para facilitar sua vida, fora que como disse antes, a aplicação ficará no Github para o seu auxílio.
Vou utilizar o Ruby 1.9.2, e estou usando RVM.
Crie uma aplicação nova:
rails new zblog -d mysql
Vá na pasta public e renomeie o arquivo index.
Abra o arquivo Gemfile e adicione as linhas abaixo:
gem 'aws-s3'
gem 'warden', '0.10.7'
gem 'bcrypt-ruby', '2.1.2'
gem 'devise', '1.1.rc2'
gem 'rspec'
gem 'rspec-rails'
gem 'simple_form', '1.3.1'
Irei falar de cada um com o tempo. Vamos configurar o Simple Form para você poder fazer os scaffolds já utilizando ele, primeiro instale.
rails g simple_form:install
Arquivos gerados abaixo:
create config/initializers/simple_form.rb
create config/locales/simple_form.en.yml
create lib/templates/erb/scaffold/_form.html.erb
Crie um controller home que será a nossa futura capa:
rails g controller home
Vá em config/routes e coloque:
root :to => "home#index"
Vamos gerar um scaffold de Post para você ver a mágica do Simple Form.
rails g scaffold Post title:string body:text publication:datetime enabled:boolean
Em posts/_form.html.erb:
<%= simple_form_for(@post) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :title %>
<%= f.input :body %>
<%= f.input :publication %>
<%= f.input :enabled %>
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
Se está acostomado com os scaffolds do Rails, você vai logo ver a diferença. Primeiro é adicionado um helper chamado simple_form_for, que é nosso formulário, os inputs criados criam um html automático que você teria que fazer, isso diminui e muito o código a ser colocado no seu arquivo.
Adicione o CSS abaixo para dar uma melhorada no layout:
.simple_form label {
float: left;
width: 100px;
text-align: right;
margin: 2px 10px;
}
.simple_form div.input {
margin-bottom: 10px;
}
.simple_form div.boolean, .simple_form input[type='submit'] {
margin-left: 120px;
}
.simple_form div.boolean label, .simple_form label.collection_radio {
float: none;
margin: 0;
}
.simple_form label.collection_radio {
margin-right: 10px;
margin-left: 2px;
}
.simple_form .error {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #D00;
display: block;
}
.simple_form .hint {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #555;
display: block;
font-style: italic;
}
Depois disso faça alguns teste utilizando o Simple Form, como adicionar um "hint" no input "title":
<%= f.input :title, :hint => "title" %>
também teste validação no seu model, e veja como fica o layout:
class Post < ActiveRecord::Base
validates_presence_of :title, :body
end
Existe um arquivo em config/initializers/simple_form.rb que você poderá configurar e um arquivo em config/locales/simple_form.en.yml, para tradução, se criar um arquivo simple_form.pt-br.yml, irá poder traduzir os inputs gerados pelo Simple Form. Mas se for traduzir tem que especificar em config/application.rb, qual será a língua padrão:
config.i18n.default_locale = 'pt-br'
Para não alongar muito o post e deixar cansativo, vou encerrar ele por aqui, no próximo vou mostrar o Devise e o Gravatar. Vou mostrar mais sobre o Simple Form com o tempo, por enquanto paro por aqui. Essa aplicação já está no Github(github.com/vagnerzampieri/zblog), você irá notar que tem código a mais do que foi mostrado aqui, mas nós chegaremos lá.
Até a pŕoxima.
Essa aplicação terá de tudo que um site tem normalmente, Devise, Simple Form, Sphinx, Acts As Taggable On, Gravatar, Autocomplete, Ajax, Rspec, etc.. Vou ajudar a criar tarefas que otimizem o seu tempo, como testar suas aplicações, como fazer um bom código. Muita coisa que vou escrever aqui vocês poderam encontrar fazendo uma busca no Google, mas a diferença é que vai estar tudo em um só lugar e explicado em vários posts para facilitar sua vida, fora que como disse antes, a aplicação ficará no Github para o seu auxílio.
Vou utilizar o Ruby 1.9.2, e estou usando RVM.
Crie uma aplicação nova:
rails new zblog -d mysql
Vá na pasta public e renomeie o arquivo index.
Abra o arquivo Gemfile e adicione as linhas abaixo:
gem 'aws-s3'
gem 'warden', '0.10.7'
gem 'bcrypt-ruby', '2.1.2'
gem 'devise', '1.1.rc2'
gem 'rspec'
gem 'rspec-rails'
gem 'simple_form', '1.3.1'
Irei falar de cada um com o tempo. Vamos configurar o Simple Form para você poder fazer os scaffolds já utilizando ele, primeiro instale.
rails g simple_form:install
Arquivos gerados abaixo:
create config/initializers/simple_form.rb
create config/locales/simple_form.en.yml
create lib/templates/erb/scaffold/_form.html.erb
Crie um controller home que será a nossa futura capa:
rails g controller home
Vá em config/routes e coloque:
root :to => "home#index"
Vamos gerar um scaffold de Post para você ver a mágica do Simple Form.
rails g scaffold Post title:string body:text publication:datetime enabled:boolean
Em posts/_form.html.erb:
<%= simple_form_for(@post) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :title %>
<%= f.input :body %>
<%= f.input :publication %>
<%= f.input :enabled %>
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
Se está acostomado com os scaffolds do Rails, você vai logo ver a diferença. Primeiro é adicionado um helper chamado simple_form_for, que é nosso formulário, os inputs criados criam um html automático que você teria que fazer, isso diminui e muito o código a ser colocado no seu arquivo.
Adicione o CSS abaixo para dar uma melhorada no layout:
.simple_form label {
float: left;
width: 100px;
text-align: right;
margin: 2px 10px;
}
.simple_form div.input {
margin-bottom: 10px;
}
.simple_form div.boolean, .simple_form input[type='submit'] {
margin-left: 120px;
}
.simple_form div.boolean label, .simple_form label.collection_radio {
float: none;
margin: 0;
}
.simple_form label.collection_radio {
margin-right: 10px;
margin-left: 2px;
}
.simple_form .error {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #D00;
display: block;
}
.simple_form .hint {
clear: left;
margin-left: 120px;
font-size: 12px;
color: #555;
display: block;
font-style: italic;
}
Depois disso faça alguns teste utilizando o Simple Form, como adicionar um "hint" no input "title":
<%= f.input :title, :hint => "title" %>
também teste validação no seu model, e veja como fica o layout:
class Post < ActiveRecord::Base
validates_presence_of :title, :body
end
Existe um arquivo em config/initializers/simple_form.rb que você poderá configurar e um arquivo em config/locales/simple_form.en.yml, para tradução, se criar um arquivo simple_form.pt-br.yml, irá poder traduzir os inputs gerados pelo Simple Form. Mas se for traduzir tem que especificar em config/application.rb, qual será a língua padrão:
config.i18n.default_locale = 'pt-br'
Para não alongar muito o post e deixar cansativo, vou encerrar ele por aqui, no próximo vou mostrar o Devise e o Gravatar. Vou mostrar mais sobre o Simple Form com o tempo, por enquanto paro por aqui. Essa aplicação já está no Github(github.com/vagnerzampieri/zblog), você irá notar que tem código a mais do que foi mostrado aqui, mas nós chegaremos lá.
Até a pŕoxima.
Assinar:
Postagens (Atom)