|
| 1 | +[ANN] Wrong: a minimal assertion library for Ruby |
| 2 | + |
| 3 | +For WhyDay, I took the time to clean up and document Wrong, a project |
| 4 | +Steve Conover and I have been working on. It's inspired by assert{2.0} |
| 5 | +(http://assert2.rubyforge.org/) but rewritten from scratch. Major |
| 6 | +kudos to Phlip for starting us down this path with assert{2.0}, and |
| 7 | +to Ryan Davis for his RubyParser and Ruby2Ruby libraries which made |
| 8 | +it technically possible. |
| 9 | + |
| 10 | +Before I say more, let me first say that this is totally not ready |
| 11 | +yet! We would love to get some users, but there are some big caveats, |
| 12 | +and there are plenty of things left to be done to make the results |
| 13 | +look uniformly clean and beautiful. We want your feedback, and |
| 14 | +especially to give us cases where either it blows up or the output is |
| 15 | +ugly or uninformative. |
| 16 | + |
| 17 | +Wrong provides a general assert method that takes any Ruby block. |
| 18 | +Assertion failure messages are rich in detail. The Wrong idea is to |
| 19 | +replace all those countless assert_this, assert_that library methods |
| 20 | +which only exist to give a more useful failure message than "assertion |
| 21 | +failed". Wrong replaces all of them in one fell swoop, since if you |
| 22 | +can write it in Ruby, Wrong can make a sensible failure message out of |
| 23 | +it. |
| 24 | + |
| 25 | +Examples: |
| 26 | + |
| 27 | + require "wrong" |
| 28 | + include Wrong::Assert |
| 29 | + |
| 30 | + assert {1==1} |
| 31 | + ==> nil |
| 32 | + |
| 33 | + assert {2==1} |
| 34 | + ==> Expected (2 == 1), but 2 is not equal to 1 |
| 35 | + |
| 36 | + x = 7; y = 10; assert { x == 7 && y == 11 } |
| 37 | + ==> |
| 38 | + Expected ((x == 7) and (y == 11)), but |
| 39 | + (x == 7) is true |
| 40 | + x is 7 |
| 41 | + (y == 11) is false |
| 42 | + y is 10 |
| 43 | + |
| 44 | + assert { 'hand'.include?('bird') } |
| 45 | + ==> |
| 46 | + Expected "hand".include?("bird"), but "hand" does not include "bird" |
| 47 | + |
| 48 | + age = 24 |
| 49 | + name = "Gaga" |
| 50 | + assert { age >= 18 && ["Britney", "Snooki"].include?(name) } |
| 51 | + ==> |
| 52 | + Expected ((age >= 18) and ["Britney", "Snooki"].include?(name)), but |
| 53 | + (age >= 18) is true |
| 54 | + age is 24 |
| 55 | + ["Britney", "Snooki"].include?(name) is false |
| 56 | + name is "Gaga" |
| 57 | + |
| 58 | +So how do we do it? Doesn't Ruby have poor support for AST introspection (see http://blog.zenspider.com/2009/04/parsetree-eol.html)? Well, yes, it does, so we cheat: we figure out what file and line the assert block is defined in, then open the file, read the code, and parse it directly using Ryan Davis' amazing RubyParser and Ruby2Ruby. You can bask in the kludge by examining `chunk.rb` and `assert.rb`. If you find some code it can't parse, please send it our way. |
| 59 | + |
| 60 | +Before you get your knickers in a twist about how this is totally unacceptable because it doesn't support this or that use case, here are our caveats and excuses: |
| 61 | + |
| 62 | +* It works! Tested in 1.8.6, 1.8.7, 1.9.1, and 1.9.2-rc2. (Thank you, rvm!) |
| 63 | +* Your code needs to be in a file. That means it doesn't work in IRB. (If you're developing Ruby code without saving it to a mounted disk, then sorry, Wrong is not right for you.) |
| 64 | +* It's a development-time testing library, not a production runtime library, so there are no security or filesystem issues. |
| 65 | +* eval isn't evil, it's just misunderstood. |
| 66 | +* It makes a few assumptions about the structure of your code, leading to some restrictions: |
| 67 | + * You can't have more than one call to `assert` per line. (This should not be a problem since even if you're nesting asserts for some bizarre reason, we assume you know where your Return key is. And actually, technically you can put two asserts on a line, but it always describes the first one it sees, which means that if the second one executes, its failure message will be incorrect or broken.) |
| 68 | + * You can't use metaprogramming to write your assert blocks. |
| 69 | + * All variables and methods must be available in the binding of the assertion block. |
| 70 | + |
| 71 | +Please feel free to challenge any of the above claims in a reply :-). I'd love to know if Ryan and the rest of the ruby-talk gang think this is an acceptable or an outrageous use of a parsing library. In any event, I think it constitutes a pretty compelling use case for adding Proc#to_sexp or equivalent to the MRI runtime. |
| 72 | + |
| 73 | +The gem's been released on RubyGems, so "gem install wrong" should work, but you might be better off following my github project. |
| 74 | + |
| 75 | +http://github.com/alexch/wrong |
| 76 | +http://www.slideshare.net/alexchaffee/wrong-5069976 |
| 77 | + |
| 78 | + - Alex |
0 commit comments