Posts about rails

ActiveRecord Gotcha

I came across this interesting gotcha while debugging through someone else's rails code. When you set a model's one-to-many attribute(an perhaps other association types as well), let's say:
user.friends = new_friend_list
It doesn't always set it. As far as I can figure from my blackbox testing, it does an equal check first between the old value and the new value, and then sets only if they are different.

Now, in Ruby's == is easily overridable, you can easily make it do anything you want. It happens that Array#== returns true iff both arrays have the same number of elements and each element in one array is == to the element of the other array in the same position. It also happens that ActiveRecord's base class' == returns true as long as the objects are of the same class and the primary keys equal.

Which brings us to the gotcha of the day. The code in question built an array, say new_friend_list independently, starting with an id list passed in from a from, finding each friend in the database and adding them to the array, but, in the meantime, modifying a field in each of the friends, say...friend.charma++. It then goes on to set the friends attribute in the manner you'd already seen above, and saves user. Well, the charma field that was modified in each of the friends retrieved did not get saved, because the set did not happen: the new_friend_list pointed to the same list of friends user was already associated to.

Hmm..., yeah. Watch out for this one. Debugging this one was not fun. It definitely didn't fit with the principle of least surprise.
Posted by Toby 10 months ago about gotcha, programming, rails and ruby (0 comments)

Redirect 301

I was looking at Google Analytics today and found that I was getting a bunch of traffic on Use Delicious with Chrome as well as some programming/technical articles, but on the wiki.futuretoby.com domain rather than on the tobyho.com domain. 

For those of you who don't know(probably all of you, because...why would you?), this "wiki/blog" runs the on 2 separate domains, but with a different facade over each one, if you will. One is for my family/personal stuff, for which most of the content it is intended for has access restrictions. The other is the tobyho.com domain, which I created more recently and is mainly for my public content. But all of the content can be accessed in exactly the same way on either domain.

So, basically, the former domain is still more visible on the Google search engine than the latter, and so people are coming to my articles on that one, rather than where I want them to go: tobyho.com. 

How to fix it? Well, it turns out that for things like this, a 301 redirect(as supposed to a 302) is the way to go. It will tell the search engine that the content has moved permantly, and so prompt it to update its index. The code I ended up putting together was pretty simple:
    tag_names = @page.tag_names
    if request.host != 'tobyho.com' and
        @page.authors.include?(toby) and 
        not tag_names.include?('family') and 
        (tag_names.include?('programming') or 
         tag_names.include?('tech') or 
         tag_names.include?('gadgets')) then
        headers["Status"] = "301 Moved Permanently"
        redirect_to("http://tobyho.com/#{@page.title_for_link}", :status => 301)
        return
    end
So, basically, if a visitor comes to an article from the other domain, I want to redirect to the tobyho.com domain if 1) I am the author, 2) the article is tagged with one of programming, tech, and gadgets and 3) the article is not tagged with family. The line:
headers["Status"] = "301 Moved Permanently"  
Is not needed if you're on a newer version of rails, for which setting :status => 301 on the next line would suffice. I embarrassed to say that I am still on 1.2.x.
Posted by Toby 10 months ago about programming, rails and web (0 comments)

Setting up Phusion Passenger on my slice

As my slice is running low on memory, running multiply mongrel processes really isn't an option any more(I am too cheap to pay for more memory, in these trying economic times). Phusion passenger comes to the rescue! Phusion passenger is exactly what the rails community needed for its deployment story. 

So I follow the instructions:
gem install passenger
When I did this my slice just hung indefinitely. It turns out that when gem updates the index of ruby gems, it downloads the entire index and puts it in memory, and this didn't fit in my tiny 256m I had on my slice. This thread ended up saving the day for me. I was able to do:
gem install passenger -B 1000000
it was slow but it worked.
Next up was installing the apache2 module:
passenger-install-apache2-module
First, it failed to find my apache2 installation and asked my to install it. I told it where it was by doing:
export APXS2=/usr/local/apache2/bin/apxs
Next, it didn't find the Apache Portable Runtime(APR). I told it where it was by doing:
export APR_CONFIG=/usr/local/apache2/bin/apr-1-config
Then, it didn't find the APU(whatever that is, I stopped caring). I did:
export APU_CONFIG=/usr/local/apache2/bin/apu-1-config
After all that, and running passenger-install-apache2-module again, it worked! 
Next I setup the virtual host entries. I created the file /usr/local/apache2/conf/apps/tobyho.conf with these contents:
<VirtualHost *:80>
        ServerName tobyho.com
        DocumentRoot /var/www/apps/mywiki/public
        <Directory "/var/www/apps/mywiki/public">
                Options FollowSymLinks
                AllowOverride None
                Order allow,deny
                Allow from all
        </Directory>
</VirtualHost>

And now I got the wiki running on mod_rails. Yea!

Posted by Toby about 1 year ago about deployment, passenger, programming and rails (0 comments)

Turbogears and ReML

I followed the Turbogears 20 minute tutorial here. The first impressions of Turbogears are:
  1. It's a bit more verbose than rails: there's more plumbing - you have to explicitly define which view you want to use for each action, as supposed to doing everything based on convention.
  2. you explicitly pass the local variables to the view as a hash, as supposed to using class or global variables
  3. Turbogears uses a template engine called kid by default, which is very different from rails' erb in philosophy, there's more emphesis on designer friendliness and higher level support for template inheritence
  4. Python/Turbogears in general is safer than ruby/rails - see my last post, in that you usually get more informative errors, such as NameError: global name 'pag' is not defined rather than nil when you didn't expect it
  5. The development feedback is not quite as good as rails. Turbogears requires a restart everytime you make a change. The restart is automatically triggered everytime you save a file in the project, and it is very fast, but it still takes about 5 seconds to rails' 0(ruby has this luxury because of its open classes)
I am hip to Haml so I had to see if I could get it working with Turbogears. I found a couple of implementations: ReML and GHRML. Tried them both, ReML was simpler and more approachable, so I wrote a Turbogears plugin for it. The important bit of the plugin code is here:

from reml import TemplateLoader

class RemlTg(object):
 
  def __init__(self, extra_vars_func=None, options=None):
    pass

  def load_template(self, templatename):
    "Find a template specified in python 'dot' notation."
    parts = templatename.split('.')
    return TemplateLoader('/'.join(parts[0:len(parts)-1])).load(parts[len(parts)-1] + '.reml')

  def render(self, info, format="html", fragment=False, template=None):
    "Renders the template to a string using the provided info."
    return self.load_template(template).render(info)


After that, I converted the views in the tutorial into ReML. Let me do a wc on them for comparison, wait just a minute...

