Ruby for Rails Review
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.
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:
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.
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
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. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-22 23:44:39 | |
Categories: Writing, General Programming
PyCon Decisions Delayed
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...
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-23 18:58:05 | |
Categories: Python
Code Objects: An Exploration
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 :
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.
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.
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. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-20 23:19:53 | |
ConfigObj Updates: Sometime
It looks like I will be busy for the next few months.
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.
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-19 18:46:18 | |
ConfigObj in Interesting Places
ConfigObj [1] turns up in some interesting places. The latest places it's been seen include :
-
A Python and wxPython PIM, being developed by the OSAFoundation [2].
-
A very interesting looking Debian based Live-CD which supports storing data using an encrypted harddisk; usable even by non technical users [3].
-
A Ubuntu tool which provides a GUI to install a host of applications.
-
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. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-18 14:28:33 | |
Python Programming Links (etc)
A recent crop of links from my Del.icio.us bookmarks.
The Parking Lot is Full Archives
An amusing online comic. Now deceased, but the archives go from 1993 - 2002.
UpMyStreet - For where you live
Provide social indicators of UK areas, by postcode.
DISLIN - Scientific Plotting Software
Another plotting / graphing library. With Python bindings, free for non-commercial use. The examples look ok, but not as professional as some of the others I've seen.
-
Cheap and fast newsgroup servers.
Python Jobs, Average Salary for Python Skills
I've sent this to my boss. He hasn't replied yet.
-
A very cool jacket.
SourceForge.net: tk-components
Interesting components (Tile wrapper Help Browser, balloon help etc) for Tkinter, the Python GUI toolkit.
-
Website for the London 2.0 geek gatherings.
nLite - Deployment Tool for the bootable Unattended Windows installation
Create custom Windows installations.
Using System.Array from IronPython
Nice and clear example.
ASPN : Python Cookbook : Implementing the make statement by hacking bytecodes
A good example of bytecode hacking.
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-18 14:03:17 | |
Categories: Fun, Life, General Programming, Python, IronPython
Ultimate XP Office Tool
Announcing the ultimate tool for for pair-programming. Only for use in the most agile of programming environments, the PairOn :

We've got three on order for Resolver.
Like this post? Digg it or Del.icio.us it. Looking for a great tech job? Visit the Hidden Network Jobs Board.
Posted by Fuzzyman on 2006-11-18 13:58:18 | |
Categories: General Programming, Fun
For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store. If you're looking for a new techie job, try the Voidspace Tech Job Board. This is part of the Hidden Network of technology and programming jobs.

IronPython in Action


