os piores códigos ruby já vistos - tdc florianópolis 2016

161
@Prodis Os piores códigos Ruby já vistos TDC Florianópolis 2016 @Prodis

Upload: fernando-hamasaki-de-amorim

Post on 17-Jan-2017

310 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Os piores códigos Ruby já vistos

TDC Florianópolis 2016

@Prodis

Page 2: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Fernando Hamasaki de Amorim

• Desenvolvedor Ruby desde 2009

• Trabalho na Locaweb, a maior empresa de hospedagem do Brasil

• Desenvolvo aplicações web desde 2000

• .NET, Java, JavaScript, PHP, ASP.

Page 3: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Fernando Hamasaki de Amorim

Page 4: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Fernando Hamasaki de Amorim

Page 5: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

WOP

Page 6: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

WOP Workaround Oriented Programming

Page 7: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

POG Programação Orientada a Gambiarras

Page 8: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

POGPOG é uma técnica avançada de desenvolvimento de

software que tem como base a utilização de todo tipo

de gambiarra, remendo e tudo de pior que um código

pode ter.

POG se baseia em conceitos como duplicação de

código, fluxos redundantes, tarefas desnecessáriase reinvenção de rodas.

Page 9: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

"The names have been changed to protect the innocent."

Os piores códigos Ruby já vistos

Page 10: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Um primeiro exemplo de POG:mascarando números de

cartão de crédito

Page 11: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

describe '#mask_credit_card' do let(:number) { '5464193830403276' }

it 'returns masked credit card number' do masked = mask_credit_card(number) expect(masked).to eq '************3276' end end

Page 12: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def mask_credit_card(number) limit = number.length - 4 “#{'*' * limit}#{number[limit..-1]}” end

Page 13: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def mask_credit_card_pog(number) (number.length - 4).times do |i| number[i] = '*' end

number end

Page 14: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

describe '#mask_credit_card_pog' do let(:number) { '5464193830403276' }

it 'returns masked credit card number' do masked = mask_credit_card_pog(number)

expect(masked).to eq '************3276' end

it 'does not change number variable' do mask_credit_card_pog(number) expect(number).to eq '5464193830403276' end end

Page 15: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

#mask_credit_card_pog returns masked credit card number does not change number variable (FAILED - 1)

Failures:

1) #mask_credit_card_pog does not change number variable Failure/Error: expect(number).to eq '5464193830403276'

expected: "5464193830403276" got: "************3276"

(compared using ==) # ./spec/mask_credit_card/mask_credit_card_spec.rb:23:in `block (2 levels) in <top (required)>'

Finished in 0.0202 seconds (files took 0.17324 seconds to load) 2 examples, 1 failure

Page 16: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def mask_credit_card_pog(number) (number.length - 4).times do |i| number[i] = '*' end

number end

Page 17: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Fluxos obscuros

Page 18: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 19: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 20: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 21: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 22: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 23: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 24: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

if site.blank? flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return else domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

if domain.save flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path return else flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end end end end

Page 25: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como corrigir isso?

Page 26: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

unless site flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return end

domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

unless domain.save flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end

flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path end end

Page 27: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Support::DomainsController < Support::BaseController def create site = Site.find_by(name: params[:domain][:site])

unless site flash[:alert] = I18n.t('support.domains.errors.without_site') redirect_to new_support_domain_path return end

domain = site.domains.build(address: params[:domain][:address]) domain.support_create = true

unless domain.save flash[:alert] = I18n.t('support.domains.errors.invalid') redirect_to new_support_domain_path return end

flash[:success] = I18n.t('support.domains.success') redirect_to support_domains_path end end

Page 28: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

No Ruby way

Page 29: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGatewayWOP def initialize(options = {}) raise ArgumentError if options[:email].to_s.strip.empty? raise ArgumentError if options[:token].to_s.strip.empty?

@options = options end

def email @options[:email] end

def token @options[:token] end

def identification @options[:identification] end

def billing_type @options[:billing_type] end

def billing_status @options[:billing_status] end

def message @options[:message] end

def exists? @options[:message] =~ /Account found/ end

def is_active? @options[:billing_status] == 'active' end

def is_seller? @options[:billing_type] == 'seller' || @options[:billing_type] == 'company' end

# other methods omitted end

Page 30: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGatewayWOP def initialize(options = {}) raise ArgumentError if options[:email].to_s.strip.empty? raise ArgumentError if options[:token].to_s.strip.empty?

@options = options end

def email @options[:email] end

def token @options[:token] end

def identification @options[:identification] end

def billing_type @options[:billing_type] end

# other methods omitted end

Page 31: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGatewayWOP def initialize(options = {}) raise ArgumentError if options[:email].to_s.strip.empty? raise ArgumentError if options[:token].to_s.strip.empty?

@options = options end

def email @options[:email] end

def token @options[:token] end