$ wc wiki20/templates/*.kid
  25   76 1068 wiki20/templates/edit.kid
  71  173 2802 wiki20/templates/master.kid
  21   56  773 wiki20/templates/page.kid
  21   50  705 wiki20/templates/pagelist.kid
 138  355 5348 total
airport@wedding-singer ~/documents/play/turbogears/Wiki-20
$ wc wiki20/templates/*.reml
  16   41  492 wiki20/templates/edit.reml
  38   86 1338 wiki20/templates/master.reml
  12   30  327 wiki20/templates/page.reml
   8   24  190 wiki20/templates/pagelist.reml
  74  181 2347 total


So that's about a 50% code reduction, not bad. Here's a Sample for comparison:

page.kid:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:py="http://purl.org/kid/ns#"
      py:extends="'master.kid'">
<head>
<title> ${page.pagename} - 20 Minute Wiki </title>
</head>
<body>
    <div class="main_content">
        <div style="float:right; width: 10em">
            Viewing <span py:replace="page.pagename">Page Name Goes Here</span>
            <br/>
            You can return to the <a href="/">FrontPage</a>.
        </div>

        <div py:replace="XML(data)">Page text goes here.</div>
        <p><a href="${tg.url('/edit', pagename=page.pagename)}">Edit this page</a></p>

    </div>
</body>
</html>


page.reml
- append('master.reml')
- def title():
  =page.pagename
- def content():
  %div: 'style':'float:right; width: 10em'
    Viewing
    %span=page.pagename
    You can return to the
    %a: 'href':tg.url('/')
      Frontpage
  %div=unescaped(data)
  %a: 'href':tg.url('/edit', pagename=page.pagename)
    Edit this page


Posted by Toby about 1 year ago about haml, programming, python, rails and turbogears (0 comments)

capistrano and deprec

I got slicehost recently to host osspinions, and started getting into using capistrano and deprec to deploy the app and set up the box. You can think of capistrano as a remote control enabled make, and deprec as bunch of tasks build on capistrano that automates the setting up of everything from your user account to deploying your rails app - for ubuntu only. I was really impressed, just 4 commands installed everything up to the rails stack. But the deployment of the app was harder, a lot harder, in fact. I had to install ferret(which I just more recently ditched), FreeImage for image science, and rcov as a gem, and ruby-openid as a gem as well. These things are the ones that I could not stuff into the vendor directory and have it work. After that, for ferret I had to use symlinks to share the index directory, and for uploaded files I had to symlink them too because the deprec setup is such that every deployment you do creates a brand new directory for the new release. This is good in many ways, but it also creates some complications. I heard the guys on the slicehost podcast talk about this and they pretty much confirmed as much: it's hard to make deprec work for everybody because everybody does things differently, Oh well. Capistrano, on the other hand, is pretty nice. I wrote a python script at my last job to build and deploy a Java app, and if I had came across capistrano then, I would have definitely used it instead and would have saved me a lot of work.
Posted by Toby over 2 years ago about deployment, programming and rails (0 comments)

Gigantic Rails Log File Slows Down App

My Wiki has been mysterious getting slower and slow and I didn't have a clue why. Today I happened to check the log file's size, it was over 10m. So I removed it and the Wiki was back to normal. Conclusion: you have to have a way to manage the size of rails applications. Some people just turn off logging altogether. I opted to set the log level to error. Another option would be to have a cron job remove the file periodically.

Update: Not so sure the large log file was the cause anymore. I still don't know what caused it, may a memory leak of some sort, or maybe this is just the woes of using a shared host. The problem hasn't come back so far, so I haven't been able to look further.
Posted by Toby over 2 years ago about programming and rails (0 comments)

Changing the rendering for displaying exceptions

I just added autofresh support for the stacktrace pages, now you get get the benefits of autofresh while you are refactoring your code, which can cause temporary breakage, for example. It was easy, altough not at first easy to find. The answer was found in the comments of actionpack/lib/action_controller/rescue.rb. It bascially said you just need to override the rescue_action_in_public - for "public" errors - and rescue_action_locally - for local errors, which shows the stacktraces. I just copied the existing rescue_action_locally method over and swapped in my template file instead(changed code in orange):

module ActionController
  class Base
    def rescue_action_locally(exception)
      render :text => 'it bombed'
      add_variables_to_assigns
      @template.instance_variable_set("@exception", exception)
      @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
      @template.send!(:assign_variables_from_controller)
 
      @template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
 
      response.content_type = Mime::HTML
      render_for_file("#{File.dirname(__FILE__)}/templates/layout.erb", response_code_for_rescue(exception))
    end
  end
end

This will tell it to find the layout.erb in my plugin directory instead of the one buried deep inside rails.
Posted by Toby over 2 years ago about programming and rails (0 comments)

Autofresh

Out of frustration with the work I was doing, I decided to switch gears a tiny bit and work on this idea I had, and Autofresh was the result. I enjoyed this experiment, it did not take much to make it work - I just based it on the rspec_autotest plugin by Nick Sieger, but it turned out to be very useful, Yeh!
Posted by Toby over 2 years ago about plugin, programming and rails (0 comments)