Something I came across worth remembering in future.
Internet explorer puts a 3D border on iframes... this is removed by adding a frameborder="0" on the element. This is not W3C compliant but is the only way to remove it in IE. Firefox and other browsers respect the border: none; in CSS.
Thursday, 2 September 2010
Monday, 23 August 2010
Using retry to run exception block again
One cool thing that I've not personally seen in another language is the retry keyword. If you use it in a rescue block, it'll rerun the block of code the exception occurred in.. Take a look at this code:
Now, if your web service call fails 5 times, the code will fail. This makes your applications more resilient to external problems, as services timing out does happen once in a while.. I'd rather my application handled that nicely instead of just bombing out.
retry_count = 0 begin ShoddyWebService.new.call rescue Timeout::Error => ex retry_count += 1 raise ex if retry_count > 5 puts "Shoddy web service timed out..retry #{retry_count}/5" retry end
Now, if your web service call fails 5 times, the code will fail. This makes your applications more resilient to external problems, as services timing out does happen once in a while.. I'd rather my application handled that nicely instead of just bombing out.
Wednesday, 18 August 2010
Slick code with simple DSLs thanks to Ruby
One of the things I love about Ruby is the massive advantages gained by the way it's built - things like blocks make it so easy for Ruby to have amazing looking, clean code. The great thing is, this also extends to writing your code in Ruby!
Want the people consuming your code to have easy to understand, learn and read code? Write a nice DSL for it. (I'm going to take a second here - DSL stands for Domain Specific Language.. kind of like another programming language, that is structured in the best possible way to interact a specific domain)
Take for example an email sending class..
Now, it's pretty straightforward what this does, but it'll be easy to forget which email it'll send to and which email it'll appear to come from.. Lets write a DSL to make this way more readable, and more extensible, should we need to add more parameters and options later
Now, that code may be longer, but no developer will ever find it hard to see exactly what the code is doing, they'll never have to go and look at the definition of Email to find out.
Now, there is no harm in providing both ways to use your code, you don't want to restrict people to use a DSL, it's usually a layer built on top of an API. Here is how I wrote the code initially
Now this works nicely by using a cool method called instance_eval. This basically takes a block and runs it but with a 'self' of whatever you are calling instance_eval on. In this case, when the block is run, and it comes across to "clocKwize@gmail.com" - this is calling self.to("clocKwize@gmail.com") on the Email object created in send.
The only draw back to this is, once self is our email object, we can no longer access instance stuff in the class we are using the DSL in!
For example
Now, this looks like it should work, but it wont because Ruby will be looking for @magic_number on the Email object, which isn't what the user of your DSL intended at all.
The best way around this to pass the instance of the Email to the block as a parameter. Not quite as slick but the block keeps its original scope and works fine:
This is accomplished by changing the self.send method as follows
But you know, I kind of like the first way, and if the user doesn't need any instance stuff from the class they are in, we shouldn't stop them using the prettier DSL. A very simple way to do this is to ask the block how many parameters it accepts..If it takes one, pass the Email instance, if it doesn't use self as the Email instance.
There we go.. the best of both worlds! Consumers of your DSL can use whatever way they are comfortable with and your code will work either way. Hopefully this will help someone on their path of discovery.
Want the people consuming your code to have easy to understand, learn and read code? Write a nice DSL for it. (I'm going to take a second here - DSL stands for Domain Specific Language.. kind of like another programming language, that is structured in the best possible way to interact a specific domain)
Take for example an email sending class..
Email.new("clocKwize@gmail.com", "spammaster@yahoo.com", "I love you", "Not really!").send
Now, it's pretty straightforward what this does, but it'll be easy to forget which email it'll send to and which email it'll appear to come from.. Lets write a DSL to make this way more readable, and more extensible, should we need to add more parameters and options later
Email.send do to "clocKwize@gmail.com" from "spammaster@yahoo.com" subject "I love you" body "Not really!" end
Now, that code may be longer, but no developer will ever find it hard to see exactly what the code is doing, they'll never have to go and look at the definition of Email to find out.
Now, there is no harm in providing both ways to use your code, you don't want to restrict people to use a DSL, it's usually a layer built on top of an API. Here is how I wrote the code initially
class Email def initialize(to = nil, from = nil, subject = nil, body = nil) @to, @from, @subject, @body = to, from, subject, body end def self.send(&block) email = Email.new email.instance_eval &block email.send end def to(value) @to = value end def from(value) @from = value end def subject(value) @subject = value end def body(value) @body = value end def send puts "Sending an email!" end end
Now this works nicely by using a cool method called instance_eval. This basically takes a block and runs it but with a 'self' of whatever you are calling instance_eval on. In this case, when the block is run, and it comes across to "clocKwize@gmail.com" - this is calling self.to("clocKwize@gmail.com") on the Email object created in send.
The only draw back to this is, once self is our email object, we can no longer access instance stuff in the class we are using the DSL in!
For example
class TestClass def initialize(magic_number) @magic_number = magic_number end def go Email.send do ... body "Magic number is: #{@magic_number}" end end end
Now, this looks like it should work, but it wont because Ruby will be looking for @magic_number on the Email object, which isn't what the user of your DSL intended at all.
The best way around this to pass the instance of the Email to the block as a parameter. Not quite as slick but the block keeps its original scope and works fine:
Email.send do |email| ... email.body "Magic number is: #{@magic_number}" end
This is accomplished by changing the self.send method as follows
def self.send(&block) email = Email.new block[email] email.send end
But you know, I kind of like the first way, and if the user doesn't need any instance stuff from the class they are in, we shouldn't stop them using the prettier DSL. A very simple way to do this is to ask the block how many parameters it accepts..If it takes one, pass the Email instance, if it doesn't use self as the Email instance.
def self.send(&block) email = Email.new block.arity == 1 ? block[email] : email.instance_eval(&block) email.send end
There we go.. the best of both worlds! Consumers of your DSL can use whatever way they are comfortable with and your code will work either way. Hopefully this will help someone on their path of discovery.
Labels:
blocks,
dsl,
instance_eval
Subscribe to:
Posts (Atom)