def identification @options[:identification] end

def billing_type @options[:billing_type] end

# other methods omitted end

Page 32: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGateway attr_reader :email, :token

def initialize(options = {}) @email = options.fetch(:email) @token = options.fetch(:token) @options = options end

def identification options[:identification] end

def billing_type options[:billing_type] end

# other public methods omitted

private

attr_reader :options

# other methods omitted end

Page 33: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGateway attr_reader :email, :token

def initialize(options = {}) @email = options.fetch(:email) @token = options.fetch(:token) @options = options end

def identification options[:identification] end

def billing_type options[:billing_type] end

# other public methods omitted

private

attr_reader :options

# other methods omitted end

Page 34: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGateway attr_reader :email, :token

def initialize(options = {}) @email = options.fetch(:email) @token = options.fetch(:token) @options = options end

[:identification, :billing_type, :billing_status, :message].each do |method| define_method(method) do options[method] end end

# other public methods omitted

private

attr_reader :options

# other methods omitted end

Page 35: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGateway attr_reader :email, :token

# constructor omitted

[:identification, :billing_type, :billing_status, :message].each do |method| define_method(method) do options[method] end end

def exists? message =~ /Account found/ end

def is_active? billing_status == 'active' end

def is_seller? billing_type == 'seller' || billing_type == 'company' end

private

attr_reader :options

# other methods omitted end

Page 36: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGateway attr_reader :email, :token

# other methods omitted

def exists? message =~ /Account found/ end

def active? billing_status == 'active' end

def seller? billing_type == 'seller' || billing_type == 'company' end

private

attr_reader :options

# other methods omitted end

Page 37: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGatewayWOP def initialize(options = {}) raise ArgumentError if options[:email].to_s.strip.empty? raise ArgumentError if options[:token].to_s.strip.empty?

@options = options end

def email @options[:email] end

def token @options[:token] end

def identification @options[:identification] end

def billing_type @options[:billing_type] end

def billing_status @options[:billing_status] end

def message @options[:message] end

def exists? @options[:message] =~ /Account found/ end

def is_active? @options[:billing_status] == 'active' end

def is_seller? @options[:billing_type] == 'seller' || @options[:billing_type] == 'company' end

# other methods omitted end

Page 38: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class PaymentGateway attr_reader :email, :token

def initialize(options = {}) @email = options.fetch(:email) @token = options.fetch(:token) @options = options end

[:identification, :billing_type, :billing_status, :message].each do |method| define_method(method) do options[method] end end

def exists? message =~ /Account found/ end

def active? billing_status == 'active' end

def seller? billing_type == 'seller' || billing_type == 'company' end

private

attr_reader :options

# other methods end

Page 39: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Problemas de nomenclatura

Page 40: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter def import(img_element, row_number, position) return if img_element.blank? || img_element['src'].blank? create_image_widget(img_element, row_number, position) end

def import! @page.widgets.where(kind: 'text').each do |widget| content = Nokogiri::HTML(widget.content, nil, 'UTF-8') next unless has_internal_image?(content)

images = content.css('img').select do |image| internal_image?(image) end

images.each { |image| download_and_change_image_src(image) } widget.update_attribute(:content, content.inner_html) end end

private

def kind 'image' end

def create_image_widget(img_element, row_number, position) widget = create(row_number: row_number, position: position, remote_image_url: img_element['src']) source = (AppConfig.assets_host + widget.image.url) widget.content = @template_adapter.render_widget_content('image', alt: '', src: source) widget.save!

widget end

# Create widget_image to Text Widget def create_widget_image(url) widget_image = WidgetImage.new remote_image_url: url widget_image.site_id = @page.site.id widget_image.save!

widget_image end

# other methods omitted end

Page 41: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter def import(img_element, row_number, position) return if img_element.blank? || img_element['src'].blank? create_image_widget(img_element, row_number, position) end

def import! @page.widgets.where(kind: 'text').each do |widget| content = Nokogiri::HTML(widget.content, nil, 'UTF-8') next unless has_internal_image?(content)

images = content.css('img').select do |image| internal_image?(image) end

images.each { |image| download_and_change_image_src(image) } widget.update_attribute(:content, content.inner_html) end end

private

def kind 'image' end

# other methods omitted end

Page 42: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter def import(img_element, row_number, position) return if img_element.blank? || img_element['src'].blank? create_image_widget(img_element, row_number, position) end

def import! @page.widgets.where(kind: 'text').each do |widget| content = Nokogiri::HTML(widget.content, nil, 'UTF-8') next unless has_internal_image?(content)

images = content.css('img').select do |image| internal_image?(image) end

images.each { |image| download_and_change_image_src(image) } widget.update_attribute(:content, content.inner_html) end end

private

def kind 'image' end

# other methods omitted end

