Tips and Treats for New Developers

Tips and Treats for New Developers

0f9c9bbecc4067b9bce445cb11ed5d53?s=128

David Padilla

April 25, 2017
Tweet

Transcript

  1. Tips and tricks for new developers

  2. David Padilla @dabit

  3. @micheladaio

  4. None
  5. None
  6. None
  7. Tips and tricks for new developers

  8. A long time ago in a galaxy far, far away

  9. 2009

  10. None
  11. None
  12. Solving the Riddle of Search: Using Sphinx with Rails The

    more complex your search queries becomes, the uglier your SQL statements get, even with ActiveRecord's helpful magic. Reclaim some clarity in your code by using the Sphinx search engine, a powerful tool that lets you search across your models in fast and complex ways.
  13. Webrat: Rails Acceptance Testing Evolved Webrat, a Ruby DSL for

    interacting with Web applications, helps you write expressive, maintainable acceptance tests while sidestepping the issues traditionally associated with in-browser approaches like Selenium and Watir. We'll look at how you can use Webrat to develop a robust acceptance test suite to ensure your app stays working as you refactor mercilessly.
  14. Smacking Git Around - Advanced Git Tricks Much of the

    Ruby and Rails community is now using Git, but there are a number of fun things that are a bit more difficult to get the hang of that are incredibly helpful to know when using Git. This session will go over some advanced Git usage for the casual or intermediate Git user.
  15. None
  16. Git is an open source, distributed version control system implemented

    as a directed acyclic graph of commit objects pointing to snapshots of content, with all data saved in it's own custom content addressable filesystem by the SHA-1 checksum hash of each objects data. Branches are simply pointers into this directed graph of commits, identifying entry points that designate the latest work on that branch, allowing Git to traverse the pointers, determining a coherent history. This makes branching cheap and easy, and merging simple, encouraging non-linear development styles and frictionless context switching while facilitating distributed development, cryptographic integrity and late decision making. Nearly all commands run locally with no network latency overhead and it is implemented mainly in C, making it incredibly fast and efficient even for very large projects. There are several large open source projects using it, not to mention the 100,000 open source repositories maintained by the 80,000 developers on GitHub alone. https://gist.github.com/schacon/111475
  17. Mongrel 8080 8082 8083 8084 8085 8081

  18. None
  19. N G I N X

  20. H A P R O X Y

  21. we need talks for beginners

  22. This talk is for beginners

  23. let’s begin

  24. config files

  25. rails new railsconf

  26. None
  27. config/database.yml $ cp config/database.yml config/database.sample.yml $ echo "config/database.yml" >> .gitignore

  28. config/secrets.yml $ cp config/secrets.yml config/secrets.sample.yml $ echo "config/secrets.yml" >> .gitignore

  29. config/payments.yml development: stripe_key: ABCXYZ test: stripe_key: ABCXYZ production: stripe_key: <%=

    ENV['STRIPE_KEY'] %>
  30. config/payments.yml # Rails.application.config_for(:payments)['stripe_key'] => "ABCXYZ"

  31. config/that_api.yml # Rails.application.config_for(:that_api)

  32. wherever there’s a string or an integer

  33. you probably want a constant or a setting

  34. if registered_at < 30.minutes.ago # ... end

  35. use a constant REGISTRATION_LIMIT = 30 if registered_at < REGISTRATION_LIMIT.minutes.ago

    # ... end
  36. use a configuration file if registered_at < Rails.application.config_for(:registration)['limit'].minutes.ago # ...

    end
  37. Database

  38. Do not ignore db/schema.rb

  39. You should always be able to restore db state with:

    $ rails db:setup or $ rails db:reset
  40. Don’t change data in db migrations

  41. Be careful when you change data in db migrations

  42. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    Company.find(:all).each do |company| name, zip_code = company.code.split('-') company.update_attributes(name: name, zip_code: zip_code) end remove_column :companies, :code end
  43. not future proof

  44. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    Company.find(:all).each do |company| name, zip_code = company.code.split('-') company.update_attributes(name: name, zip_code: zip_code) end remove_column :companies, :code end
  45. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    Company.find(:all).each do |company| name, zip_code = company.code.split('-') company.update_attributes(name: name, zip_code: zip_code) end remove_column :companies, :code end
  46. Longer deploy times

  47. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    Company.find(:all).each do |company| name, zip_code = company.code.split('-') company.update_attributes(name: name, zip_code: zip_code) end remove_column :companies, :code end
  48. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    Company.find(:all).each do |company| name, zip_code = company.code.split('-') company.update_attributes(name: name, zip_code: zip_code) end remove_column :companies, :code end
  49. Risk down time

  50. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    Company.find(:all).each do |company| name, zip_code = company.code.split('-') company.update_attributes(name: name, zip_code: zip_code) end remove_column :companies, :code end
  51. Prefer SQL over Ruby

  52. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    execute <<EOS UPDATE companies SET name = SPLIT_PART(code, '-', 1), zip_code = SPLIT_PART(code, '-', 2) EOS remove_column :companies, :code end
  53. def self.up add_column :companies, :zip_code, :integer add_column :companies, :name, :string

    execute <<EOS UPDATE companies SET name = SPLIT_PART(code, '-', 1), zip_code = SPLIT_PART(code, '-', 2) EOS remove_column :companies, :code end
  54. gem 'rails-data-migrations'

  55. $ rake -T data rake data:migrate # Apply pending data

    migrations rake data:migrate:down # Revert single data migration using VERSION rake data:migrate:pending # List pending migrations rake data:migrate:skip # Skip single data migration using VERSION rake data:migrate:up # Apply single data migration using VERSION
  56. class AddActiveToAuthors < ActiveRecord::Migration[5.1] def change add_column :authors, :active, :boolean

    Author.update_all(active: true) end end
  57. class AddActiveToAuthors < ActiveRecord::Migration[5.1] def change add_column :authors, :active, :boolean

    Author.update_all(active: true) end end
  58. $ rails g data_migration set_authors_active create db/data_migrations/20170420185616_set_authors_active.rb

  59. $ rails g data_migration set_authors_active create db/data_migrations/20170420185616_set_authors_active.rb

  60. class SetAuthorsActive < ActiveRecord::DataMigration def up Author.update_all(active: true) end end

  61. class AddActiveToAuthors < ActiveRecord::Migration[5.1] def change add_column :authors, :active, :boolean

    Author.update_all(active: true) end end
  62. None
  63. None
  64. Background jobs

  65. Do not pass objects to background jobs

  66. class BigBadJob def perform(order, user) order.assign_to(user) # ... end end

  67. class BigBadJob def perform(order_id, user_id) order = Order.find(order_id) user =

    User.find(user_id) order.assign_to(user) # ... end end
  68. Deliver your email later

  69. # # Rails 4 # class OrderMailer < ActionMailer::Base def

    confirmation(order, user) # ... end end
  70. # # Rails 4 # class OrderMailer < ActionMailer::Base def

    confirmation(order_id, user_id) @order = Order.find(order_id) @user = User.find(user_id) # ... end end
  71. # # Sidekiq or Delayed Job # OrderMailer.delay.confirmation(order_id, user_id)

  72. # # Active Job # OrderMailer.confirmation(order_id, user_id).deliver_later

  73. Be RESTful ALL THE TIME

  74. REST

  75. show new create edit update delete index

  76. class ProductsController < ApplicationController def deactivate # ... end end

    # # config/routes.rb # Rails.application.routes.draw do resources :products do get :deactivate end end
  77. class ProductStatesController < ApplicationController def update # ... end end

    # # config/routes.rb # Rails.application.routes.draw do resources :products do resource :status, only: :update, controller: :product_statuses end end
  78. class CartsController < ApplicationController def apply_coupon # ... end end

    # # config/routes.rb # Rails.application.routes.draw do resource :cart do get :apply_coupon end end
  79. class DiscountsController < ApplicationController def create # ... end end

    # # config/routes.rb # Rails.application.routes.draw do resource :cart do resource :discount, only: :create, controller: :cart_discounts end end
  80. class ContactsController < ApplicationController def search # ... end end

    # # config/routes.rb # Rails.application.routes.draw do resources :contacts do get :search end end
  81. class Contacts::SearchesController < ApplicationsController def show # ... end end

    # # config/routes.rb # Rails.application.routes.draw do resources :contacts namespace :contacts do resource :search, only: :show end end
  82. show new create edit update delete index

  83. gems you must

  84. rubocop

  85. rubocop --auto-gen-config

  86. .rubocop_todo.yml

  87. # # .rubocop.yml # inherit_from: .rubocop_todo.yml

  88. None
  89. $ echo "inherit_from: .rubocop_todo.yml" >> .rubocop.yml

  90. None
  91. # # .rubocop.yml # Metrics/LineLength: Max: 130

  92. # # .rubocop_todo.yml # Style/Documentation: Exclude: - 'spec/**/*' - 'test/**/*'

    - 'app/controllers/application_controller.rb' - 'app/helpers/application_helper.rb' - 'app/mailers/application_mailer.rb' - 'app/models/application_record.rb' - 'config/application.rb'
  93. # # .rubocop.yml # Style/Documentation: Enabled: false

  94. # # .rubocop.yml # Metrics/MethodLength: Max: 10

  95. .git/hooks/pre-push #!/bin/bash rubocop

  96. annotate

  97. None
  98. None
  99. # == Schema Information # # Table name: users #

    # id :integer not null, primary key # name :string # username :string # password :string # active :boolean # created_at :datetime not null # updated_at :datetime not null # class User < ApplicationRecord end
  100. $ annotate --routes

  101. # == Route Map # # Prefix Verb URI Pattern

    Controller#Action # contacts GET /contacts(.:format) contacts#index # POST /contacts(.:format) contacts#create # new_contact GET /contacts/new(.:format) contacts#new # edit_contact GET /contacts/:id/edit(.:format) contacts#edit # contact GET /contacts/:id(.:format) contacts#show # PATCH /contacts/:id(.:format) contacts#update # PUT /contacts/:id(.:format) contacts#update # DELETE /contacts/:id(.:format) contacts#destroy # contacts_search GET /contacts/search(.:format) contacts/searches#show # cart_discount POST /cart/discount(.:format) cart_discounts#create # new_cart GET /cart/new(.:format) carts#new # edit_cart GET /cart/edit(.:format) carts#edit # cart GET /cart(.:format) carts#show # PATCH /cart(.:format) carts#update # PUT /cart(.:format) carts#update # DELETE /cart(.:format) carts#destroy # POST /cart(.:format) carts#create # product_status PATCH /products/:product_id/status(.:format) product_statuses#update # PUT /products/:product_id/status(.:format) product_statuses#update # products GET /products(.:format) products#index # POST /products(.:format) products#create # new_product GET /products/new(.:format) products#new # edit_product GET /products/:id/edit(.:format) products#edit # product GET /products/:id(.:format) products#show # PATCH /products/:id(.:format) products#update # PUT /products/:id(.:format) products#update # DELETE /products/:id(.:format) products#destroy # Rails.application.routes.draw do resources :contacts namespace :contacts do resource :search, only: :show end resource :cart do resource :discount, only: :create, controller: :cart_discounts end resources :products do resource :status, only: :update, controller: :product_statuses end end
  102. bullet

  103. detects n+1 problem as it happens

  104. # == Schema Information # # Table name: books #

    # id :integer not null, primary key # title :string # description :text # author_id :integer # created_at :datetime not null # updated_at :datetime not null # class Book < ApplicationRecord belongs_to :author has_many :comments end
  105. # == Schema Information # # Table name: authors #

    # id :integer not null, primary key # name :string # created_at :datetime not null # updated_at :datetime not null # class Author < ApplicationRecord end
  106. # == Schema Information # # Table name: comments #

    # id :integer not null, primary key # book_id :integer # user_id :integer # comment :text # created_at :datetime not null # updated_at :datetime not null # class Comment < ApplicationRecord belongs_to :user belongs_to :book end
  107. None
  108. class BooksController < ApplicationController def index @books = Book.all end

    end
  109. None
  110. Started GET "/books" for 127.0.0.1 at 2017-04-18 13:33:52 -0700 Processing

    by BooksController#index as HTML Rendering books/index.html.erb within layouts/application Book Load (0.3ms) SELECT "books".* FROM "books" Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? L Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? L Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM CACHE Author Load (0.0ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM CACHE Author Load (0.0ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? L Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM Rendered books/index.html.erb within layouts/application (19.0ms) Completed 200 OK in 30ms (Views: 26.4ms | ActiveRecord: 1.5ms)
  111. Started GET "/books" for 127.0.0.1 at 2017-04-18 13:33:52 -0700 Processing

    by BooksController#index as HTML Rendering books/index.html.erb within layouts/application Book Load (0.3ms) SELECT "books".* FROM "books" Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? L Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? L Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM CACHE Author Load (0.0ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM CACHE Author Load (0.0ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" = ? L Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i CACHE User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIM Rendered books/index.html.erb within layouts/application (19.0ms) Completed 200 OK in 30ms (Views: 26.4ms | ActiveRecord: 1.5ms)
  112. gem 'bullet' config.after_initialize do Bullet.enable = true Bullet.console = true

    Bullet.rails_logger = true Bullet.add_footer = true end
  113. None
  114. None
  115. class BooksController < ApplicationController def index @books = Book.includes(:author, comments:

    :user) end end
  116. Started GET "/books" for 127.0.0.1 at 2017-04-18 13:42:06 -0700 Processing

    by BooksController#index as HTML Rendering books/index.html.erb within layouts/application Book Load (0.1ms) SELECT "books".* FROM "books" Author Load (0.2ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (1, Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 Rendered books/index.html.erb within layouts/application (61.6ms) Completed 200 OK in 92ms (Views: 73.1ms | ActiveRecord: 3.5ms)
  117. Started GET "/books" for 127.0.0.1 at 2017-04-18 13:42:06 -0700 Processing

    by BooksController#index as HTML Rendering books/index.html.erb within layouts/application Book Load (0.1ms) SELECT "books".* FROM "books" Author Load (0.2ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (1, Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 Rendered books/index.html.erb within layouts/application (61.6ms) Completed 200 OK in 92ms (Views: 73.1ms | ActiveRecord: 3.5ms)
  118. oink

  119. detect memory leaks before its too late

  120. Gemfile gem 'oink'

  121. config/initializers/oink.rb Rails.application.middleware.use(Oink::Middleware, :logger => Rails.logger)

  122. Started GET "/books" for 127.0.0.1 at 2017-04-18 13:46:08 -0700 (0.1ms)

    SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY " Processing by BooksController#index as HTML Rendering books/index.html.erb within layouts/application Book Load (0.2ms) SELECT "books".* FROM "books" Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (1, Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 Rendered books/index.html.erb within layouts/application (50.1ms) Completed 200 OK in 116ms (Views: 94.4ms | ActiveRecord: 2.6ms) Oink Action: books#index Memory usage: 2596436 | PID: 31331 Instantiation Breakdown: Total: 14 | Book: 5 | Comment: 5 | Author: 3 | User: 1 Oink Log Entry Complete
  123. Started GET "/books" for 127.0.0.1 at 2017-04-18 13:46:08 -0700 (0.1ms)

    SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY " Processing by BooksController#index as HTML Rendering books/index.html.erb within layouts/application Book Load (0.2ms) SELECT "books".* FROM "books" Author Load (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors"."id" IN (1, Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_i User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 Rendered books/index.html.erb within layouts/application (50.1ms) Completed 200 OK in 116ms (Views: 94.4ms | ActiveRecord: 2.6ms) Oink Action: books#index Memory usage: 2596436 | PID: 31331 Instantiation Breakdown: Total: 14 | Book: 5 | Comment: 5 | Author: 3 | User: 1 Oink Log Entry Complete
  124. That’s all!

  125. THANK YOU David Padilla @dabit david@michelada.io