Feb 18

Simple, but nice replacement for open-uri library (which uses rather slow Net::HTTP library) which uses libcurl (via curb).
It’s easy to swap the original one just by replacing ‘require open-uri’ with ‘require curb-openuri’. Should work as the original, even with better defaults (which, of course, can be changed).

http://github.com/romanbsd/curb-openuri/tree

Bookmark and Share
Feb 09

Several months back I needed to compute NMF of some relatively larges matrices.
Since the native Ruby code was painfully slow, and for some reason even failed to work for some matrices, I decided to write a C implementation which will leverage the GNU Scientific Library (GSL) and then wrap it for using in Ruby application.
It was a neat add-on to the rb-gsl ruby library. What it does is adding NMF module under the GSL::Matrix, and there you have a method nmf which receives a GSL::Matrix and number of columns as a parameter and then returns two matrices.
Since this is an iterative algorithm, the number of runs is limited to 1000, and the desired difference cost metric is set to 10-6.
I tried to contact the author and even posted my code in the issue tracker, but haven’t received any response at the time of writing.
So I decided to create a git-svn mirror on Github and add my changes there.
http://github.com/romanbsd/rb-gsl

You can install the gem using this command:

  $ gem sources -a http://gems.github.com/ # (you only have to do this once)
  $ gem install romanbsd-gsl
Bookmark and Share
Feb 08

This simple fix uses the fact, that if the second argument to the ‘add’ method is a symbol, then a generate_message is called, which does all the voodoo of Rails 2.2 I18n.
I tried to contact the original author to no avail, therefore I’ll post it here, hoping that if anyone needs it, it’ll surface on a Google search.
The fix itself is quite simple:

Index: lib/validates_date_time.rb
===================================================================
--- lib/validates_date_time.rb  (revision 403)
+++ lib/validates_date_time.rb  (working copy)
@@ -114,7 +114,7 @@
       if options[:before]
         options[:before].each do |r|
           if r.value(record) and value >= r.last_value
-            record.errors.add(attr_name, options[:before_message] % r)
+            record.errors.add(attr_name, :before, :default => options[:before_message] % r)
             break
           end
         end
@@ -123,7 +123,7 @@
       if options[:after]
         options[:after].each do |r|
           if r.value(record) and value <= r.last_value
-            record.errors.add(attr_name, options[:after_message] % r)
+            record.errors.add(attr_name, :after, :default => options[:after_message] % r)
             break
           end
         end

I’ve a fork of the git-svn mirror here:
http://github.com/romanbsd/validates_date_time/tree/master

Then, for using it, you’ll just need to have the following path in your locale yml files (for example):

  activerecord:
    errors:
      models:
        user:
          attributes:
            birth_date:
              before: 'is wrong'
              after: 'is wrong'
Bookmark and Share
Jan 30

I just wanted to display reCAPTCHA inside a ModalBox.
This simple task turned out to be quite complicated.
After playing with innerHTML, creating <script> nodes manually and attaching them and other voodoo and black magic and failing to accomplish the desired effect, I settled with displaying the recaptcha inside iframe inside ModalBox.

 Modalbox.show('<iframe src="' + this.href + '?iframe=1"></iframe>', 
  {title: this.title, width: 600});

(The iframe parameter helps to select another layout).

Bookmark and Share
Jan 30

Wouldn’t it be nice to cache some item (e.g. action) for some amount of time, before expiring it? For example, if you’re displaying some page that parts of it change very often, but you don’t want to expire cache every time, since you prefer to serve stale information instead of expiring the page?
The answer is the options[:expires_in] which is supported by the Rails’ ActiveSupport::Cache::MemCacheStore.
The current implementation of ActiveSupport::Cache::FileStore, however, lacks such feature, despite the fact that it’s really easy to implement.

This is the fix accompanied by a unit-test.
cache_fix

And then you can happily add a line like this in your controllers:

caches_action :find, :expires_in => 10.minute

The check, by the way, adds about 7usec to cache read request, which IMHO is negligible.

The Rails core team doesn’t seem to be interested in this patch, though.

Bookmark and Share
Jan 23

