SlideShare ist ein Scribd-Unternehmen logo
1 von 161
Downloaden Sie, um offline zu lesen
@Prodis
Os piores códigos
Ruby já vistos
TDC Florianópolis 2016
@Prodis
@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.
@Prodis
Fernando Hamasaki de Amorim
@Prodis
Fernando Hamasaki de Amorim
@Prodis
WOP
@Prodis
WOP
Workaround Oriented Programming
@Prodis
POG
Programação Orientada a Gambiarras
@Prodis
POG
POG é 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árias

e reinvenção de rodas.
@Prodis
"The names have been changed to protect the innocent."
Os piores códigos
Ruby já vistos
@Prodis
Um primeiro exemplo de POG:

mascarando números de
cartão de crédito
@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
@Prodis
def mask_credit_card(number)
limit = number.length - 4
“#{'*' * limit}#{number[limit..-1]}”
end
@Prodis
def mask_credit_card_pog(number)
(number.length - 4).times do |i|
number[i] = '*'
end
number
end
@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
@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
@Prodis
def mask_credit_card_pog(number)
(number.length - 4).times do |i|
number[i] = '*'
end
number
end
@Prodis
Fluxos obscuros
@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
@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
@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
@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
@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
@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
@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
@Prodis
Como corrigir isso?
@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
@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
@Prodis
No Ruby way
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@Prodis
Problemas de
nomenclatura
@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
@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
@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
@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
@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
@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
@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
@Prodis
Orientação a Objetos
@Prodis
Orientação a Objetos
Herança com propósito
de reuso de código
@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
@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
@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
@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
@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
@Prodis
Como corrigir isso?
@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
@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
@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
@Prodis
Orientação a Objetos
Equívoco de herança
@Prodis
DNS
Uma rápida introdução
@Prodis
@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
@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
@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
@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
@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
@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
@Prodis
class AWsDns < CnameWsDns
protected
def type
'A'
end
end
@Prodis
class AWsDns < CnameWsDns
protected
def type
'A'
end
end
@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
@Prodis
class TxtWsDns < CnameWsDns
protected
def type
'TXT'
end
end
@Prodis
class TxtWsDns < CnameWsDns
protected
def type
'TXT'
end
end
@Prodis
@Prodis
Como corrigir isso?
@Prodis
@Prodis
Orientação a Objetos
Classe base conhecendo
seus filhos
@Prodis
class TransactionResponseParser
attr_reader :xml
def initialize(xml)
@xml = xml
end
def parse
# omitted implementation
end
private
# specific transaction methods omitted
end
@Prodis
class ResponseParser
attr_reader :xml
def initialize(xml)
@xml = xml
end
def parse
# omitted implementation
end
# omitted protected methods
end
@Prodis
class TransactionResponseParser < ResponseParser
private
# specific transaction methods omitted
end
class AccountResponseParser < ResponseParser
private
# specific account methods omitted
end
@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
@Prodis
Como corrigir isso?
@Prodis
module ResponseParserFactory
def self.build(xml)
if xml.include?('<transaction>')
TransactionResponseParser.new(xml)
else
AccountResponseParser.new(xml)
end
end
end
@Prodis
A pior classe
@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.wsauthent
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
@Prodis
Cenário de negócio da
classe DomainChecker
@Prodis
Verificação de domínio
@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
@Prodis
class DomainChecker
extend Memoist
attr_accessor :domain
def initialize(args = {})
@domain = args[:domain]
end
# ...
end
@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
@Prodis
class DomainChecker
extend Memoist
attr_accessor :domain
def initialize(args = {})
@domain = args[:domain]
end
# ...
end
@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
@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
@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
@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?
@Prodis
class DomainChecker
extend Memoist
attr_accessor :domain
def initialize(args = {})
@domain = args[:domain]
end
# ...
end
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@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
@Prodis
class DomainChecker
# ...
private
def get_token
WsAuthentication.new(AppConfig.wsauthentication.url)
.authenticate(AppConfig.wsauthentication.user,
AppConfig.wsauthentication.pass)
end
memoize :get_token
end
@Prodis
Problemas da classe
DomainChecker
@Prodis
Classe longa
Problemas de DomainChecker
@Prodis
Construtor recebe um hash como
parâmetro, mas somente usa
o valor de uma chave do hash.
Problemas de DomainChecker
@Prodis
Uma implementação
de método que apenas chama
um método privado sem
passagem de parâmetros
Problemas de DomainChecker
@Prodis
Memoize hell:
métodos “memoizados" que
são usados como variáveis
Problemas de DomainChecker
@Prodis
Um monte de ifs: ifs com ifs
com else com if com else
(difícil até para explicar)
Problemas de DomainChecker
@Prodis
Código difícil
para entender
(internal x external checks)
Problemas de DomainChecker
@Prodis
Um método privado
não usado
Problemas de DomainChecker
@Prodis
Um método de instância
que cria outra instância
da mesma classe
Problemas de DomainChecker
@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
@Prodis
A classe DomainChecker
introduz novos
padrões e princípios
@Prodis
Write-only code
Uma vez escrito,
ninguém mais consegue ler
DomainChecker introduz
novos padrões e princípios
@Prodis
Close Closed Principle
Fechado para modificação,
mais fechado para extensão.
DomainChecker introduz
novos padrões e princípios
@Prodis
Inception Pattern
Onde 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
@Prodis
A classe DomainChecker
provavelmente é a pior classe em
Ruby que eu já vi na minha vida
@Prodis
Como foi corrigido?
@Prodis
@Prodis
Por que POG
é aplicada?
@Prodis
Falta de
conhecimento
Causas de POG
@Prodis
Imaturidade no
desenvolvimento
de software
Causas de POG
@Prodis
Ambiente não
colaborativo
Causas de POG
@Prodis
Falta de coaching
Causas de POG
@Prodis
Prazos apertados
Causas de POG
@Prodis
Por que simplificar se
você pode complicar?
Causas de POG
@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
@Prodis
Alguns mistérios da
mente humana
(que não podemos explicar)
Causas de POG
@Prodis
Como evitar POG?
@Prodis
Leia muito
Como evitar POG?
@Prodis
Como evitar POG?
@Prodis
Mas não aprenda
somente Ruby
Como evitar POG?
@Prodis
Como evitar POG?
@Prodis
Use revisão de código
Como evitar POG?
@Prodis
Leia código de outros
programadores
Como evitar POG?
@Prodis
Escreva código que
você mesmo irá
conseguir ler no futuro
Como evitar POG?
@Prodis
Participe de projetos
open source: contribuindo,
discutindo, lendo código.
Como evitar POG?
@Prodis
Coaching de programadores
menos experientes
(ensinar é uma boa maneira de aprender também)
Como evitar POG?
@Prodis
Não escreva código para você:
escreva para a aplicação,
para sua equipe.
Como evitar POG?
@Prodis
Troque experiências,
pergunte.
Como evitar POG?
@Prodis
Use programação
pareada
(não 100% do tempo IMHO)
Como evitar POG?
@Prodis
Aprenda com erros:
seus e dos outros
Como evitar POG?
@Prodis
Encare código ruim
como uma oportunidade
de melhorar
Como evitar POG?
@Prodis
Não encare código ruim
com reclamações ou
tirando sarro dos autores
Como evitar POG?
@Prodis
Este “código engraçado”
causa desperdício de
tempo, recursos e dinheiro
Como evitar POG?
@Prodis
Ao invés disso, mostre
para os autores do código
ruim o caminho certo
Como evitar POG?
@Prodis
Mostre a eles
o Ruby way
Como evitar POG?
@Prodis
Obrigado!
Espero que tenham gostado
@Prodis
@Prodis
https://twitter.com/prodis


https://github.com/prodis
https://www.linkedin.com/in/prodis
http://prodis.blog.br
@Prodis

Weitere ähnliche Inhalte

Ähnlich wie Ruby code quality and refactoring techniques

The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015Fernando Hamasaki de Amorim
 
PHP SA 2014 - Releasing Your Open Source Project
PHP SA 2014 - Releasing Your Open Source ProjectPHP SA 2014 - Releasing Your Open Source Project
PHP SA 2014 - Releasing Your Open Source Projectxsist10
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...SPTechCon
 
Porting Rails Apps to High Availability Systems
Porting Rails Apps to High Availability SystemsPorting Rails Apps to High Availability Systems
Porting Rails Apps to High Availability SystemsMarcelo Pinheiro
 
The-Power-Of-Recon (1)-poerfulo.pptx.pdf
The-Power-Of-Recon (1)-poerfulo.pptx.pdfThe-Power-Of-Recon (1)-poerfulo.pptx.pdf
The-Power-Of-Recon (1)-poerfulo.pptx.pdfnezidsilva
 
Fix me if you can - DrupalCon prague
Fix me if you can - DrupalCon pragueFix me if you can - DrupalCon prague
Fix me if you can - DrupalCon praguehernanibf
 
Simplify your professional web development with symfony
Simplify your professional web development with symfonySimplify your professional web development with symfony
Simplify your professional web development with symfonyFrancois Zaninotto
 
OWASP TOP 10 for PHP Programmers
OWASP TOP 10 for PHP ProgrammersOWASP TOP 10 for PHP Programmers
OWASP TOP 10 for PHP Programmersrjsmelo
 
Taking Web Apps Offline
Taking Web Apps OfflineTaking Web Apps Offline
Taking Web Apps OfflinePedro Morais
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin GeneratorJohn Cleveley
 
Lean Php Presentation
Lean Php PresentationLean Php Presentation
Lean Php PresentationAlan Pinstein
 
OpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con PythonOpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con PythonPyCon Italia
 
Hopping in clouds: a tale of migration from one cloud provider to another
Hopping in clouds: a tale of migration from one cloud provider to anotherHopping in clouds: a tale of migration from one cloud provider to another
Hopping in clouds: a tale of migration from one cloud provider to anotherMichele Orselli
 
Future of Serverless
Future of ServerlessFuture of Serverless
Future of ServerlessYoav Avrahami
 
Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...
Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...
Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...Carlos Tomas
 
#NewMeetup Performance
#NewMeetup Performance#NewMeetup Performance
#NewMeetup PerformanceJustin Cataldo
 
Scout xss csrf_security_presentation_chicago
Scout xss csrf_security_presentation_chicagoScout xss csrf_security_presentation_chicago
Scout xss csrf_security_presentation_chicagoknaddison
 
Heavy Web Optimization: Backend
Heavy Web Optimization: BackendHeavy Web Optimization: Backend
Heavy Web Optimization: BackendVõ Duy Tuấn
 
Render Caching for Drupal 8
Render Caching for Drupal 8Render Caching for Drupal 8
Render Caching for Drupal 8John Doyle
 

Ähnlich wie Ruby code quality and refactoring techniques (20)

The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
 
PHP SA 2014 - Releasing Your Open Source Project
PHP SA 2014 - Releasing Your Open Source ProjectPHP SA 2014 - Releasing Your Open Source Project
PHP SA 2014 - Releasing Your Open Source Project
 
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
The Magic Revealed: Four Real-World Examples of Using the Client Object Model...
 
Porting Rails Apps to High Availability Systems
Porting Rails Apps to High Availability SystemsPorting Rails Apps to High Availability Systems
Porting Rails Apps to High Availability Systems
 
The-Power-Of-Recon (1)-poerfulo.pptx.pdf
The-Power-Of-Recon (1)-poerfulo.pptx.pdfThe-Power-Of-Recon (1)-poerfulo.pptx.pdf
The-Power-Of-Recon (1)-poerfulo.pptx.pdf
 
Fix me if you can - DrupalCon prague
Fix me if you can - DrupalCon pragueFix me if you can - DrupalCon prague
Fix me if you can - DrupalCon prague
 
Simplify your professional web development with symfony
Simplify your professional web development with symfonySimplify your professional web development with symfony
Simplify your professional web development with symfony
 
OWASP TOP 10 for PHP Programmers
OWASP TOP 10 for PHP ProgrammersOWASP TOP 10 for PHP Programmers
OWASP TOP 10 for PHP Programmers
 
Taking Web Apps Offline
Taking Web Apps OfflineTaking Web Apps Offline
Taking Web Apps Offline
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin Generator
 
Lean Php Presentation
Lean Php PresentationLean Php Presentation
Lean Php Presentation
 
OpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con PythonOpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con Python
 
Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
 
Hopping in clouds: a tale of migration from one cloud provider to another
Hopping in clouds: a tale of migration from one cloud provider to anotherHopping in clouds: a tale of migration from one cloud provider to another
Hopping in clouds: a tale of migration from one cloud provider to another
 
Future of Serverless
Future of ServerlessFuture of Serverless
Future of Serverless
 
Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...
Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...
Build a notepad application with PHP, MongoDB, and IBM Bluemix - by Vikram Va...
 
#NewMeetup Performance
#NewMeetup Performance#NewMeetup Performance
#NewMeetup Performance
 
Scout xss csrf_security_presentation_chicago
Scout xss csrf_security_presentation_chicagoScout xss csrf_security_presentation_chicago
Scout xss csrf_security_presentation_chicago
 
Heavy Web Optimization: Backend
Heavy Web Optimization: BackendHeavy Web Optimization: Backend
Heavy Web Optimization: Backend
 
Render Caching for Drupal 8
Render Caching for Drupal 8Render Caching for Drupal 8
Render Caching for Drupal 8
 

Mehr von Fernando Hamasaki de Amorim

Mehr von Fernando Hamasaki de Amorim (6)

Ido Keido - ElixirConf EU 2019
Ido Keido - ElixirConf EU 2019Ido Keido - ElixirConf EU 2019
Ido Keido - ElixirConf EU 2019
 
Anotações da TDC Florianópolis 2016
Anotações da TDC Florianópolis 2016Anotações da TDC Florianópolis 2016
Anotações da TDC Florianópolis 2016
 
TDC 2015 - Torne-se um programador melhor
TDC 2015 - Torne-se um  programador melhorTDC 2015 - Torne-se um  programador melhor
TDC 2015 - Torne-se um programador melhor
 
7 coisas que todo desenvolvedor Ruby on Rails deveria fazer
7 coisas que todo desenvolvedor Ruby on Rails deveria fazer7 coisas que todo desenvolvedor Ruby on Rails deveria fazer
7 coisas que todo desenvolvedor Ruby on Rails deveria fazer
 
Locaweb + Spree: transformando código aberto em um projeto comercial
Locaweb + Spree: transformando código aberto em um projeto comercialLocaweb + Spree: transformando código aberto em um projeto comercial
Locaweb + Spree: transformando código aberto em um projeto comercial
 
Apresentação de TDD na Fatec Jundiaí
Apresentação de TDD na Fatec JundiaíApresentação de TDD na Fatec Jundiaí
Apresentação de TDD na Fatec Jundiaí
 

Kürzlich hochgeladen

Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 

Kürzlich hochgeladen (20)

Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 

