Ruby Cookbook: Recipes for Object-Oriented Scripting
by Lucas Carlson & Leonard Richardson
ISBN 0-596-52369-6
I haven't read this book cover-to-cover, but I have given it a very thorough inspection, skimming through sections to find points of interest and then stopping to read those recipes. So, actually, I've probably read a whole bunch of the book and didn't realize it.
The book is written in a very concise manner, with plenty of examples, lots of useful code, excellent cross-references and a very generous license that allows you to use the code from the book (albeit with some restrictions).
Each recipe is presented as a quartet of Problem, Solution, Discussion and See Also sections. Each problem is presented succinctly, and the solutions dive right into the required code (with code comments explaining some of the more complicated lines of code, but not explaining the self-documenting lines). The discussion section of each solution really digs into what is going on, code choices that were made, variations, and basically anything else that might be interesting. The see also section cross-references other recipes that may be pertinent to the given problem.
All told, this book does an amazing job of going through a wide range of problems, from Strings & Numbers to Meta-Programming & Rails and Graphics. This book is loaded with useful bits of information and I would highly recommend adding it to your collection.
Monday, January 22, 2007
Wednesday, January 17, 2007
Some Weird Weird Ruby (or nest Classes and Inheritence)
Thinking cap time. I am working on a CMS and the short of it is that a Page will have many PageBlock objects. The PageBlock is versioned and can have multiple things associated with it (i.e. Links or Files or Other Stuff, thankfully each page block is homogenous). I decided that the Links/Files will be serialized. So I wrote a serializing helper object. This serializing object
Now
DOH!!! Man that is definitely not the behavior I want. I guess my nested Class has some behavior that I'm not aware of. I'm certain the behavior is intentional, I just didn't know about it
So the solution?
Then I define PageLinkBlock and
Alas, the behavior that I want. Time to dig out the Pickaxe Book to see if there is a reference to this.
class PageBlock < ActiveRecord::Base
class << self
# I want to be able to configure the underlying DataSerializer
# in the containing class
delegate :set_valid_serialized_keys, :to => :data_serializing_class
def data_serializing_class
self::DataSerializer
end
end
class DataSerializer
class << self
def set_valid_serialized_keys( *attrs )
write_inheritable_attribute :valid_serialized_keys, attrs
class_inheritable_reader :valid_serialized_keys
end
end
def add_data(data = {})
data.assert_valid_keys(valid_serialized_keys)
... do the magic ...
end
end
end
Now
$ PageBlock.valid_serialized_keys
=> NoMethodError: undefined method `valid_serialized_keys' for PageBlock::DataSerializer:Class
class PageLinkBlock < PageBlock
set_valid_serialized_keys :path, :name
end
$ PageBlock.valid_serialized_keys
=> [:path, :name]
DOH!!! Man that is definitely not the behavior I want. I guess my nested Class has some behavior that I'm not aware of. I'm certain the behavior is intentional, I just didn't know about it
So the solution?
class PageBlock < ActiveRecord::Base
class << self
# I want to be able to configure the underlying DataSerializer
# in the containing class
delegate :set_valid_serialized_keys, :to => :data_serializing_class
def data_serializing_class
self::DataSerializer
end
def inherited(page_block_subclass)
page_block_subclass.module_eval <<-EOV
class DataSerializer < PageBlock::DataSerializer
end
EOV
super
end
end
class DataSerializer
class << self
def set_valid_serialized_keys( *attrs )
write_inheritable_attribute :valid_serialized_keys, attrs
class_inheritable_reader :valid_serialized_keys
end
end
def add_data(data = {})
data.assert_valid_keys(valid_serialized_keys)
... do the magic ...
end
end
end
Then I define PageLinkBlock and
$ PageBlock.valid_serialized_keys
=> NoMethodError: undefined method `valid_serialized_keys' for PageBlock::DataSerializer:Class
Alas, the behavior that I want. Time to dig out the Pickaxe Book to see if there is a reference to this.
Tuesday, January 16, 2007
Helper Module Testing
First and foremost
Then in your app/test/test_helper.rb add
Then make a test class
Of particular note, the name of the class that inherits from Test::Rails::HelperTestCase is very important. If the inheriting class name is MyHelperTest, make sure a module named MyHelper exists.
sudo gem install ZenTest
Then in your app/test/test_helper.rb add
require 'test/rails'
Then make a test class
class FooHelper
def print_hello_world
content_tag('p', 'Hello World!')
end
end
class FooHelperTest < Test::Rails::HelperTestCase
def test_method_goes_here
assert_equal '<p>Hello World</p>', print_hello_world
end
end
Of particular note, the name of the class that inherits from Test::Rails::HelperTestCase is very important. If the inheriting class name is MyHelperTest, make sure a module named MyHelper exists.
Wednesday, January 03, 2007
Dear RDoc,
One thing that I've quickly taken for granted is RDoc. Prior to RDoc, I lamented that the documentation that I wrote in code was impossible to discover, and thus the in-code documentation was useless for non-programmers. Thus, in order to "say" what the system did, additional documentation needed to be created. So now there were two sets of documents, both saying what the system did, but not communicating with each other.
Oh the humanity!
Enter RDoc. I can now rake that information and generate a useful document defining the system. It gets even better if I start using should-language, either in specify or test context (i.e. specify 'student should have a name' or def test_student_should_have_a_name). Then I can rake just the method names and get a document that says concisely what the system should do.
Of course this leads itself to writing test stubs earlier in the project process. Even better, it is possible for a project manager to write some of these specifications.
Suddenly, the tests, which everyone wants and needs, become useful not only for documentation but also for communication and um... testing. And better yet, the documentation is not discarded nor duplicated.
Oh the humanity!
Enter RDoc. I can now rake that information and generate a useful document defining the system. It gets even better if I start using should-language, either in specify or test context (i.e. specify 'student should have a name' or def test_student_should_have_a_name). Then I can rake just the method names and get a document that says concisely what the system should do.
Of course this leads itself to writing test stubs earlier in the project process. Even better, it is possible for a project manager to write some of these specifications.
Suddenly, the tests, which everyone wants and needs, become useful not only for documentation but also for communication and um... testing. And better yet, the documentation is not discarded nor duplicated.
Only write what you need.
I don't recall where I read this, but someone once wrote that each line of code should be viewed as a liability.
At work, a colleague and I have begun referring to the test code as a contract; it describes the behavior of the system. The more tests, the tighter the contract. By extension, the more code in the system, the more restrictive the implicit contract. The implicit contract is the written, and all to often unwritten, expectations of the system.
So the less code needed to get the job done, the better.
At work, a colleague and I have begun referring to the test code as a contract; it describes the behavior of the system. The more tests, the tighter the contract. By extension, the more code in the system, the more restrictive the implicit contract. The implicit contract is the written, and all to often unwritten, expectations of the system.
So the less code needed to get the job done, the better.
Subscribe to:
Posts (Atom)