Python Programming, news on the Voidspace Python Projects and all things techie.

Ruby for Rails Review

emoticon:objects This is a review of the Manning book, Ruby for Rails.

The review is written by Andrzej Krzywda, a colleague of mine from Resolver. In his previous job programmed in Ruby for Siemens in Poland. Smile

Ruby for Rails is a book written by David A. Black, a well known guru in the Ruby community.

In short: The book is great.

I'd recommend it for all people who have already tried Rails and appreciate its magic, but now want to understand what's going on under the hood.

The structure of the book is organised as follows:

Chapter 1 The Ruby/Rails landscape
Chapter 2 Ruby building blocks.
Chapter 3 Built-in classes and modules
Chapter 4 Rails through Ruby, Ruby through Rails

Quoting the author the goal of this book is:

(..) That's the goal - to be able to bring Ruby skills to bear seamlessly on Ruby tasks. Whether it's writing an action, or a method in a helper file, or a highly specialized suite of methods like the rankings and favourites facility in the music store application, the ideal situation is one in which you have a large number of programming techniques at your command and you use whichever ones help you get your application to do what you want it to do.

Having finished this book I feel that not only my Ruby skills got enhanced but also I understand more about Rails code. It's much easier now to follow the discussions about creating DSLs using Ruby and also actually creating my own DSLs. Even though not all of the Rails features are covered in the book, (like migrations or plugins) I'm able to see how simply they are designed. It wasn't easy, though (almost 500 pages). Finishing this book took me some time. I tried to follow most of the examples in the book but it was worth the time spent on it.

I really enjoyed the chapters about dynamic programming. They cover the whole family of eval methods, the singleton class. All of these are heavily used when you want to create a DSL.

Another very useful chapter for me was the one about regular expressions. I'm this type of developer who keeps forgetting details how to prepare a more complex regex.

Thanks to the Ruby for Rails book I feel the Ruby way better. I think it's very important to understand the whole philosophy of a language. I remember (back in December 2004) my first Ruby on Rails code was either looking exactly as in tutorials or when I needed to build something more complicated it was actually Java coding using Ruby. Wink I was in a similar situation when I started working with Python. It was very tempting to just code Ruby using Python. Thanks to Michael's patience I think I'm getting better with Python now Smile

Let's take the following Rails code as an example:

class Edition < ActiveRecord::Base
    belongs_to :publisher
end

All Rails newbies can tell you what this code does. I'm not really so sure if all of them are able to explain how is it actually done under the hood.

I like to call Rails a DSL for creating web applications. Despite the whole simplicity it provides it also means it is an additional layer of abstraction.

Quoting Joel Spolsky :

All non-trivial abstractions, to some degree, are leaky.

We must understand how is the abstraction created so that when in a need we can easily fix any problem that appears during Rails development.

Ruby for Rails is an excellent book. It covers all the important topics about Rails development and highlights the Ruby way. If you already created some Rails applications but still feel there is some magic around you definitely should read this book.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-22 23:44:39 | |

Categories: ,


PyCon Decisions Delayed

emoticon:bluetooth Well the deadlines for choosing talks and tutorials for PyCon 2007 have whizzed passed with that wonderful whooshing noise they make.

I guess they're struggling due to the number of proposals this year, but no word to submitters has left me on tenterhooks... Surprised

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-23 18:58:05 | |

Categories:


Code Objects: An Exploration

emoticon:pen_book I'd like to do some bytecode hacking, to see if I can dynamically 're-scope' blocks of code. What I'd like to do is take the code object from a function and execute it in the current context. The reasons for this are two-fold. Firstly I'd like to see if I can emulate Ruby blocks by making a function body execute in the current context. Secondly I'd like to learn more about the implementation of Python scoping rules by experimenting with byte-code.

Ok, so this sounds like a hack, and I guess it is. It's interesting to note however that Aspect Oriented Programming is a well accepted technique in Java, and is mainly implemented at the bytecode level.

