Robot Has No Heart

Xavier Shay blogs here

A robot that does not have a heart

I don't want preferences

Or why I’m writing another blog engine for ruby

I’ve been running this site on Mephisto for a number of months now. It is fantastic at what it does, but I’ve just recently realised it’s not what I want.

I want to configure my blog by hacking code

I don’t want preferences or theme support – I want to edit code. Mephisto isn’t great for this – it uses non standard routing (everything goes through dispatch), it uses liquid templates. I feel like I have to learn Mephisto to hack it.
SimpleLog is another rails option, but it sucks because it reads like a PHP app, and I don’t want to be hacking that. It’s built to be configured, not to be hacked.

So here is my grand plan.

An opinionated blog engine that does things my way. OpenID login, XHTML valid default template, RESTful stuff, code highlighting in comments, etc…
To install, you branch my master git repo and customize away. You can just keep rebasing to get all the trunk updates. You can publish a ‘theme’ in the form of a patch against trunk. The code is going to be lean since I don’t need to accommodate for 5, 10 or 15 articles per page, so it will be easy to comprehend.

Basically, it’s so you can write your own blog without having to worry about boring stuff like admin, defensio integration, and OpenID auth.

I wonder what I’ll call it.

UPDATE: Look I made it – Enki

Bad UI ruins Christmas

On my Mum’s digital camera, when you look at a movie, you get a still from the movie and two options: delete this frame, delete all frames. Delete all frames does not delete all frames of the movie. It deletes everything on the camera. We lost all our Christmas photos, and also a photo of my cake which is kind of depressing.

Don’t use ambiguous or unclear terminology in your UI. “Frame” in my mind refers to a frame of a movie, but the camera used it to refer to a photo – this is likely a case of a system term seeping through to the user interface (or bad translation – it’s a Japanese camera I think).

My brother was impressed by the feature: “Hey cool, you can delete individual frames from your movie”.

Rephrase important decisions in the confirmation, and provide important information. “Are you sure you want to delete all frames?” is useless. Try “This will irrevocably delete all 324 photos and movies on your camera. Are you sure you want to continue?”. And always provide ‘undo’. It’s not that hard to have a ‘trash’ area, only permanently delete the files when you really need the space.

If anyone has any spare Christmas shots they’re not using, please link them up. Mum’s pretty distraught.

Don't use pagination on your blog

What problem are you trying to solve? In my case, I don’t want the bottom of the page to be a dead end. Paging would appear to be a good solution – click next page, get more content. Alas, it has issues:

  • When you post a new article, it changes the content of all your pages. Google doesn’t like this – search traffic to your blog will suffer since people will click through expecting an older version of the page.
  • Invalidates your entire cache when you post something new. Admittedly not a problem for most of us, but worth considering.

Archives solve my problem – not wanting a dead end, while avoiding the two problems with pagination mentioned above. It is harder to get your window size right though (you don’t want 2 or 200 articles per page).

For bonus points, add something like the Humanized Reader. Javascript fetches the next article when you’re near the bottom of the page, seamlessly adding it to the bottom of the page so the user can just keep on reading.

I’ve just added archives to this site – an interim fix to tide me over until I do it right.

Thanks to Rick Olson for telling me I didn’t need paging.

How we use the Presenter pattern

FAKE EDIT: I wrote this article just after RailsConf but have just got around to publishing it. Jay has since written a follow up which is worthwhile reading.

I may have been zoning out during Jay Fields talk at RailsConf – not sleeping for a few days will do that to you – but I think I got the gist of his presentation: “Presenter” isn’t really a pattern because it’s use is to specific and there isn’t anything that be generalized from it. Now, I’m not going to argue with Jay, but I thought it may be helpful to give an example of how we’re using this “pattern” and how it is helpful for us at redbubble.

Uploading a piece of work to redbubble requires us to create two different models – a work and a storage, and associate them with each other. Initially, this logic was simply in the create method of one of our controllers. My problem with this was it obscured the intent of the controller. To my mind a controller is responsible for the flow of the application – the logic governing which page the user is directed to next – and kicking off any changes that need to happen at the model layer. In this case the controller was also dealing with the exact associations between the models, roll back conditions. Code that as we will see wasn’t actually specific to the controller. In addition, passing validation errors through to the views was hard because errors could exist on one or more of the models. So we introduced a psuedo-model that handled the aggregation of the models for us, it looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class UploadWorkPresenter < Presenter
  include Validatable

  attr_reader :storage
  attr_reader :work

  delegate_attributes :storage, :attributes => [:file]
  delegate_attributes :work,    :attributes => [:description]

  include_validations_for :storage
  include_validations_for :work

  def initialize(work_type, user, attributes = {})
    @work_type = work_type
    @work = work_type.new(:user => user, :publication_state => Work::PUBLISHED)
    @storage = work_type.storage_type.new

    initialize_from_hash(attributes)
  end

  def save
    return false if !self.valid?

    if @storage.save
      @work.storage = @storage
      if @work.save
        return true
      else
        @storage.destroy
      end
    end

    return false
  end
end

We have neatly encapsulated the logic of creating a work in a nice testable class that not only slims our controller, but can be reused. This came in handy when our UI guy thought it would be awesome if we could allow a user to signup and upload a work all on the same screen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class SignupWithImagePresenter < UploadWorkPresenter
  attr_reader :user

  delegate_attributes :user, :attributes => [:user_name, :email_address]

  include_validations_for :user

  def initialize(attributes)
    @user = User.new
    super(ImageWork, @user, attributes)
  end

  def save
    return false if !self.valid?

    begin
      User.transaction do
        raise(Validatable::RecordInvalid.new(self)) unless @user.save && super
        return true
      end
    rescue Validatable::RecordInvalid
      return false
    end
  end
end

So why does Jay think this is such a bad idea? I think it stems from a terminology issue. Presenters on Jay’s project were cloudy with their responsibilties – handling aggregation, helper functions, and navigation. As you can see, the Presenters we use solely deal with aggregation, keeping their responsibility narrow.

For reference, here is our base Presenter class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Presenter
  extend Forwardable
  
  def initialize_from_hash(params)
    params.each_pair do |attribute, value| 
      self.send :"#{attribute}=", value
    end unless params.nil?
  end
  
  def self.protected_delegate_writer(delegate, attribute, options)
    define_method "#{attribute}=" do |value|
      self.send(delegate).send("#{attribute}=", value) if self.send(options[:if])
    end
  end
  
  def self.delegate_attributes(*options)
    raise ArgumentError("Must specify both a delegate and an attribute list") if options.size != 2
    delegate = options[0]
    options = options[1]
    prefix = options[:prefix].blank? ? "" : options[:prefix] + "_"
    options[:attributes].each do |attribute|
      def_delegator delegate, attribute, "#{prefix}#{attribute}"
      def_delegator delegate, "#{attribute}=".to_sym, "#{prefix}#{attribute}=".to_sym
      def_delegator delegate, "#{attribute}?".to_sym, "#{prefix}#{attribute}?".to_sym
    end
  end
end

It doesn't matter

My secret to life: I don’t mind what happens.

Quoted just the other day by Everyday Wonderland

If I get a job, if I go to Japan, if I keep living at home, if I move to Melbourne – my happiness is not dependent on any of these outcomes.

The key to this is framing traditionally depressing events – missing out on a job, finding yourself with nowhere to sleep, getting rejected – as challenges. They keep life interesting. I don’t claim to be a master at this, but I am working on it.

Combine this with an acceptance of events outside of your control. You can reasonably expect trains to run on time, so if one is delayed and you are late to an event, there is nothing you can do. Why stress? It doesn’t solve anything and makes you feel like crap.

As with anything, this takes practice. Next time you find yourself getting stressed, think “why can’t I just accept these circumstances?”. Either you will find you can do something productive to alleviate them (even it that is as abstract as working on your time management skills so you’re not pressed to deadline next time…), or find a sense of peace in succumbing to the greater forces of the universe.

Food Choices

UPDATE: As of easter 2008 I am vegan again. I need to write an updated version of this post since I don’t agree with much of it anymore. I’m leaving it here for history’s sake – it’s enlightening to see my progression in thought, I think.

I am a vegetarian. Inevitably, people ask me “why?”. I think it would be more productive for them to ask themselves why they are not, but that is by the by. For me the interesting question is “why am I not vegan?”, which I will get to after I briefly cover the first.

There were two distinct ideas that led to my change of diet (I was an omnivore until mid way through last year). The first was a realisation that living the “examined” life (as Socrates put it) actually led to a dramatic increase in my quality of life, and in a similar vein that I was responsible for everything in my life (Satre’s idea of freedom). This will be the subject of future writings, but it culminated in me trying to rid my life of “contradictions”, of which my food choices presented many.

For vegetarianism, the deciding scenario was first introduced to me by Peter Singer in a public lecture he delivered at Melbourne Uni. It appeals to me because it avoids the need to take a non-mainstream stance on animal rights, but rather draws logical conclusions from common attitudes towards animals. Activities that harm animals for entertainment – bull/cock/dog fighting, for instance – are frowned upon by our culture, evidenced by the fact that they are banned by law. However, the more widespread harm of animals for non-essential food – in the form of battery farming – is condoned. This is a contradiction that I could not allow to stand, and so vowed to avoid battery farmed produce. Theoretically it is possible to continue an omnivorous diet within this constraint, but in practice finding (and affording!) organic meat is non-trivial, so I chose to abstain from meat all together. In addition, on non-ethical grounds I wanted to try the purported health benefits of vegetarianism, and also wanted to expand my cooking repertoire, which was depressingly confined to omnivorous cuisine.

After getting comfortable with vegetarianism, I decided to try veganism. The only ethical justification for this was that livestock are an order of magnitude more expensive (in near all measures of the term) than grain and vegetable sources, and as such are a burden that our growing society simply cannot sustain. Contrary to many vegans, I do not believe that animals deserve the same rights as humans, drawing the (admittedly grey) line at self-reflection and higher order thought. To illustrate, the jury is still out on chimpanzees, but farmyard animals have not demonstrated to my satisfaction that they possess the necessary reasoning, desires or aspirations to be apportioned rights akin to our own.

To allow my body and habits to adjust I mandated a one month trial period. I discovered a number of new ways to cut animal products out of my diet, for example my sandwiches do not benefit for cheese or margarine, and soy milk is a much better alternative over cereal – adjustments I still hold to today. However my social life suffered. Not having any vegan friends, and knowing only one or two vegetarians, I found it difficult to eat out anywhere (since vegan meals are generally lacking if it is not the restaurant’s main trade), and while people will usually be all too happy to cook a vegetarian meal for you, they generally blanch at the prospect of not using cheese. Not to mention that it eliminates virtually all desserts(!), and many types of beer(!!). In addition, I felt my alertness waning, and could not find ways to affordably maintain an athlete’s diet (most notably protein sources – one can only eat so much peanut butter, and it is quite high in fat).

My quality of life diminished, both socially and in health, and I could not justify this by the one ethical tenet by which I had made my decision. I feel I can contribute more to activist and economical causes to offset such a choice if the rest of life is in order, so after a month of a vegan diet I returned to eggs and dairy (and choice beer).

After more than 6 months of vegetarianism, I look and feel healthier than I have ever been. (For balance, I have also been exercising regularly, but do not feel constrained by the lack of meat in my diet). I will potentially try veganism again in the future – I feel support from my social group would help in this regard (which I can’t see happening any time soon!) – and have no desire whatsoever to return to being an omnivore. I no longer crave steak, and the only time I feel my diet is restricted is in certain restaurants that do not pay enough attention to their menu.

To end with a quick rant, the “Real Men Eat Meat” mantra I so often encounter is, if you’ll excuse the term, bullshit, and used as a facade by those too lazy to take control of their lives. I can accept you eating meat, just show me you have actually made an informed choice rather than blindly digesting the empty catchphrases employed by your ignorant peers.

Mode Errors in Mobile Phones

A recent post on the humanized weblog has got me thinking about mode errors in software I use often.

One particularly nasty one occurs on my Nokia 2100 phone when sending SMS messages. After selecting “send”, a box is displayed to enter the destination number. To select a contact from your address book, you press button A. However, if a number is already present in the entry box (if you are replying to a message, and in other circumstances whose criteria I am uncertain of), the same button A sends the message!

The implication? My reflex action is to press button A immediately after selecting “send” to go to my address book. Twice in the past two days there has already (unexpectedly!) been a number there, causing me to send a message to the wrong person.

What potentially embarrassing or disastrous mode errors do you deal with regularly?

Make your resume interesting

I’ve just finished up all my IBL interviews. #1 preference is Advatel, followed by Fenwick Software. Both development roles – the other two not so much.

At every interview I got an excited question about my experience at “The World of Robotics”, since everyone knows robots are cool. Also, I’ve listed “Swing Dancing” under other interests. In every single interview, right at the very end I always got a “now I just have to ask…” question about it. It was fantastic, the interview always ended on a high note, which has to be a good thing for your job prospects.

The point I’m trying to make is – Make your resume interesting and worth talking about. You can then predict many of the questions you’ll be asked and can be well prepared to sell yourself.

A pretty flower Another pretty flower