Page 43: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter def import(img_element, row_number, position) return if img_element.blank? || img_element['src'].blank? create_image_widget(img_element, row_number, position) end

def import_from_text_widget @page.widgets.where(kind: 'text').each do |widget| content = Nokogiri::HTML(widget.content, nil, 'UTF-8') next unless has_internal_image?(content)

images = content.css('img').select do |image| internal_image?(image) end

images.each { |image| download_and_change_image_src(image) } widget.update_attribute(:content, content.inner_html) end end

private

def kind 'image' end

# other methods omitted end

Page 44: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter # other public methods omitted

private

def create_image_widget(img_element, row_number, position) widget = create(row_number: row_number, position: position, remote_image_url: img_element['src']) source = (AppConfig.assets_host + widget.image.url) widget.content = @template_adapter.render_widget_content('image', alt: '', src: source) widget.save!

widget end

# Create image widget to text widget def create_widget_image(url) widget_image = WidgetImage.new remote_image_url: url widget_image.site_id = @page.site.id widget_image.save!

widget_image end

# other methods omitted end

Page 45: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter # other public methods omitted

private

def create_image_widget(img_element, row_number, position) widget = create(row_number: row_number, position: position, remote_image_url: img_element['src']) source = (AppConfig.assets_host + widget.image.url) widget.content = @template_adapter.render_widget_content('image', alt: '', src: source) widget.save!

widget end

# Create image widget to text widget def create_widget_image(url) widget_image = WidgetImage.new remote_image_url: url widget_image.site_id = @page.site.id widget_image.save!

widget_image end

# other methods omitted end

Page 46: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ImageWidgetImporter < WidgetImporter # other public methods omitted

private

def create_image_widget(img_element, row_number, position) widget = create(row_number: row_number, position: position, remote_image_url: img_element['src']) source = (AppConfig.assets_host + widget.image.url) widget.content = @template_adapter.render_widget_content('image', alt: '', src: source) widget.save!

widget end

def create_image_widget_to_text_widget(url) widget_image = WidgetImage.new remote_image_url: url widget_image.site_id = @page.site.id widget_image.save!

widget_image end

# other methods omitted end

Page 47: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Orientação a Objetos

Page 48: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Orientação a Objetos Herança com propósito

de reuso de código

Page 49: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Installation::FromFeed < Installation::FromBase def install(args) # implementation omitted end end

class Installation::FromHosting < Installation::FromBase def install(args) # implementation omitted end end

class Installation::FromMigration < Installation::FromBase def install(args) # implementation omitted end end

Page 50: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Installation::FromFeed < Installation::FromBase def install(args) # implementation omitted end end

class Installation::FromHosting < Installation::FromBase def install(args) # implementation omitted end end

class Installation::FromMigration < Installation::FromBase def install(args) # implementation omitted end end

Page 51: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Installation::FromBase include Rails::LabeledLog::Logging

attr_writer :customers_api, :installer, :mailer

def install(args) raise NotImplementedError end

def customers_api @customers_api ||= CustomersApi.new end

def installer @installer ||= Installation::Installer.new end

def mailer @mailer ||= Installation::Mailer.new end end

Page 52: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Installation::FromBase include Rails::LabeledLog::Logging

attr_writer :customers_api, :installer, :mailer

def install(args) raise NotImplementedError end

def customers_api @customers_api ||= CustomersApi.new end

def installer @installer ||= Installation::Installer.new end

def mailer @mailer ||= Installation::Mailer.new end end

Page 53: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Installation::FromFeed < Installation::FromBase def install(args) # implementation omitted end end

class Installation::FromHosting < Installation::FromBase def install(args) # implementation omitted end end

class Installation::FromMigration < Installation::FromBase def install(args) # implementation omitted end end

Page 54: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como corrigir isso?

Page 55: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

module Installation::Infra include Rails::LabeledLog::Logging

attr_writer :customers_api, :installer, :mailer

def customers_api @customers_api ||= CustomersApi.new end

def installer @installer ||= Provisioner::Installation::Installer.new end

def mailer @mailer ||= Provisioner::Installation::Mailer.new end end

Page 56: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

module Installation::Infra include Rails::LabeledLog::Logging

attr_writer :customers_api, :installer, :mailer

def customers_api @customers_api ||= CustomersApi.new end

def installer @installer ||= Provisioner::Installation::Installer.new end

def mailer @mailer ||= Provisioner::Installation::Mailer.new end end

Page 57: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class Installation::FromFeed include Installation::Infra

def install(args) # implementation omitted end end

class Installation::FromHosting include Installation::Infra

def install(args) # implementation omitted end end

class Installation::FromMigration include Installation::Infra

def install(args) # implementation omitted end end

Page 58: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Orientação a Objetos Equívoco de herança

Page 59: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

DNS Uma rápida introdução

Page 60: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Page 61: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class WsDns attr_reader :host, :user, :timeout

