Tag Archives: ruby

What is “frozen_string_literal” in Ruby?

ruby3

Freezing Strings feature improves apps performance by freezing Strings. So, Matz – Ruby’s creator – decided to make all String literals frozen (immutable) by default in Ruby 3.0.

In order to have a transition path to this coming big change, it was decided to have a magic comment at the beginning of files, so you can use in Ruby 2.3.

To do so, just add this comment in the first line of your files:

# frozen_string_literal: true

class YourClass
  # ...
end

Trying adding it to your spec_helper file and let me know in the comments if you performance improved!

More info: Ruby issue #8976.

If you really need to create a monkey patch, do it properly

Ruby

Sometimes we need to create a monkey patch for a gem or external lib.
In these cases, it’s good to force it to fail if the gem has been bumped up:

if Paperclip::VERSION != '1.2.3'
  # If you see this message, please test removing this file
  # If it's still required, please bump up the version above
  fail 'Please remove me, Paperclip version has changed'
end

If the gem doesn’t provide you the version through a method, you can solve it with:

if Bundler.load.specs.find { |gem| gem.name == 'paperclip' }.version.to_s != '1.2.3'
  # If you see this message, please test removing this file
  # If it's still required, please bump up the version above
  fail 'Please remove me, Paperclip version has changed'
end

Or even when you’re waiting for a new Rails version, e.g.:

fail 'Remove this file' if Rails::VERSION::MAJOR >= 5

Ps.: Paperclip gem was just an example :-)

Como aprender Ruby e Rails

ruby_rails

Vários amigos tem me perguntado como podem aprender a programar em Ruby e Rails. Vou postar aqui os mesmos vídeos e dicas que mandei pra eles, caso alguém tenha interesse também:

An awesome Wiki built with Ruby and Rails!

I’ve been worked on a small (but awesome) open-source project.

I’m talking about ruby_wiki – a simple wiki built with Ruby on Rails:
https://github.com/lucascaton/ruby_wiki

As I said before – it’s quite simple, although it works fine.
Anyway, would be nice to have more features in this project, so, if you’re a developer and have any interest in that, feel free to fork the project and contribute.

Some screenshots:


1


2


3


4


5


6

Have a Rails 2 app? You can run it on the newest Ruby!

old_rails_with_new_ruby

Do you have a legacy Rails application which is still running on Rails 2?

There are several reasons to migrate your application to new Rails versions, like to improve the security, to be able to use a better syntax, to take advantage of new features and also because most of current gems will only work on Rails 3 or higher. However, sometimes it’s hard to do that, especially for big projects. And certainly today there’re many project still running on Rails 2.

But there’s one good thing you can (and should) do! I’m talking about to use the newest Ruby version. Yes, I’m serious. When I wrote this post, the current Ruby version was 2.1.1 – and it’s not so hard to get it working fine with Rails 2.

Obviously, would be better if you have a good test coverage.

That said, let’s do it in a few steps:

Replacements

1. Gemfile

Rails 2 apps don’t use Bundler by default, so if you don’t have Bundler managing your gems yet, you should check here how to do that.

# There's no way to ensure that next Ruby versions will work,
# but so far the current one works fine:
ruby '2.1.1'

# The same for rake:
rake '10.1.1'

# You might need the iconv gem:
gem 'iconv'

2. Rakefile

# Replace:
# require 'rake/rdoctask'

# with:
require 'rake/task'

3. config.ru

# Replace:
# require 'config/environment'

# with:
require File.dirname(__FILE__) + '/config/environment'

4. FasterCSV => CSV

Replace all FasterCSV constant with CSV. Also, include require 'csv' to relevant files (or include this require to config/environment.rb).

Inclusions

5. config/environment.rb

# Include this before the `Rails::Initializer.run` line:
if RUBY_VERSION >= '2.0.0'
  module Gem
    def self.source_index
      sources
    end

    def self.cache
      sources
    end

    SourceIndex = Specification

    class SourceList
      # If you want vendor gems, this is where to start writing code.
      def search(*args); []; end
      def each(&block); end
      include Enumerable
    end
  end
end

6. config/initializers/paperclip.rb

# The patches below are needed when using an old version of PaperClip + Ruby 2.x
# https://github.com/thoughtbot/paperclip/issues/262
# https://github.com/thoughtbot/paperclip/commit/1bcfc14388d0651c5fc70ab9ca3511144c698903

module Paperclip
  class Tempfile < ::Tempfile
    def make_tmpname(basename, n)
      extension = File.extname(basename)
      sprintf('%s,%d,%d%s', File.basename(basename, extension), $$, n.to_i, extension)
    end
  end
