%w(hashrockets spaceships bangbangs)

> non-optimized bits & pieces <

Falling in Love With AR’s Scoping

If you do activerecord, you should fall in love with scoping, as it can really boast ur AR-fu.

Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
class Shirt
  belongs_to :owner, :polymorphic => true
end

class Cat
  has_many :shirts, :as => :owner
end

class Dog
  has_many :shirts, :as => :owner
end

Let’s say i want to be able to retrieve doggie’s red shirts:

1
2
3
4
5
6
7
8
9
10
class Dog
  # (blah blah)

  def red_shirts
    shirts.where(:color => 'red')
  end
end

doggie.shirts     # all shirts
doggie.red_shirts # all red shirts

Ok, what if we want to retrieve kitty’s red shirts as well ?

1
2
3
4
5
6
7
8
9
10
class Cat
  # (blah blah)

  def red_shirts
    shirts.where(:color => 'red')
  end
end

kitty.shirts     # all shirts
kitty.red_shirts # all red shirts

Hmmm, duplication detected, but not so bad (yet) .. how abt i want to retrieve all red shirts ? AR supports this via the scope declarative:

1
2
3
4
5
6
7
8
9
10
11
12
class Shirt
  # (blah blah)

  scope :red, where(:color => 'red')

  # * A less DSL-ish approach is to declare a class method
  # def self.red
  #   where(:color => 'red')
  # end
end

Shirt.red # all red shirts

And you know what, with the new Shirt.red, i can throw away Cat#red_shirts & Dog#red_shirts. If i ever want to fetch the red shirts of doggie & kitty:

1
2
3
4
5
doggie.shirts     # all doggie's shirts
doggie.shirts.red # all doggie's red shirts

kitty.shirts      # all kitty's shirts
kitty.shirts.red  # all kitty's red shirts

And to make it even sweeter, i can do the following as well:

1
2
doggie.shirts.red.create(:size => :xl) # doggie gets a new red :xl shirt
kitty.shorts.red.create(:size => :xs)  # kitty gets a new red :xs shirt

Honestly, we should love AR’s scoping rite ?!

If u have the time, take a look at the activerecord doc.

OOP vs DOP

One anti-pattern that i come across very frequently while doing rails (& activerecord) related work is that alot of times, people tend to do DOP (database oriented programming), when they should instead be doing OOP (object oriented programming).

Most of the applications we write require support for:

  • modelling our business knowledge, AND
  • persisting the state of business entities

In most rails apps, the above functionalities are provided by the model layer, which in turn is powered by the orm framework (usually activerecord). It is important to bear in mind the priority ~ the modelling of business knowledge. Persistence gets into the equation only when we want to persist the application’s state.

By placing higher priority on business modelling & less on persistence, we better allocate our resources to tackle changes in business logic, abstracting & minimising changes required on (or necessitated by) the persistence layer.

Let’s set the stage for the classic join-model example of students attending a class, & how DOP & OOP tackle fare against each other:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Clazz
  has_many :participations
  has_many :students, :through => :participations
end

class Student
  has_many :participations
  has_many :clazzes, :through => :participations
end

class Participation
  belongs_to :clazz
  belongs_to :student
end

1) The DOP Way

Base on the original modelling:

1
Participation.create(:clazz => physics_class, :student => alice)

What if there comes a day we need to rename Participation to ClazzParticipation ? Besides renaming the class Participation, you also need to:

  • change the above Participation.create to ClazzParticipation.create
  • declare the :class_name for Student#participations & Clazz#participations

Changes necessitated by change in business requirements are inevitable in software engineering, but the above changes are unnecesssary & as the cause comes from the persistent side, rather than the business side.

Let’s improve on this DOP way by trying the hybrid approach.

2) The Hybrid Way

Using the original modelling, we do:

1
physics_class.participations.create(:student => alice)

This is better than the pure DOP approach. When renaming of Participation to ClazzParticipation is required, besides the obvious renaming of the class Participation, you just need to:

  • declare the :class_name for Student#participations & Clazz#participations

Pretty good, but bare in mind that this approach is forcing other engineers to be aware that:

in order to add alice (student) to physics_class (class), you create a participation for alice w.r.t physics_class

