Projeto “Depot” do livro “Agile Web Development With Ruby”;
Aplicação de vendas de produtos a clientes;
Uma interfaces para clientes, outra para a p , p administração;
README Documentação
Rakefile Arquivo de build Rakefile Arquivo de build
components/ Esqueça =P
doc/ Mais documentação
lib/ Código genérico da aplicação
script/ Scripts de build
d / Pl i li ã
vendor/ Plugins para a sua aplicação
app/ Código da sua aplicação!
db/ Migrations e geração de banco db/ Migrations e geração de banco
log/ Arquivos de log
test/ Testes da sua aplicação
config/ Configuração geral
tmp/ Arquivos temporários do Rails public/ Arquivos estáticos (html, js, css)
development: adapter: mysql database: depot_development username: root password: host: localhost host: localhost
class CreateProducts ActiveRecord::Migration
class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|_ p | | t.column :title, :string
t.column :description, :text t l i g l t i g t.column :image_url, :string
t.column :price, :decimal, :precision => 8, :scale => 2, :default => 0
end
end
def self down
def self.down
drop_table :products
end end
O b d d d l d
O seu banco de dados com controle de versão!
Define tabelas, adiciona linhas, cria índices e ainda faz a janta!
ainda faz a janta!
Contém métodos utilitários para criar alterar
Contém métodos utilitários para criar, alterar e remover tabelas, colunas e linhas;
Você faz no “up” e desfaz no “down”, não se esqueça da ordem!q ç
ll i di d ll ã (
:null – indica se pode ser null ou não (true-false)
:limit – indica o tamanho máximo do campo
:default – indica o valor padrão do campo
:precision – indica a precisão do número
:scale – indica a escala do número (quantas casas depois da vírgula)
create_table :authors_books, :id => false do
|t|
t.column :author_id, :integer, :null => false
t.column :book_id, :integer, :null => false end
class CreateLineItems < ActiveRecord::Migration
def self.up
execute "alter table line_items
add constraint fk_line_item_products foreign key (product_id) references
products(id)“
end
def self.down
drop_table :line_items
end end
ruby script/generate scaffold product admin
def index
def index list
render :action => 'list'
end
def list
@ d t g @ d t gi t d t @product_pages, @products = paginate :products, :per_page => 10 end def show @product = Product.find(params[:id]) end end def new @product = Product.new end
def create
def create
@product = Product.new(params[:product])
if @product.savep
flash[:notice] = 'Product was successfully created.'
redirect to :action => 'list' redirect_to :action => list
else
render :action => 'new'
end end
def edit
@product = Product.find(params[:id])
d f d t
def update
@product = Product.find(params[:id])
if @product update attributes(
if @product.update_attributes( params[:product])
flash[:notice] = 'Product was flash[:notice] Product was successfully updated.'
redirect_to :action => 'show' , :id => @product
else
d ti ' dit' render :action => 'edit'
end end end
rake db:migrate #rodando o migration
ruby script/server webrick #iniciando o servidor
Todo mundo vem de ApplicationController;
As URLs nomalmente são
.../’nomeDoController’/’metodoDoController ’/’aqui pode vir um id ou não’, como em
“/admin/edit/10”;
Todos os métodos públicos de um controller podem se chamados, métodos não públicos não ficam disponíveis (segurança rapá!);
ti d ti tá action_name – nome da action que está
executando;
headers – hash com os cabeçalhos HTTP (você pode colocar novos se quiser);
p q );
params – parametros que vieram da requisição, í
podem ser acessados usando símbolos ou strings; fl h bj t d d
flash – objeto que pode guardar uma mensagem entre uma requisição e outra (como enviar uma mensagem pra um redirect);g p );
domain – retorna o domínio que veio na requisição
remote_ip – retorna o ip que gerou a requisição;
env – hash de parametros enviados pelo browser (como língua);
method – retorna o método http invocado como um símbolo, :get, :post e ect;
get?, post?, put?, delete?, xml_http_request?, xhr?
É ê
É um hash de strings, onde você pode passar qualquer valor;
Você só pode passar Strings;
Sempre use o atributo “cookies” para enviar p p cookies para o navegador, nunca envie
R ã HTTP d á i
Representa uma sessão HTTP de um usuário com a aplicação;
Você pode colocar valores dentro dela, como se ela fosse um hash;
se ela fosse um hash;
Os valores podem ser qualquer coisa
Os valores podem ser qualquer coisa,
contanto que seja um objeto que possa ser serializado;;
rake db:sessions:create
rake db:migrate
config.action_controller.session_store = :active_record_store_ _
<% form_tag :action => 'update', :id => @product do %>
<%= render :partial => 'form' %> <%= submit_tag 'Editar' %>
<% end %>
<%= link_to 'Mostrar', :action => 'show', :id => @product %> |
@p |
f é f ã d j d
form_tag é uma função de ajuda que gera um formulário através de um bloco de
código; código;
render :partial => ‘form’ vai “incluir” o
render :partial => form , vai incluir o arquivo “_form” dentro da página
submit_tag ‘Editar’ gera um botão clicável
link_to ‘Editar’ :action => ‘show’ gera um link para o método ‘show’ do controllerp atual;
<% error messages for 'product' %> <%= error_messages_for product %> <!--[form:product]-->
<p><label for="product_name">Nome</label><br/> % t t fi ld d t % /
<%= text_field :product, :name %></p>
<p><label for="product_category">Categoria</label><br/>
l d d lh
<%= select :product, :category_id, @categories, :prompt => "Escolha uma categoria"%> </p>
p label for "prod ct label" Marca /label br/ <p><label for= product_label >Marca</label><br/>
<%= select :product, :label_id, @labels, :prompt => "Escolha uma marca" %> </p>
<p><label for="product_price">Preço real</label><br/> <%= text_field :product, :real_price %></p>
<! [eoform:product] > <!--[eoform:product]-->
text_field e select são utilitários para gerar um campo de entrada e um select HTML,
i t t tilitá i t d
existem outros utilitários para todos os outros componentes HTML;
O uso de símbolos, como em “text_field
d t ” é di d d
:product, :name” é para dizer de onde vem o campo e que campo é que nós queremos
mostrar (veja o código fonte gerado!); mostrar (veja o código fonte gerado!);
@categories = Category.find( :all, :order => :name).map { |c| [c.name, c.id] }
Ao mostrar alguma coisa em um select, ele deve vir em um array de arrays, onde o primeiro item é o “label” do select e o
d it é “ l ” d l t
% f f d t l { ti t } d <% form_for :product, :url => { :action => :create } do
|form| %>
<p>Title: <%= form.text_field :title, :size => 30 %></p> <p>Description: <%= form.text_area :description, :rows
=> 3 %></p>
<p>Price: <%= form.text_field :price, :size => 10 %></p>
<p><%= submit_tag %></p> % d %
É
É feito especificamente para gerar formulários para objetos do modelo;
Os métodos utilitários de criação de
componentes são chamados direto no “form” e não é necessário repetir diversas vezes
l bj t l qual o objeto real;
class Product < ActiveRecord::Base class Product < ActiveRecord::Base
validates_presence_of :title, :description, :image_url validates_numericality_of :price
validates_uniqueness_of :title validates format of :image url, validates_format_of :image_url,
:with => %r{\.(gif|jpg|png)$}i,
:message => “deve ser um gif, jpg ou png" protected
def validate
errors.add(:price, “deve ser ao menos 0.01" ) if price.nil? || price < 0.01
end end
T d lid ã é d fi id d d ó i
Toda a validação é definida dentro do próprio objeto do modelo;
Existem diversos métodos utilitários para diversos tipos de validação;
diversos tipos de validação;
Se nenhum dos métodos resolve o seu
Se nenhum dos métodos resolve o seu
problema, implemente o método “validate”;
As mensagens aparecem em -> <%= error_messages_for 'product' %>_ g _ p
ruby script/generate migration add test data ruby script/generate migration add_test_data
class AddTestData < ActiveRecord::Migrationg
def self.up
Product.create(
titl 'P g ti V i C t l' :title => 'Pragmatic Version Control' ,
:description => %{<p>With Subversion</p>}, :image url => '/images/svn.jpg' ,g _ g jpg ,
:price => 28.50) end d f lf d def self.down Product.delete_all end end
<%= link_to ‘Remover',
{ :action => 'destroy', :id => product },
{ y , p },
:confirm => “Tem certeza?" , :method => :post %>
module ApplicationHelper
def format_price( price )
number_to_currency( price, :precision => 2, :unit => 'R$ ', :separator => ',', :delimiter => '.' )
end end
Cada controller tem o seu próprio helper e existe um helper padrão que sempre está
di í l t d t ll
disponível para todos os controllers;
Todo o trabalho que possa ser de lógica das views (como formatação, geração de
t l t t ) d f it l
templates e etc) deve ser feito por eles;
Ó
NAS VIEWS NÃO PODE HAVER LÓGICA, entendeu? Ou quer que desenhe?
% f f ( i t
<% form_for(:picture,
:url => {:action => 'save'},
:html > { :multipart > true }) do |form| :html => { :multipart => true }) do |form| %>
Comment: Comment:
<%= form.text_field("comment" ) %><br/> Upload your picture:
<%= form.file_field("uploaded_picture" ) %><br/>( p p ) <%= submit_tag("Upload file" ) %>
class Picture < ActiveRecord::Base
class Picture < ActiveRecord::Base
validates_format_of :content_type, :with => /^image/,
" l l d i t “ :message => "-- you can only upload pictures“
def uploaded_picture=(picture_field) lf b f f ld l f l self.name = base_part_of(picture_field.original_filename) self.content_type = picture_field.content_type.chomp self.data = picture_field.read end def base_part_of(file_name) File.basename(file_name).gsub(/[^\w._-]/, '' ) end end
def picture @picture = Picture.find(params[:id]) send_data(@picture.data, :filename => @picture.name,p , :type => @picture.content_type, :disposition => "inline" ) :disposition > inline ) end
Ei, você já está mexendo com ele desde o primeiro migration!
É
É a abstração de banco de dados de Rails;
Baseado no padrão de projeto “active record”;
Cada coluna que você definiu lá no q
‘save’ e ‘save!’, salva ou atualiza um objeto, ‘save’ retorna true se conseguir salvar, false
ã bj t f i álid ‘ !’ l
senão ou o objeto for inválido, ‘save!’ lança uma exeção se não conseguir salvar ou se o objeto for inválido;
objeto for inválido;
‘d t ’ bj t d b d
‘destroy’ remove um objeto do banco de dados e não deixa que nenhum dos seus atributos seja alterado;
update_attributes e update_attributes!, recebem um hash com os parâmetros e
l d i d d d bj t t t
valores das propriedades do objeto e tentam enviar uma atualização para o banco de
dados; dados;
‘ t ’ i bj t h h d
‘create’ cria um novo objeto com o hash de parametros passado e salva ele no banco de dados automaticamente;
Define um relacionamento de 1 pra N (como em um item pertence a um produto, mas um
d t d t N it )
produto pode ter N itens);
Métodos adicionados:
product(reload = false) product=
build_product( hash ) create_product( hash )
D fi l d 1 d l i
Define o lado 1 do relacionamento um para muitos;
Adiciona os métodos (e ainda outros...):
orders(force reload=false) orders(force_reload=false) orders <<order orders.push(order1, ...)orders.push(order1, ...) orders.replace(order1, ...) orders.delete(order1, ...) orders.destroy_all orders.size d ? orders.empty?
class Article < ActiveRecord::Base
has_and_belongs_to_many :users
end end
class User < ActiveRecord::Base
has and belongs to many :articles_ _ g _ _ y
Account.transaction do
account1.deposit(100)p ( ) account2.withdraw(100)
end end
name = params[:name] pos =
Order.find(:all,( ,
:conditions => "name = '#{name}' and pay_type = 'po'" )
name = params[:name] pos = Order.find(:all,
:conditions => ["name = ? and pay_type = 'po'" , name])
name = params[:name]
pay_type = params[:pay_type] pos =
Order.find(:all,( , :conditions =>
["name = :name and pay type = :pay type" [ name :name and pay_type :pay_type , {:pay_type => pay_type, :name => name}])
:order – para ordenar a consulta, use símbolos;
:limit – quantidade máxima de itens que deve ser trazida;
:offset – primeiro item a ser trazido;
:first – no lugar de :all, para trazer apenas um item;
items = LineItem.find_by_sql("select *, " + " quantity*unit_price as total_price, " + " products.title as title " +
" from line_items, products " +_ , p
" where line_items.product_id = products.id " )
li = items[0]
puts "#{li.title}: #{li.quantity}x#{li.unit_price} > #{li total price}"
average = Order.average(:amount) max = Order.maximum(:amount) min = Order.minimum(:amount) total = Order.sum(:amount)( )
number = Order.count
#tudo isso pode receber :conditions, como o find
order = Order.find_by_name("Dave Thomas" )
orders = Order.find_all_by_name("Dave Thomas" )
order = Order.find_all_by_email(params['email' _ _ y_ (p [ ])
Obrigado pessoal! \o/