2010
03.14
Scope is a little different in NodeJS but only in a artificial sense. let’s dive into a quick example of what i mean.
Circle.js
var PI = 3.14;
exports.getPi = functon() {
return PI;
}
If you were to include this file into a browser you would expect to be able to access the variable PI from another file (ignoring the syntax error which would be created by assigning to the undefined object “exports”). This is not the case in NodeJS, when you call require to include this script the contents of the file is inserted into a function as follows…
var wrapper = "(function (exports, require, module, __filename, __dirname) { "
+ content
+ "\n});";
By calling this wrapper function and passing in an object to the exports parameter, your exported variables/functions are added to the exports object which is then returned as a result of a “require” call.
Because your exported functions are defined inside another function (the one generated when the script is imported) any variables (like PI) are still accessible by your exported functions due to JavaScripts normal scoping. Take a look at JavaScript closures for more information.
Conclusion
- Each script can access what you “require” into it and what you define in it.
- Requires are cached so every call to require for the same module should get a reference to the same thing
- Finally this is my current understanding, ill update as i learn, if you can enlighten me any further leave me a message
2010
01.17
Continuing with my current javascript theme i’ve been thinking quite a bit about the way in which we write RIAs. It would seem from many examples i’ve seen that it’s always approached like someones first windows forms application. All the code is in one file with a mixture of purposes and frequently very difficult to test. I’ve seen a few attempts to solve the problem but the intention/responsibility of the code is never quite clear.
After some experimentation the best way seems to be to adopt a MVP (Passive View) style approach. Here’s an example…
var CalculatorPresenter = {
setup: function() {
CalculatorView.setup();
},
addNumbers: function() {
var firstNumber = CalulatorView.getFirstNumber();
var secondNumber = CalculatorView.getSecondNumber();
CalcualtorView.setResults(firstNumber + secondNumber);
}
}
var CalculatorView = {
setup: function() {
$("addbutton").click(CalculatorPresenter.addNumbers);
},
getFirstNumber: function() {
return parseInt($("firstnumber").val());
},
getSecondNumber: function() {
return parseInt($("secondnumber").val());
},
setResult: function(value) {
$("result").val(value);
}
};
$(document).ready(CalculatorPresenter.setup);
I really like this approach because the intention is not lost in a sea of UI code. Everything that relates to DOM manipulation or traversing should be in the view and the “logic” should be in the presenter. Testing this now becomes very simple, here’s an example using jsTestDriver to test the view…
CalculatorViewTest = TestCase("CalculatorViewTest", {
testThatTheValueOfTheFirstFieldCanBeRetrieved: function() {
$("body").append(
$("<input/>")
.attr("id","firstnumber")
.attr("type","text")
.attr("value","10"));
assertEquals(10, CalculatorView.getFirstNumber());
}
});
Same again but for the presenter, I’m also using the Jack mocking framework to isolate it from the view so i’m not testing both.
CalculatorPresenterTest = TestCase("CalculatorPresenterTest", {
testAddingTwoNumbersTogether: function() {
jack(function() {
jack.expect("CalculatorView.getFirstNumber").returnValue(10);
jack.expect("CalculatorView.getSecondNumber").returnValue(10);
jack.expect("CalculatorView.setResult").stub().withArguments(20);
CalculatorPresenter.addNumbers();
});
}
});
2010
01.16
Few conversations with colleagues later and a few small changes to the validation framework.
- Fixed typo in adding custom rules test.
- Added “length less than”, “length greater than” and “is a number” rules
- Updated example to include some of the new validation rules
What next?
Date validation seems to be one that keeps cropping up. DateJs provides a great library for dealing with all sorts of date formats. Im considering whether this should be an optional set of rules as the sizes of DateJs would negate the bennifit of the small validation core if included with it. I’m also considering an optional set of patterns for common international problems (e.g.Email address and Uri), if they are small enough perhaps they should be in the core as well.