There’s a nice plugin which adds helper for the reCAPTCHA. The downside is that it doesn’t support I18n at the moment.
This is a straight forward approach patch that adds this functionality. (I don’t know what will be the fate of I18n in Rails 3, thus the simple check for version).
The error message path then will be:
activerecord.errors.models.model_name.captcha

diff --git a/lib/recaptcha.rb b/lib/recaptcha.rb
index 87c26e9..44413bc 100644
--- a/lib/recaptcha.rb
+++ b/lib/recaptcha.rb
@@ -65,7 +65,11 @@ module Ambethia
             session[:recaptcha_error] = error
             if model = options[:model]
               model.valid?
-              model.errors.add_to_base "Captcha response is incorrect, please try again."
+              if Rails::VERSION::MAJOR >= 2 and Rails::VERSION::MINOR >= 2
+                model.errors.add_to_base I18n.translate("#{model.class.name.underscore}.captcha", :scope => %w(activerecord errors models), :default => "Captcha response is incorrect, please try again.")
+              else
+                model.errors.add_to_base "Captcha response is incorrect, please try again."
+              end
             end
             return false
           else

I wonder if this is the proper way of adding I18n support…
P.S. I reported this on lighthouse.

Bookmark and Share
Jan 06

This trick is great for actions which receive lots of parameters, for example – search controller. Obviously, you wouldn’t want to do that with something that receives a POST, but I omitted the check for it (using :if => Proc.new {|controller| controller.request.get?} , for example), since I think that I know what I’m doing.
So the idea is to map all the parameters to some unique key, and then use that key as an :id for the url_for.

  # This caches the find action by the parameters
  require 'digest/sha1'
  caches_action :find, :expires_in => 10.minute,
    :cache_path => Proc.new { |controller|
        search_key = Digest::SHA1.hexdigest(controller.params.inspect)
        logger.debug "Current search key: #{search_key}"
        controller.url_for(:action => 'find', :id => search_key)
      }

The only minor issue that might arise, is that if you’re using pagination, the page number 1 when reached from the pagination links will have page=1 parameter, but when it’s initially presented, the parameter is absent. Adding such parameter won’t do any good, since the order of keys in hash in undefined. It’s possible, though, to build the key using some other technique, for example by massaging the request string. But since the case is quite rare, I think that any fix will be a waste of CPU cycles.

Bookmark and Share
Dec 28

I’m quite surprised, that even of version 2.2.2 of Rails, the logs are written in a way that makes them almost useless, as there’s no time stamp or severity level.
The solution I’ve come up with, is (instead of monkey patching the BufferedLogger) to create a new type of logger which inherits from the BufferedLogger and then change the ‘add’ method in order to produce nicer logs.
This file is lib/audit_logger.rb :

class AuditLogger < ActiveSupport::BufferedLogger
 
  SEVERITIES = Severity.constants.inject([]) {|arr,c| arr[Severity.const_get(c)] = c; arr}
 
  # Redefine the ActiveSupport::BufferedLogger#add
  def add(severity, message = nil, progname = nil, &block)
    return if @level > severity
    message = (message || (block && block.call) || progname).to_s
    # This is the line that was changed:
    message = "#{Time.now.to_formatted_s(:db)} #{SEVERITIES[severity]} #{message.strip}\n"
    buffer << message
    auto_flush
    message
  end
end

Then, in config/environments/production.rb you need to add:

require 'audit_logger'
config.logger = AuditLogger.new(config.log_path)
config.logger.level = AuditLogger::INFO #or other level
# This is what Rails does by default in production mode when using the default logger
config.logger.auto_flushing = false
Bookmark and Share
Nov 14

Yesterday I packed my ruby interface to ispell into a gem and published it on Rubyforge.
The rdoc can be found here.
I wonder if anyone still works with ispell besides myself.

Bookmark and Share
Nov 07

Interesting phenomenon; for finding whenever some string occurs inside other string, String#match is 5 (!) times faster than String#include? for long strings (50k), and 2 times slower for short strings (tens of bytes).
The slowness can be explained by the time it takes to compile the regexp.
But how can it be so slow for long strings? Going to ask this in ruby.core.

Bookmark and Share

 

February 2010
S M T W T F S
« Feb    
 123456
78910111213
14151617181920
21222324252627
28  

Pages