Show full content
Mais um post para funcionar de memória e não mais ficar buscando como fazer para mover a última linha de um arquivo para o topo.
$ sed '1h;1d;$!H;$!d;G' file
Referências:
Mais um post para funcionar de memória e não mais ficar buscando como fazer para mover a última linha de um arquivo para o topo.
$ sed '1h;1d;$!H;$!d;G' file
Referências:
Nem falo nada sobre quanto tempo sem escrever nada, mas vou contar uma novidade rapidinha: Agora Gaveteiro virou NEI. Mas vamos ao que interessa nesse post.
Métodos de Classe? Bom, sabemos que isso não existe em Ruby. Na verdade são métodos singleton da classe, mas enfim, vamos abistrair essa parte.
Bom, então, digamos que você vai mapear um recurso de uma API e ela tem um campo/atributo type, e essa pode retornar os valores ClientA.administrador, ClientA.manager e ClientA.buyer como valores para este atributo. Mas você quer ter um código mais limpo, algo que você possa perguntar ao seu objeto: resource.admin? em vez de ficar espalhado pelo seu código algo como resource.type == "ClientA.administrador" e etc. E o mesmo para criar uma instância desse objeto, teria que fazer algo como Resource.new(type: 'ClientA.administrador'). Não seria melhor ter algo como Resource.new_admin(...)?. Nesse caso, vou apresentar dois métodos para criar dinamicamente métodos. o define_method serve para criar os métodos de instância e o define_singleton_method para criar os “métodos de classe”.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Resource < OpenStruct
include ActiveModel::Validations
TYPES = {
"admin" => "ClientA.administrator",
"manager" => "ClientA.manager",
"buyer" => "ClientA.buyer"
}.freeze
validates :api_id, presence: true
validates :type, inclusion: TYPES.values
TYPES.each do |key, value|
# define "question" methods for each type (e.g.: def admin?; type == "ClientA.administrador"; end)
define_method "#{key}?" do
type == value
end
# define constructors for each type (e.g.: Resource.new_admin({...}))
self.define_singleton_method("new_#{key}".to_sym) do |**args|
new(args.merge(type: value))
end
end
end
Então agora, é só usar
resource = Resource.new_admin
resource.type # => it returns "ClientA.administrator"
resource.admin? # => it returns true
resource.buyer? # => it returns false
resource = Resource.new_buyer(name: "Tino")
resource.type # => it returns "ClientA.buyer"
resource.admin? # => it returns false
resource.buyer? # => it returns true
resource.name # => it returns "Tino" - YOU DON'T SAY?O que achou?
PS1: Claro que ainda estamos limitados a conhecer todos os valores que virão no atributo
type. Se quisermos que seja 100% dinâmico… (Será que rola outro post?)PS2: Deu para perceber que desde o último post, estou fazendo integrações com APIs.
Referências:
Quem nunca quis validar um JSON usando as validações do ActiveModel do Rails? Mas transformar um JSON multinível em um objeto Ruby parece ser um passo chato de realizar, né? Bom, hoje eu descobri que não.
Exemplo de um JSON bem simples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"id": "740b3e24-8686-48b6-9eb0-1b09d04bf18e",
"name": "Carl Johnson",
"document": "12345",
"addresses": [
{
"street": "414, East 137th Street",
"neiborhood": "Compton",
"city": "Los Angeles",
"state": "CA",
"zipcode": "90061"
}
]
}
Importando o JSON para uma classe
1
2
3
4
5
6
7
8
9
10
11
12
13
class AccountFromJson < OpenStruct
include ActiveModel::Validations
validates :name, :document, presence: true
end
json = File.read('./any/path/file.json')
account = JSON.parse json, object_class: AccountFromJson
account.valid? # => true
account.name # => "Carl Johnson"
account.addresses[0].street # => "414, East 137th Street"
Ah, e Também funciona se no JSON for um array.
Pois é, quem diz que macaco velho não aprende “novos” truques?
Referências:
Olha quem está vivo? Sim, sou eu. Sem mais enrolação.
Como já publicado no post do DRY config/database.yml, é possível usar aliases para evitar duplicidade de valores, mas como fazer isso para chaves simples? E porque eu tive essa necessidade?
Em uma view onde eu preciso exibir uma mensagem de disponibilidade de expedição de um produto, tenho algo como:
app/views/products/show.html
1
2
3
4
5
6
...
<dl>
<dt>Prazo para expição</dt>
<dd><%= I18n.t('.delivery_days', count: product.delivery_days) %></dd>
</dl>
...
config/locales/pt-BR.yml com repetição
1
2
3
4
5
6
7
pt-BR:
products:
show:
delivery_days:
one: Pronta Entrega
other: Disponível em %{count} dias úteis
zero: Pronta Entrega
Considerando que eu tenha que mostrar a mensagem quando da disponibilidade de entrega é de zero ou um dia, mas não quero duplicar a mensagem. O que podemos fazer?
Antes da chave que nós queremos “copiar”, criamos o alias &alias_name, e no lugar do valor que vamos repetir, usamos o *alias_name
config/locales/pt-BR.yml sem repetição
1
2
3
4
5
6
7
pt-BR:
products:
show:
delivery_days:
&oneDeliveryDay one: Pronta Entrega
other: Disponível em %{count} dias úteis
zero: *oneDeliveryDay
No exemplo acima, nós identificamos a chave :one com o alias onDeliveryDay e aplicanos na chave :zero
Senta que lá vem história.
Atualmente temos algumas ferramentas online, como Code Climate e Codacy, para fazer análise do código do nosso projeto, em busca de falhas de segurança, duplicação de código e etc. Também estamos diante da possibilidade de levantar um servidor de CI, como o jenkins, e configurar para fazer o mesmo processo. Usando essas ferramentas, nossos códigos são avaliados somente após feito o commit e enviado (git push) para o repositório “central”. No caso, havendo algum problema no resultado dos testes, podemos ser somos notificados por e-mail e, sendo isso parte do fluxo do CI, teoricamente, nossas últimas atualizações serão um bloqueio para continuar nosso trabalho em outra coisa.
Eu tenho preferido passar essas ferramentas antes de fazer o commit, aproveitando que estou com a mente fresca com as minhas últimas atualizações, ficando mais fácil resolver o problema detectado por alguma dessas ferramentas.
Com isso, resolver colocar o git hook pre_commit. No caso em questão, estou em um projeto Ruby on Rails e nele estão configurados algumas ferramentas de análise de código, como brakeman, rucobop, rails_best_practices e rubycritic.
Segue abaixo o hook pre-commit:
Outras referências:
Depois da milésia vez buscando por como usar o xargs resolvi escrever esse post para não precisar mais.
O comando xargs é utilizado para, dado uma lista de argumentos, executar um comando passando essa lista como argumento(s) desse comando. Então, você pode juntar todos os itens da lista para serem passados como argumentos de um comando, ou para que se execute um comando por item da lista. Por padrão, cada termo separado por espaço é considerado um item de lista e toda a lista é passada como argumentos para o comando.
Sintaxe com os parâmetros que mais eu uso:
command-list | xargs [-0 | -n NUM] [-I NAME] [-p] [command-exec [args]]command-list
Qualquer comando que gere uma lista. Pode ser ls, cat, echo, find e etc…
-0
Muda o separador padrão para o caracter nulo. Muito útil quando os itens da lista possuem espaço.
-n NUM
Pega NUM itens da lista para executar o comando.
-I NAME
Dá um nome para o(s) item(ns) a ser passado para o comando.
-p
Exibe o comando com os argumentos que serão executados e pede confirmação para executar o comando.
command-exec [args]
Comando a ser executado com os argumentos lidos pelo xargs. Por padrão, é usado um echo
Exemplos:
$ seq -s ' item\n' 3 | xargs
1 item 2 item 3 item
$ seq -s ' item\n' 3 | xargs -0
item 1
item 2
item 3
$ seq -s ' item\n' 3 | xargs -n 1
1
item
2
item
3
item
$ seq -s ' item\n' 3 | xargs -n 2
item 1
item 2
item 3
$ seq -s ' item\n' 3 | xargs -n 3
1 item 2
item 3 item
$ seq -s ' item\n' 3 | xargs -p
/bin/echo 1 item 2 item 3 item?...
$ seq -s ' item\n' 3 | xargs -INUM echo NUM arg
1 item arg
2 item arg
3 item argAgora espero não mais buscar por isso.
Segue algumas referências que usei:
Hoje (30/abril/2015) foi meu último dia no Hotel Urbano (HU). Muita coisa mudou em menos de um ano, onde era uma empresa que era 100% PHP e hoje trabalha com várias tecnologias, como PHP, Ruby, Python, NodeJS, Scala e Java. Tive o prazer de trabalhar em diversas equipes, com diversas pessoas, tudo gente boa, tanto no pessoal como no profissional. Um obrigado especial ao Bruno que me convidou para me juntar ao HU a um ano atrás. Desejo todo sucesso a todos e ao HU (uhull - \o/)
Um novo ciclo começa e com isso, um novo desafio. Estou me juntando ao time do Gaveteiro, a convite do Luiz Rocha.
Essa é mais para ficar para lembrança do que uma dica, mas enfim, sempre que vou começar um novo projeto em Ruby, eu costumo a usar o RVM (sim, ainda o RVM) e gosto de separar os projetos por gemsets (sim, isso também ocupa mais espaço).
Para criar um novo gemset, na versão do Ruby correta e os arquivos de carregamento automático, executo o comando abaixo:
$ rvm 2.2.1@project_name --create --ruby-versionPronto!
As dicas abaixo foram extraídas do post 10 tips for writing efficient Bash scripts.
Usar Array em lugar de múltiplas variáveisAo invés de criar muitas variáveis para valores em um mesmo contexto.
1
2
3
4
5
6
color1='Red'
color2='Green'
color3='Blue'
echo $color1
echo $color2
Crie um array.
1
2
3
4
$colors=('Red' 'Green' 'Blue')
echo ${colors[0]}
echo ${colors[1]}
Precisa de um arquivo temporário? Use mktemp para criar arquivos ou diretórios temporários.
1
2
3
4
5
tempfile=$(mktemp)
tempdir=$(mktemp -d)
echo $tempfile
echo $tempdir
PS: No FreeBSD (OSX), tem que passar um template para o nome do arquivo/diretório
Nesta minha última saga em busca de uma nova oportunidade, entre conversas em várias empresas, startups e amigos, minha decisão foi ingressar no Hotel Urbano, a convite do Bruno, onde pessoalmente o grande desafio será a tecnologia, pois a linguagem base usada no HU é PHP e nos últimos 7 anos venho trabalhando com Ruby (ou rãby?). Também serão outros desafios que no momento não estou habilitado a compartilhar, mas esperamos a curto prazo compartilhamos o sucesso desses desafios.
Então, este post é de agradecimento a todos que me indicaram, outros que se despuseram em me ouvir oferecendo uma oportunidade de me juntar a seus negócios e, principalmente, ao Bruno por efetivamente me aceitar a unir ao time do HU.
PS: Espero não fazer PHuby on PHails :P
Cansei de criar git branches assim:
$ git checkout -b fix/some_bugfix_with_long_name
Agora é só:
$ fix-branch some bugfix with long name
Segue abaixo minhas novas funções bash:
function fix-branch() {
local new_branch_name=$(echo "$*" | tr " " _)
git checkout -b fix/$new_branch_name
}
function feature-branch() {
local new_branch_name=$(echo "$*" | tr " " _)
git checkout -b feature/$new_branch_name
}
Mudei o tema do blog apenas por luto pelo falecimento de Jim Weirich,
criador do Rake e outras ferramentas incriveis para Ruby.
Também rolou uma homenagem da comunidade no último commit do Jim no github.
Descanse em paz e força aos familiares!
Como em toda casa de ferreiro, nem sempre o espero é de ferro.
Em nosso dia a dia, as vezes precisamos fazer umas intervenções via console do Rails e, com isso, temos alguns métodos auxiliares para fazer tarefas simples, porém rotineiras.
Entre essas rotinas, fazemos busca de uma conta através do e-mail de um usuário e então atualizamos a informação necessária. Como as vezes temos somente o e-mail do usuário, precisamos fazer uma busca do usuário pelo e-mail e, dado o usuário, obtemos a lista de contas e então, temos a conta para fazer o ajuste. Outras vezes já temos o ID da conta a ser ajustada, o que agiliza o processo. Para auxiliar essa busca, estava criando mais um método para encontrar a conta, dado o e-mail do usuário, algo como:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ./lib/console_helpers.rb
module ConsoleHelper
def update_info(id_or_email, new_data = {})
account = Account.find_by_id(id_or_email)
unless account
user = User.find_by_email!(id_or_email)
raise "User have more than 1 account: #{user.account_ids}" if user.accounts.count > 1
account = user.accounts.first
end
account.update_info(new_date)
puts "New data for account##{account.id}: #{new_date.inspect}"
end
end
# ./config/application.rb
console do
Rails::ConsoleMethods.send :include, ::ConsoleHelpers
end
Neste caso, quando entro no console, posso facilmente fazer:
=> update_info 'johndoe@domain.com', {attribute: 'new_value'}
New data for account #123: {:attribute => 'new_value'}
Porém, fazendo os testes no console (após os testes automatizados, claro!), ao passar um número de ID que não existia (número zero), fui surpreendido com a atualização de uma conta que eu não esperava.
=> update_info 0, {attribute: 'new_value'}
New data for account #456: {:attribute => 'new_value'}
O que notei, foi que a busca do MySQL retornou um usuário, cujo o e-mail dele era 00022e34f23a72@domain.com, pois a query SQL criada foi:
SELECT `users`.* FROM `users` WHERE `users`.`email` = 0 ORDER BY email ASC LIMIT 1;
Ou seja, o MySQL tem o mesmo comportamento do Ruby, ao tentar converter uma string para número.
=> '102-string'.to_i # results: 102
102
Logo, fiz o ajuste na função para converter para string na busca por e-mail.
1
2
3
4
5
6
7
8
9
10
11
12
13
# ./lib/console_helpers.rb
module ConsoleHelper
def update_info(id_or_email, new_data = {})
account = Account.find_by_id(id_or_email)
unless account
user = User.find_by_email!(id_or_email.to_s)
raise "User have more than 1 account: #{user.account_ids}" if user.accounts.count > 1
account = user.accounts.first
end
account.update_info(new_date)
puts "New data for account##{account.id}: #{new_date.inspect}"
end
end
Não caia da mesma armadilha.
Em agosto, minha família e eu, voltamos a morar na Cidade Maravilhosa e desde então, estou trabalhando na Myfreecomm, no produto MyFinance.
Com isso, deixamos para trás a Terra da Garoa e muito amigos que vão deixar saudades do convívio diário, principalmente os com espírito Webco, que já espalharam mundo afora.
Quero agradecer a toda família Balbino todos que conviveram comigo durante esses 5 anos paulistamos, em especial: Rocha, Nando, Zagari, Cipriani, Pluguinho, MV, Fais, GB, Hungaro, Shiota, Bibinha, Fabinho.
Esse mundo é pequeno e em breve estaremos juntos novamente…
O que é bom, tem que ser divulgado!
Precisamos do seu feedback, para saber no que podemos melhorar para o próximo DEV in SAMPA.
Por favor, acesse http://say2me.com.br/devinsampa e responda ao questionário.
Obrigado.
Para tirar um pouco a poeira do site, segue abaixo um anúncio.
Este ano, o DEVinSAMPA vai acontecer dia 18 de maio e as inscrições estão abertas através do site da Eventick.
Para ter mais informações sobre a programação, acesse: http://devinsampa.com.br#agenda
Não vai ficar de fora!
ATUALIZAÇÃO 1: Se você, como nós, tem uma gem pública, existe a possibilidade de usar o Travis CI para verificação em várias versões de Rails e Rubies.
ATUALIZAÇÃO 2: deve-se usar o comando DO do RVM https://rvm.io/set/do/
Preciso verificar se o Brazilian Rails funciona em várias versões do Rails, 2.3.x, 3.0.x, 3.1.x e 3.2.x e Ruby 1.8.7 e 1.9.x. Como faz?
Com RVM, seria:
$ rvm 1.8.7@rails23x,1.8.7@rails30x,1.8.7@rails31x,1.8.7@rails32x,[até],1.9.3@rails32x do rake
Claro, existe um script automatizando isto, mas ainda sim é feio d+.
Uma solução, seria usar o infinity_test, mas o Brazilian Rails tem testes em TestUnit e também em RSpec e como o infity_test é para execução em apenas um “ambiente”, não sei se irá funcionar (não testei)
Outra é usar o Appraisal, que se integra com o Bundler e Rake, o que fica bastante flexível.
Seguindo os passos conforme indicado no README do projeto, no meu ambiente fico com apenas um gemset por versão de Ruby.
$ rvm 1.8.7@brazilianrails,1.9.1@brazilianrails,1.9.2@brazilianrails,1.9.3@brazilianrails do rake test_spec
Para quem não separa as instalações por gemset, usando apenas o default, fica ainda mais limpo.
$ rvm 1.8.7,1.9.1,1.9.2,1.9.3 do rake test_spec
E se seguir a sugestão de David Czarneck, basta criar uma tarefa Rake,
1
2
3
4
5
6
7
8
9
10
11
# Rakefile
desc "Ruby test units and RSpec"
task :test_spec do
Rake::Task.execute["test"]
Rake::Task.execute["spec"]
end
desc "Runs tests on Ruby 1.8.7 and 1.9.2"
task :test_all do
system "rvm 1.8.7@brazilianrails,1.9.1@brazilianrails,1.9.2@brazilianrails,1.9.3@brazilianrails do rake test_spec"
end
e executar:
$ rake test_all
Referências:
Basta apenas adicionar uma configuração, em uma variável de ambiente
$ heroku config:add TZ=America/Sao_Paulo
Adding config vars and restarting app... done, v24
TZ => America/Sao_Paulo
$ heroku run console
Running console attached to terminal... Time.oup, run.1
Time.now
irb(main):001:0> Time.now
=> 2012-05-14 09:51:51 -0300
A lista de timezones disponíveis está na Wikipedia.
Referências:
Digamos que por motivo de segurança, resolveram mudar o número da porta SSH e você não consegue mais fazer um simples git pull então, basta adicionar o prefixo do protocolo ssh:// e então adicionar o número da porta. Considerando que a porta agora é 2222.
$ git clone ssh://user@git.repo:2222/path/repo.git
Referências: