Posts tagged programming

Jan 19, 2009

Rails date calculations could stand some timezone love

programming rails ruby | comments

New Rails handles a lot of timezone stuff for you. Set the appropriate time zone for the request and get an object from the DB, it’s created_at date will be translated from UTC to the request’s time zone.

Rails doesn’t do anything with the Ruby enhancements that create time objects from dates. Like:

Date.today.end_of_day

That’ll be in the server’s time zone. Ick.

I needed to find the beginning of a day as related to the request’s timezone. I moved the fix into some date extensions:

class ::Date
  def beginning_of_day_in_zone
    Time.zone.parse(self.to_s)
  end
  alias_method :at_beginning_of_day_in_zone, :beginning_of_day_in_zone
  alias_method :midnight_in_zone, :beginning_of_day_in_zone
  alias_method :at_midnight_in_zone, :beginning_of_day_in_zone

  def end_of_day_in_zone
    Time.zone.parse((self+1).to_s) - 1
  end
end

So now we have:

Time.zone = "Auckland"
=> "Auckland"
> Date.today.beginning_of_day
=> Tue Dec 09 00:00:00 -0600 2008
> Date.today.beginning_of_day_in_zone
=> Tue, 09 Dec 2008 00:00:00 NZDT +13:00

Realistically, I think Rails could be improved in this area. Or maybe I’m missing out on a better way to take a date object and make it zone-aware for generating related times. I spent more than an hour reading through the Rails date and time extensions and realized I need to keep my timezone pain relegated to the work week. For the time being, the above is a nice crutch for my current needs.


Oct 16, 2008

Fading flash message

flash message programming rails ruby | comments

Rails apps love flash messages. Little notes providing information, confirmation or warnings to the user. Typically implemented in a partial like so:

<% if flash[:warning] -%>
   <div id='warning'><%= flash[:warning] -%></div>
<% end -%>
<% if flash[:notice] -%>
  <div id='notice'><%= flash[:notice] -%></div>
<% end -%>

There are many cases where the message does not need to stick around for long. Consider a page with lots of AJAX interactions. Perhaps the user sends a message, which causes a page reload with a flash confirmation note (e.g. “You’re message was sent.”). After this the user will remain on the page for a relatively long time, maybe inline editing some properties or settings with AJAX tools.

It sure would be nice if that flash message would fade into the sunset after a few seconds and give back its valuable real estate, yes? Especially without forcing every flash message to disappear like Bobby Fischer.

Ask and receive, my friends. Introducing the fading_flash_message method, to be added to your nearest ApplicationController:

def fading_flash_message(text, seconds=3)
  text +
    <<-EOJS
      <script type='text/javascript'>
        Event.observe(window, 'load',function() { 
          setTimeout(function() {
            message_id = $('notice') ? 'notice' : 'warning';
            new Effect.Fade(message_id);
          }, #{seconds*1000});
        }, false);
      </script>
    EOJS
end

Setting a fading flash message in your controller is simple:

flash[:notice] = fading_flash_message("Thank you for your message.", 5)

This is change we can believe in, my friends.

Update

For Rails 3, this still works with a simple inclusion of the RawOutputHelper.

include ActionView::Helpers::RawOutputHelper
def fading_flash_message(text, seconds=3)
  raw text +
    <<-EOJS
      <script type='text/javascript'>
        Event.observe(window, 'load',function() { 
          setTimeout(function() {
            message_id = $('notice') ? 'notice' : 'warning';
            new Effect.Fade(message_id);
          }, #{seconds*1000});
        }, false);
      </script>
    EOJS
end

Aug 21, 2008

Blogiversaire

programming rails ruby | comments

Four years baby. Soon the blog will be toting a lunch pail and school bag, off to decades of education. Next thing I know, my little baby will be married and have little blogs of its own. Blog, you grow up so fast!

Top visited blog posts written in the past year:

As you can see, the popularity of this blog is directly related to what problems I’ve had with Ruby on Rails and people who search Google for the same issues. This is kind of unfortunate in some ways. It makes the blog pretty much narrative-free. But I’m still happy to have helped those who needed it.

While 2008‘s posts have been much more rare, I think there are some gems in there. Going forward, I hope to include a mix of posts explaining a solved problem, displaying a new technique, and generally talking about the unique work life I live with day-to-day.


Nov 28, 2007

Rails before_destroy gotcha

Ruby Rails programming | comments

So there’s a Category that has many Expenses. The expenses for a category are destroyed when the category is destroyed. Elsewhere in my logic I would like to know if the category is removable based on whether it has any expenses and, if it does, whether any of the expenses it has are greater than zero.

class Category < ActiveRecord::Base
  has_many   :items, :dependent => :destroy

  def removable?
    self.expenses.find(:first, :conditions => ['total_cost > ?', 0], :select => 'id').nil?
  end

end

Should really get closer to the vest when protecting the data. So I added a callback to protect the category. Conveniently I can reuse the removable? method:

before_destroy :removable?

Let’s test it:

def test_should_destroy__when_removable
  assert_difference(Category, :count, -1) do
    @category.destroy
  end
end

Yay!

Loaded suite /Users/bjhess/Sites/.../test/unit/category_test
Started
.
Finished in 1.950119 seconds.

1 tests, 3 assertions, 0 failures, 0 errors

And the other test:

def test_should_not_destroy__when_not_removable
  create_expense(:total_cost => 50.00)
  assert_no_difference(Category, :count) do
    @category.destroy
  end
end

NO!

Loaded suite /Users/bjhess/Sites/.../test/unit/expense_category_test
Started
F

Finished in 1.965099 seconds.

  1) Failure:
test_should_not_destroy__when_not_removable(CategoryTest)
method assert_no_difference in test_helper.rb at line 58
method test_should_not_destroy__when_not_removable in category_test.rb at line 66
<8> expected but was
<7>.

1 tests, 2 assertions, 1 failures, 0 errors

What the crap? Apparently the conditional in my removable? method must be working within the transactional space of the destroy call. See, a category has many expenses, set with :dependent => :destroy. In terms of the transaction, all of my expenses have already been destroyed and the category is indeed removable.

If I were to work entirely in memory, things come out alright.

before_destroy :check_expenses_total

private

  def check_expenses_total
    self.expenses.inject(0){ |sum, expense| sum + expense.total_cost } <= 0
  end

So the lesson, I suppose, is be careful how DB-interactive you’re being in your destroy callbacks. Oh, and test.

(Another weird thing. self.expenses.sum(&:total_cost) didn’t seem to work. I had to use inject.)


Jul 30, 2007

Don't forget the return value on Rails callbacks

Ruby on Rails programming Ruby on Rails programming | comments

This morning I spent a good while hashing on a problem in some new code I’m updating for a freelance project. I added a couple callbacks to an ActiveRecord model, and things that previously worked were broken. Unfortunately there were no errors in the model or exceptions in the system to give me a hint as to what went wrong. When I finally uncovered the problem, I was thankful that it was something I had never run into before.

That being said, I sure don’t want to make the mistake again. The offending code looked similar to this:

before_save :update_show_on_navigation
   
private
 
   def update_show_on_navigation
     if !self.navigation.blank?
       self.navigation.show_on_nav = self.published?  # If published, show on nav
     end
   end

What’s going on here? Why was my model not saving successfully and also providing no errors or exceptions? The section “cancelling callbacks” in the callbacks documentation gave me my answer:

If a before_* callback returns false, all the later callbacks and the associated action are cancelled.

Oh boy! Sure enough, that code above could easily resolve to “false” if the model is not “published”. To be sure that the action I’m targeting (save) isn’t cancelled, I simply return “true” at the end of the method.

before_save :update_show_on_navigation
private
 
   def update_show_on_navigation
     if !self.navigation.blank?
       self.navigation.show_on_nav = self.published?  # If published, show on nav
     end
     true
   end