Saturday, November 17, 2012

Rails 3: Improve :presence validation

In Rails 3.0 (and probably in modern versions) you can ensure the validity of your models by using validations . One I use is the :presence , to make an object invalid if a variable is not present.

Example:

class Car < ActiveRecord::Base
  belongs_to :user
  validates :user, :presence => true
  ...
end

The problem comes when you investigate how validation is made. Each time we save the validated object is got from the database (*). Something simple as:


car = Car.last
car.save

will hit the database checking if user exists.

Solution: build a custom Validator and use it:


class SexyForeignKeyValidator < ActiveModel::EachValidator
  #http://guides.rubyonrails.org/active_record_validations_callbacks.html#performing-custom-validations
  def validate_each(record, attribute, value)
     return if value.present?
     relation_name = attribute.to_s.gsub(/_id$/,'')
     unless record.send(relation_name)
        record.errors.add relation_name.to_sym, :blank
     end
  end
end

The tricky part is to check for the foreign_key BUT if this is not already set (on create), check for the relation.
This validation run on the foreign key, so we modify the code in the model.


class Car < ActiveRecord::Base
  belongs_to :user
  validates :user_id, :sexy_foreign_key => true
  ...
end

One last note. The code for the validator must be included in some path that is automatically loaded ( read this)

Profit!


(*) Note that this is a very particular case, when we check the presence of a relation. Checking for the foreign_key user_id won't work, since on creation, this foreign_key can be null

Friday, November 2, 2012

Test the HTML sent from rails

Sending emails in Rails is a piece of cake when you follow the official mailers guide . However testing the html is a bit problematic.

You can use the assert_select_email or the usual assert_select methods. I prefer the second option, and my code looks like (Rails 3.0.X)

email = ActionMailer::Base.deliveries.last
body = email.html_part.decoded
 
node = HTML::Document.new(body).root
assert_select node, ':root' do
   #do your assertions here
end
 
