La Coctelera

in web we trust Fernando Blat's blog, developer at La Coctelera and Partigi.

Categoría: Snippets y Trucos

3 Octubre 2009

Escrito el: 3 oct 2009 @ 07:05 PM

Categorías: Snippets y Trucos

Tags: i18n

Comentarios:
4 comentarios

compártelo

DRYing a little bit the translation of your views with I18n

Since we have been using I18n in Partigi for the last months we have notice that we were always repeating ourselves when localising a sentence like this:

This film has been saved by 4 friends

What is special in this sentece is that, depending on the number of friends that saved the film, the sentence could be "This film has been saved by one friend". It can be solved with pluralize helper, but it requires the counter to be at the beginning of the sentence.

Finally we decided to adopt a small convention: if a translation has a parameter count then, if count is singular the key for the translation will be the one in the view, but if it's plural, it will be the one in the view plus a suffix _plural. For example:

Before we had:

   <% if @reviews_count == 1 %>
     <%= t('films.saved_by_friends', :count => @reviews_count) %>
   <% else %>
     <%= t('films.saved_by_friends_plural', :count => @reviews_count) %>
   <% end %>
 

And now, with this hack:

   <%= t('films.saved_by_friends', :count => @reviews_count) %>
 

The hack is this (put it where you like more):

 module I18n 
   class << self
     # Returns true if a given key exists
     def exists?(key, options = {}) 
       locale = options.delete(:locale) || I18n.locale
       backend.exists?(locale, key, options = {})
     end
     
     # Overwrite I18n.trasnlate method
     def translate(key, options = {})
       locale = options.delete(:locale) || I18n.locale
       if options[:count] && options[:count].to_i != 1
         plural_key = "#{key}_plural" 
         if exists?(plural_key)
           key = plural_key
         end
       end
       backend.translate(locale, key, options)
     rescue I18n::ArgumentError => e
       raise e if options[:raise]
       send(@@exception_handler, e, locale, key, options)
     end
   end
 end
 
 

The good thing is that if the pluralized key doesn't exists, your views won't be broken, so you can start using it now and change your views when you have time.

18 Noviembre 2008

Small tip for debugging Javascript in IE

Today we had to find a small bug in La Coctelera's javascript: the bug was a hash with a colon after the last element. The problem was that, of course, it didn't work on IE 6 and don't have any debugger for Javascript installed on my Vmware.

The only information I had was the generic error (an object was expected), and the line and column where the mistake was. So I decide to understand how to find that line in all the linked javascript files.

My conclusions are that the line could be from the HTML resulted from loading the page, or that line in one of each javascript files. It is, you have to find that line in all your javascripts and in the result page (viewing the source code) and then try to deduce which of all causes the error.

If the number of line is big enough you could reject some files, but not always you'll be so lucky.

And that's what I have learnt today.

20 Octubre 2008

Escrito el: 20 oct 2008 @ 07:34 AM

Categorías: Snippets y Trucos

Tags: apache, mongrel, thin

Comentarios:
2 comentarios

compártelo

Application servers serving static files

Since mongrel and thin appear as the people favorites application server (with all my respects to Passenger), the configuration of webservers changed in order that you have a web server plus a kind of proxy and then a lot of application servers.

The idea is that, given a request, the webserver has to decide if it is an static or a dynamic url and then serve the request itself or pass it to the proxy of application servers..

The problem is that, as thin and mongrel are also web servers, if you don't take care with the configuration your application server can be serving all the static files too and be busy when it has to attend a dynamic request, with all the problems it causes.

That's why for me is very surprising when you search in Google about Apache + Mongrel, for example, and you arrive to virtual hosts configurations that doesn't take this into account.

The rules are very easy:

 RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_URI} !/stylesheets/.* [NC]
 RewriteCond %{REQUEST_URI} !/images/.* [NC]
 RewriteCond %{REQUEST_URI} !/javascripts/.* [NC]
 RewriteCond %{REQUEST_URI} !.*\.js$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.jpg$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.gif$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.css$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.js$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.png$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.ico$ [NC]
 RewriteCond %{REQUEST_URI} !.*\.bmp$ [NC]
 RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]
 

And this is an example: in order to your needs you can tune this configuration including more filet ypes and static directories.

For nginx the rule is also very simple.

So, you know, don't forge to include rewrite rules for serving static files.

2 Octubre 2008

Improving the accesibility of link_to_remote

It is very ease make link_to_remote accessible, it is, forcing it has a href value different than #, which is the default value if you only indicate the :url parameter.

But, it is quite possible that both parameters have the same value. If so, you can redefine link_to_remote in this way:

 module ActionView
   module Helpers
     module PrototypeHelper
       def link_to_remote(name, options = {}, html_options = {}) 
         url = if options[:url].is_a?(Hash)
           url_for(options[:url])
         else
           options[:url]
         end
         html_options[:href] = url unless html_options[:href] 
         link_to_function(name, remote_function(options), html_options)
       end
     end
   end
 end
 

18 Agosto 2008

Generating static maps with attachment_fu and static-gmaps gem

Google Maps is, may be, the coolest service for integrating maps in your web applications: it offers a powerful and easy API with no limit usage.

The problem is that it loads quite Javascript that has to connect to the main Google Maps page in order to draw our map. In order to avoid that we can use static Google Maps, it is, a picture (generated in real time) of the map we want to visualize. The problem with this is that you can't interact with the map (because it is an image) and that there is a limitation per day. But may be you find it useful in a particular moment.

For example, in iwannagothere.net we have a small map in every place that shows the location of that city:

