Wednesday, September 29, 2010

Why Ruby?

Emancipate yourself from mental slavery, none but ourselves can free our minds. --Bob Marley, Redemption Song

There are a number of reasons to love Ruby and I will share some of them here. Ruby is influenced by some of the greatest languages ever invented: Perl, probably the most pragmatic language in the world, hell, even God used it :)

Lisp from XKCD

Ruby is also influenced by Smalltalk and Lisp. Both these languages are still living, vibrant languages despite their respectable age, 40 and 60 years respectively.

When Matz chose parts from the above programming languages his primary design goal was Programmer Happiness. He succeeded.

Here are some features that I love about the language.

Parallel Assignment

You can assign many values on the same line by separating them by commas. They are assigned in parallel. No need for temporary variables.

# Swap a and b, without a temporary variable
a, b = b, a

Multiple Return Values

If you want to return more than one value in Ruby all you need to do is to wrap it in an array. Then you can get the values by using commas. Also note, that return is not required in Ruby, the last evaluated expression is returned.

def div(a, b)
  [a/b, a%b]
end

a, b = div(5, 2)
# a = 2, b = 1

String Interpolation

Double-quoted strings can be interpolated.

kid = 'Kid'
"Hello #{kid} ##{1+3}"
# => Hello Kid #4

Here documents

You have seen them in other scripting languages and they are as good as they look. No need for String concatenation or embedded newlines (\n).

sql = <<-SQL
SELECT allocations.project_id as project_id, year, week, allocations.id as allocation_id
  FROM weeks
  LEFT OUTER JOIN allocations ON allocations.start_date < weeks.end_date
    AND allocations.end_date > weeks.start_date
    AND allocations.project_id = ?
  WHERE weeks.start_date >= ? AND weeks.end_date <= ?
SQL

Higher Order Functions

Higher order functions are easily expressed with Ruby blocks.

# Map converts an array of values to a new array of values
["a", "b", "c"].map { |item| item.upcase }
# => ["A", "B", "C"]

# Zip zips arrays together
[1, 2, 3].zip([10, 20, 30])
# => [[1, 10], [2, 20], [3, 30]]

# Zip with a function applies the function to the results
[1, 2, 3].zip([10, 20, 30]) {|arr| arr[0] + arr[1]}
# => [11, 22, 33]

Simple Command Line Access

The command line is always readily available in Ruby

# Backticks invokes the command and returns the result as a string
`ls`
# => "Scripts\nStylesheets\nbin\nget_destinations.scpt\nlib\nnibs\n"
`ls`.split
# => ["Scripts", "Stylesheets", "bin", "get_destinations.scpt", "lib", "nibs"]

# System invokes the command, prints the result to stdout, and return a boolean
system('ls -l')
total 64
drwxr-xr-x  3 andersjanmyr  admin    102 Aug 29  2009 Scripts
drwxr-xr-x  3 andersjanmyr  admin    102 Aug 29  2009 Stylesheets
drwxr-xr-x  5 andersjanmyr  admin    170 Aug 29  2009 bin
-rw-r--r--  1 andersjanmyr  admin  12312 Aug 29  2009 get_destinations.scpt
drwxr-xr-x  3 andersjanmyr  admin    102 Aug 29  2009 lib
drwxr-xr-x  3 andersjanmyr  admin    102 Aug 29  2009 nibs
# => true

system('ls tapir')
ls: tapir: No such file or directory
# => false

Open Classes

Open classes mean that it is possible to add new methods to already existing classes. This is very powerful.


class String
  def vowels
    self.scan(/[aeiouy]/i)
  end
end

"A tapir is beautiful".vowels
# => ["A", "a", "i", "i", "e", "a", "u", "i", "u"]

There are two common reactions to this.

  • Wow! This is really cool, this will allow me to put everything in the right place.
  • What! That is too dangerous, our programmers will create an unmaintainable system.

I'm in the first camp!

Class Inheritance

In Ruby the classes are objects and follow the inheritance rules of objects. No statics are needed.

class Mammal
  # This is a class method, that returns all mammals of this kind
  def self.all
    mammals.select do |m|
      m.kind_of? self
    end
  end

  # Another class method that gives access to the class varibable
  def self.mammals
    # @@ is a class variable prefix
    @@all_mammals ||= []
  end

end

class Tapir < Mammal
end

# Add some mammals and a tapir
Mammal.mammals << Mammal.new << Mammal.new << Tapir.new

Tapir.all
# => [#<Tapir:0x000001010fc1a0>]

Mammal.all
# => [#<Mammal:0x000001010fc218>, #<Mammal:0x000001010fc1c8>, #<Tapir:0x000001010fc1a0>]

Meta Programming

When the classes are parsed, Ruby is already running and since it is possible to create methods and classes on the fly you can do some very powerful meta-programming with Ruby. Here is a simple example.

class Tapir

  [:sniff, :eat].each do |method_name|
    send :define_method, method_name do |thing|
      "I'm #{method_name}ing #{thing}!"
    end
  end

end


t = Tapir.new
t.eat 'bananas'
I'm eating bananas!

t.sniff 'glue'
I'm sniffing glue!

This type of meta-programming is what enables the simplicity of Rails' has_many, and belongs_to methods for declaring relationships.

Method Missing

The last feature of Ruby that I want to share is method_missing. method_missing is a feature that Ruby inherited from Smalltalk. If an object is invoked with a method that is not defined by its class or ancestors, method_missing(method, args) is called instead. By implementing this method you can effectively create all sorts of cool stuff such as proxies, prototypes, etc.

# Simple implementation of a Javascript-like structure without inheritance
class Prototype

  def props
    @props ||= {}
  end

  def method_missing(method, *args)
    m = method.to_s.sub('=', '') # Strip the trailing '=' to allow setters
    if args.empty?
      props[m]
    else
      props[m] = args[0]
    end
  end
end

prot = Prototype.new
prot.age
# => nil
prot.age 14
# => 14
prot.age
# => 14
prot.age= 16
# => 16
prot.age
# => 16

Inside method_missing it is of course also possible to define new methods and classes dynamically, but I'll leave that for another day.

Ruby is a supreme dynamic language that lets you do almost anything. Try it!

When you can do anything, you have to become more responsible. You own your fate. And, that, I think, is a situation which promotes responsibility. -- Michael Feathers, author of Working Effectively with Legacy Code

Saturday, September 18, 2010

Webkit CSS Transitions and Transformations

This blog is best viewed in Safari or Chrome since it uses Webkit-specific functionality.

Transitions

transition, the process or a period of changing from one state or condition to another

Transitions are usually performed using jQuery or some other Javascript framework to animate the changes to a property. When using Webkit (Safari, Chrome, iPad, iPhone) Javascript is not necessary anymore.

In Webkit the transitions are described using four different properties. It is worth noting that the properties only define how the transitions will work when the values on the element is changed. They don't actually do anything until the values are changed!

/* This sets up a transition property on all divs on opacity.*/
div {
  -webkit-transition-property: opacity;
  -webkit-transition-duration: 2s;
}
/* The fade-in class, changes the opacity when set on an element.
  And thus triggers the transition defined above if the element is a div. */
.fade-in {
  opacity: 1;
}
// Adds the class 'fade-in' on all divs, via jQuery.
$("div").each(function() {
  $(this).addClass('fade-in');
});

-webkit-transition-property

This is the property that will be affected by the transition, this can be any valid property, and it defaults to all. What this means is that a change to any attribute on an element will be affected by the transition and occur according to the specification.

/* only width should be affected */
-webkit-transition-property: width;

/* multiple comma-separated properties are supported */
-webkit-transition-property: opacity, width, height;

/* all properties should be affected, this is the default */
-webkit-transition-property: all;

-webkit-transition-duration

How long the transition should take? Valid units are s and ms.

/* Set the duration to 400 milliseconds */
-webkit-transition-duration: 400ms;

/* Multiple values are supported here too, in combination with
multiple -webkit-transition-property's */
-webkit-transition-property: opacity, width, height;
-webkit-transition-duration: 400ms, 1s, 3s;

-webkit-transition-delay

How long should we wait before the transition starts. Valid units are s and ms.

/* Set the delay to 1 second */
-webkit-transition-delay: 1s;

/* Multiple values are supported here too, in combination with
multiple -webkit-transition-property's */
-webkit-transition-property: width, height;
-webkit-transition-delay: 1s, 3s;

-webkit-transition-timing-function

The timing function is a function that changes the speed of the transition over its duration. It is described with a cubic-bezier curve, but it has some predefined values, ease (default), ease-in, ease-out, ease-in-out, and linear. In none of those values are to your liking you can define your own with the cubic-bezier function.

/* Transition with the default timing*/
-webkit-transition-timing-function: ease;
/* Multiple values are supported here too, in combination with
multiple -webkit-transition-property's */
-webkit-transition-property: opacity, -webkit-transform;
-webkit-transition-timing-function: ease-out, cubic-bezier(0.5, 0.2, 0.3, 1.0);

Thats it for transitions. But wait, what was that -webkit-transform in the end?

Transformations

transformation, a thorough or dramatic change in form or appearance

Webkit supports some really cool transformations, that allow you to animate the pages in remarkable ways.

rotate()

<div style = "width: 12em;
  margin-top: 5em;
  -webkit-transform: rotate(45deg)">
I am rotated!
</div>
I am rotated!

Rotate accepts the units deg, rad, and grad

It also comes in 3D forms, rotateX, rotateY, rotateZ, and rotate3d.

scale()

<div style = "width: 12em;
  margin: 2em 0 2em 18em;
  -webkit-transform: scale(4)">
I am Scaled!
</div>
I am Scaled!

Scale can take an additional parameter scale(x, y) If you want to scale un-proportionally. It also comes in 3D versions, scaleX, scaleY, scaleZ, and scale3d.

translate()

<div style = "width: 12em;
  margin: 2em;
  -webkit-transform: translate(4em, -2em)">
I am Translated!
</div>
I am Translated!

Translate takes one or two parameters and moves the element to the new location. It, too, comes in 3D forms, translateX, translateY, translateZ, and translate3d.

Additional Transforms

Apart from the translations above, Webkit also supports skew, perspective, and matrix. Read more in the Safari Reference Library

I'll finish up with a composite transform combined with transitions, best viewed with Safari. This transformations are initiated by changing the style from Javascript.

<div style="width: 4em; height: 4em;
  margin: 2em; background-color: blue;
  color: white;
  -webkit-transition-duration: 5s;
  -webkit-transition-function: ease-out;"
  onclick='style.webkitTransform="translate(16em, -16em) scale(6) rotate(720deg)"; style.backgroundColor="red";'> Click Me!
</div>
Click Me!

Thursday, September 02, 2010

Using Git with Subversion

I had the unfortunate experience of having to use Subversion again after using Git for a long time. It is amazing how fast I can forget. After renaming a directory at the prompt, and the agony that goes with it, I decided to switch back to Git.

$ mv requester sampler
# svn agony after renaming a directory
$ svn st
?       sampler
!       requester
!       requester.rb
A       sampler.rb

The tool to use when using Git with Subversion is, of course, git svn.

git svn works very well as long as you remember that Subversion is not Git. It does not handle merging well, and it will bite you if you don't respect that. So what does this actually mean? It means:

Always keep the Git master in sync with Subversion

To do this you have two commands you can use.

# Rebases the commits from the upstream Subversion server with your local master.
$ git svn rebase

You should only git svn rebase in your Git master, and you should ALWAYS do it before you git svn dcommit anything to the subversion repository. git svn rebase keeps the upstream subversion in sync with your local master by pulling down the changes, kind of like svn update.

# Commits the changes you have in your local master to the upstream Subversion server.
$ git svn dcommit

When you have changes ready to commit, you commit them to subversion with git svn dcommit. You should ALWAYS git svn rebase before you do the update, or it will fail.

That's it! As long as you follow these two simple rules, your life with git svn will be easy. If you forget to follow them, you will be bitten. When you get bitten, the cool thing about Git is that even if you screw up, it is always possible to sort it out.

It that was all there was to it, there would be no reason to use Git instead of Subversion. Git really shines when it comes to branching and merging. You may create as many local branches as you like with git branch branch_name or git checkout -b branch_name. You can hack around in these local branches as much as you want and merge them together. But, before you merge them into the master branch, you must rebase with master! Not merge, rebase! Rebase means replay the commits on top of the named branch. It creates new commits, the same content, but with a different SHA-1.

# Example session

(master)$ git svn rebase
(master)$ git checkout -b dev
hack, hack, hack, ...
(dev)$ git commit -am 'Commit the changes' 

(dev)$ git checkout master
(master)$ git checkout -b bugfix
hack, hack, hack, ..., done

(bugfix)$ git checkout master
(master)$ git svn rebase
(master)$ git checkout bugfix
$ git rebase master
(bugfix)$ git checkout master
(master)$ git merge --ff bugfix # --ff only fast-forwards, merges that don't need to merge. 
(master)$ git svn dcommit
(master)$ git branch -D bugfix # delete the branch it is not needed anymore

(master)$ git checkout dev
hack, hack, hack, ..., done

(dev)$ git checkout master
(master)$ git svn rebase
(master)$ git checkout dev
(dev)$ git rebase master
(dev)$ git checkout master
(master)$ git merge --ff dev # --ff only fast-forwards, merges that don't need to merge.
(master)$ git svn dcommit

Another thing to be aware of is that git svn dcommit creates an extra commit, so even if you haven't changed anything in the master you need to rebase the local branch with the master. This is only needed if you don't delete the branches after you are done with a commit.

In the example above, I ended with a git svn dcommit and I didn't remove the dev branch.

(master)$ git svn dcommit # from above
(master)$ git co dev
(dev)$ git rebase master # rebases the extra commit created by git svn dcommit

If you forget to rebase or something else happens that hinders a clean merge into the master. You can always back out of it with git reset --hard.

(master)$ git svn dcommit
... failed miserably, because I failed to git svn rebase, bollocks!
(aa..88dd|MERGING)$ git reset --hard
(master)$ git svn rebase
(master)$ git svn dcommit # Nice and clean commit

To get started you need to clone a subversion repository.

$ git svn clone http://svn.example.com/project/trunk
$ cd trunk
(master)$ git ...

Now, is a good time to start using Git. Get yourself anything by Scott Chacon, such as the book or the screencasts.