SlideShare uma empresa Scribd logo
1 de 148
Baixar para ler offline
RUBY ON RAILS 
MAURÍCIO EDUARDO LOSCHI BATISTA
RUBY 
● Interpretada 
● Dinâmica 
● Multiparadigma
HISTÓRIA 
● Criada por Yukihiro “Matz” Matsumoto em 
Fev/1993. 
● Primeira release pública (v0.95) em 
Dez/1995 
● Primeira versão estável em Dez/1996
AS INSPIRAÇÕES DE 
MATZ 
● Perl 
● Smalltalk 
● Eiffel 
● Ada 
● Lisp
RUBY POR MATZ 
“Ruby is simple in appearance, but is very 
complex inside, just like our human body.” 
Matz -
$ irb
1 # número inteiro 
'a' # string 
:a # símbolo 
[1, 'a'] # array 
{a: 1, 'a' => 2} # hash 
0..9 # range inclusivo 
0...10 # range exclusivo 
true # booleano verdadeiro 
false # booleano falso 
nil # nulo 
LITERAIS
v = 1 # escopo local 
@v = 2 # escopo da instância 
@@v = 3 # escopo da classe 
$v = 4 # escopo global 
print v, @v, @@v, $v # 1234 
ESCOPO DE VARIÁVEIS
NO = 2 # tudo que se inicia com letra maiúscula é uma constante 
NO = 3 # warning: already initialized constant NO 
# warning: previous definition of NO was here 
CONSTANTES
s = 'abc' 
s.reverse # => 'cba' 
s # => 'abc' 
s.reverse! # => 'cba' 
s # => 'cba' 
s.include? 'a' # => true 
MÉTODOS
def splat(*args) 
p args 
end 
def keywords(x:, y: 1) 
print x, y 
end 
splat 1, 2, 3 # [1, 2, 3] 
keywords x: 2 # 21 
MÉTODOS E PARÂMETROS
'abc'.reverse # => "cba" 
'abc'.send :reverse # => "cba" 
MÉTODOS MENSAGENS
10.to_f # => 10.0 
10.to_s # => "10" 
'10'.to_i # => 10 
'ab'.to_sym # => :ab 
(1..5).to_a # => [1, 2, 3, 4, 5] 
to_*
def m(x) 
if x > 0 
puts x 
elsif x == 0 
puts "zero" 
else 
puts "-" 
end 
end 
m 1 # + 
m 0 # zero 
m -1 # - 
def u(x) 
unless x.nil? 
puts x 
end 
end 
u 1 # 1 
u nil 
ESTRUTURAS DE CONTROLE 
x = 0 
until x == 5 
print x 
x += 1 
end # 01234 
x = 0 
while x < 5 
print x 
x += 1 
end # 01234 
for x in 0..4 
print x 
end # 01234
a = [] 
a.push(a.length) while a.length < 3 
a.push(a.length) until a.length == 5 
p a if a.length > 4 # [0, 1, 2, 3, 4] 
p a unless a.length < 5 # [0, 1, 2, 3, 4] 
MODIFICADORES
[1, 2, 3].map { |i| i**2 } # => [1, 4, 9] 
[1, 2, 3].each do |i| 
puts i 
puts i**2 
end 
BLOCOS
def block_yield 
yield 'BLOCO' 
end 
def block_call(&b) 
b.call 'BLOCO' 
end 
block_yield { |s| puts s.downcase } # bloco 
block_call { |s| puts s.reverse } # OCOLB 
BLOCOS E ARGUMENTOS
for x in 0..4 
print x 
end 
(0..4).each { |x| print x} # 01234 
FOR MAIS RUBISTA
FILOSOFIA DO RUBY 
● Felicidade do programador 
● Orientação à objeto 
● Flexibilidade
Ruby lhe permite programar de forma eficiente 
e divertida
TUDO É UM OBJETO 
“I wanted a scripting language that was more 
powerful than Perl, and more object-oriented 
than Python.” 
Matz -
MODELO DE OBJETOS DO RUBY
class Pessoa 
attr_accessor :nome, :idade 
def to_s 
"Nome: #{nome}; Idade: #{idade}" 
end 
end 
eu = Pessoa.new 
eu.nome = "Eu" 
eu.idade=(101) 
puts eu # Nome: Eu; Idade: 101
class Pessoa 
attr_accessor :nome, :idade 
def to_s 
"Nome: #{nome}; Idade: #{idade}" 
end 
end 
eu = Pessoa.new 
eu.nome = "Eu" 
eu.idade=(101) 
puts eu # Nome: Eu; Idade: 101 
self.nome self.idade
module M 
def self.m(x) 
puts x 
end 
end 
M.m 1 # 1 
MODULE
module M 
class C 
def m(x) 
puts x 
end 
end 
end 
M::C.new.m 1 # 1 
MODULE & NAMESPACE
module M 
def m(x) 
puts x 
end 
end 
class C 
include M 
end 
C.new.m 1 # 1 
MODULE & MIXIN
CLASSES ABERTAS 
TODAS as classes podem ser modificadas a 
qualquer momento (também conhecido como 
monkeypatch)
10.length #NoMethodError: undefined method `length' for 10:Fixnum 
class Fixnum 
def length 
to_s.length 
end 
end 
10.length # => 2 
CONTANTO ALGARISMOS DE UM NÚMERO
class C 
def method_missing(meth, *args) 
puts "#{meth} chamado com #{args}" 
yield args if block_given? 
end 
end 
o = C.new 
z = o.matematica(5, 2) { |x, y| x * y } # matematica chamado com [5, 2] 
puts z # 10 
MÉTODOS FANTASMAS
DSL 
● Linguagem dedicada a um domínio de 
problema específico 
○ HTML, CSS, SQL (DSLs externas) 
● Ruby é uma GPL (linguagem de propósito 
geral) 
○ Permite a criação de DSL’s internas (Capybara, 
RSpec)
# rspec 
describe "true or false" do 
it "should be true" do 
expect(true || false).to be true 
end 
end 
# capybara 
visit '/' 
fill_in 'e-mail', with: 'mail@mail.com' 
click_on 'Ok'
ECOSSISTEMA 
● RubyGems 
● Bundler 
● Rake
RAILS 
● Framework MVC de aplicações web 
● Criado em 2003 por David Heinemeier 
Hansson 
● Extraído do Basecamp 
● + de 3400 contribuidores
FILOSOFIA DO RAILS 
● DRY: não se repita 
● COC: convenção sobre configuração 
● Tem o mesmo propósito do ruby: fazer os 
programadores felizes
MVC ORIGINAL
Banco de Dados 
Rails routes 
2 
ActionController 
1 
8 3 
ActionView ActiveRecord 
4 
5 
6 
7 
1. navegador envia a requisição 
2. roteamento encontra o controller 
3. controller requisita o model 
4. model requisita os dados 
5. BD retorna os dados 
6. model retorna 
7. controller requisita a renderização 
8. view retorna a página 
9. controller responde a requisição 
HTTP com a página 
9 
ARQUITETURA
$ rails new notas 
$ cd notas
DIRETÓRIO app 
● assets: js, css, imagens, etc. 
● controllers: controladores do MVC 
○ application_controller.rb: controller geral da aplicação 
● helpers: métodos auxiliares para controllers e views 
● mailers: classes para enviar e-mails 
● models: modelos do MVC 
○ concerns: comportamentos compartilhados por modelos 
● views: Visões do MVC 
○ layouts: layouts para os templates
DIRETÓRIO test 
Testes da classes da aplicação 
● fixtures: dados para os testes 
● integration: testes de integração de todos 
os componentes da aplicação
DIRETÓRIO config 
● environments: configurações exclusivas para cada 
ambiente de execução 
○ Rails provê 3 por padrão: development, test e production 
● initializers: rotinas de inicialização 
● locales: traduções 
● application.rb: configurações comuns para todos os 
ambientes 
● database.yml: configurações do banco de dados 
● routes.rb: roteamento do rails
OUTROS DIRETÓRIOS 
● bin: wrappers de executáveis 
● db: rotinas relacionadas à persistencia 
○ migrate: migrações de BDs relacionais 
● lib: rotinas não específicas da aplicação 
○ assets: assets não específicos da aplicação 
○ tasks: tarefas personalizadas do rake 
● log: logs da aplicação 
● public: arquivos servidos diretamente pelo servidor 
web 
● tmp: arquivos temporários 
● vendor: rotinas criadas por terceiros 
○ assets: assets criados por terceiros
gem 'rails-i18n', github: 'svenfuchs/rails-i18n', branch: 
'master' 
gem 'devise' 
gem 'devise-i18n' 
gem 'bootstrap-sass' 
gem 'autoprefixer-rails' 
gem 'mailcatcher', group: :development 
Gemfile
$ bundle 
$ rails g devise:install
config.i18n.default_locale = :"pt-BR" 
config/application.rb
config.action_mailer.default_url_options = {host: 'localhost'} 
config.action_mailer.delivery_method = :smtp 
config.action_mailer.smtp_settings = {address: 'localhost', port: 1025} 
config/environments/development.rb
$ mailcatcher 
$ rails s 
$ rails g devise User 
$ rake db:migrate
YAML 
● Padrão amigável de serialização de dados 
● Baseada em indentação
$ rails c 
> User.new(password: "12345678").encrypted_password 
=> … 
eu: 
email: eu@mail.com 
encrypted_password: "..." 
voce: 
email: voce@mail.com 
encrypted_password: "..." 
test/fixtures/users.yml
BDD 
Desenvolvimento Orientado a 
Comportamento 
● Outside-in 
● Testa-se o comportamento, não a 
implementação
gem 'capybara', group: [:development, :test] 
gem 'capybara-webkit', group: [:development, :test] 
Gemfile
# … 
require 'minitest/mock' 
require 'capybara/rails' 
# … 
class ActionDispatch::IntegrationTest 
include Capybara::DSL 
end 
# … 
class ActionController::TestCase 
include Devise::TestHelpers 
end 
test/test_helper.rb
$ bundle 
$ rails g integration_test notes_listing
TESTES DE INTEGRAÇÃO 
(ACEITAÇÃO) 
● Testes de alto nível (caixa preta) 
● Simulam a interação do usuário 
● Baseados em cenários
class NotesListingTest < ActionDispatch:: 
IntegrationTest 
setup do 
visit '/users/sign_in' 
fill_in 'Email', with: users(:eu).email 
fill_in 'Password', with: '12345678' 
click_on 'Log in' 
end 
teardown do 
Capybara.reset_sessions! 
end 
test/integration/notes_listing_test.rb (continua...)
test 'listagem de notas' do 
visit '/' 
assert page.has_content? notes(:one).title 
assert page.has_content? notes(:one).body 
assert page.has_no_content? notes(:two).title 
assert page.has_no_content? notes(:two).body 
end 
test/integration/notes_listing_test.rb (continua...)
test 'cor das notas' do 
visit '/' 
assert page.all('.note .well').first['style'] 
.include? "background-color: #{notes(:one).color}" 
end 
end 
test/integration/notes_listing_test.rb
$ rake test
Rails.application.routes.draw do 
resources :notes, path: '/' 
# ... 
config/routes.rb
$ rails g controller notes
TESTES FUNCIONAIS 
● Testa o resultado de uma funcionalidade 
● Efeitos colaterais e resultados intermediários 
não importam
class NotesControllerTest < ActionController::TestCase 
test 'index' do 
get :index 
assert_response :success 
assert_not_nil assigns(:notes) 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < ApplicationController 
def index 
@notes = Note.all 
end 
end 
app/controllers/notes_controller.rb
$ rails g model Note title:string  body: 
text color:string user:references 
$ rake db:migrate
one: 
title: Nota1 Busca 
body: Texto1 
color: Gold 
user: eu 
two: 
title: Nota2 
body: Texto2 
color: Gold 
user: voce 
test/fixtures/notes.yml (continua)
three: 
title: Nota3 
body: Texto3 Busca 
color: Gold 
user: eu 
four: 
title: Nota4 
body: Texto4 
color: Gold 
user: voce 
test/fixtures/notes.yml
TESTES UNITÁRIOS 
● Testam as menores unidades de 
funcionalidade 
● Não deve haver interação com outros 
componentes
class NoteTest < ActiveSupport::TestCase 
test "deve ter texto" do 
n = notes(:one) 
n.body = nil 
assert_not n.save 
end 
test "deve ter uma cor válida" do 
n = notes(:one) 
n.color = 'Black' 
assert_not n.save 
end 
end 
test/models/note_test.rb
class Note < ActiveRecord::Base 
@allowed_colors = %w(Gold LightGreen Pink SkyBlue) 
# … 
validates :body, presence: true 
validates :color, inclusion: { in: @allowed_colors } 
end 
app/models/note.rb
$ rm app/assets/stylesheets/application.css 
$ touch app/assets/stylesheets/application.css.scss
ASSETS PIPELINE 
● Framework para pré-processar, concatenar 
e minificar JS e CSS 
● Reduz a quantidade e o tamanho das 
requisições
SASS 
● Linguagem de script que adiciona 
funcionalidades ao CSS 
● “CSS com superpoderes” 
● CSS válido é SCSS válido
body { padding-top: 70px; } 
@import "bootstrap-sprockets"; 
@import "bootstrap"; 
@import "notes" 
app/assets/stylesheets/application.css.scss
// ... 
//= require turbolinks 
//= require bootstrap-sprockets 
// ... 
app/assets/javascripts/application.js
TEMPLATES & ERB 
● Embedded Ruby (Ruby Incorporado) 
● Processa código ruby e gera HTML
<div class="row notes"> 
<% @notes.each do |note| %> 
<div class="note"> 
<div class="well well-sm" 
style="background-color: <%= note.color %>"> 
<h4> 
<strong><%= note.title %></strong> 
</h4> 
<p><%= simple_format note.body %></p> 
</div> 
</div> 
<% end %> 
</div> 
app/views/notes/index.html.erb
LAYOUTS 
● Template para definir estruturas comuns a 
outros templates
<!DOCTYPE html> 
<!-- ... --> 
<title>Notas</title> 
<%= stylesheet_link_tag 'application', media: 'all', 
'data-turbolinks-track' => true %> 
<%= javascript_include_tag 'application', 
'data-turbolinks-track' => true %> 
<%= csrf_meta_tags %> 
<!-- ... --> 
<body> 
<% if user_signed_in? %> 
<!-- ... --> 
</button> 
<%= link_to 'Notas', notes_path , class: "navbar-brand" %> 
<!-- ... --> 
app/views/layouts/application.html.erb (continua)
<ul class="nav navbar-nav navbar-right"> 
<li><%= link_to t('actions.log_out'), 
destroy_user_session_path, method: :delete %></li> 
<!-- ... --> 
</nav> 
<% end %> 
app/views/layouts/application.html.erb (continua)
<div class="container"> 
<% if flash[:notice] %> 
<!-- ... --> 
</button> 
<%= simple_format flash[:notice] %> 
</div> 
<% end %> 
<% if flash[:alert] %> 
<!-- ... → 
</button> 
<%= simple_format flash[:alert] %> 
</div> 
<% end %> 
<%= yield %> 
<!-- ... --> 
</html> 
app/views/layouts/application.html.erb
INTERNACIONALIZAÇÃO 
● Traduções e formatação de números, datas, 
etc.
pt-BR: 
actions: 
log_out: Sair 
config/locales/pt-BR.yml
$ rake db:fixtures:load 
$ rails s
class NotesControllerTest < ActionController::TestCase 
setup do 
sign_in users(:eu) 
end 
# ... 
test 'index deve mostrar somente as notas do usuário atual' do 
get :index 
assert_not assigns(:notes).include? notes(:two) 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < 
ApplicationController 
before_action :authenticate_user! 
def index 
@notes = Note.where(user: current_user) 
end 
end 
app/controllers/notes_controller.rb
class NotesControllerTest < ActionController::TestCase 
# ... 
test 'index deve retornar a nota mais recente primeiro' do 
note = Note.last 
note.touch 
note.save 
get :index 
assert assigns(:notes).first 
.updated_at > assigns(:notes).last.updated_at 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < ApplicationController 
# ... 
def index 
@notes = Note.where(user: current_user) 
.order(updated_at: :desc) 
end 
end 
app/controllers/notes_controller.rb
$ rails g integration_test note_creation
# ... 
module JSHelper 
def use_js 
Capybara.current_driver = :webkit 
@js = true 
end 
def teardown 
super 
if @js 
Capybara.use_default_driver 
@js = false 
end 
end 
end 
test/test_helper.rb (continua)
module SessionHelper 
def log_in 
visit '/users/sign_in' 
fill_in 'Email', with: users(:eu).email 
fill_in 'Password', with: '12345678' 
click_on 'Log in' 
end 
def teardown 
super 
Capybara.reset_sessions! 
end 
end 
test/test_helper.rb (continua)
# ... 
class ActionDispatch:: 
IntegrationTest 
# ... 
include JSHelper 
include SessionHelper 
end 
test/test_helper.rb (continua)
# ... 
class ActiveRecord::Base 
mattr_accessor :shared_connection 
@@shared_connection = nil 
def self.connection 
@@shared_connection || retrieve_connection 
end 
end 
ActiveRecord::Base.shared_connection = 
ActiveRecord::Base.connection 
test/test_helper.rb
class NotesListingTest < ActionDispatch::IntegrationTest 
setup do 
log_in 
end 
# ... 
test/integration/notes_listing_test.rb
class NoteCreationTest < ActionDispatch::IntegrationTest 
test 'criação de nota' do 
use_js 
log_in 
visit '/' 
click_on I18n.t('actions.create_note') 
fill_in 'note_title', with: 'Nova Nota' 
fill_in 'note_body', with: 'Novo Texto' 
click_on 'SkyBlue' 
click_on I18n.t('actions.save_note') 
assert page.has_content?('Nova Nota'), 'Título não 
encontrado' 
assert page.has_content?('Novo Texto'), 'Texto não 
encontrado' 
assert page.all('.note .well').first['style'] 
.include?("background-color: SkyBlue"), "Cor errada" 
end 
test/integration/note_creation_test.rb (continua)
test 'erro na criação de nota' do 
log_in 
visit '/' 
click_on I18n.t('actions.create_note') 
click_on I18n.t('actions.save_note') 
assert page.has_selector? 'div#errors' 
end 
end 
test/integration/note_creation_test.rb
<!-- ... --> 
<div id="navbar" class="navbar-collapse collapse"> 
<%= link_to t('actions.create_note'), new_note_path, 
class: "btn btn-success navbar-btn navbar-left" %> 
<!-- ... --> 
app/views/layouts/application.html.erb
# ... 
test 'new' do 
get :new 
assert_response :success 
assert_not_nil assigns(:note) 
end 
test 'new deve instanciar uma nova nota' do 
get :new 
assert_not assigns(:note).persisted? 
end 
test 'nova nota deve ser dourada por padrão' do 
get :new 
assert 'Gold', assigns(:note).color 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < 
ApplicationController 
# ... 
def new 
@note = Note.new(color: 'Gold') 
end 
end 
app/controllers/notes_controller.rb
FORM HELPERS 
● Helpers para criação de formulários 
● Facilitam a criação de formulários para 
ações em modelos
<%= form_for @note, html: {role: 'form'} do |n| %> 
<% if @note.errors.any? %> 
<!-- ... --> 
</button> 
<strong> 
<%= t('errors.save_note', count: @note.errors.count) %> 
</strong> 
<ul> 
<% @note.errors.full_messages.each do |msg| %> 
<li><%= msg %></li> 
<% end %> 
</ul> 
</div> 
<% end %> 
<%= n.hidden_field :color %> 
app/views/notes/new.html.erb (continua)
<!-- ... --> 
<div id="note-card" class="well" style="background-color: <%= 
@note.color %>"> 
<div class="form-group"> 
<%= n.text_field :title, class: 'form-control input-lg', 
placeholder: t('placeholders.title') %> 
</div> 
<div class="form-group"> 
<%= n.text_area :body, class: 'form-control', 
placeholder: t('placeholders.body'), rows: 10 %> 
<!-- ... --> 
<ul class="list-unstyled"> 
<% Note.allowed_colors.each do |color| %> 
<li> 
<a id="<%= color %>" href="#" class="btn" 
style="background-color: <%= color %>" 
onclick="change_color('<%= color %>')"></a> 
</li> 
<% end %> 
app/views/notes/new.html.erb (continua)
<!-- ... --> 
<div class="col-xs-12"> 
<%= n.submit t('actions.save_note'), 
class: 'btn btn-success' %> 
</div> 
</div> 
<% end %> 
app/views/notes/new.html.erb
EIGENCLASS 
● Metaclasse ou Classe Singleton 
● Implícita e exclusiva de cada objeto
class C; end 
obj = C.new 
def obj.hey 
puts 'hey' 
end 
# hey está definido na eigenclass de obj 
obj.hey # hey 
C.new.hey # NoMethodError: undefined method `hey' for #<C:0x...>
class Note < ActiveRecord::Base 
# ... 
class << self 
attr_reader :allowed_colors 
end 
end 
app/models/note.rb
COFFEESCRIPT 
● Linguagem que compila para JS 
● Simplifica o JS 
● Inspirado em Ruby, Python e Haskell
@change_color = (color) -> 
$('#note-card').css 'background-color', color 
$('input[name="note[color]"]').val color 
app/assets/javascripts/notes.js.coffee
pt-BR: 
actions: 
create_note: Criar nota 
log_out: Sair 
save_note: Salvar 
errors: 
save_note: 
one: 1 erro 
other: "%{count} erros" 
placeholders: 
body: Texto 
title: Título 
config/locales/pt-BR.yml
# ... 
test 'create' do 
post :create, 
note: {title: 'Note', body: 'Text', color: 'Gold'} 
assert_redirected_to controller: 'notes', action: 'index' 
end 
test 'create deve salvar a nota' do 
assert_difference('Note.where(user: users(:eu)).count') do 
post :create, 
note: {title: 'Note', body: 'Text', color: 'Gold'} 
end 
end 
test/controllers/notes_controllers_test.rb (continua)
test 'create deve renderizar o formulario de criação novamente 
em caso de erro' do 
post :create, note: {title: 'Note', color: 'Gold'} 
assert_template :new 
end 
test 'create deve retornar um erro se a nota não for salva' do 
post :create, note: {title: 'Note', color: 'Gold'} 
assert_response :unprocessable_entity 
end 
end 
test/controllers/notes_controllers_test.rb
STRONG PARAMETERS 
● Permite controlar especificamente quais 
atributos podem ser definidos em massa
class NotesController < ApplicationController 
# ... 
def create 
@note = Note.new(note_params.merge(user: 
current_user)) 
if @note.save 
redirect_to notes_path 
else 
render :new, status: :unprocessable_entity 
end 
end 
private 
def note_params 
params.require(:note).permit(:title, :body, :color) 
end 
end 
app/controllers/notes_controller.rb
$ rails g integration_test note_editing
class NoteEditingTest < ActionDispatch::IntegrationTest 
test 'editação de nota' do 
use_js 
log_in 
visit '/' 
click_on "actions-#{notes(:one).id}" 
click_on "edit-#{notes(:one).id}" 
fill_in 'note_title', with: 'Nova Nota' 
fill_in 'note_body', with: 'Novo Texto' 
click_on 'SkyBlue' 
click_on I18n.t('actions.save_note') 
assert page.has_content?('Nova Nota'), 'Título não 
encontrado' 
assert page.has_content?('Novo Texto'), 'Texto não 
encontrado' 
assert page.all('.note .well').first['style'] 
.include?("background-color: SkyBlue"), "Cor errada" 
end 
test/integration/note_editing_test.rb (continua)
test 'erro na edição de nota' do 
use_js 
log_in 
visit '/' 
click_on "actions-#{notes(:one).id}" 
click_on "edit-#{notes(:one).id}" 
fill_in 'note_body', with: '' 
click_on I18n.t('actions.save_note') 
assert page.has_selector? 'div#errors' 
end 
end 
test/integration/note_editing_test.rb (continua)
<!-- ... --> 
<div class="well well-sm" 
style="background-color: <%= note.color %>"> 
<div class="dropdown pull-right"> 
<button id="actions-<%= note.id %>" type="button" 
class="btn btn-link note-actions" data-toggle="dropdown"> 
<span class="glyphicon glyphicon-cog"></span> 
</button> 
<ul class="dropdown-menu"> 
<li> 
<%= link_to edit_note_path(note.id), 
id: "edit-#{note.id}" do %> 
<span class="glyphicon glyphicon-pencil note-actions 
note-action"> 
</span><%= t('actions.edit_note') %> 
<% end %> 
</li> 
</ul> 
</div> 
app/views/notes/index.html.erb
class NotesControllerTest < ActionController::TestCase 
# ... 
test 'edit' do 
get :edit, id: notes(:one).id 
assert_response :success 
assert_not_nil assigns(:note) 
end 
test 'edit deve retornar a nota correta para ser editada' do 
get :edit, id: notes(:one).id 
assert_equal notes(:one), assigns(:note) 
end 
test 'edit deve disparar uma exceção se a nota não for 
encontrada' do 
assert_raises(ActiveRecord::RecordNotFound) { get :edit, id: 1 
} 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < ApplicationController 
# ... 
def edit 
@note = Note.find(params[:id]) 
end 
# ... 
app/controllers/notes_controller.rb
PARTIALS 
● DRY 
● Extração de partes comuns entre templates
<%= render 'form' %> 
app/views/notes/{new,edit}.html.erb
pt-BR: 
actions: 
create_note: Criar nota 
edit_note: Editar 
# ... 
config/locales/pt-BR.yml
class NotesControllerTest < ActionController::TestCase 
# ... 
test 'update' do 
patch :update, id: notes(:one).id, 
note: {title: 'Update Note', 
body: 'Update Text', color: 'Pink'} 
assert_redirected_to controller: 'notes', action: 'index' 
end 
test 'update deve atualizar a nota' do 
patch :update, id: notes(:one).id, 
note: {title: 'Update Note', 
body: 'Update Text', color: 'Pink'} 
notes(:one).reload 
assert_equal 'Update Note', notes(:one).title 
assert_equal 'Update Text', notes(:one).body 
assert_equal 'Pink', notes(:one).color 
end 
test/controllers/notes_controller_test.rb (continua)
test 'update deve renderizar o formulario de edição novamente em 
caso de erro' do 
patch :update, id: notes(:one).id, 
note: {title: 'Update Note', body: '', color: 'Pink'} 
assert_template :edit 
end 
test 'update deve retornar um erro se a nota não for salva' do 
patch :update, id: notes(:one).id, 
note: {title: 'Update Note', body: '', color: 'Pink'} 
assert_response :unprocessable_entity 
end 
test 'update deve disparar uma exceção se a nota não for 
encontrada' do 
assert_raises(ActiveRecord::RecordNotFound) { patch :update, 
id: 1 } 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < ApplicationController 
# ... 
def update 
@note = Note.find(params[:id]) 
if @note.update(note_params) 
redirect_to notes_path 
else 
render :edit, status: : 
unprocessable_entity 
end 
end 
# ... 
app/controllers/notes_controller.rb
$ rails g integration_test note_deleting
class NoteDeletingTest < ActionDispatch::IntegrationTest 
test 'exclusão de nota' do 
use_js 
log_in 
visit '/' 
click_on "actions-#{notes(:one).id}" 
page.accept_confirm do 
click_on "delete-#{notes(:one).id}" 
end 
assert page.has_no_content? notes(:one).title 
end 
end 
test/integration/note_deleting_test.rb
<ul class="dropdown-menu"> 
<!-- ... --> 
<li> 
<%= link_to note_path(note), method: :delete, 
data: {confirm: t('messages.are_you_sure?')}, 
id: "delete-#{note.id}" do %> 
<span class="glyphicon glyphicon-trash note-actions note-action"></ 
span> 
<%= t('actions.delete_note') %> 
<% end %> 
</li> 
</ul> 
<!-- ... -->
pt-BR: 
actions: 
create_note: Criar nota 
delete_note: Excluir 
# … 
errors: 
delete_note: Não foi possível excluir a nota 
# … 
messages: 
are_you_sure?: Tem certeza? 
# ... 
config/locales/pt-BR.yml
class NotesControllerTest < ActionController::TestCase 
# ... 
test 'destroy' do 
delete :destroy, id: notes(:one).id 
assert_redirected_to controller: 'notes', action: 'index' 
end 
test 'destroy deve excluir a nota' do 
assert_difference('Note.count', -1) { delete :destroy, id: 
notes(:one).id } 
end 
test 'destroy deve disparar uma exceção se a nota não for 
encontrada' do 
assert_raises(ActiveRecord::RecordNotFound) { delete : 
destroy, id: 1 } 
end 
test/controllers/notes_controller_test.rb (continua)
MOCKS & STUBS 
● Mocks: simulam o comportamento de 
objetos reais 
● Stubs: simulam a execução de métodos 
reais
test 'destroy deve colocar as mensagens de erro no flash alert' 
do 
note = MiniTest::Mock.new 
errors = MiniTest::Mock.new 
note.expect :id, notes(:one).id 
note.expect :destroy, false 
note.expect :errors, errors 
errors.expect :full_messages, ['error'] 
Note.stub :find, note do 
delete :destroy, id: notes(:one).id 
assert_equal I18n.t('errors.delete_note'), flash[:alert] 
assert_response :unprocessable_entity 
end 
end 
end 
test/controllers/notes_controller_test.rb
class NotesController < ApplicationController 
# ... 
def destroy 
@note = Note.find(params[:id]) 
if @note.destroy 
redirect_to notes_path 
else 
flash[:alert] = I18n.t('errors.delete_note') 
redirect_to notes_path, status: :unprocessable_entity 
end 
end 
# ... 
app/controllers/notes_controller.rb
$ rails g integration_test notes_searching
class NotesSearchingTest < ActionDispatch::IntegrationTest 
setup do 
log_in 
end 
test 'buscando por título exato' do 
visit '/' 
fill_in 'query', with: notes(:one).title 
click_on 'search' 
assert page.has_content? notes(:one).body 
assert page.has_no_content? notes(:three).title 
assert page.has_content? I18n.t('messages.n_notes_found', 
count: 1) 
end 
test/integration/notes_searching_test.rb (continua)
test 'buscando por texto exato' do 
visit '/' 
fill_in 'query', with: notes(:one).body 
click_on 'search' 
assert page.has_content? notes(:one).title 
assert page.has_no_content? notes(:three).title 
assert page.has_content? I18n.t('messages.n_notes_found', 
count: 1) 
end 
test 'buscando por título parcial' do 
visit '/' 
fill_in 'query', with: notes(:one).title[0..2] 
click_on 'search' 
assert page.has_content? notes(:one).title 
assert page.has_content? notes(:three).title 
assert page.has_content? I18n.t('messages.n_notes_found', 
count: 2) 
end 
test/integration/notes_searching_test.rb (continua)
test 'buscando por texto parcial' do 
visit '/' 
fill_in 'query', with: notes(:one).body[0..2] 
click_on 'search' 
assert page.has_content? notes(:one).title 
assert page.has_content? notes(:three).title 
assert page.has_content? I18n.t('messages.n_notes_found', 
count: 2) 
end 
test 'resultados no título e no texto de notas diferentes' do 
visit '/' 
fill_in 'query', with: 'Busca' 
click_on 'search' 
assert page.has_content? notes(:one).title 
assert page.has_content? notes(:three).title 
assert page.has_content? I18n.t('messages.n_notes_found', 
count: 2) 
end 
test/integration/notes_searching_test.rb (continua)
<!-- ... --> 
<%= link_to t('actions.create_note'), new_note_path, 
class: "btn btn-success navbar-btn navbar-left" %> 
<%= form_tag search_notes_path, method: :get, 
class: 'navbar-form navbar-left', role: 'search' do %> 
<div class="input-group"> 
<%= text_field_tag :query, params[:query], 
class: 'form-control', 
placeholder: t('placeholders.search_notes') %> 
<span class="input-group-btn"> 
<%= button_tag id: 'search', type: 'submit', 
class: 'btn btn-primary', name: nil do %> 
<span class="glyphicon glyphicon-search"></span> 
<% end %> 
</span> 
</div> 
<% end %> 
<!-- ... --> 
app/views/layouts/application.erb.html
test 'nenhum resultado' do 
visit '/' 
fill_in 'query', with: 'abcd' 
click_on 'search' 
assert page.has_content? I18n.t('messages.no_notes_found') 
assert page.has_no_content? notes(:one).title 
assert page.has_no_content? notes(:three).title 
end 
end 
test/integration/notes_searching_test.rb
Rails.application.routes.draw do 
resources :notes, path: '/' do 
get 'search' => 'notes#search', on: :collection, as: :search 
end 
# ... 
config/routes.rb
class NotesControllerTest < ActionController::TestCase 
# ... 
test 'search' do 
get :search, query: notes(:one).title 
assert_response :success 
assert_not_nil assigns(:notes) 
end 
test 'search deve buscar somente as notas do usuario atual' do 
get :search, query: 'N' 
assert_equal 2, assigns(:notes).count 
end 
test 'search deve buscar pelo titulo completo' do 
get :search, query: notes(:one).title 
assert_equal notes(:one), assigns(:notes).first 
end 
test/controllers/notes_controllers_test.rb (continua)
test 'search deve buscar pelo texto completo' do 
get :search, query: notes(:one).body 
assert_equal notes(:one), assigns(:notes).first 
end 
test 'search deve buscar pelo titulo parcial' do 
get :search, query: notes(:one).title[0..2] 
assert assigns(:notes).include? notes(:one) 
assert assigns(:notes).include? notes(:three) 
end 
test 'search deve buscar pelo texto parcial' do 
get :search, query: notes(:one).body[0..2] 
assert assigns(:notes).include? notes(:one) 
assert assigns(:notes).include? notes(:three) 
end 
test/controllers/notes_controllers_test.rb (continua)
test 'search deve retornar resultados de título e texto em notas 
diferentes' do 
get :search, query: 'Busca' 
assert assigns(:notes).include? notes(:one) 
assert assigns(:notes).include? notes(:three) 
end 
test 'search deve colocar a quantidade de resultados no flash 
notice' do 
get :search, query: notes(:one).title 
assert_equal I18n.t('messages.n_notes_found', count: 1), 
flash[:notice] 
end 
test 'search deve colocar a mensagem de nenhum resultados no 
flash alert' do 
get :search, query: 'abcd' 
assert_equal I18n.t('messages.no_notes_found'), flash[:alert] 
end 
test/controllers/notes_controllers_test.rb
class NotesController < ApplicationController 
# ... 
def search 
@notes = Note.where(user: current_user) 
.where(' notes.title LIKE ? OR notes.body LIKE ?', 
"%#{params[:query]}%", "%#{params[:query]}%") 
.order(updated_at: :desc) 
if @notes.count > 0 
flash.now[:notice] = I18n.t('messages.n_notes_found', 
count: @notes.count) 
else 
flash.now[:alert] = I18n.t('messages.no_notes_found') 
end 
end 
# ... 
app/controllers/notes_controller.rb
<%= render 'list' %> 
app/views/notes/index.html.erb
<%= render 'list' %> 
app/views/notes/search.html.erb
pt-BR: 
# ... 
messages: 
are_you_sure?: Tem certeza? 
n_notes_found: 
one: 1 nota encontrada 
other: "%{count} notas encontradas" 
no_notes_found: Nenhuma nota encontrada :-( 
placeholders: 
body: Texto 
search_notes: Pesquisar nas notas 
# ... 
config/locales/pt-BR.yml
REFERÊNCIAS 
PERROTTA, Paolo. Metaprogramming Ruby. 1 ed. The 
Pragmatic Bookshelf, 2010. 
MATSUMOTO, Yukihiro. Ruby in a Nutshell. 1 ed. O’Reilly, 
2001. 
THOMAS, Dave; FOWLER, Chad; HUNT, Andy. 
Programming Ruby 1.9 & 2.0: The Pragmatic Programmers 
Guide. 4 ed. The Pragmatic Bookshelf, 2013. 
BLACK, David A.. The Well-Grounded Rubyist. 1 ed. 
Manning, 2009.
REFERÊNCIAS 
About Ruby - https://www.ruby-lang.org/en/about/ 
The Philosophy of Ruby - http://www.artima. 
com/intv/rubyP.html 
RubyConf: History of Ruby - http://blog.nicksieger. 
com/articles/2006/10/20/rubyconf-history-of-ruby/ 
Ruby on Rails Guides - http://guides.rubyonrails.org/

Mais conteúdo relacionado

Mais procurados

PHP Experience 2016 - [Palestra] Rumo à Certificação PHP
PHP Experience 2016 - [Palestra] Rumo à Certificação PHPPHP Experience 2016 - [Palestra] Rumo à Certificação PHP
PHP Experience 2016 - [Palestra] Rumo à Certificação PHPiMasters
 
O que mudou no Ruby 1.9
O que mudou no Ruby 1.9O que mudou no Ruby 1.9
O que mudou no Ruby 1.9Nando Vieira
 
RSpec Best Friends @ Rupy Natal 2014
RSpec Best Friends @ Rupy Natal 2014RSpec Best Friends @ Rupy Natal 2014
RSpec Best Friends @ Rupy Natal 2014Mauro George
 
Proxy, Man-In-The-Middle e testes
Proxy, Man-In-The-Middle e testesProxy, Man-In-The-Middle e testes
Proxy, Man-In-The-Middle e testesStanislaw Pusep
 
Iniciando com javaScript 2017
Iniciando com javaScript 2017Iniciando com javaScript 2017
Iniciando com javaScript 2017Romualdo Andre
 
Introdução ao Shell Script (versão estendida)
Introdução ao Shell Script (versão estendida)Introdução ao Shell Script (versão estendida)
Introdução ao Shell Script (versão estendida)Hugo Maia Vieira
 
Crash Course Ruby & Rails
Crash Course Ruby & RailsCrash Course Ruby & Rails
Crash Course Ruby & RailsFrevo on Rails
 
Yet Another Ruby Framework - Como o Rails funciona por dentro
Yet Another Ruby Framework - Como o Rails funciona por dentroYet Another Ruby Framework - Como o Rails funciona por dentro
Yet Another Ruby Framework - Como o Rails funciona por dentroCarlos Brando
 
Memcached, Gearman e Sphinx
Memcached, Gearman e SphinxMemcached, Gearman e Sphinx
Memcached, Gearman e SphinxElton Minetto
 
Ruby para programadores PHP
Ruby para programadores PHPRuby para programadores PHP
Ruby para programadores PHPJuan Maiz
 

Mais procurados (19)

Doctrine2 Seminário PHP
Doctrine2 Seminário PHPDoctrine2 Seminário PHP
Doctrine2 Seminário PHP
 
PHP Experience 2016 - [Palestra] Rumo à Certificação PHP
PHP Experience 2016 - [Palestra] Rumo à Certificação PHPPHP Experience 2016 - [Palestra] Rumo à Certificação PHP
PHP Experience 2016 - [Palestra] Rumo à Certificação PHP
 
O que mudou no Ruby 1.9
O que mudou no Ruby 1.9O que mudou no Ruby 1.9
O que mudou no Ruby 1.9
 
RSpec Best Friends @ Rupy Natal 2014
RSpec Best Friends @ Rupy Natal 2014RSpec Best Friends @ Rupy Natal 2014
RSpec Best Friends @ Rupy Natal 2014
 
Proxy, Man-In-The-Middle e testes
Proxy, Man-In-The-Middle e testesProxy, Man-In-The-Middle e testes
Proxy, Man-In-The-Middle e testes
 
Iniciando com javaScript 2017
Iniciando com javaScript 2017Iniciando com javaScript 2017
Iniciando com javaScript 2017
 
Introdução ao Shell Script (versão estendida)
Introdução ao Shell Script (versão estendida)Introdução ao Shell Script (versão estendida)
Introdução ao Shell Script (versão estendida)
 
PHP 7
PHP 7PHP 7
PHP 7
 
Lista de exercícios em Bash (resolvida)
Lista de exercícios em Bash (resolvida) Lista de exercícios em Bash (resolvida)
Lista de exercícios em Bash (resolvida)
 
PHP GERAL
PHP GERALPHP GERAL
PHP GERAL
 
Crash Course Ruby & Rails
Crash Course Ruby & RailsCrash Course Ruby & Rails
Crash Course Ruby & Rails
 
Redis na Prática
Redis na PráticaRedis na Prática
Redis na Prática
 
Palestra Ruby
Palestra RubyPalestra Ruby
Palestra Ruby
 
Yet Another Ruby Framework - Como o Rails funciona por dentro
Yet Another Ruby Framework - Como o Rails funciona por dentroYet Another Ruby Framework - Como o Rails funciona por dentro
Yet Another Ruby Framework - Como o Rails funciona por dentro
 
Memcached, Gearman e Sphinx
Memcached, Gearman e SphinxMemcached, Gearman e Sphinx
Memcached, Gearman e Sphinx
 
PHP fora da Web
PHP fora da WebPHP fora da Web
PHP fora da Web
 
Ruby para programadores PHP
Ruby para programadores PHPRuby para programadores PHP
Ruby para programadores PHP
 
Doctrine for Dummies
Doctrine for DummiesDoctrine for Dummies
Doctrine for Dummies
 
servlet-respostas
servlet-respostasservlet-respostas
servlet-respostas
 

Destaque

Curso de Ruby on Rails
Curso de Ruby on RailsCurso de Ruby on Rails
Curso de Ruby on RailsCJR, UnB
 
Rest API's with Ruby on Rails
Rest API's with Ruby on RailsRest API's with Ruby on Rails
Rest API's with Ruby on RailsRicardo Silva
 
Curso de Ruby on Rails - Aula 04
Curso de Ruby on Rails - Aula 04Curso de Ruby on Rails - Aula 04
Curso de Ruby on Rails - Aula 04Maurício Linhares
 
Criando uma aplicação simples com ruby on rails
Criando uma aplicação simples com ruby on railsCriando uma aplicação simples com ruby on rails
Criando uma aplicação simples com ruby on railsCOTIC-PROEG (UFPA)
 
Logica de programação. introdução[AULA-1]
Logica de programação. introdução[AULA-1]Logica de programação. introdução[AULA-1]
Logica de programação. introdução[AULA-1]Ricardo Silva
 
Minicurso de Rails - WTISC 2014
Minicurso de Rails - WTISC 2014Minicurso de Rails - WTISC 2014
Minicurso de Rails - WTISC 2014Zarathon Maia
 
Big data: Conceitos e Desafios
Big data: Conceitos e DesafiosBig data: Conceitos e Desafios
Big data: Conceitos e DesafiosFlávio Sousa
 

Destaque (12)

Curso de Ruby on Rails
Curso de Ruby on RailsCurso de Ruby on Rails
Curso de Ruby on Rails
 
Rest API's with Ruby on Rails
Rest API's with Ruby on RailsRest API's with Ruby on Rails
Rest API's with Ruby on Rails
 
Ruby and Rails
Ruby and RailsRuby and Rails
Ruby and Rails
 
Rails Usergroup Hamburg: Heroku
Rails Usergroup Hamburg: HerokuRails Usergroup Hamburg: Heroku
Rails Usergroup Hamburg: Heroku
 
Curso de Ruby on Rails - Aula 04
Curso de Ruby on Rails - Aula 04Curso de Ruby on Rails - Aula 04
Curso de Ruby on Rails - Aula 04
 
Criando uma aplicação simples com ruby on rails
Criando uma aplicação simples com ruby on railsCriando uma aplicação simples com ruby on rails
Criando uma aplicação simples com ruby on rails
 
Rails na pratica
Rails na praticaRails na pratica
Rails na pratica
 
Curso Ruby
Curso RubyCurso Ruby
Curso Ruby
 
Logica de programação. introdução[AULA-1]
Logica de programação. introdução[AULA-1]Logica de programação. introdução[AULA-1]
Logica de programação. introdução[AULA-1]
 
Minicurso de Rails - WTISC 2014
Minicurso de Rails - WTISC 2014Minicurso de Rails - WTISC 2014
Minicurso de Rails - WTISC 2014
 
SEA Rails na pratica
SEA Rails na praticaSEA Rails na pratica
SEA Rails na pratica
 
Big data: Conceitos e Desafios
Big data: Conceitos e DesafiosBig data: Conceitos e Desafios
Big data: Conceitos e Desafios
 

Semelhante a Minicurso Ruby on Rails

Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)
Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)
Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)Julio Betta
 
Ruby on rails gds 2011
Ruby on rails   gds 2011Ruby on rails   gds 2011
Ruby on rails gds 2011JogosUnisinos
 
Ruby - Criando código para máquinas e humanos
Ruby - Criando código para máquinas e humanosRuby - Criando código para máquinas e humanos
Ruby - Criando código para máquinas e humanosGregorio Kusowski
 
Quick introduction to Ruby on Rails
Quick introduction to Ruby on RailsQuick introduction to Ruby on Rails
Quick introduction to Ruby on RailsWhitesmith
 
Palestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVAPalestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVAThiago Cifani
 
Curso de introdução ao ruby
Curso de introdução ao rubyCurso de introdução ao ruby
Curso de introdução ao rubyFrancis Wagner
 
Aula c++ estruturas de dados
Aula c++   estruturas de dadosAula c++   estruturas de dados
Aula c++ estruturas de dadosJean Martina
 
Criando sua própria linguagem de programação
Criando sua própria linguagem de programaçãoCriando sua própria linguagem de programação
Criando sua própria linguagem de programaçãoronaldoferraz
 
Esta começando a programar para a web? Então começe com Rails
Esta começando a programar para a web? Então começe com RailsEsta começando a programar para a web? Então começe com Rails
Esta começando a programar para a web? Então começe com Railsismaelstahelin
 
TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...
TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...
TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...tdc-globalcode
 
Redis um banco chave valor
Redis um banco chave valorRedis um banco chave valor
Redis um banco chave valorKinn Julião
 
Ruby para-programadores-php
Ruby para-programadores-phpRuby para-programadores-php
Ruby para-programadores-phpJuan Maiz
 
Introdução á linguagem Ruby com aplicativo em Rails
Introdução á linguagem Ruby com aplicativo em RailsIntrodução á linguagem Ruby com aplicativo em Rails
Introdução á linguagem Ruby com aplicativo em Railsoverduka
 
Desenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDBDesenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDBiMasters
 

Semelhante a Minicurso Ruby on Rails (20)

Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)
Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)
Ruby on Rails (VERSAO COM LAYOUT CONSERTADO)
 
Ruby on rails gds 2011
Ruby on rails   gds 2011Ruby on rails   gds 2011
Ruby on rails gds 2011
 
Ruby - Criando código para máquinas e humanos
Ruby - Criando código para máquinas e humanosRuby - Criando código para máquinas e humanos
Ruby - Criando código para máquinas e humanos
 
Quick introduction to Ruby on Rails
Quick introduction to Ruby on RailsQuick introduction to Ruby on Rails
Quick introduction to Ruby on Rails
 
Introducao rubyonrails
Introducao rubyonrailsIntroducao rubyonrails
Introducao rubyonrails
 
Ruby & Rails
Ruby & RailsRuby & Rails
Ruby & Rails
 
Palestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVAPalestra Desenvolvimento Ágil para Web com ROR UVA
Palestra Desenvolvimento Ágil para Web com ROR UVA
 
Curso de introdução ao ruby
Curso de introdução ao rubyCurso de introdução ao ruby
Curso de introdução ao ruby
 
Aula c++ estruturas de dados
Aula c++   estruturas de dadosAula c++   estruturas de dados
Aula c++ estruturas de dados
 
Criando sua própria linguagem de programação
Criando sua própria linguagem de programaçãoCriando sua própria linguagem de programação
Criando sua própria linguagem de programação
 
Esta começando a programar para a web? Então começe com Rails
Esta começando a programar para a web? Então começe com RailsEsta começando a programar para a web? Então começe com Rails
Esta começando a programar para a web? Então começe com Rails
 
TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...
TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...
TDC2017 | POA Trilha Programacao Funicional - (Nunca) Ouvi falar de Rust... m...
 
Ruby On Rails Regis
Ruby On Rails RegisRuby On Rails Regis
Ruby On Rails Regis
 
Rails na prática
Rails na práticaRails na prática
Rails na prática
 
Redis um banco chave valor
Redis um banco chave valorRedis um banco chave valor
Redis um banco chave valor
 
Ruby para-programadores-php
Ruby para-programadores-phpRuby para-programadores-php
Ruby para-programadores-php
 
Introdução Ruby 1.8.7 + Rails 3
Introdução Ruby 1.8.7 + Rails 3Introdução Ruby 1.8.7 + Rails 3
Introdução Ruby 1.8.7 + Rails 3
 
Oficial
OficialOficial
Oficial
 
Introdução á linguagem Ruby com aplicativo em Rails
Introdução á linguagem Ruby com aplicativo em RailsIntrodução á linguagem Ruby com aplicativo em Rails
Introdução á linguagem Ruby com aplicativo em Rails
 
Desenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDBDesenvolvendo soluções com banco de dados não relacional - MongoDB
Desenvolvendo soluções com banco de dados não relacional - MongoDB
 

Minicurso Ruby on Rails

  • 1. RUBY ON RAILS MAURÍCIO EDUARDO LOSCHI BATISTA
  • 2. RUBY ● Interpretada ● Dinâmica ● Multiparadigma
  • 3. HISTÓRIA ● Criada por Yukihiro “Matz” Matsumoto em Fev/1993. ● Primeira release pública (v0.95) em Dez/1995 ● Primeira versão estável em Dez/1996
  • 4. AS INSPIRAÇÕES DE MATZ ● Perl ● Smalltalk ● Eiffel ● Ada ● Lisp
  • 5. RUBY POR MATZ “Ruby is simple in appearance, but is very complex inside, just like our human body.” Matz -
  • 7. 1 # número inteiro 'a' # string :a # símbolo [1, 'a'] # array {a: 1, 'a' => 2} # hash 0..9 # range inclusivo 0...10 # range exclusivo true # booleano verdadeiro false # booleano falso nil # nulo LITERAIS
  • 8. v = 1 # escopo local @v = 2 # escopo da instância @@v = 3 # escopo da classe $v = 4 # escopo global print v, @v, @@v, $v # 1234 ESCOPO DE VARIÁVEIS
  • 9. NO = 2 # tudo que se inicia com letra maiúscula é uma constante NO = 3 # warning: already initialized constant NO # warning: previous definition of NO was here CONSTANTES
  • 10. s = 'abc' s.reverse # => 'cba' s # => 'abc' s.reverse! # => 'cba' s # => 'cba' s.include? 'a' # => true MÉTODOS
  • 11. def splat(*args) p args end def keywords(x:, y: 1) print x, y end splat 1, 2, 3 # [1, 2, 3] keywords x: 2 # 21 MÉTODOS E PARÂMETROS
  • 12. 'abc'.reverse # => "cba" 'abc'.send :reverse # => "cba" MÉTODOS MENSAGENS
  • 13. 10.to_f # => 10.0 10.to_s # => "10" '10'.to_i # => 10 'ab'.to_sym # => :ab (1..5).to_a # => [1, 2, 3, 4, 5] to_*
  • 14. def m(x) if x > 0 puts x elsif x == 0 puts "zero" else puts "-" end end m 1 # + m 0 # zero m -1 # - def u(x) unless x.nil? puts x end end u 1 # 1 u nil ESTRUTURAS DE CONTROLE x = 0 until x == 5 print x x += 1 end # 01234 x = 0 while x < 5 print x x += 1 end # 01234 for x in 0..4 print x end # 01234
  • 15. a = [] a.push(a.length) while a.length < 3 a.push(a.length) until a.length == 5 p a if a.length > 4 # [0, 1, 2, 3, 4] p a unless a.length < 5 # [0, 1, 2, 3, 4] MODIFICADORES
  • 16. [1, 2, 3].map { |i| i**2 } # => [1, 4, 9] [1, 2, 3].each do |i| puts i puts i**2 end BLOCOS
  • 17. def block_yield yield 'BLOCO' end def block_call(&b) b.call 'BLOCO' end block_yield { |s| puts s.downcase } # bloco block_call { |s| puts s.reverse } # OCOLB BLOCOS E ARGUMENTOS
  • 18. for x in 0..4 print x end (0..4).each { |x| print x} # 01234 FOR MAIS RUBISTA
  • 19. FILOSOFIA DO RUBY ● Felicidade do programador ● Orientação à objeto ● Flexibilidade
  • 20. Ruby lhe permite programar de forma eficiente e divertida
  • 21. TUDO É UM OBJETO “I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python.” Matz -
  • 22. MODELO DE OBJETOS DO RUBY
  • 23. class Pessoa attr_accessor :nome, :idade def to_s "Nome: #{nome}; Idade: #{idade}" end end eu = Pessoa.new eu.nome = "Eu" eu.idade=(101) puts eu # Nome: Eu; Idade: 101
  • 24. class Pessoa attr_accessor :nome, :idade def to_s "Nome: #{nome}; Idade: #{idade}" end end eu = Pessoa.new eu.nome = "Eu" eu.idade=(101) puts eu # Nome: Eu; Idade: 101 self.nome self.idade
  • 25. module M def self.m(x) puts x end end M.m 1 # 1 MODULE
  • 26. module M class C def m(x) puts x end end end M::C.new.m 1 # 1 MODULE & NAMESPACE
  • 27. module M def m(x) puts x end end class C include M end C.new.m 1 # 1 MODULE & MIXIN
  • 28. CLASSES ABERTAS TODAS as classes podem ser modificadas a qualquer momento (também conhecido como monkeypatch)
  • 29. 10.length #NoMethodError: undefined method `length' for 10:Fixnum class Fixnum def length to_s.length end end 10.length # => 2 CONTANTO ALGARISMOS DE UM NÚMERO
  • 30. class C def method_missing(meth, *args) puts "#{meth} chamado com #{args}" yield args if block_given? end end o = C.new z = o.matematica(5, 2) { |x, y| x * y } # matematica chamado com [5, 2] puts z # 10 MÉTODOS FANTASMAS
  • 31. DSL ● Linguagem dedicada a um domínio de problema específico ○ HTML, CSS, SQL (DSLs externas) ● Ruby é uma GPL (linguagem de propósito geral) ○ Permite a criação de DSL’s internas (Capybara, RSpec)
  • 32. # rspec describe "true or false" do it "should be true" do expect(true || false).to be true end end # capybara visit '/' fill_in 'e-mail', with: 'mail@mail.com' click_on 'Ok'
  • 33. ECOSSISTEMA ● RubyGems ● Bundler ● Rake
  • 34. RAILS ● Framework MVC de aplicações web ● Criado em 2003 por David Heinemeier Hansson ● Extraído do Basecamp ● + de 3400 contribuidores
  • 35. FILOSOFIA DO RAILS ● DRY: não se repita ● COC: convenção sobre configuração ● Tem o mesmo propósito do ruby: fazer os programadores felizes
  • 37. Banco de Dados Rails routes 2 ActionController 1 8 3 ActionView ActiveRecord 4 5 6 7 1. navegador envia a requisição 2. roteamento encontra o controller 3. controller requisita o model 4. model requisita os dados 5. BD retorna os dados 6. model retorna 7. controller requisita a renderização 8. view retorna a página 9. controller responde a requisição HTTP com a página 9 ARQUITETURA
  • 38. $ rails new notas $ cd notas
  • 39. DIRETÓRIO app ● assets: js, css, imagens, etc. ● controllers: controladores do MVC ○ application_controller.rb: controller geral da aplicação ● helpers: métodos auxiliares para controllers e views ● mailers: classes para enviar e-mails ● models: modelos do MVC ○ concerns: comportamentos compartilhados por modelos ● views: Visões do MVC ○ layouts: layouts para os templates
  • 40. DIRETÓRIO test Testes da classes da aplicação ● fixtures: dados para os testes ● integration: testes de integração de todos os componentes da aplicação
  • 41. DIRETÓRIO config ● environments: configurações exclusivas para cada ambiente de execução ○ Rails provê 3 por padrão: development, test e production ● initializers: rotinas de inicialização ● locales: traduções ● application.rb: configurações comuns para todos os ambientes ● database.yml: configurações do banco de dados ● routes.rb: roteamento do rails
  • 42. OUTROS DIRETÓRIOS ● bin: wrappers de executáveis ● db: rotinas relacionadas à persistencia ○ migrate: migrações de BDs relacionais ● lib: rotinas não específicas da aplicação ○ assets: assets não específicos da aplicação ○ tasks: tarefas personalizadas do rake ● log: logs da aplicação ● public: arquivos servidos diretamente pelo servidor web ● tmp: arquivos temporários ● vendor: rotinas criadas por terceiros ○ assets: assets criados por terceiros
  • 43. gem 'rails-i18n', github: 'svenfuchs/rails-i18n', branch: 'master' gem 'devise' gem 'devise-i18n' gem 'bootstrap-sass' gem 'autoprefixer-rails' gem 'mailcatcher', group: :development Gemfile
  • 44. $ bundle $ rails g devise:install
  • 45. config.i18n.default_locale = :"pt-BR" config/application.rb
  • 46. config.action_mailer.default_url_options = {host: 'localhost'} config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = {address: 'localhost', port: 1025} config/environments/development.rb
  • 47. $ mailcatcher $ rails s $ rails g devise User $ rake db:migrate
  • 48. YAML ● Padrão amigável de serialização de dados ● Baseada em indentação
  • 49. $ rails c > User.new(password: "12345678").encrypted_password => … eu: email: eu@mail.com encrypted_password: "..." voce: email: voce@mail.com encrypted_password: "..." test/fixtures/users.yml
  • 50. BDD Desenvolvimento Orientado a Comportamento ● Outside-in ● Testa-se o comportamento, não a implementação
  • 51. gem 'capybara', group: [:development, :test] gem 'capybara-webkit', group: [:development, :test] Gemfile
  • 52. # … require 'minitest/mock' require 'capybara/rails' # … class ActionDispatch::IntegrationTest include Capybara::DSL end # … class ActionController::TestCase include Devise::TestHelpers end test/test_helper.rb
  • 53. $ bundle $ rails g integration_test notes_listing
  • 54. TESTES DE INTEGRAÇÃO (ACEITAÇÃO) ● Testes de alto nível (caixa preta) ● Simulam a interação do usuário ● Baseados em cenários
  • 55. class NotesListingTest < ActionDispatch:: IntegrationTest setup do visit '/users/sign_in' fill_in 'Email', with: users(:eu).email fill_in 'Password', with: '12345678' click_on 'Log in' end teardown do Capybara.reset_sessions! end test/integration/notes_listing_test.rb (continua...)
  • 56. test 'listagem de notas' do visit '/' assert page.has_content? notes(:one).title assert page.has_content? notes(:one).body assert page.has_no_content? notes(:two).title assert page.has_no_content? notes(:two).body end test/integration/notes_listing_test.rb (continua...)
  • 57. test 'cor das notas' do visit '/' assert page.all('.note .well').first['style'] .include? "background-color: #{notes(:one).color}" end end test/integration/notes_listing_test.rb
  • 59. Rails.application.routes.draw do resources :notes, path: '/' # ... config/routes.rb
  • 60. $ rails g controller notes
  • 61. TESTES FUNCIONAIS ● Testa o resultado de uma funcionalidade ● Efeitos colaterais e resultados intermediários não importam
  • 62. class NotesControllerTest < ActionController::TestCase test 'index' do get :index assert_response :success assert_not_nil assigns(:notes) end end test/controllers/notes_controller_test.rb
  • 63. class NotesController < ApplicationController def index @notes = Note.all end end app/controllers/notes_controller.rb
  • 64. $ rails g model Note title:string body: text color:string user:references $ rake db:migrate
  • 65. one: title: Nota1 Busca body: Texto1 color: Gold user: eu two: title: Nota2 body: Texto2 color: Gold user: voce test/fixtures/notes.yml (continua)
  • 66. three: title: Nota3 body: Texto3 Busca color: Gold user: eu four: title: Nota4 body: Texto4 color: Gold user: voce test/fixtures/notes.yml
  • 67. TESTES UNITÁRIOS ● Testam as menores unidades de funcionalidade ● Não deve haver interação com outros componentes
  • 68. class NoteTest < ActiveSupport::TestCase test "deve ter texto" do n = notes(:one) n.body = nil assert_not n.save end test "deve ter uma cor válida" do n = notes(:one) n.color = 'Black' assert_not n.save end end test/models/note_test.rb
  • 69. class Note < ActiveRecord::Base @allowed_colors = %w(Gold LightGreen Pink SkyBlue) # … validates :body, presence: true validates :color, inclusion: { in: @allowed_colors } end app/models/note.rb
  • 70. $ rm app/assets/stylesheets/application.css $ touch app/assets/stylesheets/application.css.scss
  • 71. ASSETS PIPELINE ● Framework para pré-processar, concatenar e minificar JS e CSS ● Reduz a quantidade e o tamanho das requisições
  • 72. SASS ● Linguagem de script que adiciona funcionalidades ao CSS ● “CSS com superpoderes” ● CSS válido é SCSS válido
  • 73. body { padding-top: 70px; } @import "bootstrap-sprockets"; @import "bootstrap"; @import "notes" app/assets/stylesheets/application.css.scss
  • 74. // ... //= require turbolinks //= require bootstrap-sprockets // ... app/assets/javascripts/application.js
  • 75. TEMPLATES & ERB ● Embedded Ruby (Ruby Incorporado) ● Processa código ruby e gera HTML
  • 76. <div class="row notes"> <% @notes.each do |note| %> <div class="note"> <div class="well well-sm" style="background-color: <%= note.color %>"> <h4> <strong><%= note.title %></strong> </h4> <p><%= simple_format note.body %></p> </div> </div> <% end %> </div> app/views/notes/index.html.erb
  • 77. LAYOUTS ● Template para definir estruturas comuns a outros templates
  • 78. <!DOCTYPE html> <!-- ... --> <title>Notas</title> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> <!-- ... --> <body> <% if user_signed_in? %> <!-- ... --> </button> <%= link_to 'Notas', notes_path , class: "navbar-brand" %> <!-- ... --> app/views/layouts/application.html.erb (continua)
  • 79. <ul class="nav navbar-nav navbar-right"> <li><%= link_to t('actions.log_out'), destroy_user_session_path, method: :delete %></li> <!-- ... --> </nav> <% end %> app/views/layouts/application.html.erb (continua)
  • 80. <div class="container"> <% if flash[:notice] %> <!-- ... --> </button> <%= simple_format flash[:notice] %> </div> <% end %> <% if flash[:alert] %> <!-- ... → </button> <%= simple_format flash[:alert] %> </div> <% end %> <%= yield %> <!-- ... --> </html> app/views/layouts/application.html.erb
  • 81. INTERNACIONALIZAÇÃO ● Traduções e formatação de números, datas, etc.
  • 82. pt-BR: actions: log_out: Sair config/locales/pt-BR.yml
  • 84. class NotesControllerTest < ActionController::TestCase setup do sign_in users(:eu) end # ... test 'index deve mostrar somente as notas do usuário atual' do get :index assert_not assigns(:notes).include? notes(:two) end end test/controllers/notes_controller_test.rb
  • 85. class NotesController < ApplicationController before_action :authenticate_user! def index @notes = Note.where(user: current_user) end end app/controllers/notes_controller.rb
  • 86. class NotesControllerTest < ActionController::TestCase # ... test 'index deve retornar a nota mais recente primeiro' do note = Note.last note.touch note.save get :index assert assigns(:notes).first .updated_at > assigns(:notes).last.updated_at end end test/controllers/notes_controller_test.rb
  • 87. class NotesController < ApplicationController # ... def index @notes = Note.where(user: current_user) .order(updated_at: :desc) end end app/controllers/notes_controller.rb
  • 88. $ rails g integration_test note_creation
  • 89. # ... module JSHelper def use_js Capybara.current_driver = :webkit @js = true end def teardown super if @js Capybara.use_default_driver @js = false end end end test/test_helper.rb (continua)
  • 90. module SessionHelper def log_in visit '/users/sign_in' fill_in 'Email', with: users(:eu).email fill_in 'Password', with: '12345678' click_on 'Log in' end def teardown super Capybara.reset_sessions! end end test/test_helper.rb (continua)
  • 91. # ... class ActionDispatch:: IntegrationTest # ... include JSHelper include SessionHelper end test/test_helper.rb (continua)
  • 92. # ... class ActiveRecord::Base mattr_accessor :shared_connection @@shared_connection = nil def self.connection @@shared_connection || retrieve_connection end end ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection test/test_helper.rb
  • 93. class NotesListingTest < ActionDispatch::IntegrationTest setup do log_in end # ... test/integration/notes_listing_test.rb
  • 94. class NoteCreationTest < ActionDispatch::IntegrationTest test 'criação de nota' do use_js log_in visit '/' click_on I18n.t('actions.create_note') fill_in 'note_title', with: 'Nova Nota' fill_in 'note_body', with: 'Novo Texto' click_on 'SkyBlue' click_on I18n.t('actions.save_note') assert page.has_content?('Nova Nota'), 'Título não encontrado' assert page.has_content?('Novo Texto'), 'Texto não encontrado' assert page.all('.note .well').first['style'] .include?("background-color: SkyBlue"), "Cor errada" end test/integration/note_creation_test.rb (continua)
  • 95. test 'erro na criação de nota' do log_in visit '/' click_on I18n.t('actions.create_note') click_on I18n.t('actions.save_note') assert page.has_selector? 'div#errors' end end test/integration/note_creation_test.rb
  • 96. <!-- ... --> <div id="navbar" class="navbar-collapse collapse"> <%= link_to t('actions.create_note'), new_note_path, class: "btn btn-success navbar-btn navbar-left" %> <!-- ... --> app/views/layouts/application.html.erb
  • 97. # ... test 'new' do get :new assert_response :success assert_not_nil assigns(:note) end test 'new deve instanciar uma nova nota' do get :new assert_not assigns(:note).persisted? end test 'nova nota deve ser dourada por padrão' do get :new assert 'Gold', assigns(:note).color end end test/controllers/notes_controller_test.rb
  • 98. class NotesController < ApplicationController # ... def new @note = Note.new(color: 'Gold') end end app/controllers/notes_controller.rb
  • 99. FORM HELPERS ● Helpers para criação de formulários ● Facilitam a criação de formulários para ações em modelos
  • 100. <%= form_for @note, html: {role: 'form'} do |n| %> <% if @note.errors.any? %> <!-- ... --> </button> <strong> <%= t('errors.save_note', count: @note.errors.count) %> </strong> <ul> <% @note.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <%= n.hidden_field :color %> app/views/notes/new.html.erb (continua)
  • 101. <!-- ... --> <div id="note-card" class="well" style="background-color: <%= @note.color %>"> <div class="form-group"> <%= n.text_field :title, class: 'form-control input-lg', placeholder: t('placeholders.title') %> </div> <div class="form-group"> <%= n.text_area :body, class: 'form-control', placeholder: t('placeholders.body'), rows: 10 %> <!-- ... --> <ul class="list-unstyled"> <% Note.allowed_colors.each do |color| %> <li> <a id="<%= color %>" href="#" class="btn" style="background-color: <%= color %>" onclick="change_color('<%= color %>')"></a> </li> <% end %> app/views/notes/new.html.erb (continua)
  • 102. <!-- ... --> <div class="col-xs-12"> <%= n.submit t('actions.save_note'), class: 'btn btn-success' %> </div> </div> <% end %> app/views/notes/new.html.erb
  • 103. EIGENCLASS ● Metaclasse ou Classe Singleton ● Implícita e exclusiva de cada objeto
  • 104. class C; end obj = C.new def obj.hey puts 'hey' end # hey está definido na eigenclass de obj obj.hey # hey C.new.hey # NoMethodError: undefined method `hey' for #<C:0x...>
  • 105. class Note < ActiveRecord::Base # ... class << self attr_reader :allowed_colors end end app/models/note.rb
  • 106. COFFEESCRIPT ● Linguagem que compila para JS ● Simplifica o JS ● Inspirado em Ruby, Python e Haskell
  • 107. @change_color = (color) -> $('#note-card').css 'background-color', color $('input[name="note[color]"]').val color app/assets/javascripts/notes.js.coffee
  • 108. pt-BR: actions: create_note: Criar nota log_out: Sair save_note: Salvar errors: save_note: one: 1 erro other: "%{count} erros" placeholders: body: Texto title: Título config/locales/pt-BR.yml
  • 109. # ... test 'create' do post :create, note: {title: 'Note', body: 'Text', color: 'Gold'} assert_redirected_to controller: 'notes', action: 'index' end test 'create deve salvar a nota' do assert_difference('Note.where(user: users(:eu)).count') do post :create, note: {title: 'Note', body: 'Text', color: 'Gold'} end end test/controllers/notes_controllers_test.rb (continua)
  • 110. test 'create deve renderizar o formulario de criação novamente em caso de erro' do post :create, note: {title: 'Note', color: 'Gold'} assert_template :new end test 'create deve retornar um erro se a nota não for salva' do post :create, note: {title: 'Note', color: 'Gold'} assert_response :unprocessable_entity end end test/controllers/notes_controllers_test.rb
  • 111. STRONG PARAMETERS ● Permite controlar especificamente quais atributos podem ser definidos em massa
  • 112. class NotesController < ApplicationController # ... def create @note = Note.new(note_params.merge(user: current_user)) if @note.save redirect_to notes_path else render :new, status: :unprocessable_entity end end private def note_params params.require(:note).permit(:title, :body, :color) end end app/controllers/notes_controller.rb
  • 113. $ rails g integration_test note_editing
  • 114. class NoteEditingTest < ActionDispatch::IntegrationTest test 'editação de nota' do use_js log_in visit '/' click_on "actions-#{notes(:one).id}" click_on "edit-#{notes(:one).id}" fill_in 'note_title', with: 'Nova Nota' fill_in 'note_body', with: 'Novo Texto' click_on 'SkyBlue' click_on I18n.t('actions.save_note') assert page.has_content?('Nova Nota'), 'Título não encontrado' assert page.has_content?('Novo Texto'), 'Texto não encontrado' assert page.all('.note .well').first['style'] .include?("background-color: SkyBlue"), "Cor errada" end test/integration/note_editing_test.rb (continua)
  • 115. test 'erro na edição de nota' do use_js log_in visit '/' click_on "actions-#{notes(:one).id}" click_on "edit-#{notes(:one).id}" fill_in 'note_body', with: '' click_on I18n.t('actions.save_note') assert page.has_selector? 'div#errors' end end test/integration/note_editing_test.rb (continua)
  • 116. <!-- ... --> <div class="well well-sm" style="background-color: <%= note.color %>"> <div class="dropdown pull-right"> <button id="actions-<%= note.id %>" type="button" class="btn btn-link note-actions" data-toggle="dropdown"> <span class="glyphicon glyphicon-cog"></span> </button> <ul class="dropdown-menu"> <li> <%= link_to edit_note_path(note.id), id: "edit-#{note.id}" do %> <span class="glyphicon glyphicon-pencil note-actions note-action"> </span><%= t('actions.edit_note') %> <% end %> </li> </ul> </div> app/views/notes/index.html.erb
  • 117. class NotesControllerTest < ActionController::TestCase # ... test 'edit' do get :edit, id: notes(:one).id assert_response :success assert_not_nil assigns(:note) end test 'edit deve retornar a nota correta para ser editada' do get :edit, id: notes(:one).id assert_equal notes(:one), assigns(:note) end test 'edit deve disparar uma exceção se a nota não for encontrada' do assert_raises(ActiveRecord::RecordNotFound) { get :edit, id: 1 } end end test/controllers/notes_controller_test.rb
  • 118. class NotesController < ApplicationController # ... def edit @note = Note.find(params[:id]) end # ... app/controllers/notes_controller.rb
  • 119. PARTIALS ● DRY ● Extração de partes comuns entre templates
  • 120. <%= render 'form' %> app/views/notes/{new,edit}.html.erb
  • 121. pt-BR: actions: create_note: Criar nota edit_note: Editar # ... config/locales/pt-BR.yml
  • 122. class NotesControllerTest < ActionController::TestCase # ... test 'update' do patch :update, id: notes(:one).id, note: {title: 'Update Note', body: 'Update Text', color: 'Pink'} assert_redirected_to controller: 'notes', action: 'index' end test 'update deve atualizar a nota' do patch :update, id: notes(:one).id, note: {title: 'Update Note', body: 'Update Text', color: 'Pink'} notes(:one).reload assert_equal 'Update Note', notes(:one).title assert_equal 'Update Text', notes(:one).body assert_equal 'Pink', notes(:one).color end test/controllers/notes_controller_test.rb (continua)
  • 123. test 'update deve renderizar o formulario de edição novamente em caso de erro' do patch :update, id: notes(:one).id, note: {title: 'Update Note', body: '', color: 'Pink'} assert_template :edit end test 'update deve retornar um erro se a nota não for salva' do patch :update, id: notes(:one).id, note: {title: 'Update Note', body: '', color: 'Pink'} assert_response :unprocessable_entity end test 'update deve disparar uma exceção se a nota não for encontrada' do assert_raises(ActiveRecord::RecordNotFound) { patch :update, id: 1 } end end test/controllers/notes_controller_test.rb
  • 124. class NotesController < ApplicationController # ... def update @note = Note.find(params[:id]) if @note.update(note_params) redirect_to notes_path else render :edit, status: : unprocessable_entity end end # ... app/controllers/notes_controller.rb
  • 125. $ rails g integration_test note_deleting
  • 126. class NoteDeletingTest < ActionDispatch::IntegrationTest test 'exclusão de nota' do use_js log_in visit '/' click_on "actions-#{notes(:one).id}" page.accept_confirm do click_on "delete-#{notes(:one).id}" end assert page.has_no_content? notes(:one).title end end test/integration/note_deleting_test.rb
  • 127. <ul class="dropdown-menu"> <!-- ... --> <li> <%= link_to note_path(note), method: :delete, data: {confirm: t('messages.are_you_sure?')}, id: "delete-#{note.id}" do %> <span class="glyphicon glyphicon-trash note-actions note-action"></ span> <%= t('actions.delete_note') %> <% end %> </li> </ul> <!-- ... -->
  • 128. pt-BR: actions: create_note: Criar nota delete_note: Excluir # … errors: delete_note: Não foi possível excluir a nota # … messages: are_you_sure?: Tem certeza? # ... config/locales/pt-BR.yml
  • 129. class NotesControllerTest < ActionController::TestCase # ... test 'destroy' do delete :destroy, id: notes(:one).id assert_redirected_to controller: 'notes', action: 'index' end test 'destroy deve excluir a nota' do assert_difference('Note.count', -1) { delete :destroy, id: notes(:one).id } end test 'destroy deve disparar uma exceção se a nota não for encontrada' do assert_raises(ActiveRecord::RecordNotFound) { delete : destroy, id: 1 } end test/controllers/notes_controller_test.rb (continua)
  • 130. MOCKS & STUBS ● Mocks: simulam o comportamento de objetos reais ● Stubs: simulam a execução de métodos reais
  • 131. test 'destroy deve colocar as mensagens de erro no flash alert' do note = MiniTest::Mock.new errors = MiniTest::Mock.new note.expect :id, notes(:one).id note.expect :destroy, false note.expect :errors, errors errors.expect :full_messages, ['error'] Note.stub :find, note do delete :destroy, id: notes(:one).id assert_equal I18n.t('errors.delete_note'), flash[:alert] assert_response :unprocessable_entity end end end test/controllers/notes_controller_test.rb
  • 132. class NotesController < ApplicationController # ... def destroy @note = Note.find(params[:id]) if @note.destroy redirect_to notes_path else flash[:alert] = I18n.t('errors.delete_note') redirect_to notes_path, status: :unprocessable_entity end end # ... app/controllers/notes_controller.rb
  • 133. $ rails g integration_test notes_searching
  • 134. class NotesSearchingTest < ActionDispatch::IntegrationTest setup do log_in end test 'buscando por título exato' do visit '/' fill_in 'query', with: notes(:one).title click_on 'search' assert page.has_content? notes(:one).body assert page.has_no_content? notes(:three).title assert page.has_content? I18n.t('messages.n_notes_found', count: 1) end test/integration/notes_searching_test.rb (continua)
  • 135. test 'buscando por texto exato' do visit '/' fill_in 'query', with: notes(:one).body click_on 'search' assert page.has_content? notes(:one).title assert page.has_no_content? notes(:three).title assert page.has_content? I18n.t('messages.n_notes_found', count: 1) end test 'buscando por título parcial' do visit '/' fill_in 'query', with: notes(:one).title[0..2] click_on 'search' assert page.has_content? notes(:one).title assert page.has_content? notes(:three).title assert page.has_content? I18n.t('messages.n_notes_found', count: 2) end test/integration/notes_searching_test.rb (continua)
  • 136. test 'buscando por texto parcial' do visit '/' fill_in 'query', with: notes(:one).body[0..2] click_on 'search' assert page.has_content? notes(:one).title assert page.has_content? notes(:three).title assert page.has_content? I18n.t('messages.n_notes_found', count: 2) end test 'resultados no título e no texto de notas diferentes' do visit '/' fill_in 'query', with: 'Busca' click_on 'search' assert page.has_content? notes(:one).title assert page.has_content? notes(:three).title assert page.has_content? I18n.t('messages.n_notes_found', count: 2) end test/integration/notes_searching_test.rb (continua)
  • 137. <!-- ... --> <%= link_to t('actions.create_note'), new_note_path, class: "btn btn-success navbar-btn navbar-left" %> <%= form_tag search_notes_path, method: :get, class: 'navbar-form navbar-left', role: 'search' do %> <div class="input-group"> <%= text_field_tag :query, params[:query], class: 'form-control', placeholder: t('placeholders.search_notes') %> <span class="input-group-btn"> <%= button_tag id: 'search', type: 'submit', class: 'btn btn-primary', name: nil do %> <span class="glyphicon glyphicon-search"></span> <% end %> </span> </div> <% end %> <!-- ... --> app/views/layouts/application.erb.html
  • 138. test 'nenhum resultado' do visit '/' fill_in 'query', with: 'abcd' click_on 'search' assert page.has_content? I18n.t('messages.no_notes_found') assert page.has_no_content? notes(:one).title assert page.has_no_content? notes(:three).title end end test/integration/notes_searching_test.rb
  • 139. Rails.application.routes.draw do resources :notes, path: '/' do get 'search' => 'notes#search', on: :collection, as: :search end # ... config/routes.rb
  • 140. class NotesControllerTest < ActionController::TestCase # ... test 'search' do get :search, query: notes(:one).title assert_response :success assert_not_nil assigns(:notes) end test 'search deve buscar somente as notas do usuario atual' do get :search, query: 'N' assert_equal 2, assigns(:notes).count end test 'search deve buscar pelo titulo completo' do get :search, query: notes(:one).title assert_equal notes(:one), assigns(:notes).first end test/controllers/notes_controllers_test.rb (continua)
  • 141. test 'search deve buscar pelo texto completo' do get :search, query: notes(:one).body assert_equal notes(:one), assigns(:notes).first end test 'search deve buscar pelo titulo parcial' do get :search, query: notes(:one).title[0..2] assert assigns(:notes).include? notes(:one) assert assigns(:notes).include? notes(:three) end test 'search deve buscar pelo texto parcial' do get :search, query: notes(:one).body[0..2] assert assigns(:notes).include? notes(:one) assert assigns(:notes).include? notes(:three) end test/controllers/notes_controllers_test.rb (continua)
  • 142. test 'search deve retornar resultados de título e texto em notas diferentes' do get :search, query: 'Busca' assert assigns(:notes).include? notes(:one) assert assigns(:notes).include? notes(:three) end test 'search deve colocar a quantidade de resultados no flash notice' do get :search, query: notes(:one).title assert_equal I18n.t('messages.n_notes_found', count: 1), flash[:notice] end test 'search deve colocar a mensagem de nenhum resultados no flash alert' do get :search, query: 'abcd' assert_equal I18n.t('messages.no_notes_found'), flash[:alert] end test/controllers/notes_controllers_test.rb
  • 143. class NotesController < ApplicationController # ... def search @notes = Note.where(user: current_user) .where(' notes.title LIKE ? OR notes.body LIKE ?', "%#{params[:query]}%", "%#{params[:query]}%") .order(updated_at: :desc) if @notes.count > 0 flash.now[:notice] = I18n.t('messages.n_notes_found', count: @notes.count) else flash.now[:alert] = I18n.t('messages.no_notes_found') end end # ... app/controllers/notes_controller.rb
  • 144. <%= render 'list' %> app/views/notes/index.html.erb
  • 145. <%= render 'list' %> app/views/notes/search.html.erb
  • 146. pt-BR: # ... messages: are_you_sure?: Tem certeza? n_notes_found: one: 1 nota encontrada other: "%{count} notas encontradas" no_notes_found: Nenhuma nota encontrada :-( placeholders: body: Texto search_notes: Pesquisar nas notas # ... config/locales/pt-BR.yml
  • 147. REFERÊNCIAS PERROTTA, Paolo. Metaprogramming Ruby. 1 ed. The Pragmatic Bookshelf, 2010. MATSUMOTO, Yukihiro. Ruby in a Nutshell. 1 ed. O’Reilly, 2001. THOMAS, Dave; FOWLER, Chad; HUNT, Andy. Programming Ruby 1.9 & 2.0: The Pragmatic Programmers Guide. 4 ed. The Pragmatic Bookshelf, 2013. BLACK, David A.. The Well-Grounded Rubyist. 1 ed. Manning, 2009.
  • 148. REFERÊNCIAS About Ruby - https://www.ruby-lang.org/en/about/ The Philosophy of Ruby - http://www.artima. com/intv/rubyP.html RubyConf: History of Ruby - http://blog.nicksieger. com/articles/2006/10/20/rubyconf-history-of-ruby/ Ruby on Rails Guides - http://guides.rubyonrails.org/