January 6th, 2009 No Comments »
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.
December 28th, 2008 3 Comments »
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
November 14th, 2008 2 Comments »
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.
September 7th, 2008 2 Comments »
The Rails framework includes a simple “delegate” method, which is mixed in the models, and can be used in order to delegate methods to other classes. However it’s limited at least in the following:
1. It doesn’t prevent “train wrecks” – e.g. if the class that the method is delegated to is nil, then there will be an attempt to call this method on the nil class.
2. It doesn’t allow renaming of the methods, e.g. if you have a method called “name” in your class, and a method called “name” in other class you want to delegate to, you have no means of doing this.
The implementation below remedies these shortcomings.
class Module
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
end
prefix = options[:prefix] || ''
orig_prefix = prefix
if prefix != true and !prefix.empty?
prefix += '_'
end
methods.each do |method|
prefix = to.to_s + '_' if orig_prefix == true
module_eval(<<-EOS, "(__DELEGATION__)", 1)
def #{prefix}#{method}(*args, &block)
return nil unless #{to}
#{to}.__send__(#{method.inspect}, *args, &block)
end
EOS
end
end
end
You can use it like this:
delegate :name, :to=> :other, :prefix=>true
and it will delegate a method “name” in “other” class as “other_name” in your class.
Or, alternatively
delegate :name, :to=> :other, :prefix=>'some'
will delegate “some_name” to class “other”, method “name”.
August 30th, 2008 No Comments »
While the acts_as_tree rails plugin ( http://dev.rubyonrails.com/svn/rails/plugins/acts_as_tree ) provides some basic methods for accessing tree like data structure, I found that it lacks some basic ones, for example – has_children? and has_siblings?
Other than that, it would have been nice to know the tree width and depth (especially if you need to draw it, and need to calculate the scaling.
module ActiveRecord
module Acts
module Tree
module InstanceMethods
def has_children?
!self.children.size.zero?
end
def has_siblings?
!self.siblings.size.zero?
end
# Return the tree depth
def depth
return 0 unless has_children?
children.inject(0) {|dep,c| dep > c.depth ? dep : c.depth} + 1
end
# Return the tree width
def width
return 1 unless has_children?
children.inject(0) {|sum,c| sum + c.width}
end
end
end
end
end
Maybe if I won’t be lazy, I’ll write some tests and send this for inclusion in the plugin…
August 20th, 2008 No Comments »
When playing with LSI, I noticed that the program runs for too long and uses enormous amounts of memory.
Using a great tool ruby-prof, I found, to my astonishment, that I waste more time in stemming than in SVD.
So I wanted to try to see, if using a compiled C extension will make a difference. So I took the thread-safe porter algorithm from http://tartarus.org/~martin/PorterStemmer/ and wrapped it with swig.
The results were almost in an order of magnitude (10000 rounds for 11 words):
user system total real
stem : 3.480000 0.250000 3.730000 ( 3.719107)
fstem: 0.440000 0.090000 0.530000 ( 0.526526)
This I call “performance boost”
porter.i (for swig):
%module stemmer
%{
char *stem_word(char *word)
{
int length, i;
char *res;
struct stemmer * z = create_stemmer();
length = stem(z, word, strlen(word)-1);
/* length is the index of last char, add one for size and one for '�' */
res = (char *)malloc((length+2) * sizeof(char));
for (i=0; i<=length; i++)
{
res[i] = word[i];
}
res[length+1] = 0;
free_stemmer(z);
return res;
}
%}
%newobject stem_word;
char *stem_word(char *);
June 24th, 2008 2 Comments »
The dhtmlxTabbar is a nice tab-bar component from http://www.dhtmlx.com/
In order to use the AJAX loading feature I had to make a small change in the dhtmlxcommon.js so it mimics the behavior of Prototype by adding a X-Requested-With HTTP header. Thus, it’s possible to use request.xhr? in Rails.
dhtmlxcommon.js
August 27th, 2007 2 Comments »
I needed to present products and their corresponding branches and tags in Subversion in a Rails app. So I’ve come up with this quite simple solution.
The GenericModel class (attached) is ihnerited by Tag, Branch and Product.
The Product class supports methods find, branches and tags for which it uses ‘svn/client’.
The config.yml (which is read into OpenStruct) is very simple:
svn:
url: http://svn/svn/repos
user: roman
password: roman
The Product class tries to provide interface similar to the ActiveRecord (of course I didn’t immitate everything, just the basic stuff). Implementing interfaces in a contract-less language is an adventure
generic_model.rb
product.rb
August 14th, 2007 3 Comments »
I finally posted my Bugzilla plugin on Rubyforge.
The project page is:
http://rubyforge.org/projects/rubzilla/
The source can be found here:
svn://rubyforge.org/var/svn/rubzilla/bugzilla/trunk
License:
BSD
It is probably not complete, and has some bugs, and surely lacks documentation, but this is better than nothing
August 11th, 2007 1 Comment »
Lately I’ve been playing with the Bugzilla 3.0 XML-RPC API.
It turned out to be quite basic and inconsistent, but nevertheless – it’s a progress.
It’s also a CPU hog, so I’d not recommend running Bugzilla without mod_perl (latest versions finally added this long-time awaited feature).
The last time I wanted to integrate Bugzilla in one of our projects, I’d to write a class which mimicked a browser and this way contacted Bugzilla.
But this time I’ve come up with a nice Ruby module for Bugzilla, which has API similar of that of ActiveRecord, making integration of it in Ruby-on-rails application quite simple.
I’ll probably add some comments, throw in some more tests and open a project on rubyforge or something. Not to forget to notify the Bugzilla guys, perhaps they’ll find some use for this code.