November 10, 2007 by squarewheel
ActiveRecord is one of the central components of Rails… unfortunately, quite bad one.
Perhaps I should explain one detail first – it is a perfectly good ORM if you are writing something as simple as a blog. That’s what Rails can be used for successfully. But if you want to write a more complex application, this becomes a nightmare.
The first problem I want to describe is inherent problem of the active record as a design pattern – there is no identity map, so same object might be loaded into the application multiple times. This does not only have negactive impact on performance, but also leads to some hard-to-find bugs.
Lets consider a simple has_many/belongs_to relation (one-to-many). Sometimes one might want to iterate through all the objects in the collection and call some method of them, which in turns calls some method of the parent object. But here is the problem – each child object will load new copy of the parent, so each method call would go to different copy. A simple solution is to add the parent object as a parameter to the method of child objects, but in more complex situations it becomes a nightmare to trace.
Here goes the code sample:
class ParentObject < ActiveRecord::Base
has_many :child_objectsdef do_sth_on_children
child_objects.each { |c| c.upd_parent}
end
def do_sth
end
end
class ChildObject < ActiveRecord::Base
belongs_to :parent_object
def upd_parent
parent_object.do_sth
end
end
I know there is a plugin for ActiveRecord that addresses this problem, at least in the “data consistency” layer, but leaves the performance problem untouched
Posted in Rails | 2 Comments »
November 3, 2007 by squarewheel
Update — this plugin won’t work as of Rails 2.3. See comments.
Solution for 2.3 is here: http://squarewheel.pl/posts/3
By default Rails do not support setting expiration of session cookies to ” after last visit”. The code at Rails wiki shows how to set up cookie expiration for a whole application, but it does not support setting this per-controller (or per-action) like other session options, due to the use of class variables.
This became a problem for us recently, as I needed to set different session cookie expiration times for different controllers.
Here is the solution, rewritten from scratch:
# -- put this into vendor/plugins/sliding_sessions/init.rb --
module SlidingSessions
# Allows using :session_expires_after option for session cookies
# NOTE it does not perform any checks on server-side, it just
# sets the session cookie expiration time.
# see http://wiki.rubyonrails.org/rails/pages/HowtoChangeSessionOptions
# for a recipe to perform server-side expiration checks
# (C) Paweł Stradomski , released under MIT licence
def self.included(base)
base.class_eval do
alias_method_chain :set_session_options, :sliding
end
end
def set_session_options_with_sliding(request) #:nodoc:
set_session_options_without_sliding(request)
if request.session_options && request.session_options[:session_expires_after] then
request.session_options = request.session_options.clone
request.session_options[:session_expires] = Time.now + request.session_options[:session_expires_after]
# cookie store only sends cookie if data changed. But we need to send it with every request
# to update the expiration time
request.session[:sliding_session_expiration] = request.session_options[:session_expires].to_s
end
request.session_options
end
end
ActionController::Base.send(:include, SlidingSessions)
# -- end of vendor/plugins/sliding_sessions/init.rb --
The above code (module definition + ActionController::Base.extend(SlidingSessions)) should be put into vendor/plugins/sliding_sessions/init.rb
Now you can use
session :session_expires_after => 8.hour # in any controller
in any of your controllers, even application.rb
Of course it only deals with session cookies, you still need to perform server-side checks for expired sessions and write a cron job to remove stale sessions.
Posted in Rails | 20 Comments »
November 2, 2007 by squarewheel
This is not really about Rails, more about Ruby itself. Look at this snippet of code:
print Date.today + 10.weeks
The intention of the programmer is quite obvious. The result – not so much:
18566-09-12
Rationale behind this result is quite simple – 10.weeks returns 6048000 – plain Fixnum being 10 weeks in seconds. So the correct code should be:
print (Time.now + 10.weeks).to_date.to_s
Obvious, isn’t it?
Posted in Rails | 3 Comments »