Ruby code quality and refactoring techniques

  • 1. @Prodis Os piores códigos Ruby já vistos TDC Florianópolis 2016 @Prodis
  • 2. @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.
  • 8. @Prodis POG POG é 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árias
 e reinvenção de rodas.
  • 9. @Prodis "The names have been changed to protect the innocent." Os piores códigos Ruby já vistos
  • 10. @Prodis Um primeiro exemplo de POG:
 mascarando números de cartão de crédito
  • 11. @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
  • 12. @Prodis def mask_credit_card(number) limit = number.length - 4 “#{'*' * limit}#{number[limit..-1]}” end
  • 13. @Prodis def mask_credit_card_pog(number) (number.length - 4).times do |i| number[i] = '*' end number end
  • 14. @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
  • 15. @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
  • 16. @Prodis def mask_credit_card_pog(number) (number.length - 4).times do |i| number[i] = '*' end number end
  • 18. @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
  • 19. @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
  • 20. @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
  • 21. @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
  • 22. @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
  • 23. @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
  • 24. @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
  • 26. @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
  • 27. @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
  • 29. @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
  • 30. @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
  • 31. @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
  • 32. @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
  • 33. @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
  • 34. @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
  • 35. @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
  • 36. @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
  • 37. @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
  • 38. @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
  • 40. @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
  • 41. @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
  • 42. @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
  • 43. @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
  • 44. @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
  • 45. @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
  • 46. @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
  • 48. @Prodis Orientação a Objetos Herança com propósito de reuso de código
  • 49. @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
  • 50. @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
  • 51. @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
  • 52. @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
  • 53. @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
  • 55. @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
  • 56. @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
  • 57. @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
  • 61. @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
  • 62. @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
  • 63. @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
  • 64. @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
  • 65. @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
  • 66. @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
  • 67. @Prodis class AWsDns < CnameWsDns protected def type 'A' end end
  • 68. @Prodis class AWsDns < CnameWsDns protected def type 'A' end end
  • 69. @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
  • 70. @Prodis class TxtWsDns < CnameWsDns protected def type 'TXT' end end
  • 71. @Prodis class TxtWsDns < CnameWsDns protected def type 'TXT' end end
  • 75. @Prodis Orientação a Objetos Classe base conhecendo seus filhos
  • 76. @Prodis class TransactionResponseParser attr_reader :xml def initialize(xml) @xml = xml end def parse # omitted implementation end private # specific transaction methods omitted end
  • 77. @Prodis class ResponseParser attr_reader :xml def initialize(xml) @xml = xml end def parse # omitted implementation end # omitted protected methods end
  • 78. @Prodis class TransactionResponseParser < ResponseParser private # specific transaction methods omitted end class AccountResponseParser < ResponseParser private # specific account methods omitted end
  • 79. @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
  • 81. @Prodis module ResponseParserFactory def self.build(xml) if xml.include?('<transaction>') TransactionResponseParser.new(xml) else AccountResponseParser.new(xml) end end end
  • 83. @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.wsauthent 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
  • 84. @Prodis Cenário de negócio da classe DomainChecker
  • 86. @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
  • 87. @Prodis class DomainChecker extend Memoist attr_accessor :domain def initialize(args = {}) @domain = args[:domain] end # ... end
  • 88. @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
  • 89. @Prodis class DomainChecker extend Memoist attr_accessor :domain def initialize(args = {}) @domain = args[:domain] end # ... end
  • 90. @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
  • 91. @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
  • 92. @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
  • 93. @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?
  • 94. @Prodis class DomainChecker extend Memoist attr_accessor :domain def initialize(args = {}) @domain = args[:domain] end # ... end
  • 95. @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
  • 96. @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
  • 97. @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
  • 98. @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
  • 99. @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
  • 100. @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
  • 101. @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
  • 102. @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
  • 103. @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
  • 104. @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
  • 105. @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
  • 106. @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
  • 107. @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
  • 108. @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
  • 109. @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
  • 110. @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
  • 111. @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
  • 112. @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
  • 113. @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
  • 114. @Prodis class DomainChecker # ... private def get_token WsAuthentication.new(AppConfig.wsauthentication.url) .authenticate(AppConfig.wsauthentication.user, AppConfig.wsauthentication.pass) end memoize :get_token end
  • 117. @Prodis Construtor recebe um hash como parâmetro, mas somente usa o valor de uma chave do hash. Problemas de DomainChecker
  • 118. @Prodis Uma implementação de método que apenas chama um método privado sem passagem de parâmetros Problemas de DomainChecker
  • 119. @Prodis Memoize hell: métodos “memoizados" que são usados como variáveis Problemas de DomainChecker
  • 120. @Prodis Um monte de ifs: ifs com ifs com else com if com else (difícil até para explicar) Problemas de DomainChecker
  • 121. @Prodis Código difícil para entender (internal x external checks) Problemas de DomainChecker
  • 122. @Prodis Um método privado não usado Problemas de DomainChecker
  • 123. @Prodis Um método de instância que cria outra instância da mesma classe Problemas de DomainChecker
  • 124. @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
  • 125. @Prodis A classe DomainChecker introduz novos padrões e princípios
  • 126. @Prodis Write-only code Uma vez escrito, ninguém mais consegue ler DomainChecker introduz novos padrões e princípios
  • 127. @Prodis Close Closed Principle Fechado para modificação, mais fechado para extensão. DomainChecker introduz novos padrões e princípios
  • 128. @Prodis Inception Pattern Onde 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
  • 129. @Prodis A classe DomainChecker provavelmente é a pior classe em Ruby que eu já vi na minha vida
  • 138. @Prodis Por que simplificar se você pode complicar? Causas de POG
  • 139. @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
  • 140. @Prodis Alguns mistérios da mente humana (que não podemos explicar) Causas de POG
  • 144. @Prodis Mas não aprenda somente Ruby Como evitar POG?
  • 146. @Prodis Use revisão de código Como evitar POG?
  • 147. @Prodis Leia código de outros programadores Como evitar POG?
  • 148. @Prodis Escreva código que você mesmo irá conseguir ler no futuro Como evitar POG?
  • 149. @Prodis Participe de projetos open source: contribuindo, discutindo, lendo código. Como evitar POG?
  • 150. @Prodis Coaching de programadores menos experientes (ensinar é uma boa maneira de aprender também) Como evitar POG?
  • 151. @Prodis Não escreva código para você: escreva para a aplicação, para sua equipe. Como evitar POG?
  • 153. @Prodis Use programação pareada (não 100% do tempo IMHO) Como evitar POG?
  • 154. @Prodis Aprenda com erros: seus e dos outros Como evitar POG?
  • 155. @Prodis Encare código ruim como uma oportunidade de melhorar Como evitar POG?
  • 156. @Prodis Não encare código ruim com reclamações ou tirando sarro dos autores Como evitar POG?
  • 157. @Prodis Este “código engraçado” causa desperdício de tempo, recursos e dinheiro Como evitar POG?
  • 158. @Prodis Ao invés disso, mostre para os autores do código ruim o caminho certo Como evitar POG?
  • 159. @Prodis Mostre a eles o Ruby way Como evitar POG?