Slide 1

Slide 1 text

Rails 5 And its new features by examples

Slide 2

Slide 2 text

Yoshio Teruia

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Estamos contratando! http://plataformatec.workable.com

Slide 6

Slide 6 text

Rails 5 And its new features by examples

Slide 7

Slide 7 text

E o Action Cable?

Slide 8

Slide 8 text

É uma integração do websocket com o Rails que possibilita o desenvolvimento de real-time features. Action Cable

Slide 9

Slide 9 text

goo.gl/qT8DIs

Slide 10

Slide 10 text

goo.gl/Slautj

Slide 11

Slide 11 text

goo.gl/UpwmRD

Slide 12

Slide 12 text

CLI

Slide 13

Slide 13 text

Agora o `rails` pode ser usado para rodar rake tasks na CLI. Comandos do Rails centralizado

Slide 14

Slide 14 text

rails db:migrate rails db:create rails test rails routes

Slide 15

Slide 15 text

Testes

Slide 16

Slide 16 text

Executar os testes ficou mais simples. Test Runner

Slide 17

Slide 17 text

Executar os testes ficou mais simples. Rodar um único teste

Slide 18

Slide 18 text

rails test test/models/user_test.rb:4

Slide 19

Slide 19 text

rails test test/models/user_test.rb:4

Slide 20

Slide 20 text

rails test test/models/user_test.rb:4

Slide 21

Slide 21 text

Não é mais necessário adicionar uma gem. Opção fail fast

Slide 22

Slide 22 text

rails test -f

Slide 23

Slide 23 text

rails test -f

Slide 24

Slide 24 text

A fase de exercise nos testes de integração ficam mais explícitas. Kwarg nos métodos de requisição

Slide 25

Slide 25 text

Rails 4 class UserListTest < ActionDispatch::IntegrationTest test 'returns all users' do … get users_url, nil, { 'X-Custom-Header' => 'Header Value', formats: :json } … end end

Slide 26

Slide 26 text

Rails 4 class UserListTest < ActionDispatch::IntegrationTest test 'returns all users' do … get users_url, nil, { 'X-Custom-Header' => 'Header Value’, formats: :json } … end end

Slide 27

Slide 27 text

Rails 4 class UserListTest < ActionDispatch::IntegrationTest test 'returns all users' do … get users_url, nil, { 'X-Custom-Header' => 'Header Value’, formats: :json } … end end

Slide 28

Slide 28 text

Rails 5 class UserListTest < ActionDispatch::IntegrationTest test 'returns all users' do … get users_url, headers: { 'X-Custom-Header' => 'Header Value’ }, as: :json … end end

Slide 29

Slide 29 text

Rails 5 class UserListTest < ActionDispatch::IntegrationTest test 'returns all users' do … get users_url, headers: { 'X-Custom-Header' => 'Header Value’ }, as: :json … end end

Slide 30

Slide 30 text

Asset Pipeline

Slide 31

Slide 31 text

Os logs ficam mais enxuto no modo de desenvolvimento. Possibilidade de desabilitar os logs dos assets

Slide 32

Slide 32 text

Started GET "/users" for ::1 at 2016-09-23 04:14:49 -0300 ActiveRecord::SchemaMigration Load (0.7ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by UsersController#index as HTML Rendering users/index.html.erb within layouts/application User Load (0.5ms) SELECT "users".* FROM "users" Rendered users/index.html.erb within layouts/application (16.8ms) Completed 200 OK in 366ms (Views: 340.1ms | ActiveRecord: 5.0ms) Started GET "/assets/jquery.self-bd7ddd393353a8d2480a622e80342adf488fb6006d667e8b42e4c0073393abee.js? body=1" for ::1 at 2016-09-23 04:14:50 -0300 Started GET "/assets/turbolinks.self-c5acd7a204f5f25ce7a1d8a0e4d92e28d34c9e2df2c7371cd7af88e147e4ad82.js? body=1" for ::1 at 2016-09-23 04:14:50 -0300 Started GET "/assets/application.self- b89234cf2659d7fedea75bca0b8d231ad7dfc2f3f57fcbaf5f44ed9dc384137b.js?body=1" for ::1 at 2016-09-23 04:14:50 -0300 Started GET "/assets/jquery_ujs.self-784a997f6726036b1993eb2217c9cb558e1cbb801c6da88105588c56f13b466a.js? body=1" for ::1 at 2016-09-23 04:14:50 -0300 Started GET "/assets/ action_cable.self-1641ec3e8ea24ed63601e86efcca7f9266e09f390e82199d56aa7e4bd50e1aa9.js?body=1" for ::1 at 2016-09-23 04:14:50 -0300 Started GET "/assets/application.self- e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css?body=1" for ::1 at 2016-09-23 04:14:50 -0300 Started GET "/assets/cable.self-6e0514260c1aa76eaf252412ce74e63f68819fd19bf740595f592c5ba4c36537.js? body=1" for ::1 at 2016-09-23 04:14:50 -0300

Slide 33

Slide 33 text

Rails 4 A única forma de desabilitar os logs dos assets era instalando uma gem externa.

Slide 34

Slide 34 text

Rails 5 Existe uma configuração para desabilitar os logs dos assets e que vem habilitado por padrão.

Slide 35

Slide 35 text

Rails.application.configure do config.cache_classes = false # Do not eager load code on boot. config.eager_load = false config.assets.debug = true # Suppress logger output for asset requests. config.assets.quiet = true … end

Slide 36

Slide 36 text

Rails.application.configure do config.cache_classes = false # Do not eager load code on boot. config.eager_load = false config.assets.debug = true # Suppress logger output for asset requests. config.assets.quiet = true … end

Slide 37

Slide 37 text

Started GET "/users" for ::1 at 2016-09-23 04:24:36 -0300 ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by UsersController#index as HTML Rendering users/index.html.erb within layouts/application User Load (0.4ms) SELECT "users".* FROM "users" Rendered users/index.html.erb within layouts/application (12.2ms) Completed 200 OK in 315ms (Views: 293.4ms | ActiveRecord: 3.9ms)

Slide 38

Slide 38 text

Funciona no Rails 4.2.7‼ Essa funcionalidade foi portada para o sprocket-rails 3.1.0, ou seja, essa configuração funciona no Rails 4.2.7 sem a necessidade da gem quiet_assets.

Slide 39

Slide 39 text

Active Record

Slide 40

Slide 40 text

Utilizar o UUID como padrão de chave primária

Slide 41

Slide 41 text

Rails 4 Já havia a possibilidade de utilizar o UUID como chave primária mas era um pouco verboso.

Slide 42

Slide 42 text

rails g model user name:string test/models/user_t

Slide 43

Slide 43 text

rails g model user name:string class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.timestamps null: false end end end

Slide 44

Slide 44 text

rails g model user name:string class CreateUsers < ActiveRecord::Migration def change create_table :users, id: :uuid do |t| t.string :name t.timestamps null: false end end end

Slide 45

Slide 45 text

Rails 5 É possível adicionar uma configuração para que todas as tabelas utilizem o UUID como pk.

Slide 46

Slide 46 text

rails g model user name:string test/models/user_t

Slide 47

Slide 47 text

module Rails5 class Application < Rails::Application config.generators do |g| g.orm :active_record, primary_key_type: :uuid end end end

Slide 48

Slide 48 text

module Rails5 class Application < Rails::Application config.generators do |g| g.orm :active_record, primary_key_type: :uuid end end end

Slide 49

Slide 49 text

class CreateUsers < ActiveRecord::Migration def change create_table :users, id: :uuid do |t| t.string :name t.timestamps null: false end end end

Slide 50

Slide 50 text

⚠Alguns métodos como o `.first` e o `.last` não funcionam mais como o esperado.

Slide 51

Slide 51 text

Índice nos erros dos nested attributes

Slide 52

Slide 52 text

Rails 4 Não há uma forma de relacionar os erros com as instâncias da associação.

Slide 53

Slide 53 text

class User < ActiveRecord::Base has_many :addresses validates :name, presence: true accepts_nested_attributes_for :addresses end class Address < ActiveRecord::Base belongs_to :user validates :street, :city, presence: true end

Slide 54

Slide 54 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 55

Slide 55 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 56

Slide 56 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 57

Slide 57 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 58

Slide 58 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 59

Slide 59 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 60

Slide 60 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => {:"addresses.city"=>["can't be blank"]} u.addresses.new u.valid? => false u.errors.messages => { :"addresses.city"=>["can't be blank”], :"addresses.street"=>["can't be blank”] }

Slide 61

Slide 61 text

Rails 5 Um índice pode ser adicionado para cada erro.

Slide 62

Slide 62 text

class Address < ActiveRecord::Base belongs_to :user validates :street, :city, presence: true end class User < ActiveRecord::Base has_many :addresses, index_errors: true validates :name, presence: true accepts_nested_attributes_for :addresses end

Slide 63

Slide 63 text

class Address < ActiveRecord::Base belongs_to :user validates :street, :city, presence: true end class User < ActiveRecord::Base has_many :addresses, index_errors: true validates :name, presence: true accepts_nested_attributes_for :addresses end

Slide 64

Slide 64 text

module Rails5 class Application < Rails::Application … config.active_record.index_nested_attribute_errors = true end end

Slide 65

Slide 65 text

module Rails5 class Application < Rails::Application … config.active_record.index_nested_attribute_errors = true end end

Slide 66

Slide 66 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 67

Slide 67 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 68

Slide 68 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 69

Slide 69 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 70

Slide 70 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 71

Slide 71 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 72

Slide 72 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 73

Slide 73 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 74

Slide 74 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 75

Slide 75 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 76

Slide 76 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 77

Slide 77 text

u = User.new(name: 'João') u.addresses.new(street: 'Rua joãozinho') u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”] } u.addresses.new u.valid? => false u.errors.messages => { :"addresses[0].user"=>["must exist”], :"addresses[0].city"=>["can't be blank”], :"addresses[1].user"=>["must exist”], :"addresses[1].street"=>["can't be blank”], :"addresses[1].city"=>["can't be blank”] }

Slide 78

Slide 78 text

ActiveRelation#or

Slide 79

Slide 79 text

Rails 4 A única forma de fazer o `or` é escrevendo sql.

Slide 80

Slide 80 text

class Order < ActiveRecord::Base enum status: [:posted, :in_transit, :delivered] scope :pending, -> do where( 'status = :posted OR status = :in_transit', { posted: statuses[:posted], in_transit: statuses[:in_transit] } ) end end

Slide 81

Slide 81 text

class Order < ActiveRecord::Base enum status: [:posted, :in_transit, :delivered] scope :pending, -> do where( 'status = :posted OR status = :in_transit', { posted: statuses[:posted], in_transit: statuses[:in_transit] } ) end end

Slide 82

Slide 82 text

Rails 5 Tem o ActiveRelation#or.

Slide 83

Slide 83 text

class Order < ApplicationRecord enum status: [:posted, :in_transit, :delivered] scope :pending, -> { posted.or(in_transit) } end

Slide 84

Slide 84 text

class Order < ApplicationRecord enum status: [:posted, :in_transit, :delivered] scope :pending, -> { posted.or(in_transit) } end

Slide 85

Slide 85 text

Left outer join

Slide 86

Slide 86 text

Rails 4 A única forma de fazer o `left join` é escrevendo sql.

Slide 87

Slide 87 text

class User < ApplicationRecord User.joins('LEFT JOIN posts ON posts.user_id = users.id') end

Slide 88

Slide 88 text

class User < ApplicationRecord User.joins('LEFT JOIN posts ON posts.user_id = users.id') end

Slide 89

Slide 89 text

Rails 5 Tem um método exclusivo para fazer left joins.

Slide 90

Slide 90 text

class User < ApplicationRecord has_many :posts User.left_outer_joins(:posts) end

Slide 91

Slide 91 text

Uma forma prática de identificar os atributos que estão sendo utilizadas na view. #accessed_fields

Slide 92

Slide 92 text

class UsersController < ApplicationController def index @users = User.all end end

Slide 93

Slide 93 text

Nome Idade <% @users.each do |user| %> <%= user.name %> <%= user.age %> <% end %>

Slide 94

Slide 94 text

class UsersController < ApplicationController after_action :print_accessed_fields def index @users = User.all end private def print_accessed_fields p @users.first.accessed_fields end end

Slide 95

Slide 95 text

class UsersController < ApplicationController after_action :print_accessed_fields def index @users = User.all end private def print_accessed_fields p @users.first.accessed_fields end end

Slide 96

Slide 96 text

Started GET "/users" for ::1 at 2016-09-17 23:47:25 -0300 ActiveRecord::SchemaMigration Load (0.7ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by UsersController#index as HTML Rendering users/index.html.erb within layouts/ application User Load (0.4ms) SELECT "users".* FROM "users" Rendered users/index.html.erb within layouts/ application (20.5ms) ["name", "age"] Completed 200 OK in 395ms (Views: 361.8ms | ActiveRecord: 4.2ms)

Slide 97

Slide 97 text

Started GET "/users" for ::1 at 2016-09-17 23:47:25 -0300 ActiveRecord::SchemaMigration Load (0.7ms) SELECT "schema_migrations".* FROM "schema_migrations" Processing by UsersController#index as HTML Rendering users/index.html.erb within layouts/ application User Load (0.4ms) SELECT "users".* FROM "users" Rendered users/index.html.erb within layouts/ application (20.5ms) ["name", "age"] Completed 200 OK in 395ms (Views: 361.8ms | ActiveRecord: 4.2ms)

Slide 98

Slide 98 text

class UsersController < ApplicationController def index @users = User.select(:name, :age) end end

Slide 99

Slide 99 text

⚠Deve ser usado apenas no ambiente de desenvolvimento.

Slide 100

Slide 100 text

⚠Deve ser usado apenas no ambiente de desenvolvimento. ⚠Apenas os campos que foram lidos são identificados, então cuidado com os que estão dentro de condicionais.

Slide 101

Slide 101 text

As queries ficaram mais legíveis nos logs. Colourful query logs

Slide 102

Slide 102 text

Rails 4 User Load (2.8ms) SELECT "users"."id", "users"."gender", "users"."name" FROM "users" Rails 5 User Load (11.1ms) SELECT "users"."id", "users"."gende "users"."name" FROM "users"

Slide 103

Slide 103 text

Rails 4 User Load (2.8ms) SELECT "users"."id", "users"."gender", "users"."name" FROM "users" Rails 5 User Load (11.1ms) SELECT "users"."id", "users"."gender", "users"."name" FROM "users"

Slide 104

Slide 104 text

Rails 5 Cada query tem uma cor.

Slide 105

Slide 105 text

User Load (11.1ms) SELECT "users"."id", "users"."gender", "users"."name" FROM "users" Select

Slide 106

Slide 106 text

SQL (11.3ms) INSERT INTO "users" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" Insert

Slide 107

Slide 107 text

Update SQL (0.4ms) UPDATE "users" SET "name" = $1, "updated_at" = $2 WHERE "users"."id" = $3

Slide 108

Slide 108 text

Delete SQL (6.6ms) DELETE FROM "users" WHERE "users"."id" = $1

Slide 109

Slide 109 text

Attributes API Os atributos do banco de dados podem ser convertidos para um objeto do Ruby.

Slide 110

Slide 110 text

irb(main):002:0> User.first.address User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]] => {"city"=>"São Paulo", "street"=>"Rua marília"}

Slide 111

Slide 111 text

irb(main):002:0> User.first.address User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]] => {"city"=>"São Paulo", "street"=>"Rua marília"}

Slide 112

Slide 112 text

class Address def initialize(street:, city:) @street = street @city = city end … end

Slide 113

Slide 113 text

class AddressType < ActiveRecord::Type::Value def serialize(value) ::ActiveSupport::JSON.encode(value) end def deserialize(value) if value address_attrs = JSON.parse(value) ::Address.new( street: address_attrs[‘street'], city: address_attrs[‘city'] ) end end end

Slide 114

Slide 114 text

class User < ApplicationRecord attribute :address, AddressType.new end

Slide 115

Slide 115 text

User.first.address User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]] => #

Slide 116

Slide 116 text

User.first.address User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]] => #

Slide 117

Slide 117 text

Referências

Slide 118

Slide 118 text

goo.gl/oRfJzd

Slide 119

Slide 119 text

goo.gl/mS7e8M

Slide 120

Slide 120 text

goo.gl/J18on9

Slide 121

Slide 121 text

http://blog.bigbinary.com/

Slide 122

Slide 122 text

Obrigado