What I’m trying to make
I was recently reading a post on using Ioke’s web kit, IKanServe, on Google’s App Engine. It comes with a built-in HtmlBuilder object, but I wanted to try making something more like a rudimentary erb. Basically a way to embed code in template files.
Evaluating on the property bag
So far I have created a basic property bag, which is really just a basic Ioke Object. At it does is serve as a portable ground on which to evaluate messages on. Next there is the Evaluator which takes a bag when it’s initialized and evaluates messages against it, returning the results.
Don’t be put off by Ioke’s advanced features, it’s quite usable
When I first thought using this as an example, this was as far as I got. I figured that If I could get to the point where I could have a parameter object that could be be evaluated against from another context, I would probably have put in a siginificant amount of work for a basic example and I could wrap it up call it a day and try using it with IKanServe. Well as it turns out it wasn’t all that difficult to do. Really it was quite simple. I updated my test/evaluator_spec.ik to read:
use("test/helper")
use("evaluator")
use("bag")
describe("Evaluator",
it("should have the correct kind",
Evaluator kind should == "Evaluator"
)
it("should have accept a bag on initialization",
bag = Bag mimic
bag foo = "bar"
evaluator = Evaluator mimic (bag)
evaluator bag foo should == "bar"
)
describe("Evaluation",
it("should evaluate nil as an empty string",
bag = Bag mimic
evaluator = Evaluator mimic(bag)
evaluator evaluate(nil) should == ""
)
it("should evaluate string on bag",
bag = Bag mimic
bag foo = "wuzzle"
evaluator = Evaluator mimic (bag)
evaluator evaluate(foo) should == "wuzzle"
)
)
)Looking at it now, the last spec should read “should evaluate message on bag” but you get the idea. At this point, I set out to implement this spec.
Don’t forget commas
My biggest problem by far was of my own design, and my own fault. When I first typed up the spec I left off the comma at the end of
it("should evaluate nil as an empty string",This caused the compiler to output a not so pretty error message, that that took me quite some time to track down. You see, I was sure that the issue was with my implementation of Evaluator rather than a syntax error in my spec. So, if you are getting an error that says that it can’t import your spec, look for syntax errors there first. You might be missing a comma.
Principle of least surprise
One of the things I really enjoy about Ruby is that it subscribes to the principle of least surprise. That is, if you don’t know the syntax for something, there is a good chance you will find it by trying the syntax that would make sense to you. I think that this is a side effect (or not so side-effecty) of what Ola Bini recently termed Syntax over Explicit APIs in the Ioke Philosophy.
In any case, the code to pass the spec was simply:
Evaluator = Origin mimic Evaluator initialize = method ( "Pass the property bag into the evaluator, this will be used for future evaluations.", bag, @bag = bag ) Evaluator evaluate = macro ( "Takes the supplied message and evaluates it on the property bag. If nil is passed, it will return an empty string", msg = call arguments first msg evaluateOn(@bag) ) )
Bag = Origin mimic Bag nil = ""
I figure that there is probably a pretty big hole in my code here somewhere that I’m going to expose as I play with this more, but for now, I’m happy that it’s passing my spec.
Update: Thanks to Sam Aaron for pointing out the flaw in my spec.
Documentation arity
Before I conclude this post I wanted to mention the optional documetation that can be added to a method / macro / etc by passing the string that documents it in the first position. It reads well and it’s better than standard comments because the documentation is treated as it’s own first class member of the language.
Code
Source for this example can be found on github tagged as added_evaluator.
Tags: ispec
Another interesting post, thanks
Great that you spotted the missing comma that was present in the previous post – you’re right it can be really hard to track them down, I guess you start to get really conscious of their existence over time as they really are an important part of the Ioke syntax and a minor change in position or presence can really affect the way the code works.
I should still point out that you’re missing parens around the nil. The reason why the test still passes is to do with how cell resolution happens.
Ok, note that the cell “nil” is defined in IokeGround which is one of the base mimics of Origin. Origin also happens to be a base mimic of the oddball object nil. This means that accessing the cell “nil” on nil will wind up as accessing the “nil ” on IokeGround which by default returns the nil object.
That is to say:
iik> nil
+> nil
iik> nil nil
+> nil
iik> nil nil nil
+> nil
iik> IokeGround nil
+> nil
iik> Origin nil
+> nil
iik> DefaultBehavior nil
*** – couldn’t find cell ‘nil’ on ‘DefaultBehavior_0×4F4DB0E3′ (Condition Error NoSuchCell)
(note that nil isn’t available on DefaultBehavior)
Ok, the second thing to observe is, like Ruby, a method which doesn’t return anything returns nil:
iik> meth = method()
+> meth:method(nil)
iik> meth
+> nil
Therefore, putting these two things together, Evaluator evaluate nil can be reduced to nil nil which, as we have seen, reduces to nil. Therefore your spec was essentially testing:
nil should == nil
Also, the spec should probably be testing that the result of evaluating nil is “” (which is the behaviour in the implementation) and you should probably pass it a bag and such like the other spec. Perhaps the following is more akin to the intention of the doc string:
it(“should evaluate nil as an empty string”,
bag = Bag mimic
evaluator = Evaluator mimic(bag)
evaluator evaluate(nil) should == “”
)
Anyway, it’s great to see you having fun – especially now you’ve started playing with macros. Fun times await!