Archive for the ‘Rails’ Category

new_record?

June 11, 2008

Just in case anybody is interested – when a transaction fails (is rolled back), all the objects created in that transaction still respond false to new_record?, even though they are not saved to the db. One more reason to go for identity-mapping ORMs.

Some rails plugins

March 10, 2008

The company I work for – Code Sprinters – released three rails plugins I wrote under the MIT licence.

OutputStream and FlashMessages

Those two plugins allow safe mixing of unescaped text and html content. Strings are marked as being in either format and then are escaped appropriately in the view layer. This allows the developer to embed eg. links to other pages in flash messages without worrying about having some other piece of data unescaped.

Strings not marked explicitly as safe for html are escaped with default rails h() helper.

Although currently the plugin only knows how to escape text output to HTML, it can be easily extended to support other formats.

Expose

This plugin is inspired by CherryPy (http://www.cherrypy.org) – a simple yet powerful HTTP application server (and microframework).

This plugin changes the default policy of exporting all public methods of controllers via HTTP protocol to only exporting explicitly stated methods – and only to specified HTTP verbs. This helps prevent mistakingly exposing methods that should only be filters – or exposing via GET methods that should only accept POSTs.

Also, this is generally good practice to deny access by default – and allow access only when explicitly stated.

For actual downloads, go to the plugin page.

Why I hate ActiveRecord – part II (validation)

January 25, 2008

Everybody using Rails probably knows that ActiveRecord provides many methods for declaring validation of records – the validate method as well as validates_xxx methods which generate code for most common validation tasks. There is a problem though…

The validation can be easily skipped. Just use the update_attribute method and you can set any values without validation. Kinda scary if you’re used to encapsulating consistency checks in model – some other coder writing controllers for your model can just break your rules…

Just one more reason to ignore DHH’s words on moving validation to application layer and putting it back in database (where it should always be, even if you have the rules in your application layer too; skip consistency checks provided by the RDBMS only if they are too complicated to be effectively expressed with SQL or you’re using RDBMS with limited capabilites in this regard, like MySQL or SQLite)

Threading in RoR 2.0.2

January 12, 2008

We’re working on a project using Ruby on Rails 2.0.2 currently.

As you probably know, RoR is not thread-safe. But even if you are not using ActiveRecord, ActionPack nor any other Rails part you still might run into some problems if you thread.

We use the probably most common setup – small loop launching threads to query some web services in parallel. This seemed safe, as it would not use any of RoR classes/modules.

Now here’s the gotcha – automatic constant resolver (part of ActiveSupport) that loads classes from properly named files. It’s a part of RoR too…. and has threading problems, just like the rest of RoR. If a constant is not known before the threads launched, every thread would try to load that constant on its own; only the first thread would succeeded, all the other would raise “constant X is not unknown” exception.

Currently we worked this around by requiring files defining all dependencies in the file that launches the thread before going parallel. It’s ugly and not easily maintainable, as threads use different functions, each with it’s own dependencies – but it works, for now.

Perhaps it’s time to port that project to python – it has proper thread support (not to mention the fact that I wouldn’t have to deal with RoR worst part, that is ActiveRecord – SqlAlchemy is way better)

Unicode “support”

December 6, 2007

Just a simple console session:

>> "Zażółć"[0,3]
=> "Za\305"
>> "Zażółć"[0,4]
=> "Zaż"
>> "Zażółć".length
=> 10

OK, it quite works with Chars class:

>> "Zażółć".chars[0,3].to_s
=> "Zaż"
>> "Zażółć".chars[0,4].to_s
=> "Zażó"
>> "Zażółć".chars.length
=> 6

Not the most elegant solution, but works.

Why I hate ActiveRecord – part I

November 10, 2007

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

Session cookie expiration time in Rails

November 3, 2007

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.

Longest ten weeks ever

November 2, 2007

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?