end

module IOStream
  def to_tempfile
    name = respond_to?(:original_filename) ? original_filename : (respond_to?(:path) ? path : 'stream')
    tempfile = Tempfile.new(['stream', File.extname(name)])
    tempfile.binmode
    self.stream_to(tempfile)
  end
end

New files

7. config/initializers/ruby2.rb

# This is a very important monkey patch to make Rails 2.3.18 to work with Ruby 2+
# If you're thinking to remove it, really, don't, unless you know what you're doing.

if Rails::VERSION::MAJOR == 2 && RUBY_VERSION >= '2.0.0'
  module ActiveRecord
    module Associations
      class AssociationProxy
        def send(method, *args)
          if proxy_respond_to?(method, true)
            super
          else
            load_target
            @target.send(method, *args)
          end
        end
      end
    end
  end
end

8. config/initializers/rails_generators.rb

It’ll prevent Rails migration generator from stop working, otherwise you’ll receive the following error message:

undefined local variable or method `vars' for #<Rails::Generator::Commands::Create

(Thanks Mr. S and jnwheeler44 for helping me to fix this one)

# This is a very important monkey patch to make Rails 2.3.18 to work with Ruby 2+
# If you're thinking to remove it, really, don't, unless you know what you're doing.

if Rails::VERSION::MAJOR == 2 && RUBY_VERSION >= '2.0.0'
  require 'rails_generator'
  require 'rails_generator/scripts/generate'

  Rails::Generator::Commands::Create.class_eval do
    def template(relative_source, relative_destination, template_options = {})
      file(relative_source, relative_destination, template_options) do |file|
        # Evaluate any assignments in a temporary, throwaway binding
        vars = template_options[:assigns] || {}
        b = template_options[:binding] || binding
        # this no longer works, eval throws "undefined local variable or method `vars'"
        # vars.each { |k, v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
        vars.each { |k, v| b.local_variable_set(:"#{k}", v) }

        # Render the source file with the temporary binding
        ERB.new(file.read, nil, '-').result(b)
      end
    end
  end
end

RSpec

9. Make sure you’re using the last compatible version with Rails 2.3.18:

gem 'rspec', '1.3.2'
gem 'rspec-rails', '1.3.4'

10. Remove the file script/spec.

11. lib/tasks/rspec.rake

Remove all the following lines:

gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9
rspec_gem_dir = nil

Dir["#{Rails.root}/vendor/gems/*"].each do |subdir|
  rspec_gem_dir = subdir if subdir.gsub("#{Rails.root}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb")
end

rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec')

if rspec_gem_dir && (test ?d, rspec_plugin_dir)
  raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n"
end

if rspec_gem_dir
  $LOAD_PATH.unshift("#{rspec_gem_dir}/lib")
elsif File.exist?(rspec_plugin_dir)
  $LOAD_PATH.unshift("#{rspec_plugin_dir}/lib")
end

Ruby syntax

12. Some details has been changed in Ruby syntax, especially from 1.8.x to 1.9.x.

Example 1:

# Replace:
when 'foo': bar

# with:
when 'foo' then bar

Example 2:

The behaviour for protected methods in new Ruby versions is a little bit different. See more in this post.

# In some cases, you might need to replace:
respond_to?(:foobar)

# with:
respond_to?(:foobar, true)

Example 3 (Yaml files):

# Replace:
order: [ :day, :month, :year ]

# with:
order:
  - :year
  - :month
  - :day

Ruby changes

13. The default encoding for Ruby 2.0 (or higher) is UTF-8. So, remove all the code similar to:

# encoding: utf-8

Or:

$KCODE = 'UTF-8'

Important note (included on July 27, 2014)

Check below the comments of this post — Gabriel Sobrinho, Kyle Ries and Greg made some very interesting and useful comments.

Conclusion

Each project could have different issues.
But I hope this little guide helps you to use new Ruby versions in legacy Rails applications!

Apresentação online sobre as novidades do Rails 4

Screen Shot 2013-07-02 at 10.22.18 PM

O Rails 4 acabou de ser lançado e tem várias novidades bacanas! Aprenda o que mudou, o que são Strong Parameters, turbolinks, etags, requests com streaming e outras coisas interessantes.

Hoje fiz uma apresentação online pela Eventials e pela Locaweb sobre as novidades do Rails 4. Quem quiser assistir, a palestra está gravada e disponível no link abaixo:

https://www.eventials.com/pt-br/locaweb/o-que-ha-de-novo-no-rails-4-com-lucas-caton/

Seguem os slides:


Contar a frequência de objetos em um array usando Ruby

Uma dica rápida: às vezes precisamos contabilizar qual a frequência de objetos em um array usando Ruby.

Por exemplo, um array %w(dog dog cat) tem dois elementos “dog” e um “cat”. Para transformar isso em um hash, podemos fazer:

words = %w(dog dog cat)

Hash[words.group_by{ |w| w }.map { |w, words| [w, words.size] }]

O retorno disso seria:

{
    "dog" => 2,
    "cat" => 1
}

[#IF] Screencasts de Ruby on Rails para iniciantes

ruby_rails

Pra quem não entendeu o que é a hashtag #IF, veja esse post do Fabio Akita.

No ano passado, eu comecei a gravar alguns vídeos pra ajudar quem gostaria de começar a programar com Ruby on Rails.

Por enquanto são 3 screencasts apenas, mas já é o suficiente pra quem gostaria de começar.


Linguagem de programação “Ruby” (para iniciantes)



Ruby on Rails (para iniciantes)



Ruby on Rails – introdução às rotas


Por favor, me mandem feedback, com sugestões, críticas ou até mesmo dúvidas. Espero que gostem e que seja útil ;-)

Interesting script written in Ruby: “The Globe”

Minutes ago I received this link from some friends. It’s very interesting (and crazy).

Create an empty ruby file, paste this content and save as a.rb.

v=0000;eval$s=%q~d=%!^Lcf<LK8,                  _@7gj*LJ=c5nM)Tp1g0%Xv.,S[<>YoP
4ZojjV)O>qIH1/n[|2yE[>:ieC       "%.#%  :::##"       97N-A&Kj_K_><wS5rtWk@*a+Y5
yH?b[F^e7C/56j|pmRe+:)B     "##%      ::##########"     O98(Zh)'Iof*nm.,$C5Nyt=
PPu01Avw^<IiQ=5$'D-y?    "##:         ###############"    g6`YT+qLw9k^ch|K'),tc
6ygIL8xI#LNz3v}T=4W    "#            #.   .####:#######"    lL27FZ0ij)7TQCI)P7u
}RT5-iJbbG5P-DHB<.   "              ##### # :############"   R,YvZ_rnv6ky-G+4U'
$*are@b4U351Q-ug5   "              #######################"   00x8RR%`Om7VDp4M5
PFixrPvl&<p[]1IJ   "              ############:####  %#####"   EGgDt8Lm#;bc4zS^
y]0`_PstfUxOC(q   "              .#############:##%   .##  ."   /,}.YOIFj(k&q_V
zcaAi?]^lCVYp!;  " %%            .################.     #.   "  ;s="v=%04o;ev"%
(;v=(v-($*+[45,  ":####:          :##############%       :   "  ])[n=0].to_i;)%
360)+"al$s=%q#{  "%######.              #########            "  ;;"%c"%126+$s<<
126}";d.gsub!(/  "##########.           #######%             "  |\s|".*"/,"");;
require"zlib"||  "###########           :######.             "  ;d=d.unpack"C*"
d.map{|c|n=(n||  ":#########:           .######: .           "  )*90+(c-2)%91};
e=["%x"%n].pack   " :#######%           :###### #:          "   &&"H*";e=Zlib::
Inflate.inflate(   "  ######%           .####% ::          "   &&e).unpack("b*"
)[0];22.times{|y|   "  ####%             %###             "   ;w=(Math.sqrt(1-(
(y*2.0-21)/22)**(;   " .###:             .#%             "   ;2))*23).floor;(w*
2-1).times{|x|u=(e+    " %##                           "    )[y*z=360,z]*2;u=u[
90*x/w+v+90,90/w];s[(    " #.                        "    ;y*80)+120-w+x]=(""<<
32<<".:%#")[4*u.count((     " .                   "     ;"0"))/u.size]}};;puts\
s+";_ The Qlobe#{" "*18+ (       "#  :#######"       ;"Copyright(C).Yusuke End\
oh, 2010")}";exit~;_ The Qlobe                  Copyright(C).Yusuke Endoh, 2010

After that, just run this script using:

while true; do clear; ruby a.rb | tee b.rb; sleep 0.2; mv -f b.rb a.rb; done

Ruby 2.0 lançado! O que há de novo?

ruby2

A versão 2.0 do Ruby foi lançada há dois dias.

Eu pensei em escrever um post listando e comentando cada uma das novas features, alterações, melhorias e incompatibilidades (quase nenhuma, felizmente) da linguagem. Mas acabei encontrando dois posts muito bons que fazem exatamente isso. O primeiro utilizando exemplos de códigos e o segundo são os slides do Urabe Shyouhe. Então, não deixe de ler:

