Let’s say i want to be able to retrieve doggie’s red shirts:
12345678910
classDog# (blah blah)defred_shirtsshirts.where(:color=>'red')endenddoggie.shirts# all shirtsdoggie.red_shirts# all red shirts
Ok, what if we want to retrieve kitty’s red shirts as well ?
12345678910
classCat# (blah blah)defred_shirtsshirts.where(:color=>'red')endendkitty.shirts# all shirtskitty.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:
123456789101112
classShirt# (blah blah)scope:red,where(:color=>'red')# * A less DSL-ish approach is to declare a class method# def self.red# where(:color => 'red')# endendShirt.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:
12345
doggie.shirts# all doggie's shirtsdoggie.shirts.red# all doggie's red shirtskitty.shirts# all kitty's shirtskitty.shirts.red# all kitty's red shirts
And to make it even sweeter, i can do the following as well:
12
doggie.shirts.red.create(:size=>:xl)# doggie gets a new red :xl shirtkitty.shorts.red.create(:size=>:xs)# kitty gets a new red :xs shirt
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:
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.
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 :)
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:
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:
12345
$ 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:
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 :)
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.
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:
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: