Rails QR Code
Neste artigo de leitura rápida vamos implementar o uso de QR Code para uma lista de contatos.
Nos dias de hoje os QR Code (Quick Response Code) facilitam a vida de muitas pessoas.
É tão comum, que nem percebemos o quão implicito eles estão em nosso dia a dia, para citar alguns exemplos:
- Cardápio Digital
- Pagamentos
- Wi-Fi
- Check-in
- Correspondências
- Realidade Aumentada
- E a lista continua…
Ao se deparar com a imagem de um QR Code, é possível que você pense: como é possível que um monte de pixels aleatórios possam conter informações?
Bem, se você tiver curiosidade técnica, sugiro que pesquise a respeito da ISO/IEC 18004:2015.
Mas pra facilitar, posso te explicar de uma forma resumida, ao criar um QR Code, a informação é convertida e armazenada em uma matriz bidimensional. Essa matriz é então convertida em uma imagem, que pode ser lida por um leitor de QR Code.
A sua principal diferença entre um código de barras e um QR Code, é que o QR Code pode armazenar muito mais informações, e também pode ser lido em qualquer direção, enquanto o código de barras só pode ser lido na horizontal.
Portanto, vamos implementar uma aplicação Rails simples para demonstrar o uso de QR Code.
Criando a aplicação
Em seu terminal, execute o comando abaixo para criar uma nova aplicação Rails:
1
rails new rails-qrcode --css=tailwind
Agora, vamos criar um scaffold para o nosso modelo de contato:
1
rails g scaffold Contact name:string email:string phone:string
Em seguida, vamos definir as rotas da aplicação para que a página inicial seja a lista de contatos:
1
2
3
4
5
# config/routes.rb
Rails.application.routes.draw do
root to: 'contacts#index'
resources :contacts
end
Implementando a Funcionalidade
Para implementar a funcionalidade de QR Code, vamos utilizar a gem rqrcode.
Em Gemfile
, adicione as linhas:
1
2
# rqrcode [https://github.com/whomwah/rqrcode].
gem "rqrcode", "~> 2.0"
Salve o arquivo, e execute bundle
para instalar a gem.
Como o QR Code gerado será salvo em PNG, vamos utilizar recursos de ActiveStorage para armazenar o arquivo.
Em seu terminal execute:
1
rails active_storage:install
e em seguida:
1
rails db:migrate
Com isto feito, vamos definir que cada Contato pode ter um QR Code.
Em app/models/contact.rb
, adicione a linha:
1
2
3
4
# app/models/contact.rb
class Contact < ApplicationRecord
has_one_attached :qrcode
end
Certo, as coisas começam a ficar interessante a partir de agora.
Esta funcionalidade irá gerar um QR Code para cada contato, portanto, cada imagem deve ser única e ser gerada automaticamente quando um novo contato for criado.
Para que isto aconteça, vamos alterar o modelo conforme o código abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Contact < ApplicationRecord
include Rails.application.routes.url_helpers
has_one_attached :qrcode, dependent: :destroy
before_commit :generate_qrcode, on: :create
private
def generate_qrcode
# Generate QR Code
# qrcode = RQRCode::QRCode.new("http://localhost:3000/contacts/#{self.id}")
qrcode = RQRCode::QRCode.new(contact_url(self))
# Convert QR Code the PNG file
png = qrcode.as_png(
size: 200
)
# Attach the PNG to the model
self.qrcode.attach(
io: StringIO.new(png.to_s),
filename: "qrcode.png",
content_type: "image/png"
)
end
end
Vamos fazer uma pausa para entender o que está acontecendo aqui.
A instrução before_commit
é executada antes de um registro ser salvo no banco de dados.
O método generate_qrcode
é responsável por fazer a mágica acontecer.
RQRCode::QRCode.new
recebe a URL do contato como parâmetro, e gera a matriz com os dados, retornando os dados em formato RQRCode::QRCode
.
O parâmetro deste método poderia ser escrito de forma concatenada (conforme linha comentada), mas para boas práticas vamos utilizar contact_url
que é fornecido pelo UrlHelper de Rails.
Para que isto esta acessível no modelo, é necessário incluir o módulo Rails.application.routes.url_helpers
no topo do arquivo.
Certo, agora com nosso qrcode gerado, vamos converter isto para png.
Para fazer isto, utilizamos o método .as_png
que recebe como parâmetro o tamanho da imagem.
Vale destacar que o método .as_png
retorna um objeto ChunkyPNG::Image
, e que é possível informar mais argumentos para este método. (Consulte a documentação)
Importante
O objeto ChunkyPNG::Image é uma representação em memória de uma imagem PNG. Quando você chama o método .as_png
, ele retorna uma string
de bytes que representa a imagem PNG.
Por último, o PNG é anexado ao modelo através do método attach
. Neste caso, self
é representa o contato que esta sendo criado, qrcode
seu atributo, e attach
o método que anexa o arquivo.
Os parametros io
, filename
e content_type
são necessários para que o ActiveStorage possa salvar o arquivo.
Mas uma pergunta pode surgir:
Por que estamos utilizando
StringIO.new
e nãoFile.open
ouFile.new
?
A resposta para isto é porque File.open
ou File.new
seria usado se você tivesse um arquivo físico no sistema de arquivos que você queria abrir e ler.
No entanto, neste caso, a imagem PNG já está em memória como uma string de bytes, então não há necessidade de um arquivo físico.
Portanto, o que está sendo passado para o StringIO.new
é a string de bytes que representa a imagem PNG.
Isso permite que ActiveStorage crie a imagem diretamente da memória, em vez de ter que escrevê-los em um arquivo físico primeiro e depois fazer a leitura do mesmo.
Por fim, este método será executado para todos contatos que forem criados, e um único QR Code será gerado para cada um deles.
Exibindo o QR Code
Para exibir o QR Code, vamos utilizar o método image_tag
do Rails.
Em app/views/contacts/_contact.html.erb
, adicione o código logo em seguida de email:
1
2
3
4
<p class="my-5">
<strong class="block font-medium mb-1">QR Code:</strong>
<%= image_tag(contact.qrcode) %>
</p>
Para testar se tudo esta funcionando, vamos iniciar o servidor e criar um novo registro.
1
./bin/dev
O resultado deverá ser conforme o gif abaixo:
Conclusão
Ao escanear o QR Code, o link para o contato será aberto no navegador.
Portanto, adicionar a funcionalidade de QR Code em sua aplicação é muito simples, e pode ser feito em poucos minutos. Neste artigo, utilizamos o link do contato como QR Code, mas você pode utilizar a informação que desejar.
Espero que este artigo tenha sido útil para você.
Vale lembrar que este QR Code ficará hospedado em seu servidor, e que você pode utilizá-lo para diversas finalidades.
Neste cenário, como o link esta associado ao id
do contato, é possível realizar alterações no registro, como por exemplo, alterar o nome do contato, que o QR Code continuará funcionando.
Extra
Até o momento, a aplicação esta utilizando o link baseado em id
, isto é: http://127.0.0.1:3000/contacts/2, mas você pode utilizar o link baseado em slug, algo como http://127.0.0.1:3000/contacts/john-doe.
Gerando Link Personalizado
Para criar o link baseado no atributo name
, é necessário fazer algumas alterações no código.
No arquivo config/routes.rb
modifique o código para:
1
2
3
4
5
Rails.application.routes.draw do
root to: 'contacts#index'
resources :contacts, except: %i[show]
get 'contacts/:slug', to: 'contacts#show'
end
Agora, em app/controllers/contacts_controller.rb
, modifique o código para:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class ContactsController < ApplicationController
before_action :set_contact, only: %i[ edit update destroy ]
before_action :set_contact_by_slug, only: %i[ show ]
# ... index, show, new, edit - nothing change.
# POST /contacts or /contacts.json
def create
@contact = Contact.new(contact_params)
respond_to do |format|
if @contact.save
format.html { redirect_to contact_url(@contact.name.parameterize), notice: "Contact was successfully created." }
format.json { render :show, status: :created, location: @contact }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @contact.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /contacts/1 or /contacts/1.json
def update
respond_to do |format|
if @contact.update(contact_params)
format.html { redirect_to contact_url(@contact.name.parameterize), notice: "Contact was successfully updated." }
format.json { render :show, status: :ok, location: @contact }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @contact.errors, status: :unprocessable_entity }
end
end
end
# ... destroy - nothing change.
private
#... set_contact, contact_params - nothing change.
def set_contact_by_slug
name = params[:slug].tr('-', ' ').downcase
@contact = Contact.find_by('lower(name) = ?', name)
end
end
Para finalizar, em app/views/contacts/_contact.html.erb
, altere o link_to
de ‘Show this contact’ para:
1
<%= link_to "Show this contact", contact_path(contact.name.parameterize), class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
Em app/views/contacts/show.html.erb
, faça a mesma alteração, substituindo o link_to
de ‘Show this contact’ para:
1
<%= link_to "Show this contact", contact_path(@contact.name.parameterize), class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
E para que o QR Code seja gerado corretamente, em app/models/contact.rb
, altere o código para:
1
2
# Generate QR code
qrcode = RQRCode::QRCode.new(contact_url(self)) # http://localhost:3000/contacts/1
Para:
1
2
# Generate QR code
qrcode = RQRCode::QRCode.new(contact_url(self.name.parameterize)) # http://localhost:3000/contacts/john-doe
Lembre-se também de adicionar uma validação de segurança, para que não seja possível criar dois contatos com o mesmo nome, e também modificar o before_commit
com a ação :update
, para que o QR Code seja atualizado sempre que o nome do contato for alterado.
1
2
3
# app/models/contact.rb
before_commit :generate_qrcode, on: [:create, :update]
validates :name, presence: true, uniqueness: true
Pronto! Agora ao criar ou editar um registro, um novo QR Code será gerado com base no Slug, enquanto as demais ação continuam funcionando normalmente.
Repósitório no Github
Leia também