Functions work by using a combination of function attributes and the underlying code object to setup and execute the function. So the first thing to do is to learn a bit more about code objects. (I have a couple of other ideas I'd like to try out as well, but this entry only gets as far as exploring code objects.)

When you create a function, it has a code object (the function body) as the attribute func_code :

def function():
    pass

codeObject = function.func_code
print type(codeObject)
<type 'code'>

The best reference I can find on Python code objects is: Code Objects (Unofficial Reference Wiki).

This explains what the attributes on a code object are, but it doesn't explain what they mean. For example :

  • co_nlocals is the number of local variables used by the function (including arguments)
  • co_varnames is a tuple containing the names of the local variables (starting with the argument names)
  • co_cellvars is a tuple containing the names of local variables that are referenced by nested functions
  • co_freevars is a tuple containing the names of free variables
  • co_code is a string representing the sequence of bytecode instructions
  • co_consts is a tuple containing the literals used by the bytecode
  • co_names is a tuple containing the names used by the bytecode

We have six types of variables listed here. Some of them sound obvious, but why so many and what is a 'free variable' ? [1]

I'm going to create some code objects (including a nested function) and look at these values to see if I can work anything out.

v = 0
def function1(a, b, c=1):
    w = 2
    x = 3
    w += 1
    print a
    print b
    print c
    print v
    print w
    print x
    def function2(d, e, f=4):
        y = 5
        z = 6
        z += 1
        print a
        print b
        print c
        print v
        print w
        print x
        print y
        print z
    return function2

code1 = function1.func_code
code2 = function1(4, 5).func_code

code3 = compile("""w = 2
x = 3
w += 1
print v
print w
print x\n"""
, '<Something>', 'exec')


for code, name in zip((code1, code2, code3), ('code1', 'code2', 'code3')):
    print 'Looking at %s.' % name
    for entry in dir(code):
        if entry[:2] == entry[-2:] == '__':
            continue
        print entry, getattr(code, entry)
    print '\n'

And the output (extraneous prints from calling 'function1' removed and unprintable characters replaced...) :

First the object code1 :

Looking at code1.
co_argcount     3
co_cellvars     ('a', 'b', 'c', 'x', 'w')
co_code         Unprintable stream - the code object
co_consts       (None, 2, 3, 1, 4, <code object function2>)
co_filename     bytcodetest.py
co_firstlineno  2
co_flags        3
co_freevars     ()
co_lnotab       More unmentionables
co_name         function1
co_names        ('w', 'x', 'a', 'b', 'c', 'v', 'function2')
co_nlocals      6
co_stacksize    7
co_varnames     ('a', 'b', 'c', 'function2', 'w', 'x')

So the 'cellvars' are all the names created in the scope of function1, including the arguments but not the nested function definition.

'freevars' is empty.

'names' is all the names used in the scope of function1.

'varnames' is all the local variables. It includes the nested function (which is local), but not 'v' which is global.

'consts' is the values used in the function, including the function object function2. The leading None means the function has no docstring.

Next the object code2 :

Looking at code2.
co_argcount     3
co_cellvars     ()
co_code         Unprintable stream - the code object
co_consts       (None, 5, 6, 1)
co_filename     bytcodetest.py
co_firstlineno  12
co_flags        3
co_freevars     ('a', 'c', 'b', 'w', 'x')
co_lnotab       More unmentionables
co_name         function2
co_names        ('y', 'z', 'a', 'b', 'c', 'v', 'w', 'x')
co_nlocals      5
co_stacksize    2
co_varnames     ('d', 'e', 'f', 'y', 'z')

'cellvars' is empty !?

'freevars' contains all the names used from the scope above ('function1').

'names' is all the names used in the scope of function2.

'varnames' is all the local variables for function2. It doesn't include names defined in function1.

Last, object code3 :

Looking at code3.
co_argcount     0
co_cellvars     ()
co_code         Unprintable stream - the code object
co_consts       (2, 3, 1, None)
co_filename     <Something>
co_firstlineno  1
co_flags        64
co_freevars     ()
co_lnotab       More unmentionables
co_name         ?
co_names        ('w', 'x', 'v')
co_nlocals      0
co_stacksize    2
co_varnames     ('x', 'w')

'cellvars' is empty again. (So it looks like the two code objects which don't have functions in them don't have any 'cellvars'.)

'freevars' is also empty.

'names' is all the names used in the scope of code3.

'varnames' is all the local variables. It doesn't include 'v'.

This code object does not belong to a function, so the 'flags' are different. It also has no leading None, but it has a trailing one ?

Hmmm... so next time I'll look at the bytecode and see how they use different opcodes to load values. I will probably use a tool called byteplay. By creating new code objects, and transforming some of the byte-codes, I might be able to do something interesting. Smile

For an interesting recipe which transforms byte-codes (and inspired me to actually do this rather than just think about it occasionally) see Implementing the make statement by hacking bytecodes.

Note

Michael Hudson said :

In Python, and in a given scope, a free variable is one that is defined in some outer scope and a cell variable is one that is referenced in some inner scope (i.e. is a free var for some inner scope).

In a more formal setting, globals would be free variables too.

Kent Johnson suggested Free and Bound Variables as a good reference.

[1]Feel free to answer.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-20 23:19:53 | |

Categories: ,


ConfigObj Updates: Sometime

emoticon:ghostradio It looks like I will be busy for the next few months. Smile

There are several changes to ConfigObj that have already been waiting in the wings too long. Thankfully Nicola Larosa has stepped up to shepherd the changes in.

The forthcoming changes include :

  • Making ConfigObj picklable (patch supplied by Jack Kuan)
  • Fixing a couple of minor bugs reported on the Sourceforge Page
  • Possibly supporting multi-line list values, though probably not for unrepr mode (patch supplied Tomi Kyöstilä)
  • Simpler string interpolation using PEP 292 $templates (patch supplied by Robin Munn)
  • Adding a semi-private attribute _indent_size to allow control of number of spaces in indentation when writing
  • Allow string interpolation from the current section
  • Support IronPython by making the parser import conditional
  • Ensure compatibility with Python 2.5

This will be ConfigObj 4.4, but no promises about how long it will take. Razz

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-19 18:46:18 | |

Categories: ,


ConfigObj in Interesting Places

emoticon:torch ConfigObj [1] turns up in some interesting places. The latest places it's been seen include :

  • Chandler

    A Python and wxPython PIM, being developed by the OSAFoundation [2].

  • CryptoBox

    A very interesting looking Debian based Live-CD which supports storing data using an encrypted harddisk; usable even by non technical users [3].

  • Simple64

    A Ubuntu tool which provides a GUI to install a host of applications.

  • Debian-cd-ng

    Debian-cd-ng recommends ConfigObj for parsing the Debian-cd configuration files.

[1]A Python module for ultra-simple reading and writing of 'INI' style configuration files, with a host of powerful and flexible options.
[2]See here.
[3]See this

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-18 14:28:33 | |

Categories: ,


Python Programming Links (etc)

emoticon:mirrormask A recent crop of links from my Del.icio.us bookmarks.

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-18 14:03:17 | |

Categories: , , , ,


Ultimate XP Office Tool

emoticon:car Announcing the ultimate tool for for pair-programming. Only for use in the most agile of programming environments, the PairOn :

The PairOn Chair - the Ultimate XP Experience

We've got three on order for Resolver. Smile

Like this post? Digg it or Del.icio.us it.

Posted by Fuzzyman on 2006-11-18 13:58:18 | |

Categories: ,


Hosted by Webfaction

Counter...