http://blog.marc-andre.ca/2013/02/23/ruby-2-by-example/
https://speakerdeck.com/shyouhei/whats-new-in-ruby-2-dot-0

A simple way to deploy your Rails applications

Sometimes I prefer to use a simpler way to deploy my Rails applications instead of install and configure some complex tool (eg.: capistrano).

A very simple way to do this is creating a shell script within the /script directory, which will access the server via SHH:

#! /bin/bash
# script/deploy.sh

TAG=deployed_at_$(date +"%F_%H-%M")
git tag -m '' -a $TAG
git push --tags

ssh user@your_domain.com << 'SSH'
  cd /var/rails_apps/my_app
  rm -rf public/assets
  git pull
  bundle install --without development test
  bundle exec rake db:migrate db:seed assets:clean assets:precompile
  touch tmp/restart.txt
  git describe > public/version
SSH

After that, a tag will be created in the git repository. Now, you know exactly the date and time when it was deployed.

And you can find out which version (tag from git) is in production accessing the URL your_domain.com/version.

your_domain

Update on March 8, 2013:

I’ve created another script which accepts the -q param (quick).
https://gist.github.com/lucascaton/5118852

Transformando um Array em um Enumerator no Ruby

Existem alguns casos em que você precisa modificar um array (aumentá-lo por exemplo) repetindo os valores presentes no array original.

Uma forma bem interessante de fazer isso é convertendo tal array para um objeto Enumerator. Para isso, existe o método Enumerator#cycle. Imagine o seguinte array:

array = [1, 3, 5, 7]

Ao rodar o método cycle, veja o que acontece:

array.cycle
#=> #<Enumerator: [1, 3, 5, 7]:cycle>

Podemos agora usar o método Enumerator#take e modificar o array original como quisermos. Por exemplo, vamos dobrar o seu tamanho:

array.cycle.take(8)
#=> [1, 3, 5, 7, 1, 3, 5, 7]

Foi criado um array do tamanho especificado no parametro do método take.

Isso é útil – por exemplo – em casos similares à esse código da gem boleto_bancario. Em algoritmos de IA também é bastante comum precisar desse tipo de modificação em arrays.

Replacing “Selenium” with “Poltergeist”

Poltergeist

My friend @pellegrino give me an awesome tip: to replace “Selenium” with “Poltergeist”. For those who don’t know, Poltergeist is a PhantomJS driver for Capybara. I’ve done it in some projects and it works so well!

So, let’s do it:

Include the gem to your Gemfile:

gem 'poltergeist'

Update Capybara configuration:

Capybara.configure do |config|
  # config.javascript_driver = :selenium
  config.javascript_driver = :poltergeist
end

Run the specs!

If you are testing some confirm() javascript method and you have a code similar to page.driver.browser.switch_to.alert.accept, you’ll got this error:

undefined method `switch_to'

While I was trying to fix it, I find out that Poltergeist always returns true from window.confirm. There’s no way (at the moment) to make it return false, but it should not prevent your test from running.

So, I just removed that line and it worked fine!

This is the improvement from the first project I’ve done it:

Before:
Finished in 1 minute 35.45 seconds

After:
Finished in 41.03 seconds

Hope that can be useful for you!

My dotfiles

Finally I created a repository with my dotfiles.

Dotfiles are configurations files, usually with their names beginning with a dot (hidden files) and located in your home directory (~/).

The repository is: https://github.com/lucascaton/dotfiles

Check the instructions in the link above, in order to get the dotfiles.

The dotfiles are focused on the Ruby language, but there’s also Git and PostgreSQL configuration files. I’ve included files as .bash_profile, .gitconfig, .irbrc (using pry), .pryrc, .psqlrc and .rvmrc. There is also a list of ruby gems (file “default.gems”).

My vimfiles can be found at: https://github.com/lucascaton/vimfiles

I have tested it only in OSX 10.7, but it should work in any *nix without problems.

Regex to match Youtube URLs (using Ruby)

I created a regular expression in order to match Youtube URLs.

Currently, the most common URLs from Youtube are http://youtube.com and http://youtu.be:

/^(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/

This regex will match:

  • http://youtube.com/watch?v=1234567890
  • https://youtube.com/watch?v=1234567890
  • http://www.youtube.com/watch?v=1234567890
  • http://www.youtube.com/watch?v=12345-67890
  • https://www.youtube.com/watch?v=1234567890
  • http://youtu.be/1234567890
  • https://youtu.be/1234567890
  • http://www.youtu.be/1234567890
  • https://www.youtu.be/1234567890
  • http://www.youtube.com/watch?v=1234567890&feature=context&context=G2de15aaFAAAAAAAAAAA
  • www.youtube.com/watch?v=1234567890
  • youtube.com/watch?v=1234567890
  • youtu.be/1234567890

And won’t match:

  • youtube.com
  • youtube.com/
  • youtube.com/watch
  • youtube.com/watch/
  • youtu.be
  • youtu.be/

Minha palestra sobre Ruby on Rails no “Café com Java”

Ontem aconteceu mais um encontro do evento “Café com Java”, na Caelum, organizado pelo Eduardo Bregaida. Eu palestrei sobre Ruby on Rails, com um ligeiro foco para o pessoal do Java.

Um obrigado especial à minha namorada Aline Nepomuceno, por filmar minha apresentação! :-)

Seguem os slides e o vídeo da palestra:

(Se não conseguir ver os slides, clique aqui)


(Se não conseguir ver o vídeo, clique aqui)

E algumas fotos:

Por que parei de usar os gemsets do RVM

Uso RVM (Ruby Version Manager) desde que este foi lançado e pretendo continuar utilizando, dada a facilidade para utilizar diferentes implementações / versões do Ruby em uma mesma máquina.

Algo muito interessante que ele traz são os gemsets, ferramenta para separar suas gems em contextos separados, geralmente um para cada projeto. Isso fazia total sentido antes do Rails chegar em sua versão 3, quando Bundler foi incluso para o gerenciamento das gems. Depois disso, apenas projetos Rails 2 teriam o problema de versões diferentes das gems.

Mas atualmente é possível usar o Bundler em projetos Rails 2, tornando os gemsets inúteis. Juntei todas as minhas gems no gemset “global”, que já vem criado por default e há 2 meses tenho usado desta forma, onde tudo tem funcionado perfeitamente – tanto projetos em Rails 3 quanto em Rails 2.

O único detalhe, é que se você tiver duas ou mais versões de alguma gem que inclui um executável (rspec por exemplo) você precisa chamá-lo da seguinte maneira:

bundle exec rspec ...

Para que o bundler chame o executável correto, baseado no seu Gemfile.
Mas eu resolvi isso com os alias:

alias rake='bundle exec rake'
alias spec='bundle exec spec'
alias rspec='bundle exec rspec'

Como vocês estão fazendo? Alguma sugestão ou crítica?

Meu ambiente de trabalho

O Anderson Casimiro e o Augusto Pascutti criaram um meme sobre ambientes de trabalho. As regras são simples:

1. Escreva sobre seu ambiente de trabalho – fale sobre qualquer ponto que quiser;
2. Indique de 3 à 5 pessoas para que possivelmente façam um artigo sobre seu ambiente.

Fui convidado pelo Bruno Codeman para montar minha lista!

1. OS: Ubuntu 10.10

Atualmente, a mais popular distriuição Linux. Como é baseada no Debian, herdou o apt pra gereciar os pacotes. Todo o meu hardware funciona bem e raramente o sistema fica lento ou trava. Ultimamente tem ficado até mais bonito…

2. IDE: Vim

Rápido, flexível, snippets, atalhos produtivos e ainda conta com excelentes plugins. Desenvolvo em Ruby e Ruby on Rails utilizando somente ele e sempre dá conta do recado.

3. Browser: Google Chrome

Há um ano atrás, o experimentei e este substituiu o meu querido Firefox. Com todas as extensions disponíveis hoje, quase não preciso de outro browser, embora exporadiamente use o Firefox pra usar os add-ons WebDeveloper e Firebug.

4. Versionamento: Git

Git ganhou meu respeito. O utilizo há 1 ano e meio e definitivamente me atendeu melhor que o SVN. Não dá pau e é muito eficiente. É atualmente um dos softwares que mais me orgulho de usar.

5. Linguagem de programação: Ruby

Já desenvolvi em PHP, Delphi, Java e até brinquei um pouco com .Net e Phython. Mas nada, nada me faz mais feliz que programar em Ruby. A sintaxe é limpa, o suporte à metaprogramação é ótimo, desenvolver usando BDD é muito fácil e a comunidade é fantástica.

6. Banco de Dados: PostgreSQL

Usei MySQL por muito tempo e me atendia muitíssimo bem. Mas ano passado tive que começar a trabalhar em alguns projetos que usavam PostgreSQL e seu poder me convenceu e se tornou meu database padrão.

Indicação de 5 amigos pra continuar o meme:

1. Eduardo Ramos
2. Vitor Laubé
3. Thiago Aléssio
4. Jésus Lopes
5. Eder Costa