Page 50
Viewing
all posts
Posted:
2 years ago
(2007-11-13 23:00:59 UTC )
/ Updated:
15 months ago
(2009-06-01 22:28:03 UTC )
Imported from WordPress
Originally posted on 2007-11-13 23:00:59
If you already understand Ruby's self variable and have a basic grasp of eigenclasses (aka singleton classes, metaclasses and several other things) then you're ready to really understand how Ruby defines methods in a class.
Sadly, this is more complex than it appears.
The easy stuff, using 'def' to define a simple class, is straightforward. But when you ask about the equivalent of static methods (also called class methods), there's more going on than you think. The classic 'static' method is "new". To make a new string, you'd type "myvar = String.new". In Ruby, this is actually a method call defined on the 'String' object. Not an instance of String -- the method is defined on the class object itself. So "new" is defined pretty much (but not quite) like these methods are:
def String.gooey
"Ew! Gooey!\n"
end
myvar = "blah!"
def myvar.gooey
"Yuck, that's all gooey"
end
print String.gooey # prints out "Ew! Gooey!"
print myvar.gooey # prints out "Yuck, that's all gooey"
Remember how Ruby allows you to define a method that's only valid on one specific instance object? "Class methods" or "static methods" are just methods defined on the type object for that class.
"Type object for that class"? That's a weird phrase. Its meaning is strongly related to the fact that you need to capitalize class names when you define them -- just like you capitalize constants. When you make a class, you're really making a constant that points to your new class's class object.
class MyClass
def foo; "foo!"; end
end
p MyClass # prints out "MyClass"
p MyClass.class # prints out "Class"
So your new "MyClass" is actually a constant, pointing to an instance of Class. It's as though you had said:
OtherClass = Class.new
OtherClass.class_eval do
def foo; "foo!"; end
end
In fact, it's *exactly* like that. A new class declaration just makes a new object of type Class (if it doesn't already exist) and then does a class_eval in it of whatever you told it to run. Remember,
defining a class is just more Ruby code that runs in your class's context.
All of this means that if you did the bit above, but instead of "OtherClass = Class.new" you said "otherclass = Class.new", you'd have a type that wasn't a constant. It's nice to be able to refer to your class by a constant, stable name, but Ruby doesn't actually force you to. If you want to make all of your classes be dynamic, or put them in a big hash table, or use them and then completely lose track of them, that's up to you. Ruby won't stop you.
In fact, though, Ruby class methods are slightly weirder than that. Remember the "String.gooey" example up above? That's actually defined in String's singleton class, because that's what the syntax "def object.function" syntax does. It puts it in the singleton class for "object". And all of that means that if you're running in a method of String, you can't directly see these methods. For instance:
def String.fooblah # A method in String's singleton class
"fooblah!"
end
class String
def my_string_method # A method within String itself
print String.fooblah # this is fine
print fooblah # but this gives an error -- fooblah isn't visible here
end
end
Remember that modules act just like classes through all of this. A Ruby module is basically just a bag of methods that a class can choose to include.
Posted:
2 years ago
(2007-11-12 21:00:18 UTC )
/ Updated:
14 months ago
(2009-06-22 20:52:51 UTC )
Imported from WordPress
Originally posted on 2007-11-12 21:00:18
Ruby metaprogramming tends to involve code in classes, modules and functions. It can involve defining regular functions and "self.whatever" functions. It's easy to get a little lost. So I'll talk a bit about the "self" variable and what it means at different times.
In Ruby, "self" means the current object. Whenever you call a function, Ruby is implicitly finding it under 'self'. Your top-level object is an instance of class Object, but mostly it's just another object. That top-level object just happens to be the value of "self" at the top level.
Eval works in Ruby pretty much like in any other language. It evaluates the code in the current context. You can do some cute tricks with bindings (sets of variable values), but basically it's as though you'd just typed the code at the current location. Eval's close relatives class_eval and instance_eval are similar. If you have a class name (say Object) and follow it with .class_eval and then a string or a block of code, it's as though you had opened up the class and were writing code in that object, so you can do things like defining methods for the class. If you take an object instance (like, say, k = Object.new) and follow it with .instance_eval (so, k.instance_eval) and then a string or block of code, you're evaluating within the context of that object, so self is set to that object. For instance:
class MyClass
attr_accessor :name, :age
end
MyClass.class_eval do
def test
print "Test function!\n"
end
end
myc = MyClass.new
myc.name = "Barry"
raise "This shouldn't happen!" unless myc.instance_eval { @name } == "Barry"
There are some times that it's probably not clear what self is. For instance, you're probably not used to the idea that defining a class in Ruby is still just running Ruby code, so it may not be obvious what "self" means inside a class declaration. So let's have a look:
class Bob
print "Class Bob:\n"
print "Self is #{self.inspect}\n"
print "Type of self is #{self.class}\n"
end
module Sam
print "\nModule Sam:\n"
print "Self is #{self.inspect}\n"
print "Type of self is #{self.class}\n"
end
class << Bob
print "\nSingleton for class Bob:\n"
print "Self is #{self.inspect}\n"
print "Type of self is #{self.class}\n"
print "Superclass of self is #{self.class.superclass}\n"
end
When you run this in Ruby, you get the following:
Class Bob:
Self is Bob
Type of self is Class
Module Sam:
Self is Sam
Type of self is Module
Singleton for class Bob:
Self is #<Class:Bob>
Type of self is Class
Superclass of self is Module
So - when you're defining a class or module, 'self' is the class or module you're defining. Makes sense. And that last thing, in case you're not familiar with it, is a
singleton class, or eigenclass (you'll see both names if you look). In Ruby, every individual object is allowed to have a singleton class associated with it where you can define functions. That means every instance of every class is allowed to have extra functions that are only called for that object, not for other instances of the same class. When you're defining an eigenclass, the 'self' variable points to that eigenclass. The eigenclass is just a class (see? Its type is 'Class') like the one above. Its parent class isn't 'Class' or anything. But it knows that it's special, because it prints out funny, as '<Class:Bob>' rather than just 'Bob'.
And now you know how the 'self' variable works, no matter where you are in Ruby. And that means you know how to find out where the functions you're calling are defined, no matter whether you're defining a method or defining a class or just typing at the top level. Since you're so educated, I'll go have a margarita and let you consider this knowledge at length.
Nah, not really. Since you're the one learning here, that makes you the student. So instead, I recommend that you drink heavily, not me. See you when you're sober!
Posted:
2 years ago
(2007-11-09 19:06:29 UTC )
/ Updated:
15 months ago
(2009-06-01 22:28:14 UTC )
Imported from WordPress
Originally posted on 2007-11-09 19:06:29
Ruby Metaprogramming is a neat subject. There's a lot going on with it. But the easiest-to-find explanation isn't very clear, no matter how neat it is. Neither is the one most people recommend. There are some excellent, if brain-bending, examples out there if you look, and even one reasonable example, even if it's very involved. There are several frameworks for Ruby that use it extensively.
What doesn't seem to exist is a simple, step-by-step buildup of what 'metaprogramming' really means in Ruby and how you do it. Metaprogramming is a little bit difficult, like anything where you're keeping track of two levels at once. But it's a lot more straightforward than it looks... So this article will try to make that clear, in ways that others don't. I think it's important to explore this topic -- it's probably the most useful set of language idioms that Ruby currently has.
In Ruby, a class definition is really a lot like a loop, an if/else, or other similar statement. Ruby runs through each line in turn and does what it says to do. For instance,
class MyClass
puts "Here I am!\n"
end
If you run this, it prints "Here I am!". It's really just going through and running your code. Most other languages aren't like this. If you put a printf() in the middle of a structure definition, your C compiler will become very grumpy and won't compile it at all. But Ruby has done a very interesting thing in letting you run random code in there. You can also use 'if':
class MyClass
if(true)
def my_func
puts "My_func is defined!\n"
end
end
end
You can also use loops and other normal Ruby code. So far, not so useful. But interesting! But what if you used a loop?
class MyClass
(0..10).each do |idx|
argname = "myfunc_#{idx}" # make a string like "myfunc_7" from the index
define_method argname do
puts "You called method number #{idx}!\n"
end
end
end
myc = MyClass.new
myc.myfunc_3
If you've ever had a bunch of irritating functions to write that all did similar things then you can probably see how that's useful. Imagine writing a set of accessors that added debugging print statements, or a set of type-checked methods. Since Ruby allows you to get a list of all the methods a class supports by calling "instance_methods" on it, you could even add a debug version of every function, or replace the old function name with one that does a debugging print and then calls the old one. Check below this code for an explanation of how it works:
class MyClass
def test(arg)
print "Test method received #{arg}.\n"
end
instance_methods.each do |method_name|
aliased_method_name = method_name + "_alias"
alias_method(aliased_method_name, method_name)
method_proc = proc do |*args|
puts "You called #{method_name}!\n"
send_alias(aliased_method_name, *args)
end
define_method(method_name, method_proc)
end
end
myc = MyClass.new
myc.test("blah")
This is a little complicated, so let's look at what it's doing. We define a class called MyClass, and a simple function called "test" that prints a message and prints out its argument. At the very end of the code we create a new object of type MyClass, and we call "test" with the string "blah". So far, so good. If we called just that, minus the weird metaprogramming stuff, it would print "Test method received blah."
But in the middle, we get a list of every instance method that MyClass supports (remember, MyClass is automatically a Class, which is a Module, so it supports a bunch of stuff automatically). For every method, we add a new alias for it -- "define_method" gets aliased as "define_method_alias", "test" gets aliased as "test_alias", and so on. Then, we replace the old method with one that prints out "You called <method>!", for whatever method we just called. And then, we call the old method.
So we'll still see "Test method received blah," but first we'll see "You called test!". Neat, no?
Our aliasing will actually handle any number of arguments for the method -- our new method takes "*args", which means "take any number of arguments and combine them into a list called 'args'." Then when calling send_alias, we add "*args" to the end of the call. In a function call it means the opposite -- take the list "args" and treat it as if it were a bunch of arguments, not a single argument of type Array.
So why do we call send_alias, rather than just send?. That's because we're using the original version of send, the one we stuffed into an alias. Otherwise we'd see "You called send!", but we'd see it an infinite number of times because it would just keep calling send, over and over, forever. Normally we'd just call send, but it's different because we're messing with send. We could also fix this problem by not aliasing send, perhaps with a line like "next if method_name == send" right after the "each".
That's where we'll leave it for now. Check back for more later.
Posted:
2 years ago
(2007-11-09 16:18:27 UTC )
/ Updated:
15 months ago
(2009-06-01 21:30:46 UTC )
Imported from WordPress
Originally posted on 2007-11-09 16:18:27
Those of you who have been privileged to work on projects with excellent unit tests are privileged. You probably already knew that. But for the rest of you: you're missing out.
A good project will often require testing before you're allowed to check code in. There is usually complex and time-consuming testing that's done daily or weekly. But the testing required for checkin usually does the most good, by far.
It keeps build breaks from happening, which is nice. But better yet, it gets you used to unit tests catching stupid bugs. A stupid bug that the unit tests don't catch becomes an offense, something the program is doing wrong. The advice "if a bug happens, add a unit test that would catch it" seems unrealistic when you start out, but it's very realistic once good unit tests feel like the default. You'll find yourself saying, "why isn't there a unit test for that? It's easy," and then you know what to add.
Unit testing is another argument for the Unix philosophy of building all your interesting program logic into a command-line program and then spreading a thin GUI layer over the top. A command-line program is likely to be easy to test. The GUI program is going to be very difficult to unit-test, if it's possible at all. There are a variety of GUI testing tools, and so far as I (or several companies I've worked for) can tell, none of them produce good, robust results.
But a thin GUI layer is easier to test than a huge mess with both GUI and program logic. Better yet, the rest of your program can be tested with a script after every code change. The GUI and the program logic are two separate programs, so you benefit from the law that says "the difficulty of program maintenance increases far greater than linearly as program size grows". And instead of a huge untestable lump, you have a small hard-to-test lump and a larger easy-to-test lump.
Near-universal unit testing also helps via the "broken window" theory of program maintenance -- that if a few things are obviously wrong then people will tend to imitate that. But if everything is nicely maintained then they will tend to keep it that way. A few "broken windows" in code will tend to cause more rapid degeneration of the code. Good unit-testing will spawn more unit tests.
So what's the upshot? The program you're working on now probably has no unit tests at all. Saying "make sure it's all done well" doesn't help you in the slightest. So why am I bothering?
One reason is so that you'll set up unit testing for your next little new project. And you should.
But also so that you'll think about dividing things up. Write unit tests for your little corner of the code and maybe the next guy will add to them. Divide up that huge, ugly, monstrous codebase and just unit-test tidy little corners of it. You'll find that they grow tidier over time. It'll be hard to add big changes like "you have to run unit tests before checkins" or "fixing bugs happens before new features", but you can still do things to clean up your bits of the code. And maybe the better programmers in the rest of the company will take notice and start tidying up their corners, too.
Posted:
2 years ago
(2007-11-08 22:34:34 UTC )
/ Updated:
15 months ago
(2009-06-01 22:28:45 UTC )
Imported from WordPress
Originally posted on 2007-11-08 22:34:34
Programmers, as they use a programming language, fall into sets of idioms. Think of every C or C++ style guide you've read, whether at a job or for some other project. Each of these is a particular set of idioms, a possible One True Way. The Linux Kernel has a very specific look to it. Ruby has some pretty specific recommendations, if you look. Even in languages where bad style is the norm, there are significant attempts to standardize and make things legible.
A style guide isn't an absolute, even among people who feel strongly. If you look through the Linux Kernel code, it's not a 100% match for the style guide, but more importantly there are a lot of idioms that you don't see in style guides. Conversely, there are particular ways that everybody creates a for() loop in C. One may quibble about details of the placement of things, but that basic structuring, indenting and formatting of it (give or take a newline and curly-brace) is remarkably identical in all experienced C programmers.
A style guide is only that -- a guide -- because it's really a proxy for a set of priorities. A good style guide will choose its recommendations to accomplish specific goals. For instance, good indentation and brace style make the code more readable, add necessary whitespace for legibility, and make it clear where blocks start and end. Well-chosen variable names make it clear what components of the program are doing. Restrictions on function length make sure that a function can be comprehended by a single programmer of reasonable ability in a reasonable period of time. Different style guides make different recommendations, partly because different groups and different programmers have different priorities. You may find a programmer who 'grew up' on Turbo Pascal and a 25-row fixed-resolution text screen expects a different function length and different indentation style than another programmer who started his work with Java and Eclipse on a 1600x1200 LCD panel.
Other parts of a style guide, and other idioms, protect programmers from misuse of the language -- for some definition of 'misuse'. For instance, nonportable C++ features may be forbidden, or error-prone parts of a language like C's "goto" statement. C++'s templates may be disallowed, particularly for a group that cares strongly about code size. An embedded platform may forbid the use of malloc() in interrupt handlers because it can take a long time for unpredictable reasons.
When I'm learning a new language, the first thing I like to look for is unique language idioms. What do they do differently in this language? Sometimes new idioms are because of the structure or capabilities of the language, but sometimes it's just that people have a blank slate, a new chance to try a new way of coding something. Either way, a new idiom is a new way of thinking of a chunk of code, and that can be very powerful.
For instance, Standard ML and SML/NJ were my introduction to the idea of higher-order functions, things like map() and reduce(). While SML allowed imperative programming, it lends itself strongly function programming -- to passing all data around by function parameters (input) and return values (output) rather than assigning anything to variables. While most modern languages can use the functional programming paradigm, few programmers do so in, say, C++ or Java. It's an example of a specific set of idioms that can turn out to be astounding useful, as Google has proven with mapreduce.
It's common for a language to have a powerful new set of idioms even when all the components were already present in earlier languages. Perl certainly didn't invent regular expressions (regexps), but its combination of easy regexp syntax with reasonable access to 'real' programming features meant that Perl users got far more power out of them than was common. You could do the same thing with awk or bash, various Unix utilities and patience. But the Perl language, and the Perl idioms that went with it, made regular expressions far more powerful, for a far broader group of users, than ever before.
This is one reason I'm so interested in Ruby's metaprogramming. It's not completely new, because you can do the same things with 'eval' in most dynamic languages. Adding new types at runtime isn't a completely new trick. But Ruby carefully makes it easy to do so. And while such languages features are never trivial to use, as LISP macros demonstrate, Ruby gives a cleaner, simpler implementation than I've seen so far. I'm paying attention because, like object-oriented programming, functional programming and aspect-oriented programming, it has the look of a very useful new "One True Way".
Maybe some day it'll even be written into a style guide :-)
Posted:
2 years ago
(2007-11-04 05:49:06 UTC )
/ Updated:
15 months ago
(2009-06-01 22:28:34 UTC )
Imported from WordPress
Originally posted on 2007-11-04 05:49:06
It's a great feeling when you're coding up a storm, building something you know is going to be excellent, getting the most vital functions written and stubbing out stuff you won't need until later. It's even better when you finish a big crucial section and you can bask in the glory for just a little while. Mmm...
And then there's that moment where you realize you'll have to test that sucker. And suddenly everything seems just a little less shiny-bright. Maybe you go back to writing a bit more since, y'know, that'll put off the testing a bit. But in the end, you know you'll have to test, and it casts a pall over your mood.
Because that's when the beautiful, golden glow of writing something really cool gives way to the drudgery of actually finding and fixing all those bugs you'd swear somebody else must have slipped in there while you weren't looking.
And an actual release to the users is even worse. That's when you have to write documentation. That's when you have to add comments. That's when you have to go back and write all that testing code you promised you'd do this time. That's when a whole bunch of users are going to pick it up, poke around at it, and the back of your mind knows, it just knows, that they're going to find a bunch of stuff to complain about.
A lot of development is, sadly, like that. But I've found one thing that really helps me get through it: test-driven development. Because when I get to that point where, ugh, I have to write a bunch of test cases and debug things and, and, and... It's easier if I can just hold my breath and type "rake test". Somehow it's easier to get started if I don't have to do much work to begin the debugging.
And hey, every so often a test just works. Not too often, but it will happen more often if you write the tests first... That's because if you write the tests beforehand, you have a better grip on what you're trying to get working. And, well, maybe it helps to know what test cases are actually likely to be checked ;-)
It focuses the mind, helps you get past release drop, looks good on a resume. Test-driven development. So why aren't you using it yet?