Clearly, it is still very database centric. This approach misses the essence of OOP ~ encapsulation. It exposes & forces anyone to be aware of:

  • the underlying join association Clazz#participations, AND
  • you have to call Clazz#participations.create, AND
  • you have to pass the argument :student => alice

Can we make it better ?? Of couse we can, let’s try the following more OOP approach:

3) The OOP Way

With the original modelling,

1
physics_class.students << alice

Read:

in order to add alice (student) to physics_class (class), you just add alice to its list of students

That’s it !! If renaming of Participation to ClazzParticipation is required, the changes required is the same as the above hybrid approach.

Summing Up

How do you know u are doing DOP or OOP ? I guess if you are doing good OOP,

the code should be so simple that it just reads :)

A Little Bit of Sed Kungfu

Just picked up a little bit of sed kungfu today while pairing with jeff.

In-place editing with “-i”

If you want to do in-place editing of a file, you can pass the -i option to sed. Let’s say we have the following file:

1
sed is cool

With:

1
$ sed -i /path/to/file -e 's/cool/awesome/'

We get:

1
sed is awesome

So how do i acheive this previously ?? This is embarrassing, it used to be like this:

1
2
$ cat /path/to/file | sed 's/cool/awesome' > /path/to/file.new
$ mv /path/to/file.new /path/to/file

Replacing only lines matching pattern

Let’s say you have the following file:

1
2
Vim is really cool,
& sed is cool as well.

We want to replace only the line with the occurrence of “sed”. We can acheive it using the script command /<pattern>/s/<original-text>/<replacement-text>, just like this:

1
sed -e '/sed/s/cool/awesome/' /path/to/file

This is what we get in stdout:

1
2
Vim is really cool,
& sed is awesome as well.

Happy Sed-ing :)

Installing the Unofficial Byobu in Archlinux

If you love byobu, & u just decided to join the archers, you probably will miss it as you can’t simply do sudo pacman -S byobu, since the package isn’t available on any of the offcial repositories.

DO NOT DESPAIR, there is an unofficial package at the aur (Arch User Repository). Just goto aur to search for it, & copy the link to the tarball, then:

1
2
3
4
5
$ cd /tmp
$ wget http://aur.archlinux.org/packages/by/byobu/byobu.tar.gz
$ tar zxf byobu.tar.gz
$ cd byobu
$ makepkg

Depending on how mature ur system is, you may be prompted to manually install some extra dependencies, note these & run:

1
$ sudo pacman -S <DEPENDENCIES>

With all dependencies installed, then:

1
2
$ makepkg
$ pacman -U *.xz

And you can start to rock-&-roll :)

Killing a Screen Window

I use screen everyday, yet i must admit i’ve always been only scratching its surface. Today, coworker +kelvin showed me how to kill a screen window with CTRL-a + k.

How did i do it in the past ?? Err, this is embarrassing, i usually either just type exit in the window.

Finally, let’s say if u start with windows 1$, 2$, 3$ & 4$. When u kill 3$, 4$ still retains its “4”. To fix this issue, you can CTRL-a :number 3 to reset “4” to “3”.

PS: a personal todo is to dig into man screen .. or bug someone to do the digging & teach me :)

Removing Untracked Files With Git Clean

The BEST thing abt pair-programming is that it facilitates knowledge diffusion. Just pick up git clean from coworker +peer today.

git clean makes it real easy to remove untracked files in ur working git repo. Files/dirs listed in .gitignore are ignored (as expected), only those extra untracked files are cleaned off.

Honestly, nothing beats git help clean if u want to learn more abt it.

Quick Commenting of Ruby Code

What is the fastest way to comment a block of ruby code ?? Sorry, i’m too fast, no time to wait, let’s install the extra plugins 1st:

1
2
3
4
$ cd ~/.vim/bundle
$ git clone https://github.com/kana/vim-textobj-user.git
$ https://github.com/nelstrom/vim-textobj-rubyblock.git
$ https://github.com/tomtom/tcomment_vim.git

To comment an entire method, goto anywhere of that method, var & gc.

To comment the inner content of a method, again anywhere of that method, vir & gc.

Sorry, too fast & too sweet ?? See this vimcast to learn more abt var & vir :)

The Motion That You Must Know

Let me tell you, i (“inside”) is the motion that you MUST know.

Given "this is awesome", anywhere from the starting " to the ending one, just do a ci", you can replace anything within the quotes. This works for backtick & single-quote char as well, but not for other delimiters like "/", "|", "\", …, etc.

Given %(this is awesome), doing ci( or ci) works as well. In fact, this works equally well for ci<, ci>, ci[, ci], ci{ & ci}.

If you have installed the vim-textobj-user & vim-textobj-rubyblock plugins:

1
2
3
$ cd ~/.vim/bundle
$ git clone https://github.com/kana/vim-textobj-user.git
$ git clone https://github.com/nelstrom/vim-textobj-rubyblock.git

You can do cir anywhere from start of a class ... end, module ... end, def ... end, do ... end, if ... end, … to replace the contents within.

Last but not least, you may wish to know the a (stands for “around”) motion as well. Try out ca", ca(, car & friends.

Have fun !!

Open File & Goto Line

I only get to know this recently, you can open a file & goto a specific line in vim with:

1
$ vim /path/to/file +100

The above opens /path/to/file & sets u on line 100. When are already in vim, you can acheive it with:

1
:e /path/to/another/file +99

But this won’t work for buffer though, meaning NONE of the following will work :(

1
2
3
:b /path/to/another/file +99
:b<id> +99
:b# +99

Flexible Method Arguments With the Splat Operator

The splat operator allows a method to accepts 0 ~ ruby’s max number (hm, i don’t know what number is that) of arguments, this makes it super flexible, & here are some ways to take advantage of that:

1) Probably simple flat list

1
2
3
4
5
6
7
8
9
10
11
12
13
def mm(*args)
  args.each_with_index {|arg, i| puts "A[#{i}]: #{arg}" }
end

mm
# >> (blank)

mm(1)
# >> A[0]: 1

mm(1, 2)
# >> A[0]: 1
# >> A[1]: 2

2) May have array & must be flattened

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def mm(*args)
  [args].flatten.each_with_index {|arg, i| puts "A[#{i}]: #{arg}" }
end

mm
# >> (blank)

mm(1) # same as mm([1])
# >> A[0]: 1

mm([1, 2]) # same as mm([1], [2]), mm(1, [2]) & mm([1], 2)
# >> A[0]: 1
# >> A[1]: 2

mm(1, [2, 3]) # same as mm([1, 2, 3]), mm([1], [2, 3]), etc
# >> A[0]: 1
# >> A[1]: 2
# >> A[2]: 3

3) May want to support options-like usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def mm(*args)
  opts = args.last.is_a?(Hash) ? args.pop : {}
  args.each_with_index {|arg, i| puts "A[#{i}]: #{arg}" }
  opts.each {|key, val| puts "O[#{key}]: #{val}" }
end

mm
# >> (blank)

mm(x:1)
# >> O[x]: 1

mm(1)
# >> A[0]: 1

mm(1, x:2)
# >> A[0]: 1
# >> O[x]: 2

mm(1, 2, x:3, y:4)
# >> A[0]: 1
# >> A[1]: 2
# >> O[x]: 3
# >> O[y]: 4

4) May want to passing in a code-block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def mm(*args, &block)
  args.each_with_index {|arg, i| puts "A[#{i}]: #{arg}" }
  puts block.call if block_given?
end

mm
# >> (blank)

mm { "B[*]: rocks" }
# >> B[*]: rocks

mm(1) { "B[*]: rocks" }
# >> A[0]: 1
# >> B[*]: rocks

mm(1, 2) { "B[*]: rocks" }
# >> A[0]: 1
# >> A[1]: 2
# >> B[*]: rocks

5) All in one

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def mm(*args, &block)
  opts = args.last.is_a?(Hash) ? args.pop : {}
  [args].flatten.each_with_index {|arg, i| puts "A[#{i}]: #{arg}" }
  opts.each {|key, val| puts "O[#{key}]: #{val}" }
  puts block.call if block_given?
end

mm(1, [2, 3], x:4, y:5) { "B[*]: rocks" }
# >> O[x]: 4
# >> O[y]: 5
# >> A[0]: 1
# >> A[1]: 2
# >> A[2]: 3
# >> O[x]: 4
# >> O[y]: 5
# >> B[*]: rocks

Extra Reading

To advance ur ruby kungfu, you probably want some extra reading from the free chapter abt designing beautiful apis, from Ruby Best Practices.