def initialize(args) @host = args[:host] @user = args[:user] @timeout = args.fetch(:timeout, 5) end

def create_entry(options) # implementation omitted end

def delete_entry(options) # implementation omitted end

def get_entry(options) # implementation omitted end

def has_entry?(options) # implementation omitted end

# other methods to DNS zone end

Page 62: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class CnameWsDns attr_reader :ws_dns, :zone, :content

def initialize(options) @ws_dns = WsDns.new(options) @zone = options[:zone] @content = options.fetch(:content, zone) end

def create_entry(subdomain) ws_dns.create_entry(type: type, content: content, name: subdomain, zone: zone) end

def delete_entry(subdomain) ws_dns.delete_entry(type: type, content: content, name: subdomain, zone: zone) end

def has_entry?(subdomain) ws_dns.has_entry?(type: type, name: subdomain, zone: zone) end

protected

def type 'CNAME' end end

Page 63: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class CnameWsDns attr_reader :ws_dns, :zone, :content

def initialize(options) @ws_dns = WsDns.new(options) @zone = options[:zone] @content = options.fetch(:content, zone) end

def create_entry(subdomain) ws_dns.create_entry(type: type, content: content, name: subdomain, zone: zone) end

def delete_entry(subdomain) ws_dns.delete_entry(type: type, content: content, name: subdomain, zone: zone) end

def has_entry?(subdomain) ws_dns.has_entry?(type: type, name: subdomain, zone: zone) end

protected

def type 'CNAME' end end

Page 64: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class CnameWsDns attr_reader :ws_dns, :zone, :content

def initialize(options) @ws_dns = WsDns.new(options) @zone = options[:zone] @content = options.fetch(:content, zone) end

def create_entry(subdomain) ws_dns.create_entry(type: type, content: content, name: subdomain, zone: zone) end

def delete_entry(subdomain) ws_dns.delete_entry(type: type, content: content, name: subdomain, zone: zone) end

def has_entry?(subdomain) ws_dns.has_entry?(type: type, name: subdomain, zone: zone) end

protected

def type 'CNAME' end end

Page 65: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class CnameWsDns attr_reader :ws_dns, :zone, :content

def initialize(options) @ws_dns = WsDns.new(options) @zone = options[:zone] @content = options.fetch(:content, zone) end

def create_entry(subdomain) ws_dns.create_entry(type: type, content: content, name: subdomain, zone: zone) end

def delete_entry(subdomain) ws_dns.delete_entry(type: type, content: content, name: subdomain, zone: zone) end

def has_entry?(subdomain) ws_dns.has_entry?(type: type, name: subdomain, zone: zone) end

protected

def type 'CNAME' end end

Page 66: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class CnameWsDns attr_reader :ws_dns, :zone, :content

def initialize(options) @ws_dns = WsDns.new(options) @zone = options[:zone] @content = options.fetch(:content, zone) end

def create_entry(subdomain) ws_dns.create_entry(type: type, content: content, name: subdomain, zone: zone) end

def delete_entry(subdomain) ws_dns.delete_entry(type: type, content: content, name: subdomain, zone: zone) end

def has_entry?(subdomain) ws_dns.has_entry?(type: type, name: subdomain, zone: zone) end

protected

def type 'CNAME' end end

Page 67: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class AWsDns < CnameWsDns protected

def type 'A' end end

Page 68: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class AWsDns < CnameWsDns protected

def type 'A' end end

Page 69: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class CnameWsDns attr_reader :ws_dns, :zone, :content

def initialize(options) @ws_dns = WsDns.new(options) @zone = options[:zone] @content = options.fetch(:content, zone) end

def create_entry(subdomain) ws_dns.create_entry(type: type, content: content, name: subdomain, zone: zone) end

def delete_entry(subdomain) ws_dns.delete_entry(type: type, content: content, name: subdomain, zone: zone) end

def has_entry?(subdomain) ws_dns.has_entry?(type: type, name: subdomain, zone: zone) end

protected

def type 'CNAME' end end

Page 70: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class TxtWsDns < CnameWsDns protected

def type 'TXT' end end

Page 71: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class TxtWsDns < CnameWsDns protected

def type 'TXT' end end

Page 72: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Page 73: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como corrigir isso?

Page 74: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Page 75: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Orientação a Objetos Classe base conhecendo

seus filhos

Page 76: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class TransactionResponseParser attr_reader :xml

def initialize(xml) @xml = xml end

def parse # omitted implementation end

private

# specific transaction methods omitted end

Page 77: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ResponseParser attr_reader :xml

def initialize(xml) @xml = xml end

def parse # omitted implementation end

# omitted protected methods end

Page 78: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class TransactionResponseParser < ResponseParser

private

# specific transaction methods omitted end

class AccountResponseParser < ResponseParser

private

# specific account methods omitted end