It took me, however, some time to fix the error
NoMethodError: undefined method `assert_select' for

In Rails 3.0.X it is as easy as including the appropriate module (within you test class)  

include ActionDispatch::Assertions::SelectorAssertions 


For newer rails versions check the official guide of teh the assert_select method, and check which module is it defined in.

Sunday, September 2, 2012

Draggable Lightbox via HTML5

There is something with HTML lightboxes / modals that annoys me: they are usually not draggable.

The usual way to go is using draggable from jqueryUI . However I tend to flee from jqUI based solutions due to the code bloat it means (they are bullet-proof but they are heavy). Other solutions are more appealing. However I realized that almost all browsers provide support for 'native' drag and drop: why not to use it for my purpose?

The solution

The solution takes only ~20 lines of javascript (it controls that the modal is not moved out of bounds).
Suppose that your lightbox has a wrapper (the window that displays content) and an overlay. You have to bind the 'dragstart' and 'dragend' events to the wrapper (and do it only ONCE, for example after it is created). For the overlay, we bind the events 'drop' and 'dragover' (again, do it only once)

var opt = {};
$wrapper.bind('dragstart', opt,  function(ev){
    var opt = ev.data, 

    // we store the difference between the element top / left, and the mouse cursor, so that we can apply them once the object is drop
    $el = $(this), off =  $el.offset();
    opt.offX = off.left - ev.screenX;
    opt.offY = off.top - ev.screenY;

    var e = ev.originalEvent;
    e.dataTransfer.effectAllowed = e.dataTransfer.dropEffect = 'move';
    e.dataTransfer.setData('text', this.id);//we need to set some data
    $el.css({
        opacity:0.4
    });

}).bind('dragend', opt, function(ev){
    var opt = ev.data,
    //only if new top AND new left makes the box stay within the window
    $el = $(this), newX = ev.screenX + opt.offX, newY = ev.screenY + opt.offY, $win = $(window);
    newX = (newX < 0) ? 0 : Math.min(newX , $win.scrollLeft() + $win.width() - $el.outerWidth() );
    newY = (newY < 0) ? 0 : Math.min(newY , $win.scrollTop() + $win.height() - $el.outerHeight() );

    $el.css({
        top: newY,
        left: newX,
        opacity: 1
    });
});


$overlay
.bind('drop', function(ev){
    ev.stopPropagation(); // Stops some browsers from redirecting.
    ev.preventDefault();
    return false;       
}).bind('dragover',function(ev){
    if (ev.originalEvent.dataTransfer.getData("text")){ //allow to drop only our things
        ev.preventDefault(); //informs that we can drag here
        return false;
    }
});


Simple and straightforward!

Note: for a JS only implementation, simple and effective, take a look at http://css-tricks.com/snippets/jquery/draggable-without-jquery-ui/ 

Friday, August 31, 2012

Git :: Show a file from other branch


Sometimes yo need to view the contents of a file in other branch, without doing a checkout. This is super easy with git:
git show branch:file
 
Where branch can be any ref (branch, tag, HEAD, ...) and file is the full path of the file.
To store the file for a later read:

git show branch:file > exported_file 
 
 
(via http://stackoverflow.com/a/7856446/1265056 ) 

Monday, July 30, 2012

Using Prawn and Rails to email a pdf

In MicroHealth we needed to email a pdf for some users.
The document that is attached can be downloaded as well, and I wanted to reuse as much code as possible.

For the views we use prawn and prawnto gems. The recipe consists of 3 steps:
- generate and save the pdf to disk
- email (attach the pdf)
- delete the pdf

The 'view' used to generate the pdf document is located in /app/views/my_controller/show.pdf.prawn and is shared for both emailing and downloading actions.

Generate a pdf file AND save it to disk

The key is to inherit from Prawn::Document, and to provide the @ instance variables that the view expects:

 class MyGenerator  < Prawn::Document
 

  def initialize(options)
    options && options.merge!({:inline=>true})
    create_instance_variables(options.delete(:variables))
    super(options)
  end

  def render_template(template)
    pdf = self
    pdf.instance_eval do
      eval(template) #this evaluates the template with your variables
    end
    ensure_path
    pdf.render_file(File.join(output_path,filename))
  end

  private

  def create_instance_variables(vars)
    return if vars.blank?
    vars.each_pair do |k,v|
        instance_variable_set("@#{k}", v)
    end
  end

  def output_path
    @output_path ||= File.join(Rails.root,'tmp','documents')
  end

  def ensure_path
    FileUtils.mkdir_p(output_path)
  end

  def filename
    @output_file ||= "#{Process.pid}::#{Thread.current.object_id}.pdf"
  end

end



For example my view expects some variables as @start, @end and @records, and that the pdf document variable is named 'pdf'

template = File.read("#{Rails.root}/app/views/my_controller/show.pdf.prawn")
writter = HemoPdfReport.new(:page_size => 'A4', :page_layout  => :landscape, :variables => {:start => params[:start], :end => params[:end], :records => @results})
attachment = writter.render_template(template)
begin
#send
  MyPdfMailer.attachment_email(
   :user => @user,
   :destination => @email,
   :message => @text,
   :attachment => attachment.path).deliver
              @report.save!
ensure
   FileUtils.rm_f(attachment.path)

end

Things to note:
  • The create_instance_variables method copies the 'variables'  parameter used in the initialization, to instance variables.
  • As our generator is a PrawnDocument, we can pass it as the 'pdf' variable that the view expects (note the line pdf = self before the eval)
  • We automatically provide a filename to the output. We could have used a timestamp but we use one based on the current thread. The key here is to avoid using an static name (we can have several processes / threads generating documents concurrently)

Emailing PDF

Emailing a file is really simple, just follow the http://guides.rubyonrails.org/action_mailer_basics.html
 
An example of my mailer
  def attachment_email(options)
    attachment = options[:attachment]
    @user = options[:user]
    @destination = options[:destination]
    @text = options[:message]
    attachment.present? && attachments['report.pdf'] = {
      :mime_type => 'application/pdf',
      :content => File.read(attachment)
    }
    subject = "Your Pdf Document"
    mail(:to => @destination, :subject => subject)
  end


Delete file

Once the file is emailed, dont forget to delete it. The ensure block is meant for this.

Profit!

Monday, June 18, 2012

Deploy with capistrano an specific branch


To deploy with capistrano a different branch than the specified in the configuration (ex: master/HEAD) just use:
cap staging deploy -s branch=my_branch
It deploys my_branch into staging environment. If you are not using the multistage configuration see , it becomes
cap staging deploy -s branch=my_branch

If capistrano does not obey, check that in your deploy files (for each environment) you are getting the branch variable from the environment:

#in  config/deploy.rb and each for config/deploy/*
set :branch, fetch(:branch, "master")
 

Profit!

Monday, May 7, 2012

Rails: Using no id keys in a has-many-belongs-to association

This small recipe is useful for when you have to link 2 models with no id keys. Our case is for users that can send and receive invitations. The key to make the lookup for the 'invited' users is the email, not an id. Since the 'invited' users could have not completed the registration process, we could not use a 'recipient_id' column. Note that the column used for the lookup takes a different name on both models.
Once built, the relationships are really easy:

class User < ActiveRecord::Base
(...)
   has_many :invitations_sent,
      :foreign_key => 'user_id',
      :class_name => 'Invitation', :dependent => :destroy, :inverse_of => :user
   has_many :invitations_received,
      :primary_key => 'email', :foreign_key => 'recipient_email',
      :class_name => 'Invitation', :inverse_of => :recipient
(...)
end




class Invitation < ActiveRecord::Base
(...)
   belongs_to :user, :inverse_of => :invitations_sent
   belongs_to :recipient,
      :primary_key => 'email', :foreign_key => 'recipient_email',
      :class_name => 'User', :inverse_of => :invitations_received
(...)
end


Et voila!

Sunday, March 18, 2012

Restore sound in Ubuntu 11.10

Sometimes, in my Ubuntu 11.10, sound is not working after the system started.
It happens from time to time, and can be solved rebooting or restarting the session. However it is really annoying making a reboot once you have all your toolbox of programs running.
The solution is as simple as opening a Terminal (ctrl + alt + T)
and restating the pulse audio daemon:
pulseaudio -k  (no need to be root)
pulseaudio --start
pulseaudio --check

now you can use you media player with sound!

Monday, February 13, 2012

Request Log Analyzer

Casi por casualidad, y a tavés de la página Ruby Toolbox he descubierto una pequeña maravilla que te permita analizar tus ficheros .log de tu aplicación Rails: tiempos de accesos medios, maximos, controladores y acciones, horas del dia más solicitadas... y seguramente mucho mas.

Es la gema request-log-analyzer , y es compatible con Rails3 (por lo menos). Disponible en github . Alli tienes una wiki bastante extensa con su uso.

A disfrutarla.


Wednesday, January 11, 2012

My GIT workflow (w rebase)

This is usually a popular topic: the following is what work for us at https://microhealth.org

..start the new branch from master
git checkout master
git pull
git checkout -b my_new_feature
..work and commit some stuff
git checkout master
git pull
git checkout my_new_feature
git pull
git rebase master
git pull
git commit -m 'rebased from master' 
..work and commit some stuff
git checkout master
git pull
git checkout my_new_feature
git pull
git rebase master
git commit -m 'rebased from master' 
..finish the feature
git checkout my_new_feature
git pull
git checkout master
git pull
git merge my_new_feature

git push origin master 
 
..Just delete your branch because after a rebase in upstream, your branch is no longer used
git branch -D my_new_feature
 
(to delete permanently from the server: git push origin :my_new_feature) 
 
Don’t rebase branches you have shared with another developer:
If you have pushed your work in the new branch, use merge instead.
 
 
Note: I will update the post in the next days.

Saturday, January 7, 2012

Using STI on Rails. Tips

If you ever think of using Single Table Inheritance on Rails, forget about it. It will cause more headaches than benefits. It is simply that Rails ActiveRecord does not work as you can expect with all the possible combinations / associations you can set on your objects: there are bugs, 'features' and tricks that made me lose a lot of time these last days. However, if you still think it fits into your models, keep reading.

When To use STI ?

As introduction, I highly recommend reading this excellent compilation of tips about STI : http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html .
Well, lets make is as painless as possible:

Quick recipe

Lets suppose that you have a User model, that will derive in Man, Child and Woman subclasses. As all share the same data (columns) but the behaviour is different it makes sense using STI in here.

1 - Make all users share the same controller
The first thing I want is that all users share the same controller: show, index, destroy, ... all the actions do the same for all Users.
However, paths automatically generated for a @child, @man, @woman, @user, are different by default: new_child_path, new_man_path, .... That is a pain in view helpers as form_for and  fields_for
This is solved overwriting the 'model_name' class method of the base class: make all the models in the inheritance chain share the same name

(in user.rb)
class << self
    def inherited(child)
      child.instance_eval do
        def model_name
          User.model_name
        end
      end
      super 

    end
end

now, all subclasses use the users resource paths in the view helpers
2 - Remove all those ugly 'where type = ' conditions in your SQL queries

By default, when you do a Man.find(id) it includes a 'type = 'Man'' condition in your queries. For example:

Man.find(3) -> "SELECT from users WHERE id = 3 and type = 'Man';"
This is true for finds, updates and destroys. Far from being useful, it can make your records are not found under certain conditions (i.e changing the type of the record after its creation). That errors can go unnoticed since they dont raise errors, but have no effect.
For example, if we try to promote a child to a man:

@child.type = 'Man' 
#now, Rails side considers it as of 'Man' type, but DB still has it as 'Child'
@child.save!

it translates to
UPDATE users SET type = 'Man' WHERE id = 3 AND type = 'Man';
As DB still has the record stored as a Child, it is not found, and so never gets updated. A subtle error, that can lead to future problems (remember that it wont raise any failure)

My solution makes these 'where type' conditions removed by default:


(in user.rb)

class << self
    def finder_needs_type_condition?
      #http://apidock.com/rails/ActiveRecord/Base/finder_needs_type_condition%3F/class
      false
    end
end

Note that as (an undesired) side effect,  SQL operations over derived classes  lose the 'type' conditions by default.

More specifically

User.count == Child.count == Man.count
are translated to the same query:
SELECT COUNT(*) from users;

Woman.all == User.all
are translated to the same query:
SELECT * from users;

You must include it explicitly
User.where(:type => 'Woman').all

you can alleviate the problem adding a scope on all the derived classe

(in user.rb)
class << self
    def inherited(child)
    #after the code on the step 1


      def of_same_type
        where({:type => self.name}) # type == class.name
      end

    end 
end

Now you can do

Child.of_same_type.all

3 -Make you can easily promote one user to other type

There is a litlle gem in ActiveRecord called 'becomes' method
It allows you to 'cast' your object to other type:

@grown = @child.becomes(Man) #now @grown is the @child, cast to a Man.

The new object will behave as a Man, while have the original data form the child (name, age, etc). Keep in mind that 'becomes' returns a newly instantiated object, that shares the variables with the original object, so you are only handling a 'Man' when manipulates this new object (@grown). The original child is not really casted (code) to a man.

To make the change permanent you still have to save the object (@grown.save). However under some circumstances you are not sure if save is invoked on the new instance (@grown) or in the original (@child): for example using autosave associations, or if it it re bound in a belongs. In that cases you want it to be saved with the new type (Man) whether the save is in the original or in the casted instance. You only have to 'patch' the instance method from ActiveRecord::Base

(in user.rb)

    def becomes(klass)
      if self.class == klass
        return self
      else
        #backported patch from https://github.com/rails/rails/commit/e781853
        became = super(klass)
        became.type = klass.name unless klass.descends_from_active_record?
        became
      end
    end

4 - Consider polymorphic relationships

Whenever you are using a polymorphic relationship on a model with STI inheritance, there is a surprising thing (or not so surprising) that will happens where this relation is persisted.


Lets suppose that we have a has_pictures plugin that sets a polymorphic relationship. You can attach a picture to  User, Flat and Car models

(in picture.rb) 
belongs_to :picture, :polymorphic => true, :inverse_of => :item

You can have pictures for Users, Cars and Flats
has_one :item, :dependent => :destroy, :as => :posteable, :inverse_of => :picture

As in polymorphic relationships in Rails, Picture has two columns: item_type and type_id that are used to find the 'item' the picture owns: in type column you stores the class name of the item.

However Rails does a weird thing here: it stores the 'base' class in the type column of the item that is saved. That means that a Child, is stored with item_type set to 'User' .
It made me lose a day of work to realise that Rails works this way: it was not my fault.

If that does not suits you, there are plugins to store the very class of the item.
for Rails 3.0 , for 3.1 .


For other reasons I ended using an extra column to store the derived type (via :before_save) and kept the item_type storing the base class. But these plugins work well.


5 - Packing everything together
I finally wrote a module to reuse everything listed before.
Whenever I want to use Inheritance in AR models I just extend HasStiInheritance .

For example. Lets say that we have an User class, with AmericanUser, EuropeanUser and AfricanUser subclasses (every other user defaults to base User). These classes exist only to provide different implementation of some methods (i.e. the format addresses and phone numbers differently).

I declared User class as

class User < ActiveRecord::Base
  # include / extends
  extend HasStiInheritance
  ...
end

class  AmericanUser < User
   ...
end

class  EuropeanUser < User
   ...
end

class  AfricanUser < User
   ...
end

and I have only to remember using 'of_same_type' if I want to retrieve users of a given continent.

EuropeanUser.of_same_type.all #will list all europeans. OK

European.all #WRONG. retrieving ALL users, since we removed 'where type=XXX' clauses

I paste the module I am using now:

module HasStiInheritance
  #about structure, @see http://yehudakatz.com/2009/11/12/better-ruby-idioms/

  #constants here

  #class methods here

  def has_sti_inheritance
    include InstanceMethods
  end

  def define_class_method name, &blk
    #from http://blog.jayfields.com/2007/10/ruby-defining-class-methods.html
    (class << self; self; end).instance_eval { define_method name, &blk }
  end

  def finder_needs_type_condition?
    #do not include where type == klass_name in all the finders, even for subclases. That will helps 'AR.becomes' usage
    #http://apidock.com/rails/ActiveRecord/Base/finder_needs_type_condition%3F/class
    false
  end

  def of_same_type
    where(nil) # the base class does not need filtering by type
  end

  #As all users share the same controller / routes, we must patch the route generation for that resources:
  # Ex: => we want users_path , not european_users_path, american_users_path etc
  #http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html
  def inherited(child)
    #here, self is the base class
    base_model_name = self.model_name
    child_model_name = child.name

    child.define_class_method :model_name do
      base_model_name
    end

    child.define_class_method :of_same_type do
      where({:type => child_model_name}) # type == class.name
    end
    super
  end
  #class methods above

  #instance methods here

  module InstanceMethods

    def becomes(klass)
      if self.class == klass
        return self
      else
        #backported patch from https://github.com/rails/rails/commit/e781853  for 'becomes' method
        became = super(klass)
        became.type = klass.name unless klass.descends_from_active_record?
        became
      end
    end

  end
  #instance methods above
end




FIN