Este é provavelmente um dos assuntos mais confusos para quem está
iniciando com Ruby on Rails. Antigamente, as regras eram simples:
- coloque todos os seus assets (imagens, stylesheets e javascripts) organizados nas pastas public/images, public/stylesheets, public/javascripts;
- utilize helpers como image_tag, stylesheet_link_tag e javascript_include_tag;
- configure seu servidor web (Apache, NGINX) para servir URIs como /images/rails.png diretamente de public/images/rails.png para não precisar passar pelo Rails;
Pronto, está tudo preparado para funcionar. Porém, existiam e ainda
existem muitas situações que essa regra não cobria e diversas técnicas,
“boas práticas” e gems externas precisaram ser criadas para resolvê-las.
Em particular, temos as seguintes situações cotidianas em
desenvolvimento web:
- quando se tem muitos assets, como javascripts, é considerado boa
prática “minificá-los”, ou seja, otimizar ao máximo a quantidade de
bytes eliminando supérfluos como espaços em branco e quebras de linha,
nomes de variáveis e funções longas, etc. E além disso concatenar a
maior quantidade de arquivos num único quanto possível. Em
desenvolvimento, precisamos ter todos abertos e individuais para
facilitar o debugging, mas em produção o correto é “compilá-los”
- cache precisa ser usado o máximo possível e escrever o caminho a um path manualmente, como <img src=“/images/rails.png”/>
é ruim, pois se precisarmos mudar o conteúdo dessa imagem, os usuários
precisariam limpar seus caches pois o correto é configurarmos os
servidores web com diretivas para manter assets no cache local por um
longo período de tempo (1 ano ou mais). Helpers como image_tag criavam caminhos como <img src=“/images/rails.png?12345678”/>,
sendo esse número derivado do timestamp de modificação do asset. Assim,
se o asset era atualizado esse número mudava. Mas isso não funciona bem
com muitos tipos de caches e proxies, que ignoram o que vem depois do
“?”
- quando uma página possui dezenas ou às vezes centenas de pequenas
imagens e ícones (setas, botões, logotipos de seção, linhas, bordas,
etc), o correto é usar a mesma técnica que usamos com stylesheets e
javascripts: concatenar muitas imagens em um único arquivo maior e então
utilizar CSS para manipular a posição x e y dentro dessa única imagem grande para posicioná-la corretamente onde precisamos.
- começamos a utilizar vários tipos diferentes de geradores de templates, como LESS e SASS para gerar stylesheets, CoffeeScript para gerar Javascript, além do próprio ERB para adicionar conteúdo dinâmico nos templates.
Para resolver essas e outras situações é que foi criado o chamado
Asset Pipeline,
que é um conjunto de bibliotecas e convenções para resolver o problema
de assets da melhor forma possível. O Asset Pipeline sozinho não resolve
tudo, ele é um framework para que seja possível integrar diferentes
soluções de forma organizada.
Tudo que será explicado neste artigo vale para o Rails 3.2 e
superior, existem diferenças importantes nas versões anteriores que não
serão tratadas aqui. Leia o Rails Guides, especialmente os Release Notes
de cada versão.
Iniciando um projeto Rails
Quando iniciamos um novo projeto com o comando
rails new novo_projeto, o primeiro arquivo que você vai querer mexer é o
Gemfile:
# Original
group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', :platforms => :ruby
gem 'uglifier', '>= 1.0.3'
end
# Recomendado para iniciar
group :assets do
gem 'sass-rails'
gem 'compass-rails'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
gem 'therubyracer', :platforms => :ruby
gem 'uglifier'
end
|
Não vamos entrar na
controvérsia agora sobre CoffeeScript. Se você está iniciando, esqueça CoffeeScript por enquanto para não complicar ainda mais. Por outro lado, usar o
Compass e particularmente o
Compass Rails é algo que nem precisamos discutir já que o Compass provê diversos mixins de Sass muito úteis.
Aliás, se você ainda não conhece
SASS, faça a você mesmo um favor e aprenda. Se você entende
CSS, não vai ter problemas entendendo Sass, em particular a versão “
SCSS” ou “Sassy
CSS” que não é mais do que um conjunto acima do CSS3. Lembrando que mesmo escolhendo usar
SASS podemos misturar arquivos
.css.scss e arquivos convencionais
.css no mesmo projeto.
Ao modificar o arquivo
Gemfile, lembre-se de executar os seguintes comandos no terminal:
Para exercitar, vamos criar um simples controller com uma única
página dinâmica para entender o que podemos fazer com isso. De volta ao
terminal faça o seguinte:
rm public/index.html
bundle exec rails g controller home index
|
O resultado será:
create app/controllers/home_controller.rb
route get "home/index"
invoke erb
create app/views/home
create app/views/home/index.html.erb
invoke test_unit
create test/functional/home_controller_test.rb
invoke helper
create app/helpers/home_helper.rb
invoke test_unit
create test/unit/helpers/home_helper_test.rb
invoke assets
invoke js
create app/uploads/javascripts/home.js
invoke scss
create app/uploads/stylesheets/home.css.scss
|
E para combinar, já que estamos recomendando
SCSS, vamos apagar o arquivo
app/stylesheets/application.css e criar um novo:
rm app/stylesheets/application.css
touch app/stylesheets/application.css.scss
|
E nesse novo arquivo podemos colocar somente:
Outra boa prática é ignorar o diretório
public/uploads do repositório Git (você utilizar
Git, correto?). Faça o seguinte:
echo "public/uploads" >> .gitignore
|
E agora já podemos iniciar o servidor local de Rails e examinar o que temos até agora:
Processo de Pré-Compilação
Resumidamente, em termos de assets temos os seguintes principais elementos e estrutura:
app
assets
images
rails.png
javascripts
application.js
home.js
stylesheets
application.css.scss
home.css.scss
views
home
index.html.erb
layouts
application.html.erb
config
application.rb
public
assets
Gemfile
Gemfile.lock
|
O código fonte do layout
app/views/layouts/application.html.erb contém o seguinte:
<!DOCTYPE html>
<html>
<head>
<title>NovoProjeto</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
|
Se você já tinha visto até o Rails 2.x, um layout padrão
ERB não é tão diferente. Com o servidor de pé, em ambiente de desenvolvimento, vejamos o
HTML gerado ao abrir
http://localhost:3000/home/index:
<!DOCTYPE html>
<html>
<head>
<title>NovoProjeto</title>
<link href="/uploads/application.css" media="all" rel="stylesheet" type="text/css" />
<script src="/uploads/jquery.js?body=1" type="text/javascript"></script>
<script src="/uploads/jquery_ujs.js?body=1" type="text/javascript"></script>
<script src="/uploads/home.js?body=1" type="text/javascript"></script>
<script src="/uploads/application.js?body=1" type="text/javascript"></script>
<meta content="authenticity_token" name="csrf-param" />
<meta content="OFmZwwtshevVgcs1DUg56WVIQ8NcJZsri/nUubhEJCk=" name="csrf-token" />
</head>
<body>
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
</body>
</html>
|
No
HTML gerado, note que os links para os assets apontam todos para
/uploads. Além disso note que a chamada
javascript_include_tag(“application”) expandiu para 4 javascripts diferentes. Para entender isso, precisamos examinar mais de perto o arquivo
app/uploads/javascripts/application.js:
...
//= require jquery
//= require jquery_ujs
//= require_tree .
|
Sobre o detalhe do jQuery, todo novo projeto Rails tem declarado
gem ‘jquery-rails’ no
Gemfile.
Os arquivos
application.∗ podendo “∗” ser “js”, “js.coffee”,
“css”, “css.scss”, “css.sass”, “js.erb”, “css.erb”, etc. Eles são
conhecidos como “Manifestos”. São arquivos “guarda-chuva” que declaram
todos os outros arquivos que eles dependem, em ordem, para serem
concatenados em um único arquivo ao serem compilados.
No exemplo padrão, no
application.js a primeira e segunda linha com
require declaram o
jquery.js e depois o
jquery_ujs.js e a terceira linha com
require_tree . manda carregar todos os outros arquivos javascripts no mesmo diretório que, por acaso, tem o
home.js criado pelo gerador de controller que usamos antes. Agora vejam novamente o
HTML
gerado e verá que são exatamente os javascripts carregados na ordem que
expliquei, sendo o quarto o próprio conteúdo do arquivo
application.js.
Normalmente usar o
require_tree . não é exatamente ruim se
os javascripts não dependem da ordem de carregamento, mas você
provavelmente vai querer declarar explicitamente coisas como plugins de
jQuery para garantir que eles estão carregados antes de poder usá-los.
Para explicar como tudo isso funciona é importante pararmos o servidor Rails que subimos antes e reexecutá-lo em modo produçao:
bundle exec rails s -e production
|
Agora, se tentarmos carregar a mesma
URL http://localhost:3000/home/index no browser, receberemos um erro 500 com o seguinte backtrace:
Started GET "/home/index" for 127.0.0.1 at 2012-07-01 03:31:55 -0300
Connecting to database specified by database.yml
Processing by HomeController#index as HTML
Rendered home/index.html.erb within layouts/application (12.0ms)
Completed 500 Internal Server Error in 155ms
ActionView::Template::Error (application.css isn't precompiled):
2: <html>
3: <head>
4: <title>NovoProjeto</title>
5: <%= stylesheet_link_tag "application", :media => "all" %>
6: <%= javascript_include_tag "application" %>
7: <%= csrf_meta_tags %>
8: </head>
app/views/layouts/application.html.erb:5:in `_app_views_layouts_application_html_erb__408740569075721590_70099961775620'
|
Este é o sinal que não realizamos um passo importante que deve ser
executado toda vez que você realizar uma atualização em produção:
pré-compilar os assets. É o processo que lê os arquivos manifesto e
realiza a concatenação dos arquivos declarados e sua minificação
(utilizando a gem
Uglifier). Portanto, precisamos executar o seguinte:
bundle exec rake assets:precompile
|
Lembrando que antes disso o diretório
public/uploads estava
originalmente vazio (e em desenvolvimento, você deve garantir que esse
diretório esteja sempre vazio, já explicamos porque). Após executar a a
pré-compilação, esse diretório terá os seguintes arquivos:
application-363316399c9b02b9eb98cd1b13517abd.js
application-363316399c9b02b9eb98cd1b13517abd.js.gz
application-7270767b2a9e9fff880aa5de378ca791.css
application-7270767b2a9e9fff880aa5de378ca791.css.gz
application.css
application.css.gz
application.js
application.js.gz
manifest.yml
rails-be8732dac73d845ac5b142c8fb5f9fb0.png
rails.png
|
E para entender vejamos o código-fonte do
HTML gerado em produção:
<!DOCTYPE html>
<html>
<head>
<title>NovoProjeto</title>
<link href="/uploads/application-7270767b2a9e9fff880aa5de378ca791.css" media="all" rel="stylesheet" type="text/css" />
<script src="/uploads/application-363316399c9b02b9eb98cd1b13517abd.js" type="text/javascript"></script>
<meta content="authenticity_token" name="csrf-param" />
<meta content="OFmZwwtshevVgcs1DUg56WVIQ8NcJZsri/nUubhEJCk=" name="csrf-token" />
</head>
<body>
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
</body>
</html>
|
Compare este
HTML com o anterior que analisamos gerado em ambiente de desenvolvimento. Em vez de 1 arquivo
CSS e 4 Javascripts, temos apenas 1
CSS e 1 Javascript.
Para entendermos melhor, vejamos o que tem no arquivo
public/uploads/manifest.yml:
rails.png: rails-be8732dac73d845ac5b142c8fb5f9fb0.png
application.js: application-363316399c9b02b9eb98cd1b13517abd.js
application.css: application-7270767b2a9e9fff880aa5de378ca791.css
|
Ou seja, o arquivo
application.js é idêntico ao
application-363316399c9b02b9eb98cd1b13517abd.js. Se algum dos arquivos declarados no manifesto
app/uploads/javascripts/application.js mudar, esse número sufixo irá mudar e o
HTML
apontará para o novo. Olhando novamente nossa lista de situações que
precisam ser solucionadas, que apresentamos no início do arquivo, temos
já até aqui a solução de 3 dos pontos:
- o ponto 1 explica o problema que é sempre melhor ter apenas um único arquivo de CSS
ou JS do que dezenas deles separados, pois o navegador só precisa ter o
peso de pedir um único arquivo (quanto mais arquivos, independente do
tamanho, mais tempo vai demorar para a página renderizar). Além disso,
graças ao Uglifier teremos esses arquivos “minificados”, ou seja,
reescritos de forma a minimizar seu tamanho em bytes sem modificar a
lógica da programação. Além disso, o pipeline vai um passo além e tem as
versões de todos esses arquivos com extensão “.gz” que significa
“gzip”. Se o browser fizer uma requisição dizendo que aceita conteúdo
compactado em formato zip, se o web server disser que entende zip, ele
pode diretamente enviar a versão do arquivo com extensão “.gz”. No
exemplo acima, isso significa enviar um JS de 34kb em vez dos 98kb
descomprimidos.
- o ponto 2 explica o problema de quanto assets mudam mas o browser guarda em cache baseado na URL que ele carregou. Se ele pedisse http://localhost:3000/uploads/application.js,
mesmo se o JS fosse modificado, o browser não pediria novamente porque a
boa prática diz que o web server deveria enviar cabeçalho dizendo para
esse tipo de arquivo ficar em cache por 1 ano. Mas como o HTML na realidade pede por http://localhost:3000/uploads/application-363316399c9b02b9eb98cd1b13517abd.js,
e se o JS mudar, esse número vai mudar também, não importa mais que
esses assets fiquem indefinidamente em cache, pois da próxima vez que
precisar dar versão mais nova, o nome do arquivo será completamente
diferente do que estava no cache.
- finalmente, o ponto 4 explica sobre os diferente geradores de assets. Implicitamente já podemos ver isso no caso do SASS, onde arquivos com extensão “.css.scss” são convertidos em “.css”.
Para reforçar o ponto 2, vamos adicionar a seguinte função no arquivo
app/uploads/javascripts/application.js:
function helloWorld() {
console.log("Hello World");
}
|
Agora executamos a pré-compilação novamente:
bundle exec rake assets:precompile
|
O que temos no diretório
public/uploads será:
application-363316399c9b02b9eb98cd1b13517abd.js
application-363316399c9b02b9eb98cd1b13517abd.js.gz
application-4fee97e9e402a9816ab9b3edf7a4c08b.js
application-4fee97e9e402a9816ab9b3edf7a4c08b.js.gz
application-7270767b2a9e9fff880aa5de378ca791.css
application-7270767b2a9e9fff880aa5de378ca791.css.gz
application.css
application.css.gz
application.js
application.js.gz
manifest.yml
rails-be8732dac73d845ac5b142c8fb5f9fb0.png
rails.png
|
Como não limpamos o diretório antes, temos a versão antiga e a recente. Compare, a anterior se chamava
application-363316399c9b02b9eb98cd1b13517abd.js e a nova com a função de demonstração se chama
application-4fee97e9e402a9816ab9b3edf7a4c08b.js. Reiniciando o servidor Rails no ambiente de produção e vendo o novo
HTML gerado, verá este trecho:
<script src="/uploads/application-4fee97e9e402a9816ab9b3edf7a4c08b.js" type="text/javascript"></script>
|
Espero que esse detalhamente deixe bem claro o objetivo do pipeline
de pré-compilação e as convenções de nomenclatura e quais problemas ele
soluciona. Num artigo que escrevi recentemente chamado
Enciclopédia do Heroku
explico como mover esses assets pré-compilados para uma conta na Amazon
S3, com o objetivo de descarregar processamento do seu servidor de
aplicação, servindo a partir de um
CDN, o que deve melhorar a experiência do usuário final ao carregar assets de um servidor mais próximo.
Ambientes de Desenvolvimento e Produção
Agora entenda uma regra básica:
- toda URL que você passar ao Rails é analisada pelo sistema de roteamento, que você configura em config/routes.rb
- a exceção é tudo que estiver no diretório public, graças ao middleware Rack::Static.
Ou seja, se você pedir por
http://localhost:3000/uploads/application.js isso nem vai passar pelo Rails porque, como vimos na listagem do diretório
public/uploads acima, esse arquivo existe lá e, portanto, será servido diretamente.
Por isso a recomendação, depois do último exercício com pré-compilação é apagar tudo:
Desta forma, quando você subir o servidor Rails no ambiente padrão de desenvolvimento, a mesma
URL irá passar pelo Rails, pelo Asset Pipeline, e processará o arquivo
app/uploads/javascripts/application.js.
Assim, toda vez que fizer modificações e recarregar a página no
navegador, você verá as mudanças imediatamente. Se estivesse
précompilado, veria a versão antiga, sem as modificações novas.
A compilação em tempo real só acontece em desenvolvimento pois,
obviamente, é um processo lento. Em produção, ninguém em sã consciência
vai alterar nenhum arquivo direto em servidor de produção, portanto é
importante ter tudo précompilado em
public/uploads para economizar o aplicativo Rails do trabalho.
Outra coisa importante, os arquivos de assets que se chamam
“application”, são automaticamente compilados. Mas digamos que você tem
um stylesheet que não vai carregar no layout principal, mas só em
algumas outra seções, digamos que se chame
special.css, ou seja, precisamos encontrá-lo em
http://localhost:3000/uploads/special.css, criamos nesta estrutura:
app
assets
stylesheets
special.css
|
Porém, se reexecutarmos o rake task
assets:precompile novamente, verá que esse arquivo não aparece em
public/uploads.
Recapitulando, arquivos “application” são automaticamente usados na
précompilação. Todo asset declarado nesses manifestos, será usado dentro
dos arquivos minificados “application”. Agora, para adicionar arquivos
extras, precisamos declará-los manualmente em
config/application.rb, adicionado uma linha assim:
config.assets.precompile += %w(special.css)
|
Colocar tudo no “application” e carregar esse arquivo no layout principal para que toda página tenha tudo? Ou separar
CSS e
JSS
que é necessário seção a seção do site? Não tenho uma regra rígida. Se a
aplicação não for tão grande assim, provavelmente centralizar tudo em
“application” é a saída mais simples de gerenciar. Porém se a aplicação
for ou muito grande, ou se determinada seção é relativamente mais pesada
em JS que as demais, por exemplo, talvez valha a pena gerenciar assets
em separado, usando a configuração acima.
Agora sim, se executar a task de precompile novamente, teremos algo como:
special-f7bf76f875ce5c9edd0075eaea3f6140.css
special-f7bf76f875ce5c9edd0075eaea3f6140.css.gz
special.css
special.css.gz
|
Compass e SASS
Agora, por que adicionamos o Compass? Não vou fazer desta seção um
tutorial de Compass, mas para que entendem o básico, podemos adicionar o
seguinte
CSS de exemplo em
app/uploads/stylesheets/application.css.scss:
@import "compass";
.box {
font {
family: Lucida Grande;
size: 12px;
}
width: 400px;
padding: 15px;
border: 1px solid black;
@include text-shadow(1px 0 1px opacify(#5c5c5c, .37));
@include box-orient(horizontal);
@include border-radius(20px, 20px);
}
|
Na maior parte parece um
CSS normal, mas a diferença maior está nos comandos
@include do Sass, que serve para carregar “Mixins”. Pense em Mixin no Sass como “funções” que retornam
CSS parametrizável e reusável. O Compass é uma biblioteca de Mixins que encapsulam alguns dos aspectos mais comuns de
CSS. No exemplo, estamos chamando os mixins
text-shadow e
border-radius, cuja função deve ser bastante óbvia. Vamos ver o
CSS que isso gera:
/* line 3, ../../app/uploads/stylesheets/application.css.scss */
.box {
width: 400px;
padding: 15px;
border: 1px solid black;
text-shadow: 1px 0 1px #5c5c5c;
-webkit-box-orient: horizontal;
-moz-box-orient: horizontal;
-ms-box-orient: horizontal;
box-orient: horizontal;
-webkit-border-radius: 20px 20px;
-moz-border-radius: 20px / 20px;
-ms-border-radius: 20px / 20px;
-o-border-radius: 20px / 20px;
border-radius: 20px / 20px;
}
/* line 4, ../../app/uploads/stylesheets/application.css.scss */
.box font {
family: Lucida Grande;
size: 12px;
}
|
Fica claro a função de “nesting”, para herdar estilos de um elemento
pai, que usamos no elemento “font” para não repetir em blocos separados,
como no
CSS final. O “text-shadow” foi
simples, mas o “box-orient” e border-radius" automaticamente adicionaram
todas as diretivas específicas de cada browser, coisas como “-webkit”,
“-moz”, etc.
Veja a documentação de
referência do Compass para encontrar todos os mixins e exemplos detalhados de como usá-los.
Isso é interessante, mas a principal função, para fechar todas as
situações que descrevi na introdução do artigo, é a que se segue.
Primeiro, vamos adicionar duas novas imagens na pasta
app/images/social-icons:
app
images
social-icons
facebook.png
linkedin.png
twitter.png
|
Agora, vamos adicionar o seguinte
HTML em
app/views/home/index.html.erb:
...
<ol class="social">
<li class="twitter"><a href="http://www.twitter.com">Twitter</a></li>
<li class="facebook"><a href="http://www.facebook.com">Facebook</a></li>
<li class="linkedin"><a href="http://www.linkedin.com">LinkedIn</a></li>
</ol>
|
Nada demais, apenas o objetivo de adicionar ícones de redes sociais com links a eles. Para estilizá-los, vamos completar nosso
SCSS assim:
@import "compass";
@import "social-icons/*.png";
...
ol.social {
@include horizontal-list;
@each $network in twitter, facebook, linkedin {
li.#{$network} a {
@include social-icons_sprite(#{$network})
}
}
a {
height: 32px;
width: 32px;
display: block;
text-indent: -9000px;
color: #FFF;
}
}
|
Novamente, estude
SASS para entender essa sintaxe e também note que novamente usamos um mixin do Compass chamado
horizontal-list. Lembre que colocamos 3 novas imagens, listados acima. Agora neste
SCSS, na segunda linha, fazemos um
@import de todos esses ícones.
Agora localize esta linha:
@include social-icons_sprite(#{$network}).
O nome da pasta, com o sufixo “_sprite” se torna um mixin, que recebe
como parâmetro o nome da imagem/sprite. O que significa isso no
CSS gerado ao final? Vejamos:
/* line 58, social-icons/*.png */
.social-icons-sprite, ol.social li.twitter a, ol.social li.facebook a, ol.social li.linkedin a {
background: url(/uploads/social-icons-s25bc94da3e.png) no-repeat;
}
...
/* line 20, ../../app/uploads/stylesheets/application.css.scss */
ol.social li.twitter a {
background-position: 0 -32px;
}
/* line 20, ../../app/uploads/stylesheets/application.css.scss */
ol.social li.facebook a {
background-position: 0 -64px;
}
/* line 20, ../../app/uploads/stylesheets/application.css.scss */
ol.social li.linkedin a {
background-position: 0 0;
}
...
|
Aqui vemos o que aconteceu: as 3 imagens separadas foram concatenadas numa única, declarada no topo do
CSS, chamada neste exemplo de
/uploads/social-icons-s25bc94da3e.png. Agora, cada uma das classes de rede social que criamos tem um reposicionamento da mesma imagem, como:
background-position: 0 -64px;.
Isto resolve o ponto 2 da introdução do artigo, que estava pendente:
gerenciamento de sprites. Dependendo da quantidade de pequenas imagens
que uma única página do seu site tem, somente este truque pode causar um
impacto positivo que seus usuários irão notar rapidamente por causa do
aumento na velocidade de renderização.
Para complementar, sempre que usarmos chamadas como
background: url(/uploads/social-icons-s25bc94da3e.png) no
CSS, nunca devemos digitar a
URL absoluta manualmente. Devemos deixar o Asset Pipeline cuidar disso, como ele fez aqui no caso dos sprites.
Vejamos um exemplo mais concreto. Vamos adicionar o seguinte ao nosso
application.css.scss:
h1 {
padding-left: 60px;
height: 70px;
background: url("/uploads/rails.png") no-repeat;
}
|
O que confunde é que isso de fato funciona. Porém, caímos nos
problema mencionados no início do artigo: sem o número timestamp, se
atualizarmos a imagem, os usuários ficarão travados na versão antiga em
cache local. Se quisermos migrar para
CDN vamos ter problemas de mudar todas essas URLs manualmente, etc. Portanto, no caso de
SASS o correto é usar a função
image-url e fazer desta forma:
background: image-url("rails.png") no-repeat;
|
Isto irá gerar a
URL correta. Temos as seguinte variações:
image-url("rails.png") # url(/uploads/rails.png)
image-path("rails.png") # "/uploads/rails.png".
asset-url("rails.png", image) # url(/uploads/rails.png)
asset-path("rails.png", image) # "/uploads/rails.png"
|
Agora, se for necessário URLs de assets dentro do Javascript, não há equivalente no
application.js puro, por isso precisaríamos renomeá-lo para
application.js.erb e então a mesma regra que usaríamos em views
HTML ERB normais valem:
var imagem = "<%= image_path("rails.png") %>";
var tag_imagem = "<%= image_tag("rails.png") %>";
var audio = "<%= audio_path("rails.mp3") %>";
var tag_audio = "<%= image_tag("rails.mp3") %>";
var video = "<%= video_path("rails.m4v") %>";
var tag_video = "<%= image_tag("rails.m4v") %>";
|
Vejam a documentação do
ActionView::Helpers::AssetTagHelper para entender melhor sobre estes helpers, mas o importante é: se estiver escrevendo a
URL de um asset manualmente, como um string, você está fazendo errado.
NGINX
Não vou entrar em detalhes sobre como configurar um
NGINX completo, mas fica um lembrete para não esquecer de adicionar à configuração do servidor o seguinte trecho:
location ~ ^/uploads/ {
expires 1y;
add_header Cache-Control public;
add_header Last-Modified "";
add_header ETag "";
break;
}
|
Isso garantirá que o browser do usuário guarde todos os assets no seu
cache local para não pedir novamente. Com o Asset Pipeline, como
explicamos exaustivamente acima, assets atualizados são imunes ao cache
local.
E para diminuir ainda mais a quantidade de bits transportado entre o
servidor e o browser dos usuários, lembre de checar se o suporte a gzip
está ativado:
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
Conclusão
Como podem ver, o Asset Pipeline é bastante simples para a grande
maioria dos casos de uso. As regras são diferentes mas simples:
- Assets devem ir todas em app/uploads
- Em desenvolvimento public/uploads deve ficar vazio. Em produção, sempre execute rake assets:precompile
- Não digite URL de assets manualmente, use os helpers de URL
Isso lhe dará, “de graça”:
- concatenação de arquivos de assets, minificação de javascript e stylesheets
- compatibilidade com caches, CDNs e suporte a gzip
- gerenciamento de sprites numa única imagem com posicionamento via CSS
- suporte a diferentes geradores de templates, como SASS
Expanda seus conhecimentos, aprenda mais sobre:
Esqueçam rumores, opiniões contrárias, rants e trolls. O Asset
Pipeline certamente não é simples. Porém está longe do bicho de sete
cabeças que costumam descrever. No início haviam muitos bugs, que já
foram corrigidos e, toda vez que alguém falar em “Asset Poopline”,
normalmente é problema de de
BIOS mais do que de ferramenta. Google e Stackoverflow, for the help.
FONTE:
AkitaOnRails
Comentários
Postar um comentário