Page 79: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class ResponseParser def self.transaction?(xml) xml.include?('<transaction>') end

def self.get_parser(xml) ResponseParser.transaction?(xml) ? TransactionResponseParser.new(xml) : AccountResponseParser.new(xml) end

def initialize(xml) @xml = xml end

def parse # omitted implementation

end end

Page 80: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como corrigir isso?

Page 81: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

module ResponseParserFactory def self.build(xml) if xml.include?('<transaction>') TransactionResponseParser.new(xml) else AccountResponseParser.new(xml) end end end

Page 82: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

A pior classe

Page 83: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker extend Memoist

DOMAIN_REGEXP = /^[a-z0-9]+(-[a-z0-9]+)*(\.[a-z0-9]+(-[a-z0-9]+)*)+$/

attr_accessor :domain

def initialize(args = {}) @domain = args[:domain] end

def check_new check_existing end

def status if dns_adapter.ns_locaweb? a_entry_locaweb = dns_adapter.a_entry_locaweb if a_entry_locaweb == AppConfig.ip_lvs_criador_de_sites return :ok elsif a_entry_locaweb == false return :unavailable else return :already_using end end

if domain_checker_result["error"] == "generic" return :generic_error end

if domain_checker_result["error"] == "unsupported_tld" return :unsupported_tld end

if domain_checker_result["available"] return :register end

if dns_adapter.a_value == AppConfig.ip_lvs_criador_de_sites return :ok else return :config_dns end end memoize :status

def available_domain_by_user(user) if domain.blank? return {valid: false, notice: :invalid, message: :blank} end

if !domain.match(DOMAIN_REGEXP) return {valid: false, notice: :invalid, message: :invalid} end

if forbidden_domain? return {valid: false, notice: :invalid, message: :forbidden_domain} end

if Domain.where(address: domain).count > 0 current_domain = Domain.where(address: domain).first if (current_domain.site.account.users.include?(user) rescue false) return {valid: false, notice: :invalid, message: :already_using} else return {valid: false, notice: :invalid, message: :already_exists} end end

if !domain_checker_result["valid"] && domain_checker_result["error"] != "unsupported_tld" return {valid: false, notice: :invalid, message: :invalid} end

if domain_checker_result["error"] == "unsupported_tld" return {valid: true, notice: :unsupported_tld} end

if domain_checker_result["available"] return {valid: true, notice: :register} end

if domain_checker_result["customer_login"].blank? return {valid: true, notice: :config_dns} end

if domain_checker_result["customer_login"].downcase == user.username.downcase Rails.logger.info "user owner domain"

if dns_adapter.a_entry_locaweb? if dns_adapter.a_entry_locaweb == AppConfig.ip_lvs_criador_de_sites_old return {valid: true, notice: :old_csit} else return {valid: true, notice: :already_using} end else Rails.logger.info "Without entry A" return {valid: true, notice: :owner_domain} end else Rails.logger.info "user does not owner domain" return {valid: false, notice: :not_owner} end end

def details { entry_a: dns_adapter.a_value, entry_ns: dns_adapter.ns_value, entry_cname: dns_adapter.cname_value }.merge(domain_checker_result) end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def dns_adapter DnsAdapter.new(domain: CGI.escape(domain)) end memoize :dns_adapter

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

def get_token WsAuthentication.new(AppConfig.wsauthentication.url).authenticate(AppConfig.wsauthentication.user, AppConfig.wsauthentication.pass) end memoize :get_token

def external_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/external_check" begin JSON(http_get(url)) rescue RestClient::NotImplemented return { "valid" => false, "available" => false, "error" => 'unsupported_tld' } rescue RestClient::InternalServerError => exception Rails.logger.error "[ERROR] GET #{url}: #{exception.message}\n" \ "Response: #{exception.http_body}" return { "valid" => false, "available" => false, "error" => 'generic' } rescue => exception Rails.logger.error exception.print raise exception end end memoize :external_check

def internal_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/internal_check" JSON(http_get(url)) end memoize :internal_check

def forbidden_domain? uri = "#{AppConfig.registro_domain_url}/domain/#{domain}/check"

begin response = JSON(CasSaas::CasRestClient.new.get(uri))

!response["valid"] rescue => e Rails.logger.info e.message true end end memoize :forbidden_domain?

def http_get(url, headers = {}) Rails.logger.info "chamando GET #{url}, headers: #{headers}" response = RestClient.get url, headers Rails.logger.info "response #{response}" response end end

Page 84: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Cenário de negócio da classe DomainChecker

Page 85: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Verificação de domínio

Page 86: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new # omitted implementation end

def status # omitted implementation end memoize :status

def available_domain_by_user(user) # omitted implementation end

def details # omitted implementation end

def check_existing # omitted implementation end

# ... end

Page 87: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker extend Memoist

attr_accessor :domain

def initialize(args = {}) @domain = args[:domain] end

# ... end

Page 88: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker extend Memoist

# ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check

end

# ... end

Page 89: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker extend Memoist

attr_accessor :domain

def initialize(args = {}) @domain = args[:domain] end

# ... end

Page 90: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def status if dns_adapter.ns_locaweb? a_entry_locaweb = dns_adapter.a_entry_locaweb if a_entry_locaweb == AppConfig.ip_lvs_criador_de_sites return :ok elsif a_entry_locaweb == false return :unavailable else return :already_using end end

if domain_checker_result["error"] == "generic" return :generic_error end

if domain_checker_result["error"] == "unsupported_tld" return :unsupported_tld end

if domain_checker_result["available"] return :register end

if dns_adapter.a_value == AppConfig.ip_lvs_criador_de_sites return :ok else return :config_dns end end memoize :status

Page 91: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def dns_adapter DnsAdapter.new(domain: CGI.escape(domain)) end memoize :dns_adapter

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

Page 92: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def get_token WsAuthentication.new(AppConfig.wsauthentication.url).authenticate(AppConfig.wsauthentication.user, AppConfig.wsauthentication.pass)

end memoize :get_token

def external_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/external_check" begin JSON(http_get(url)) rescue RestClient::NotImplemented return { "valid" => false, "available" => false, "error" => 'unsupported_tld' } rescue RestClient::InternalServerError => exception Rails.logger.error "[ERROR] GET #{url}: #{exception.message}\n" \ "Response: #{exception.http_body}" return { "valid" => false, "available" => false, "error" => 'generic' } rescue => exception Rails.logger.error exception.print raise exception end end memoize :external_check

Page 93: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def internal_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/internal_check" JSON(http_get(url)) end memoize :internal_check

def forbidden_domain? uri = "#{AppConfig.registro_domain_url}/domain/#{domain}/check"

begin response = JSON(CasSaas::CasRestClient.new.get(uri)) !response["valid"] rescue => e Rails.logger.info e.message true end end memoize :forbidden_domain?

Page 94: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker extend Memoist

attr_accessor :domain

def initialize(args = {}) @domain = args[:domain] end

# ... end

Page 95: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def status if dns_adapter.ns_locaweb? a_entry_locaweb = dns_adapter.a_entry_locaweb if a_entry_locaweb == AppConfig.ip_lvs_criador_de_sites return :ok elsif a_entry_locaweb == false return :unavailable else return :already_using end end

if domain_checker_result["error"] == "generic" return :generic_error end

if domain_checker_result["error"] == "unsupported_tld" return :unsupported_tld end

if domain_checker_result["available"] return :register end

if dns_adapter.a_value == AppConfig.ip_lvs_criador_de_sites return :ok else return :config_dns end end memoize :status

Page 96: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def available_domain_by_user(user) if domain.blank? return {valid: false, notice: :invalid, message: :blank} end

if !domain.match(DOMAIN_REGEXP) return {valid: false, notice: :invalid, message: :invalid} end

if forbidden_domain? return {valid: false, notice: :invalid, message: :forbidden_domain} end

if Domain.where(address: domain).count > 0 current_domain = Domain.where(address: domain).first if (current_domain.site.account.users.include?(user) rescue false) return {valid: false, notice: :invalid, message: :already_using} else return {valid: false, notice: :invalid, message: :already_exists} end end

if !domain_checker_result["valid"] && domain_checker_result["error"] != "unsupported_tld" return {valid: false, notice: :invalid, message: :invalid} end

if domain_checker_result["error"] == "unsupported_tld" return {valid: true, notice: :unsupported_tld} end

if domain_checker_result["available"] return {valid: true, notice: :register} end

if domain_checker_result["customer_login"].blank? return {valid: true, notice: :config_dns} end

if domain_checker_result["customer_login"].downcase == user.username.downcase Rails.logger.info "user owner domain"

if dns_adapter.a_entry_locaweb? if dns_adapter.a_entry_locaweb == AppConfig.ip_lvs_criador_de_sites_old return {valid: true, notice: :old_csit} else return {valid: true, notice: :already_using} end else Rails.logger.info "Without entry A" return {valid: true, notice: :owner_domain} end else Rails.logger.info "user does not owner domain" return {valid: false, notice: :not_owner} end end

Page 97: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def available_domain_by_user(user) if domain.blank? return {valid: false, notice: :invalid, message: :blank} end

if !domain.match(DOMAIN_REGEXP) return {valid: false, notice: :invalid, message: :invalid} end

if forbidden_domain? return {valid: false, notice: :invalid, message: :forbidden_domain} end

if Domain.where(address: domain).count > 0 current_domain = Domain.where(address: domain).first if (current_domain.site.account.users.include?(user) rescue false) return {valid: false, notice: :invalid, message: :already_using} else return {valid: false, notice: :invalid, message: :already_exists} end end

# ... end

Page 98: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def available_domain_by_user(user) if domain.blank? return {valid: false, notice: :invalid, message: :blank} end

if !domain.match(DOMAIN_REGEXP) return {valid: false, notice: :invalid, message: :invalid} end

if forbidden_domain? return {valid: false, notice: :invalid, message: :forbidden_domain} end

if Domain.where(address: domain).count > 0 current_domain = Domain.where(address: domain).first if (current_domain.site.account.users.include?(user) rescue false) return {valid: false, notice: :invalid, message: :already_using} else return {valid: false, notice: :invalid, message: :already_exists} end end

# ... end

Page 99: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def available_domain_by_user(user) if domain.blank? return {valid: false, notice: :invalid, message: :blank} end

if !domain.match(DOMAIN_REGEXP) return {valid: false, notice: :invalid, message: :invalid} end

if forbidden_domain? return {valid: false, notice: :invalid, message: :forbidden_domain} end

if Domain.where(address: domain).count > 0 current_domain = Domain.where(address: domain).first if (current_domain.site.account.users.include?(user) rescue false) return {valid: false, notice: :invalid, message: :already_using} else return {valid: false, notice: :invalid, message: :already_exists} end end

# ... end

Page 100: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def available_domain_by_user(user) # …

if domain_checker_result["customer_login"].downcase == user.username.downcase Rails.logger.info "user owner domain"

if dns_adapter.a_entry_locaweb? if dns_adapter.a_entry_locaweb == AppConfig.ip_lvs_criador_de_sites_old return {valid: true, notice: :old_csit} else return {valid: true, notice: :already_using} end else Rails.logger.info "Without entry A" return {valid: true, notice: :owner_domain} end else Rails.logger.info "user does not owner domain" return {valid: false, notice: :not_owner} end end

Page 101: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def available_domain_by_user(user) # …

if !domain_checker_result["valid"] && domain_checker_result["error"] != "unsupported_tld" return {valid: false, notice: :invalid, message: :invalid} end

if domain_checker_result["error"] == "unsupported_tld" return {valid: true, notice: :unsupported_tld} end

if domain_checker_result["available"] return {valid: true, notice: :register} end

if domain_checker_result["customer_login"].blank? return {valid: true, notice: :config_dns} end

# … end

Page 102: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 103: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 104: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 105: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 106: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 107: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 108: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def external_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/external_check" begin JSON(http_get(url)) rescue RestClient::NotImplemented return { "valid" => false, "available" => false, "error" => 'unsupported_tld' } rescue RestClient::InternalServerError => exception Rails.logger.error "[ERROR] GET #{url}: #{exception.message}\n" \ "Response: #{exception.http_body}" return { "valid" => false, "available" => false, "error" => 'generic' } rescue => exception Rails.logger.error exception.print raise exception end end memoize :external_check

def internal_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/internal_check" JSON(http_get(url)) end memoize :internal_check

Page 109: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def external_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/external_check" begin JSON(http_get(url)) rescue RestClient::NotImplemented return { "valid" => false, "available" => false, "error" => 'unsupported_tld' } rescue RestClient::InternalServerError => exception Rails.logger.error "[ERROR] GET #{url}: #{exception.message}\n" \ "Response: #{exception.http_body}" return { "valid" => false, "available" => false, "error" => 'generic' } rescue => exception Rails.logger.error exception.print raise exception end end memoize :external_check

def internal_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/internal_check" JSON(http_get(url)) end memoize :internal_check

Page 110: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def http_get(url, headers = {}) Rails.logger.info "chamando GET #{url}, headers: #{headers}" response = RestClient.get url, headers Rails.logger.info "response #{response}" response end end

Page 111: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

def external_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/external_check" begin JSON(http_get(url)) rescue RestClient::NotImplemented return { "valid" => false, "available" => false, "error" => 'unsupported_tld' } rescue RestClient::InternalServerError => exception Rails.logger.error "[ERROR] GET #{url}: #{exception.message}\n" \ "Response: #{exception.http_body}" return { "valid" => false, "available" => false, "error" => 'generic' } rescue => exception Rails.logger.error exception.print raise exception end end memoize :external_check

def internal_check url = "#{AppConfig.registro_service_url}/domain_availability/#{domain}/internal_check" JSON(http_get(url)) end memoize :internal_check

Page 112: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def check_new check_existing end

def check_existing return external_check if external_check["error"] == "generic" return external_check if external_check["error"] == "invalid_domain" return external_check if external_check["error"] == "unsupported_tld" return external_check if external_check["available"] return external_check if internal_check["available"] internal_check end

private

def domain_checker_result domain_checker = DomainChecker.new(domain: CGI.escape(domain)) domain_checker_result = domain_checker.check_new end memoize :domain_checker_result

# ... end

Page 113: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ...

def details { entry_a: dns_adapter.a_value, entry_ns: dns_adapter.ns_value, entry_cname: dns_adapter.cname_value }.merge(domain_checker_result) end

private

def dns_adapter DnsAdapter.new(domain: CGI.escape(domain)) end memoize :dns_adapter

#... end

Page 114: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

class DomainChecker # ... private

def get_token WsAuthentication.new(AppConfig.wsauthentication.url).authenticate(AppConfig.wsauthentication.user, AppConfig.wsauthentication.pass)

end memoize :get_token end

Page 115: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Problemas da classe DomainChecker

Page 116: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Classe longa

Problemas de DomainChecker

Page 117: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Construtor recebe um hash como parâmetro, mas somente usa

o valor de uma chave do hash.

Problemas de DomainChecker

Page 118: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Uma implementação de método que apenas chama

um método privado sem passagem de parâmetros

Problemas de DomainChecker

Page 119: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Memoize hell: métodos “memoizados" que são usados como variáveis

Problemas de DomainChecker

Page 120: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Um monte de ifs: ifs com ifs com else com if com else

(difícil até para explicar)

Problemas de DomainChecker

Page 121: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Código difícil para entender

(internal x external checks)

Problemas de DomainChecker

Page 122: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Um método privado não usado

Problemas de DomainChecker

Page 123: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Um método de instância que cria outra instância

da mesma classe

Problemas de DomainChecker

Page 124: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

• Muitas responsabilidades:

✴ Validação de formato de domínio

✴ Validação de lógica de domínio

✴ Retornos com formato exclusivo para a view

✴ Faz requisições HTTP

✴ Parser de respostas HTTP

Problemas de DomainChecker

Page 125: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

A classe DomainChecker introduz novos

padrões e princípios

Page 126: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Write-only codeUma vez escrito,

ninguém mais consegue ler

DomainChecker introduz novos padrões e princípios

Page 127: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Close Closed PrincipleFechado para modificação,

mais fechado para extensão.

DomainChecker introduz novos padrões e princípios

Page 128: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Inception PatternOnde uma instância de uma classe cria uma nova instância da mesma classe e agrega o

estado da nova instância para seu próprio estado

DomainChecker introduz novos padrões e princípios

Page 129: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

A classe DomainChecker provavelmente é a pior classe em Ruby que eu já vi na minha vida

Page 130: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como foi corrigido?

Page 131: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Page 132: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Por que POG é aplicada?

Page 133: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Falta de conhecimento

Causas de POG

Page 134: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Imaturidade no desenvolvimento

de software

Causas de POG

Page 135: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Ambiente não colaborativo

Causas de POG

Page 136: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Falta de coaching

Causas de POG

Page 137: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Prazos apertados

Causas de POG

Page 138: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Por que simplificar se você pode complicar?

Causas de POG

Page 139: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Uso de “coisas legais” porque elas são legais, mesmo se elas não oferecem uma solução para

o que você precisa.

Causas de POG

Page 140: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Alguns mistérios da mente humana

(que não podemos explicar)

Causas de POG

Page 141: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como evitar POG?

Page 142: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Leia muito

Como evitar POG?

Page 143: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como evitar POG?

Page 144: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Mas não aprenda somente Ruby

Como evitar POG?

Page 145: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Como evitar POG?

Page 146: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Use revisão de código

Como evitar POG?

Page 147: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Leia código de outros programadores

Como evitar POG?

Page 148: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Escreva código que você mesmo irá

conseguir ler no futuro

Como evitar POG?

Page 149: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Participe de projetos open source: contribuindo, discutindo, lendo código.

Como evitar POG?

Page 150: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Coaching de programadores menos experientes

(ensinar é uma boa maneira de aprender também)

Como evitar POG?

Page 151: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Não escreva código para você: escreva para a aplicação,

para sua equipe.

Como evitar POG?

Page 152: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Troque experiências, pergunte.

Como evitar POG?

Page 153: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Use programação pareada

(não 100% do tempo IMHO)

Como evitar POG?

Page 154: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Aprenda com erros: seus e dos outros

Como evitar POG?

Page 155: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Encare código ruim como uma oportunidade

de melhorar

Como evitar POG?

Page 156: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Não encare código ruim com reclamações ou

tirando sarro dos autores

Como evitar POG?

Page 157: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Este “código engraçado” causa desperdício de

tempo, recursos e dinheiro

Como evitar POG?

Page 158: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Ao invés disso, mostre para os autores do código

ruim o caminho certo

Como evitar POG?

Page 159: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Mostre a eles o Ruby way

Como evitar POG?

Page 160: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

Obrigado! Espero que tenham gostado

@Prodis

Page 161: Os piores códigos Ruby já vistos - TDC Florianópolis 2016

@Prodis

https://twitter.com/prodis https://github.com/prodis

https://www.linkedin.com/in/prodis

http://prodis.blog.br

@Prodis