In that situation we can change the map because the map is not for navigating along the city. For that you have a map in each item.

In Ruby on Rails, the programming framework of iwannagothere.net there is a gem called static-gmaps that does the hard work for you. What's more, we have found the way to integrate this maps with attachment_fu, in order you save a copy of the map in your filesystem and then avoid the day limitation usage.

Basically we have an attachment_fu kind of model (nothing special here):

class PlaceMap < ActiveRecord::Base
   belongs_to :place
 
   has_attachment  :content_type => :image, 
                   :storage => :file_system,
                   :processor=> :rmagick,
                   :path_prefix => 'public/userfiles/place_maps', 
                   :max_size => 1.megabyte
 
   validates_as_attachment  
 end
 

The we generate the static map instance:

	
 map = StaticGmaps::Map.new :center   => [ place.lat, place.long ],
                             :zoom     => 12,
                             :size     => [ 334, 144 ],
                             :key      => APP['gmkey']  
 

And save it in our PlaceMap model:

 PlaceMap.create(:uploaded_data => (open(map.url)), :place_id => place.id, :content_type => 'image/gif')
 

The problem here is that we are not uploading a file in the usual way: we are trying to save a raw of data. Attachment_fu doesn't know how to do that, so we have to apply a little patch:

 module Technoweenie
   module AttachmentFu
     module InstanceMethods
 
       def uploaded_data=(file_data)
         return nil if file_data.nil? || file_data.size == 0
         self.content_type = file_data.content_type
         self.filename     = Time.now.to_i.to_s+'.gif'
         if file_data.is_a?(StringIO)
           file_data.rewind
           self.temp_data = file_data.read
         else
           self.temp_path = file_data
         end
       end          
 
     end
   end
 end
 

Here we are giving a random filename, generated from the current time.

And that's all, really easy :)

9 Junio 2008

Comprobar si una relacíon contiene un elemento

Muchas veces cuando trabajamos con ActiveRecord nos olvidamos / ignoramos qué hace por debajo.

Un claro ejemplo lo encontramos cuando tenemos una relación :has_many y queremos comprobar si un elemento está incluída en dicha relación.

Por ejemplo:

 class Place < ActiveRecord::Base
   has_many :items
 end
 
 class Item < ActiveRecord::Base
   belongs_to :place
 end
 

Seguro que más de una vez, para comprobar que un place contiene un ítem hemos utilizado el "rubysta" include?:

 place.items.include?(item)
 

Pensándolo fríamente 0.2 segundos nos damos cuenta de que estamos cargando un array con todos los elementos de una relación, que pueden ser 10 o pueden ser 1.000.000. Y ya sabemos lo generoso que es Ruby con la memoria que toma y que nunca deja ir.

Un truco para evitar que esto suceda sin dejar de utilizar include? es definir dicho método dentro de la relación tal que así:

 class Place < ActiveRecord::Base
   has_many :items do 
     def include?(item)
       count(:conditions => ["item_id = ?", item.id]) > 0
     end
   end
 end
 

Es decir, resolvemos el problema con un simple COUNT de Mysql.

¿Alguien se anima a hacer un plugin? Creo que sería bastante inmediato para cuando se cumplen las convenciones y sabemos que la clave foránea de la relación es singular del nombre de la relación_id. O quizá ni eso, si encontramos la forma de preguntarle a ActiveRecord cuál es la clave foránea de una relación.

5 Junio 2008

Visualizar en el log de producción las querys lentas

Cuando tienes una aplicación en producción es más que necesario controlar los tiempos de las peticiones más lentas.

Sin embargo muchas veces con el log de Rails no es suficiente, y hay que alcanzar un mayor nivel de detalle.

El log en modo debug nos da todo el detalle que necesitamos, pero mostrando demasiada información (¿de qué nos sirve saber que un SELECT sencillo tarda 0.001 segundos?).

Pero si en ese log pudiéramos filtrar las consultas SQL más lentas sí que tendríamos información de consultas lentas asociadas a cada petición al servidor.

Gracias al plugin query trace y a una pequeña modificación podemos mostrar qué consultas tardan más de 1 segundo:

 module ActiveRecord
   module ConnectionAdapters
     class AbstractAdapter
       def log_info(sql, name, runtime)
         return unless @logger
         @logger.info(
           format_log_entry(
             "#{name.nil? ? "SQL" : name} (#{sprintf("%f", runtime)})",
             sql.gsub(/ +/, " ")
           )
         ) if (RAILS_ENV == 'production' && runtime > 1.0) || RAILS_ENV != 'production'
       end
     end
   end
 end
 

Este código lo he probado en Rails 1.2.x, no sé si funcionará en otras versiones.

31 Mayo 2008

Escrito el: 31 may 2008 @ 06:31 AM

Categorías: Snippets y Trucos

Tags: subversion

Comentarios:
7 comentarios

compártelo

Diferencias colorizadas en SVN y GIT

Si trabajáis con control de versiones una de las operaciones más habituales es ver las modificaciones de un fichero. Eso en Subversion se hace con svn diff.

Sin embargo yo utilizo un script que creo que me pasó porras para que me muestre en colores las modificaciones.

El script es este:


 #!/usr/bin/env ruby
 
 `svn diff #{ARGV.join(' ')}`.each do |line|
   puts( if line =~ /^\+(.*)$/
         "\e[32m#{$&}\e[0m"
         elsif line =~ /^-(.*)$/
           "\e[31m#{$&}\e[0m"
         else
           line
         end
       )
 end
 

Con darle permisos de ejecución ya podéis tener un svndiff por ejemplo.

No pongo el equivalente en git porque es bastante inmediato.