tag:blogger.com,1999:blog-37438418276793267132024-03-12T16:48:08.679-07:00Marc 3.0Converting cool buzzwords into coolish apps since 1996.cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-3743841827679326713.post-37758469959330058702019-11-16T03:24:00.001-08:002019-11-16T03:24:26.336-08:00Good Programming: Error HandlingError handling is one of the holy grails of software development and it's easy to see why if you consider that bad error handling can bring down an entire system.<br />
Think I'm exaggerating? I work on an eCommerce solution which fails completely if a product page has no valid image. That means the customer gets a 500 error page instead of the opportunity to buy a product because someone forgot to upload an image.<br />
<br />
I've even had discussions with developers who neglect error handling and say: "Well, if this happens, then it's *supposed* to blow up". What "this happens" means varies from context to context but, in my example above, it means "a product has no image" so let's crash.<br />
<br />
Now, I'd like to believe that this is oftentimes an oversight rather than a conscious decision. The programmer focuses on the Ideal Path and simply neglects to consider (or is never told) what should happen when the Ideal Path gets bumpy.<br />
<br />
Modern coding practices such as chaining feed into this. It looks absolutely fabulous to chain methods together in code... until we get a single method returning unexpectedly and our favourite error, the nullPointerException, brings down the system.<br />
<br />
<code>
database()<br />
.read('something')<br />
.call('something', user.property()) // Boom!<br />
.thenDoThis('never happens');<br />
</code>
<br />
Of course you can use chaining responsibly but you can't always avoid code you call behaving badly. Even if you wrote the code you are calling, it may misbehave, someone may change it, you never know when something UNEXPECTED can occur and tear down your work of art.<br />
<br />
The solution? Expect the unexpected! Be pessimistic. Like any good engineer.<br />
<br />
In my first job out of Uni I worked for a small bio-startup in Roslyn, Scotland. Yes, Dolly the Sheep, was pretty much sharing office space with us. My boss, a no-nonsense Yorkshireman kept asking me why so much of my code was error handling. I replied that you never know what might happen and he replied: I pay you to code business logic and what I'm getting is 80% error handling and paranoid disaster mitigation. Well, not in those terms exactly: he probably told me to "get yer finger ot and staaat deliverin' t'goods afoor A thump you".<br />
<br />
What is the goal of error or exception handling? IMHO it's to be transparent to the user and the operator of the system about what just happened and, ideally, what can be done.<br />
<br />
In simple terms it means telling the user that what they tried to do did not work and how to proceed or work around the problem. An example is to show a message "Your file could not be saved, please try again!". This is obviously not very helpful if the problem persists but if the problem is intermittant (network outage to google drive) then it may just work the second or third time around.<br />
<br />
Now that we've shown the user what's going on we need a way of recording this incident so that the operator or owner of the software product is also aware of the outage and can take appropriate action. We may log error NET307 to record a network save issue.<br />
<br />
In the example above there may be no action the operator or owner can take: Google Drive cannot help it if their user's WiFi is dodgy can they? Or can they? If the product owner is aware that 50% of users run into NET307 each week they may work towards mitigating the pain associated with not being able to save a document as the user's train enters a tunnel and network access disappears.<br />
<br />
Thus a sound logging, monitoring and reporting of the error feeds back into the product development cycle to produce new features such as offline saving. The document is saved to a local storage mechanism if the network is not available and later synced up to the online service when the network again becomes available.<br />
<br />
Examples of bad exception handling abound: user's seeing stack traces or misleading messages about what just happened. Operators seeing the wrong stack trace or line number (yes, I'm looking at you PowerShell). Proper error handling is about the developer understanding what just happened in their code and translating this into a user-friendly message, an operator friendly log and, ideally, an owner-friendly metric. In this way the target audience is best served.<br />
<br />
I am tired of hearing that "it works" when a crappy piece of software runs under some (ideal) conditions. It's like saying that half a barrel floats: indeed it does but you wouldn't want to cross the Atlantic in it. Your software may run in ideal conditions but the world is not ideal.<br />
<br />
This becomes more apparent the more connected the world becomes and the more distributed our systems are. If your software fails completely (e.g. the nodeJS process crashes out and your service is dead) because of an HTTP timeout due to a slow network then you need to think about better error handling.<br />
<br />
Some people say: "But my module/component is only responsible for X and not for the network. I assume the network is working or my component is useless."<br />
<br />
This is a valid argument: it does not make sense for each software component to try manage the failure of the dependencies of all it's dependencies. I do think that each component should manage the failure of it's dependencies. This can mean catching the exceptions of components you use and either passing the exception through or enriching it with additional meaning for your context.<br />
<br />
Throwing exceptions was once state of the art but I have come to question the approach in past years. Working for many years with ABAP, the programming language used within SAP, I and others built up layers of classes which all used exceptions harmoniously. The neat thing was it pushed error handling to the bottom (the CATCH block) of our code. Our code could proceed along the happy Ideal Path and readability was improved.<br />
<br />
The challenges were that we sometimes had to wrap legacy, old fashioned functions and methods, which did not use exceptions. On the other hand, legacy code which didn't work with exceptions needed to TRY....CATCH our fancy schmancy code and it was ugly.<br />
<br />
When I moved to a new company I decided to drop exceptions completely and take a different approach: logging and simple return codes. In this approach all code should log errors and warnings consistently (using a global Singleton log object) and then exit if they encounter an error. When they execute successfully they return a boolean ('X' in ABAP means true) - in all other cases they return nothing ('' == false).<br />
<br />
ABAP has a really neat keyword called CHECK which will exit the current block or function if they encounter anything that is not true. So code looks like this:<br />
<br />
<code>
CHECK obj-&gt;method1().<br />
CHECK function_call.<br />
CHECK some=&gt;static_method( 'foo').<br />
</code>
<br />
The net result is that we completely avoid any error handling in intermediate classes and only need to consider two approaches:<br />
<br />
<ul>
<li>Low-level functions should log errors and exit</li>
<li>Application-level programs should read the error log and inform the user</li>
</ul>
There are some downsides (like managing a global log object) but, in general, I'm quite happy with the results. We save all messages which occur in all our programs (even SUCCESS or INFO messages) and can even enable tracing by saving DEBUG messages. These messages are sent to ElasticSearch and can be analysed in Kibana meaning we get consolidated logging and metrics "for free".<br />
<br />
In summary I highly recommend considering all stakeholders (users, operators and owners) when developing and being pessimistic about whether our dependencies will behave or not. Log out as much as possible at the correct level and show the user what happened and how to proceed.<br />
<br />
A final word to error codes: a unique number is a good thing to have but it can be difficult to manage across many applications. I prefer a kind of hybrid code which is kindof human readable. NETFAIL707 is more useful than 0x003030301.
cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-26857358895407330062016-12-05T08:09:00.001-08:002016-12-05T08:09:05.503-08:00View and Log Output with TeeEver wanted to view and log the output of a command at the same time? I'm sure it's available natively in Linux but not, AFAIK, in Windows. So we need a little tool.<br />
<br />
Create a file called tee.cmd and save it somewhere in your PATH. The contents are as follows:<br />
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">@ECHO OFF<br />POWERSHELL -c "Tee-Object -FilePath %1"</span></blockquote>
Now just pipe your command to "tee" and pass the name of the file you want to log to.<br />
<br />
Example:<br />
<br />
<blockquote class="tr_bq">
<span style="font-family: "Courier New", Courier, monospace;">DIR | tee op.txt</span></blockquote>
You will get a directory listing and it will also be logged to op.txt.cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-68491744946967711522016-11-20T10:50:00.001-08:002016-11-20T11:27:58.506-08:00Visual PowerShell with Visual PowowShell v0.0.1I'm excited to showcase the UI for PowowShell. Check it out <a href="http://cawoodm.github.io/powowshell/ide/">here</a> or feel free to fork or browse the code on <a href="https://github.com/cawoodm/powowshell/tree/master/ide">GitHub</a>. It's just a mockup to get an idea of the look and feel of designing a pipeline as a sequence of steps in a matrix.<br />
<a href="http://cawoodm.github.io/powowshell/ide/" target="_blank"><img border="0" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAOq-dZoJnx6mhFWNEvub2RGW2CVS59Wtbnn0wMY0q9vejrQKKqw6xTMAVF7ud9wrGEboxYR5wULykvdLTP9igTrRxT6FsE-HzGFnksQh7X_ocYLrGQ8pX_twOv9l-sLGKFjrZW_Qg7bGx/s640/visual-powowshell.png" width="640" /></a>cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-63676977893927156832016-11-15T05:57:00.000-08:002016-11-15T06:30:02.853-08:00PowowShell: A Vision for a Visual Progamming Language for PowerShellI use <a href="https://en.wikipedia.org/wiki/PowerShell">PowerShell</a> a lot and, though I'm a programmer at heart, it would sometimes be nice to have a library of re-usable script components which you can just wire up together in new ways.<br />
<br />
PowowShell (<a href="https://github.com/cawoodm/powowshell">Github</a>) is my attempt at making such a system. Indeed I want to take it a step further and make it into a Visual Programming Language (like <a href="https://nodered.org/">NodeRed</a>) where you just drag and drop components onto a workspace, wire them up and press play.<br />
<br />
PowowShell lets you take any command-line utility and wrap it up as a Powow <b>Component</b>. This component can then be used as a <b>Step</b> in a Powow <b>Pipeline</b>. The entire Pipeline is itself a powershell script and also a Component and thus you can use Pipelines as Components within other Pipelines.<br />
<h3>
</h3>
<h3>
A Common Interface</h3>
Components have 3 aspects to their interface:<br />
<ul>
<li>PARAMETERS: Basic settings or values for your component. You define these when you add a Component to your Pipeline though they can be dynamic values determined at runtime.</li>
<li>INPUT: The type of data that your component accepts (piped in). This must be a string* but it can be any type of serialized data (e.g. JSON, CSV, XML, whatever).</li>
<li>OUTPUT: The type of data that your component produces (pipes out). Again, only strings*!</li>
</ul>
<div>
* All INPUT and OUTPUT is a string in PowowShell. This may sound like a limitation but it ultimately ensures components can talk to each other. A major weakness of PowerShell cmdlets today.<br />
<h3>
</h3>
<h3>
Pipelines</h3>
</div>
<div>
The pipeline is defined by a pipeline.json file (this will be built visually like a flowchart in later versions) which could look something like this:</div>
<pre>{
"id": "pipeline1",
"name": "Send mail to young voters",
"description": "Read voters.txt, get all voters under 21yrs of age and output the name, age and email as a JSON array",
"parameters": [],
"input": {},
"output": {},
"steps": [
{
"id":"A",
"name":"Read Voters File",
"reference":"../components/ReadFile.ps1",
"input":"",
"parameters": {
"Path": "./data/voters.txt"
}
},
{
"id":"B",
"name":"Convert2JSON",
"reference":"../components/CSV2JSON.ps1",
"input": "A",
"parameters": {
"Delimiter": "|",
"Header": "{\"name\", \"age\", \"email\", \"source\"}"
}
},
{
"id":"C",
"name":"Select Name and Email",
"reference":"../components/SelectFields.ps1",</pre>
<pre> "input": "B",
"parameters": {
"Fields": "{\"name\", \"age\", \"email\"}"
}
}
]
}
</pre>
<br />
<div>
Basically the pipeline has some meta data (name, description) but the real meat is the list of steps. These steps are executed in the order they occur and they get their input from the step they define. In this case:<br />
<span style="font-family: "courier new" , "courier" , monospace;">A -> B -> C</span><br />
But it would also be possible to pass A's output to another component B1 as in<br />
<span style="font-family: "courier new" , "courier" , monospace;">A -> B -> C</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> -> B1</span><br />
<br />
The key thing to notice is that each step references a component. These components are your re-usable pieces of code and should do something useful and configurably reusable.<br />
<h3>
</h3>
<h3>
Components</h3>
As an example consider a <span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">FileList.ps1</span> component. It takes two PARAMETERS "Path" and "Filter" and lists the files in that directory. It does not take any INPUT and it's OUTPUT is an array of File data. Such a component is an Extractor in <a href="https://en.wikipedia.org/wiki/Extract,_transform,_load">ETL</a> language as it is a source node for the data flow.<br />
<br />
There is a built-in cmdlet in PowerShell called Get-ChildItem which lists files. It's also known as "dir" or "ls". We will be using this cmdlet but wrapping it up nicely so that we can use it in a pipeline. In particular we will be making it output a JSON Array. Here's the code:<br />
<br />
<pre>[Parameter(Mandatory=$true,HelpMessage="The path to the files")][string]$Path,
[string]$Filter
$files = @()
Get-ChildItem -Path $Path -Filter $Filter -Recurse:$Recurse |
ForEach-Object {
$f = @{
name=$_.Name
fullName=$_.FullName
size=$_.Length
}
$files += New-Object -TypeName PSObject -Property $f
}
$files | ConvertTo-JSON</pre>
<div>
<br />
This component, saved as .\components\FileList.ps1 can be run as follows from POWERSHELL:<br />
<blockquote class="tr_bq">
.\components\FileList.ps1 -Path C:\temp -Filter *.txt</blockquote>
It will produce an output similar to the following:<br />
<pre>[
{
"fullName": "C:\\temp\\test.txt",
"name": "test.txt",
"size": 491899
},
{
"fullName": "C:\\temp\\op.txt",
"name": "op.txt",
"size": 413431
}
}</pre>
<br />
Enough details for now. The idea is that components downstream can accept this array of objects and process them in some way. One might want to delete the files, search them for text, email them to somebody. The possibilities are endless.<br />
<br />
Of course the whole system stands or falls on reusability. If our downstream component does not understand what that JSON is, or expects a different format well then we need to do some work. This is where Transform Components come it. We may well need to write many such transform components but the system will come with some basic ones like CSV2JSON and JSON2XML. As soon as we need specialized JSON or XML we will have to write our own components.<br />
<br />
Looking ahead I could imagine 2 types of generic transform components:<br />
<br />
<ol>
<li>A JSON2JSON adaptor which allows you to simply remap fields (visually).</li>
<li>A generic JavaScript component which allows you to write a javascript function which transforms your data.</li>
</ol>
</div>
</div>
cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-5273033920736703462013-04-16T11:15:00.000-07:002013-04-16T11:15:00.754-07:00NGINX Awesomeness: A Simple, Powerful Web Server and MoreA 2MB exe which runs a web server to rival Apache? Well, maybe not, but if you Like Simple<span style="background-color: white; color: #444444; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 18px;">™</span> then you will Like <a href="http://nginx.org/">NGINX</a>. In terms of power it kicks <a href="http://cawoodm.blogspot.ch/2011/08/mongoose-free-easy-web-server.html">Mongoose's</a> ass I have to say because that critter, whom I am fond of, was pretty limited. So, ff you want to run multiple sites, reverse proxy, finely control everything HTTP and more then NGINX is your new best friend<br />
<br />
<a href="http://nginx.org/en/download.html">Download</a> and run and you have a webserver on http://localhost serving up whatever is in your HTML folder. I've stripped down the conf/nginx.conf file in order to learn and explain it here:<br />
<br />
<br />
<blockquote class="tr_bq">
http {<br /> server {<br /> listen 80;<br /> server_name localhost;<br /> location / {<br /> root html;<br /> index index.html index.htm;<br /> }<br /> }<br />}<br />events {<br /> worker_connections 1024;<br />}</blockquote>
<br />
This obviously sets up an HTTP server on your machine listening on port 80. It's actually just the explicit version of the default settings and so equivalent to:<br />
<br />
<br />
<blockquote class="tr_bq">
http {<br /> server {}<br />}<br />events {}</blockquote>
<br />
If you like being Spartan!<br />
<br />
Next: want a second server (host name)? Just add a new <span style="font-family: Courier New, Courier, monospace;">server </span>section:<br />
<blockquote class="tr_bq">
server {<br /> server_name mydomain.com;<br /> location / {<br /> root /somewhere/else/;<br /> autoindex on;<br /> }<br />}</blockquote>
The <span style="font-family: Courier New, Courier, monospace;">autoindex </span>setting provides an automatic directory listing (great for local test servers).<br />
<br />
Of course you can configure logging (even per server), HTTP settings (e.g. keep alive stuff), mime types, SSL, hostname wildcards, virtual directories, error pages, gzip (nice), deny access (for .htaccess you need a <a href="http://winginx.com/htaccess">converter</a>) and all the usual stuff you need. But what get's me excited is the reverse proxy stuff.<br />
<br />
Our company uses an expensive, high tech appliance for reverse proxy (and load balancing) functionality. This helps keep our web servers out of the DMZ and provides a neat front so that we can have multiple different web servers behind one application on one domain. The downside is you need a degree in it to configure it. NGINX is Simple<span style="background-color: white; color: #444444; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 18px;">™</span> - remember! Watch this:<br />
<br />
Let's say you have another web server running PHP and you want NGINX to reverse proxy it. Simply pass all URLs ending in .php to that server with the location command's RegEx functionality:<br />
<br />
<br />
<blockquote class="tr_bq">
location ~ \.php$ {<br /> proxy_pass http://myphp.mydomain.com:8080;<br />}</blockquote>
That's awesome IMO. You need some serious expertise and clicking to get that done in our enterpise level applicance whose name begin's with "F" and ends with "5" but shall otherwise remain unnamed.<br />
<br />
So sharpen up your RegEx skills and run your whole organisation's web infrastructure through one domain if you like.<br />
<br />
cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-20864962419319023552013-04-10T07:27:00.003-07:002013-04-11T08:41:00.304-07:00OptiBrowser: Managing Links in a Multi-Browser WorldAs a web developer I have several browsers installed on my PC. This is not only for testing apps on different browsers but allows you to run the same app as a different user at the same time - something many web apps don't offer because they store session state in a cookie which is shared among windows of the same browser.<br />
<br />
That's all fine but what about the default browser? Each OS allows you one and only one default browser. This means that when you click a link in an email it will always open in your default browser. If you're like me, and your default browser is internet explorer (don't ask), you find yourself copying the link and pasting it into Chrome.<br />
<br />
Enter OptiBrowser!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf2JWxucDoOOH0nl4P6aSnsySifz3JIUNmcoMDwCfC37S56L2BnePm6XANy_eYvg3p1clNT3ZNRScoyU-82kIfer_7BtEQQf0adEmHsBJOlk5R9zUmvvi3G07-fhvDk5BEVk4k9TNTjEQi/s1600/screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf2JWxucDoOOH0nl4P6aSnsySifz3JIUNmcoMDwCfC37S56L2BnePm6XANy_eYvg3p1clNT3ZNRScoyU-82kIfer_7BtEQQf0adEmHsBJOlk5R9zUmvvi3G07-fhvDk5BEVk4k9TNTjEQi/s1600/screenshot.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
OptiBrowser is your new default browser and yet it's not a browser at all. It's simply a menu which let's you choose which browser you want to start the link you just clicked in. Press <Enter> and it will start in your default browser. Of course this only applies to links you click outside of the browser - links clicked in a browser say in that browser.<br />
<br />
Check it out on GitHub (<a href="https://github.com/cawoodm/optibrowser" target="_blank">cawoodm/optibrowser</a>) and, if you have any issues post them there. It's written in C# (.NET 4) and compiled and tested under Windows 7 so far. Feel free to fork and improve.cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-57547704870968541832013-02-07T12:14:00.000-08:002013-02-12T23:22:16.391-08:00Crafty Tennis: A Component-Entity Game in JavaScriptBeen dreaming of making a game for some time now and have been very interested in Component-Entity Systems which are a great way of keeping complexity down in a game.<br />
<br />
I won't go through all the details here but suffice it to say you can keep complexity linear by either adding new entities to your game (more "things" in the game) or by adding new components (more "functionality"). You then simply tell each new entity you add which components it has.<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">// Player Left (with AI)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Crafty.sprite(32, "img/padleft.run.png", {</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> padleft: [0, 0]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">});</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Crafty.e("Paddle, 2D, DOM, Color, Multiway, Bound, AI, padleft, SpriteAnimation")</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.color('rgb(255,0,0)')</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.attr({ x: 20, y: H/2, w: 32, h: 32, player: 1 })</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.bound({minX: 0, minY: 0, maxX: W/2, maxY: H})</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.multiway(4, { W: -90, S: 90, D: 0, A: 180 })</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.difficulty(3)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.bind('NewDirection', runner)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>.animate('run', 0, 0, 5) // From x=0, y=0 to x=5 (6 frames)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">;</span><br />
<br />
<br />
Check it out on GitGub (<a href="https://github.com/cawoodm/tennis" target="_blank">cawoodm/tennis</a>) or <a href="https://github.com/cawoodm/tennis/archive/master.zip" target="_blank">download it</a> - it's actually quite fun to play!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/cawoodm/tennis" imageanchor="1" style="margin-left: 1em; margin-right: 1em;" target="_blank"><img border="0" height="240" src="https://github.com/cawoodm/tennis/raw/master/screenshot.png" width="400" /></a></div>
<br />
Of course all this development was made possible by the excellent (and free) in-browser editor called <a href="https://github.com/scripted-editor/scripted" target="_blank">Scripted</a> - it's a poor man's Sublime Text.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjK_LKS4KxAkiR1GFt-IMR07SNE36_eLg3EFnCGNXce6PRhP8dqJLan6YlNIwsrWNIUqH8ChyphenhyphenZievYf2-k8vKRJMM80fcB9H3ciUUVPxgd-hKhojtt0QSvTwGE0vJ5QcB6QRQU-E86YwK8/s1600/scripted.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="373" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjK_LKS4KxAkiR1GFt-IMR07SNE36_eLg3EFnCGNXce6PRhP8dqJLan6YlNIwsrWNIUqH8ChyphenhyphenZievYf2-k8vKRJMM80fcB9H3ciUUVPxgd-hKhojtt0QSvTwGE0vJ5QcB6QRQU-E86YwK8/s400/scripted.png" width="400" /></a></div>
<br />cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-49716847025686630432013-01-07T13:17:00.001-08:002013-01-07T13:17:26.056-08:00Clean Your PathNote: This is a Windows tip!<br />
<br />
Each time you install new software, especially nerdy, developer stuff, it tends to add itself to your system <span style="font-family: Courier New, Courier, monospace;">PATH</span>. For single .exe programs (e.g. curl or git) I find this wasteful so I've devised a cunning plan. Let's say you install curl.exe in <span style="font-family: Courier New, Courier, monospace;">C:\some\folder\curl</span><br />
<br />
<br />
<ol>
<li>Remove the folder from your system path</li>
<li>Create a <span style="font-family: Courier New, Courier, monospace;">curl.cmd</span> text file in <span style="font-family: Courier New, Courier, monospace;">C:\windows\system32</span></li>
<li>Edit this text file and add the following: <b><span style="font-family: Courier New, Courier, monospace;">C:\some\folder\curl\curl.exe %*</span></b></li>
</ol>
Now, open a DOS window and run <span style="font-family: Courier New, Courier, monospace;">curl google.com</span> - it works! How?<br />
<br />
<ul>
<li><span style="font-family: Courier New, Courier, monospace;">%*</span> passes all parameters on to curl.exe</li>
<li><span style="font-family: Courier New, Courier, monospace;">C:\windows\system32</span> is always on your system path</li>
</ul>
I recommend doing this for all your favorite command line programs and even your favorite editor. My new favorite is <a href="https://github.com/scripted-editor/scripted/" target="_blank">Scripted</a>!cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-39727961790337372612012-12-19T06:15:00.000-08:002012-12-26T04:15:42.872-08:00Easy MicroTemplatingI'm a huge fan of templates in that I always want to separate my view from my code. But I don't like to include hundreds of kilobytes of some library just to get templating - so I made my own in a few lines which I can just copy into any project.<br />
<br />
<span style="font-family: inherit;">The most basic usage is to pass it data and a template and get the result:</span><br />
<span style="font-family: Courier New, Courier, monospace;">microTemplate({name:'Jack'}, '<h1>Hello {{name}}</h1>');</span><br />
This will return:<br />
<span style="font-family: Courier New, Courier, monospace;"><h1>Hello Jack</h1></span><br />
<br />
Now there are different ways of using it. You can specify a DOM element which contains your template so if you have this in your HTML:<br />
<span style="font-family: Courier New, Courier, monospace;"><div id="op"><h1>Hello {{name}}</h1></div></span><br />
You can just do:<br />
<span style="font-family: Courier New, Courier, monospace;">microTemplate({name:'Jack'}, $('#op')[0]);</span><br />
This will automatically write the result back into your div.<br />
<br />
It supports arrays so:<br />
<span style="font-family: Courier New, Courier, monospace;">var friends = [{name:'Jack'},{name:'Jill'}];</span><br />
<span style="font-family: Courier New, Courier, monospace;">microTemplate(friends, '<h1>Hello {{name}}</h1>', $('#op3')[0]);</span><br />
Will result in:<br />
<span style="font-family: Courier New, Courier, monospace;"><h1>Hello Jack</h1></span><br />
<span style="font-family: Courier New, Courier, monospace;"><h1>Hello Jill</h1></span><br />
<br />
If you want a table or an ordered list you'll need to specify header and footer:<br />
microTemplate(friends, '<li>{{name}}</li>', $('#op4')[0], '<ol>', '</ol>');<br />
Which will result in:<br />
<span style="font-family: Courier New, Courier, monospace;"><ol></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <li>Jack</li></span><br />
<span style="font-family: Courier New, Courier, monospace;"> <li>Jill</li></span><br />
<span style="font-family: Courier New, Courier, monospace;"></ol></span><br />
<br />
I think it's awesome!<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><!doctype html></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><html></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><head></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> </span><span style="background-color: white; color: #444444; font-family: 'Courier New', Courier, monospace; font-size: 13px; line-height: 18px;"><script src="http://code.jquery.com/jquery-1.8.3.min.js"></script></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> <script></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> $(function() {</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> microTemplate({name:'Jack'}, $('#op')[0]);</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> microTemplate({name:'Jack'}, '<h1>Hello {{name}}</h1>', $('#op2')[0]);</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> microTemplate([{name:'Jack'},{name:'>>Jill<<'}], '<h1>Hello {{name}}</h1>', $('#op3')[0]);</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> microTemplate([{name:'Jack'},{name:'Jill'}], '<li>{{name}}</li>', $('#op4')[0], '<ol>', '</ol>');</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> });</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> function microTemplate(data, temp, el, head, foot) {</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> if (typeof temp.innerHTML=='string') {el=temp;temp=temp.innerHTML;}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> data = [].concat(data);</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> var res = head||'';</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> for (var i in data) {</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> res += temp;</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> for (var f in data[i]) {</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> res = res.replace(new RegExp('{{{'+f+'}}}', 'ig'), data[i][f]);</span><br />
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span><span style="font-family: Courier New, Courier, monospace; font-size: x-small;">res = res.replace(new RegExp('{{'+f+'}}', 'ig'), data[i][f].replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'));</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> res += foot||'';</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> if (el && typeof el.innerHTML == 'string') el.innerHTML = res;</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> return res;</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> } </span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> </script></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"></head></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><body></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><div id="op"><h1>Hello {{name}}</h1></div></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><div id="op2"></div></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><div id="op3"></div></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><div id="op4"></div></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"></body></span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"></html></span><br />
<br />
<br />cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-47821732303386148442012-12-15T05:54:00.002-08:002012-12-15T05:54:57.935-08:00ssiJS: Server Side Includes on the ClientI cut my teeth on classic ASP where you build applications by including various blocks using SSI (server-side include) syntax. <span style="font-family: inherit;">If you wanted to include common content (e.g. branding or a copyright statement) inside your </span><span style="font-family: Courier New, Courier, monospace;"><body></span><span style="font-family: inherit;"> you would include it like this:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><!-- #include file="copyright.asp" --></span><br />
<br />
Aside from <iframe> there is no way to include content from other sites using HTML. You need JavaScript so I wrote a 3-liner jQuery script which does just that. Consider this HTML:<br />
<span style="font-family: Courier New, Courier, monospace;"><div class="ssi" data-url="copyright.html"></div></span><br />
<br />
Add the following JavaScript to your $(function(){}); and you'll have client side includes:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">$.each($('div.ssi'), function(a,b) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>$(b).load(b.getAttribute('data-url'));</span><br />
<span style="font-family: Courier New, Courier, monospace;">});</span><br />
<br />
<span style="font-family: inherit;">What does this do? It iterates through all divs with the class 'ssi' and loads their content asynchronously from the url specified in their 'data-url' attribute. I think this is neat.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Full source:</span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<span style="font-family: Courier New, Courier, monospace;"><!doctype html></span><br />
<span style="font-family: Courier New, Courier, monospace;"><html></span><br />
<span style="font-family: Courier New, Courier, monospace;"><head></span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><script src="http://code.jquery.com/jquery-1.8.3.min.js"></script></span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><script></span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>$(function() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>$.each($('div.ssi'), function(a,b) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>$(b).load(b.getAttribute('data-url'));</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>});</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>});</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></script></span><br />
<span style="font-family: Courier New, Courier, monospace;"></head></span><br />
<span style="font-family: Courier New, Courier, monospace;"><body></span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><div class="ssi" data-url="a.html"></div></span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><div class="ssi" data-url="b.html"></div></span><br />
<span style="font-family: Courier New, Courier, monospace;"></body></span><br />
<span style="font-family: Courier New, Courier, monospace;"></html></span><br />
<br />
cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-9495395572391759542011-11-01T09:25:00.000-07:002011-11-01T09:25:33.489-07:00YQL: All the Data you can EatI love SQL and I love data. Just give me a database and some SQL and I'll analyse the bejeesuz out of it. I also like flexible interfaces. YQL provides all of this. The <b>Y</b>ahoo <b>Q</b>uery<b> L</b>anguage is basically 2 things:<br />
<ol>
<li>A huge data source
</li>
<li>An easy query language which resembles SQL</li>
<li>Standardized output format (JSON or XML)
</li>
</ol>
OK, that was 3 things but I'm not going back to change my assertion just because I thought of an extra thing whilst typing. And, speaking of inaccuracies, I saaay "data source" but actually it's mostly just passing on data it gets from elsewhere. Let's dive in:<br />
<br />
Say you wanna <a href="http://developer.yahoo.com/yql/console/#h=SELECT%20*%20FROM%20rss%20WHERE%20url%20%3D%20%27http%3A//sports.yahoo.com/top/rss.xml%27" target="_blank">query an RSS feed</a>:<br />
<code>SELECT * FROM rss WHERE url = 'http://sports.yahoo.com/top/rss.xml'</code><br />
<br />
This will return the RSS items from the Yahoo! sports site. You can of course specify multiple feeds using <span style="font-family: "Courier New",Courier,monospace;">IN (url1, url2)</span> and you can also sort (<a href="http://stackoverflow.com/questions/2718575/how-to-use-yql-to-merge-2-rss-feeds-sorted-by-pubdate" target="_blank">example</a>).<br />
<br />
You can query <a href="http://developer.yahoo.com/blogs/ydn/posts/2009/12/extending_the_weather_api_with_yql/" target="_blank">weather</a>, <a href="http://developer.yahoo.com/yql/console/?q=select%20*%20from%20craigslist.search%20where%20location%3D%22raleigh%22%20and%20type%3D%22mis%22%20and%20query%3D%22spilled%22&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys" target="_blank">flickr</a> for photos, <a href="http://developer.yahoo.com/yql/console/#h=select%20*%20from%20local.search%20where%20query%3D%22sushi%22%20and%20location%3D%22san%20francisco%2C%20ca%22" target="_blank">find restaurants in San Francisco</a> , <a href="http://developer.yahoo.com/yql/console/#h=select%20*%20from%20music.video.category%20where%20id%3D%227318647%22" target="_blank">list music videos</a> ... I think you get the picture.<br />
<br />
There is also the YQL Console, a place to <a href="http://developer.yahoo.com/yql/console/#h=select%20*%20from%20geo.continents%20where%20place%3D%22North%20America%22" target="_blank">test out your queries</a>. It will generate the link which you can then use in your applications to query data. As you will see, you can specify the output format as XML or JSON:<br />
<div style="font-family: "Courier New",Courier,monospace;">
<span style="font-size: x-small;">http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.oceans&format=<b style="color: blue;">json</b>&callback=showOceans</span></div>
<br />
The callback function wraps your data in a javascript function which means you just AJAX the URL above and your callback function will automatically be executed.<br />
<br />
Finally the really gr8 thing about YQL is that it's free and it's cross-domain compatible. If you've ever made an application which fetches data from a different domain you'll know of the issues involved. Modern browsers just don't allow it unless the server says it's OK. YQL always adds the "it's OK header" (Access-Control-Allow-Origin: *) which means it damn well works. Very nice of dem folks at Yahoo!.<br />
<br />
Finally, the other of the final 3 great things is that you can literally query (scrape) any web page on the, er, web. You can query normal <a href="http://developer.yahoo.com/yql/console/#h=select%20*%20from%20html%20where%20url%3D%22http%3A//finance.yahoo.com/q%3Fs%3Dyhoo%22%20and%0A%20%20%20%20%20%20xpath%3D%27//div[@id%3D%22yfi_headlines%22]/div[2]/ul/li/a%27" target="_blank">HTML pages</a> or XHTML or <a href="http://developer.yahoo.com/yql/console/#h=select%20*%20from%20xml%20where%20url%3D%27http%3A//rss.news.yahoo.com/rss/topstories%27" target="_blank">XML sources</a>. <br />
<br />
Coming soon, my jQuery Mobile RSS Reader app which uses YQL and Local Storage to save your RSS.cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-19978126546499036962011-09-11T10:39:00.000-07:002019-06-07T02:40:01.941-07:00SOAP2REST Proxy with node.jsGiven the rebirth of JavaScript in the past decade it shouldn't surprise us that some wise-guy asked why we don't use the same language on the server as we use on the client: JavaScript!<br />
<br />
Actually Microsoft was doing this in the 90's with their <script runat="server"> where you could write either VBScript or JScript both client and server side. Then browsers like Firefox took off and VBScript was unsupported leading to a rebirth of the powerful JavaScript language.<br />
<br />
Node.js (or node.exe) is basically an interpreter like php.exe except you can actually use it stand-alone as it's own web service. You write a simple server.js like this:<br />
<blockquote>
<pre>var http = require('http');
var srv = http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/html'});
response.write("<h1>Hello World!</h1>");
response.end();
}).listen(8080);</pre>
</blockquote>
Next you run your script from the command prompt:
<br />
<blockquote>
C:\node.exe server.js</blockquote>
Finally, open up your browser to http://localhost:8080 and you should see Hello World! in large, friendly letters.<br />
<br />
Because I'm planning a killer hip mobile/offline app which consumes SOAP web services I decided to write a node.js script which will allow me to make simple HTTP GET requests and receive JSON data in return. The soap2rest.js proxy I wrote converts the GET request to a SOAP request and should parse the SOAP response to return JSON:<br />
myApp.html -> HTTP GET -> (soap2rest.js) -> SOAP Request -> SOAP Response -> JSON<br />
<br />
The first thing I need is an HTTP client. It's important to remember that our soap2rest.js is going to proxy the SOAP request so I made a generic HTTP request function:<br />
<br />
<blockquote>
<pre>function doHTTP(options, success, error) {
var req = http.request(options, function(res) {
var data = '';
res.setEncoding(options.encoding || 'utf8');
res.on('data', function (chunk) { data += chunk; });
res.on('end', function () { success({ data: data, response: res }); });
});
if (typeof options.body != 'undefined') req.write(options.body);
req.end();
req.on('error', function(e) { error('HTTP ERROR: ' + e.message); });
}
</pre>
</blockquote>
As you can see the function simply takes all it's parameters via the options object and has two callback functions for success and error. Now all we have to do is map the incoming GET to an HTTP SOAP request.
In order to make this proxy as generic as possible I defined the following syntax for the URL:
http://whatever/service/method?parameters=values
So, for a simple weather service I would call:
http://localhost:8080/weather/fetch?city=Zurich
Before I can call my soap2rest.js proxy I need to define the service call. This is done in a configuration file called <b>fetch.json</b> corresponding to the fetch method of my weather service. The configuration file sits in a folder called <b>weather</b> with other potential methods:
weather/fetch.json:
<br />
<blockquote>
<pre>{
"url": "http://www.webservicex.net/globalweather.asmx?op=GetWeather",
"headers": {
"SOAPAction": "http://www.webserviceX.NET/GetWeather",
"Content-Type": "text/xml"
}
}
</pre>
</blockquote>
This configuration file tells SOAP2REST.js almost everything it needs to know about calling the SOAP service globalweather.asmx. What it still needs is the request format. This can be discovered by parsing the WSDL of the service but this is seriously complicated and liable to error. And I'm too lazy. So, the developer "just" has to provide the sample request body in fetch.xml inside the weather/ folder:
<br />
<blockquote>
<pre><?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetWeather xmlns="http://www.webserviceX.NET">
<CityName>##CITY##</CityName>
<CountryName></CountryName>
</GetWeather>
</soap:Body>
</soap:Envelope>
</pre>
</blockquote>
The ##CITY## is a placeholder which will be replaced with the city parameter of the incoming GET request.
Here is where the magic happens. The SOAP2REST function does the following:
<br />
<ol>
<li>Parse the URL and determine the service and method
</li>
<li>Read the service/method.json configuration file
</li>
<li>Read the service/method.xml request file
</li>
<li>Replace all placeholders from the query string (e.g. city)
</li>
<li>Call the SOAP service POSTing the method.xml data
</li>
<li>Return the response to the client
</li>
</ol>
So here's the magic SOAP2REST:
<br />
<blockquote>
<pre>function SOAP2REST(URL, success, error) {
URL = url.parse(URL, true);
// Load Service Definition
try {
var soapJSON = fs.readFileSync('./soap' + URL.pathname + '.json', 'UTF-8');
} catch (e) {
return die({msg: "Service description " + URL.pathname + ".json could not be found!", err: e}, 404);
}
// Load SOAP Request
var soapXML = '';
try {
soapXML = fs.readFileSync('./soap' + URL.pathname + '.xml', 'UTF-8');
} catch (e) {
// No problem
}
// Parse SOAP Configuration
try {
var soapJSON = JSON.parse(soapJSON);
var soapURL = url.parse(soapJSON.url, true);
} catch (e) {
return die({msg: "Service JSON could not be parsed!", name: e.name, message: e.message}, 500);
}
// Set Parameters
for (p in URL.query) soapXML = soapXML.replace('##' + p.toUpperCase() + '##', URL.query[p]);
// Prepare SOAP Request Headers
soapJSON.headers = soapJSON.headers || {};
soapJSON.headers["Content-Length"] = soapXML.length;
soapJSON.headers["Connection"] = "close";
// Do SOAP Call
var httpOptions = {
host: soapURL.hostname,
post: soapURL.port || 80,
method: soapJSON.method || 'POST',
path: soapURL.pathname,
headers: soapJSON.headers,
};
httpOptions.body = soapXML;
doHTTP(httpOptions,
function(d) {
success(d);
},
function(e){
error(e, 500);
}
);
}
</pre>
</blockquote>
Technically, I guess, this ain't REST because I'm not using /weather/fetch/city/Zurich/ but I prefer /weather/fetch?city=Zurich because this is the standard HTTP way of passing parameters and it means AJAX forms will work automagically!<br />
<br />
If I do say so myself, I like the way you can easily add a new service on the fly simply by adding a .json and .xml file.<br />
<br />
Now to find an XML2JSON converter... <br />
<br />
Note: This code is all for node.js v5.6 and the functions seem to change with every release so no doubt this won't work in future!
<a href="http://pastebin.com/ag1DRrAg">Full Source Code</a>cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-3737017139137275872011-08-30T03:11:00.000-07:002011-08-30T03:11:19.559-07:00Tinkering with Javascript Libraries on jsfiddle.netMan I'm out of it. Such great tools that I missed during my post-2.0 slumber.<br />
<br />
<a href="http://jsfiddle.net/">jsfiddle</a> allows you to quickly and easily make and test JavaScript apps using some of the cool libraries out there (e.g. jQuery or mootools).<br />
<br />
Just go to http://jsfiddle.net and start hacking in your HTML, Javascript and CSS. Press Ctrl+Enter to run and your app will, er, run!<br />
<br />
I think it's cool.<br />
<br />
Check out my <a href="http://jsfiddle.net/cawoodm/SuTtE/">sandbox application</a> which says hello to you via ajax. Oh the wonders of modern technology.cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-21635397400714518562011-08-29T06:47:00.000-07:002011-08-29T07:25:03.697-07:00Mongoose: Free Easy Web ServerI've always been an IIS fan but the move to Windows 7 and IIS7 has shown me that I'm getting old. I can't get IIS to do anything right and so I've decided: let's re-rethink this. Back to basics. <a href="http://en.wikipedia.org/wiki/KISS_principle">KISS</a>!
<br />
<br />I googled "<a href="http://www.google.com/search?q=simple+http+server">simple http server</a>" and discovered <a href="http://code.google.com/p/mongoose/">mongoose</a>.
<br />
<br />It's Simple™. It Works™. I Like It™.
<br />
<br />Download the mongoose.exe and run it and you've just started your webserver.
<br />
<br />By default, mongoose serves from C:\ so if you browse to http://localhost/ you should see a directory listing.
<br />
<br />Mongoose is configurable via the command line or via a configuration file.
<br />
<br />Example: Server on Port 81
<br /><blockquote>C:\>mongoose.exe -p 81</blockquote>
<br />or, via config file you create a mongoose.conf file with the following content:
<br /><blockquote>p 81</blockquote>
<br />
<br />You'll probably do something wrong at some stage so you need to tell mongoose where to log errors. This is done via the e parameter:
<br /><blockquote>C:\>mongoose.exe -e error.log -p 81</blockquote>
<br />or
<br /><blockquote>e error.log</blockquote>
<br />
<br />Check out the <a href="http://code.google.com/p/mongoose/wiki/MongooseManual">full manual</a> explaining all commands.
<br />
<br />This thing can even run PHP! Just tell it which file extensions you want to be processed via CGI:
<br /><blockquote>c .php</blockquote>
<br />Next, tell it where your php executable lies:
<br /><blockquote>I c:\\php\\php-cgi.exe</blockquote>
<br />(The double backslashes are required for windows PCs like mine)
<br />
<br />Here is my sample/example mongoose.conf:
<br /><blockquote># Error Log
<br />e error.log
<br />
<br /># Ports (csv)
<br />p 80
<br />
<br /># Root directory
<br />r c:\\mywebsite\\html
<br />
<br /># Default documents
<br /># (disabled with # comment)
<br />#i index.htm,index.html
<br />
<br /># Add your special mime types here
<br />m .manifest=text/cache-manifest,.asp=text/html
<br />
<br /># CGI File extensions (csb)
<br />c .php
<br />
<br /># Location of CGI interpreter (e.g. PHP)
<br />I c:\\php\\php-cgi.exe
<br /></blockquote>
<br />
<br />Happy serving!
<br />cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0tag:blogger.com,1999:blog-3743841827679326713.post-51935696681856171732010-12-02T21:17:00.000-08:002011-08-25T20:47:30.408-07:00Offline Bible on the iPhone with jQuery and Lawnchair<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZVRFWVOVBwq0hn8j4PG7PwkdN2LL9Mg3NWRzZhB5DbpIqVtnrW0uU6KEa3rItqwFl66T1ePkWmXCXy23F6RCq6wWt_-7mV-VaE65pikUWcolMEr40lRcI5L9dqLxvy2Pn2hbSq0G9opPM/s1600/esv.offline.png"><img style="float: right; margin: 0pt 0pt 10px 10px; cursor: pointer; width: 320px; height: 215px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZVRFWVOVBwq0hn8j4PG7PwkdN2LL9Mg3NWRzZhB5DbpIqVtnrW0uU6KEa3rItqwFl66T1ePkWmXCXy23F6RCq6wWt_-7mV-VaE65pikUWcolMEr40lRcI5L9dqLxvy2Pn2hbSq0G9opPM/s320/esv.offline.png" alt="" id="BLOGGER_PHOTO_ID_5546331655004679826" border="0" /></a>
<br />Using <a href="http://jquery.com/">jQuery</a> and <a href="http://westcoastlogic.com/lawnchair/">Lawnchair</a> as well as the new offline capabilities of <a href="http://diveintohtml5.org/offline.html">HTML5</a> I've developed an offline Bible reader for the iPhone though it should work on other devices as well.
<br />
<br />The interface has been kept lean and mean and you can simply swipe to scroll down the chapter, use the <> buttons to navigate a chapter and the dropdown list to select a book.
<br />
<br />The first time you start the application it loads all books from the server. So far I've only got the ESV New Testament and when I find a free Bible version and get it converted to JSON I'll publish the app.
<br />
<br />The app works offline as well so you can browse the texts using the new offline cache of HTML5. This great new standard allows you to define which scripts and resources the browser should keep available offline (i.e. cache). You include your manifest in the HTML tag as follows:
<br /><textarea cols="60" rows="1"><!DOCTYPE html>[html manifest="default.manifest.asp"]</textarea>
<br />
<br />The DOCTYPE command tells the browser that this is an HTML5 document. Also, my manifest is an ASP file because your web server must serve the manifest with the Content-Type set to "text/cache-manifest". To avoid configuring your web server I added the header in the ASP code so it will run on any unconfigured IIS server:<pre><% Response.ContentType = "text/cache-manifest" %>CACHE MANIFEST
<br />
<br />CACHE:
<br />jquery-1.4.4.js
<br />LawnchairAdaptorHelpers.js
<br />DOMStorageAdaptor.js
<br />Lawnchair.js
<br />bible.js
<br />bible.css
<br />bible.png</pre>Lawnchair is a free JavaScript library which allows you to save data on the client. My application loads the books of the New Testament from simples text files like "Matthew.txt" which contain the chapters and verses in <a href="http://en.wikipedia.org/wiki/JSON">JSON</a> format. Here's 2 John.txt:<pre>{
<br />b:"2 John",
<br />i:63,
<br />c:[
<br />{v:[
<br />{t:"The elder to the elect lady and her children, whom I love in truth, and not only I, but also all who know the truth,"},
<br />{t:"because of the truth that abides in us and will be with us forever:"},
<br />{t:"Grace, mercy, and peace will be with us, from God the Father and from Jesus Christ the Father's Son, in truth and love."},
<br />{t:"I rejoiced greatly to find some of your children walking in the truth, just as we were commanded by the Father."},
<br />{t:"And now I ask you, dear lady - not as though I were writing you a new commandment, but the one we have had from the beginning - that we love one another."},
<br />{t:"And this is love, that we walk according to his commandments; this is the commandment, just as you have heard from the beginning, so that you should walk in it."},
<br />{t:"For many deceivers have gone out into the world, those who do not confess the coming of Jesus Christ in the flesh. Such a one is the deceiver and the antichrist."},
<br />{t:"Watch yourselves, so that you may not lose what we have worked for, but may win a full reward."},
<br />{t:"Everyone who goes on ahead and does not abide in the teaching of Christ, does not have God. Whoever abides in the teaching has both the Father and the Son."},
<br />{t:"If anyone comes to you and does not bring this teaching, do not receive him into your house or give him any greeting,"},
<br />{t:"for whoever greets him takes part in his wicked works."},
<br />{t:"Though I have much to write to you, I would rather not use paper and ink. Instead I hope to come to you and talk face to face, so that our joy may be complete."},
<br />{t:"The children of your elect sister greet you."}
<br />]}
<br />]}
<br /></pre>As you can see each book has 3 properties:
<br /><ol><li>The name b:"2 John"</li><li>The index order in the <a href="http://en.wikipedia.org/wiki/Biblical_canon">canon</a> i:63</li><li>The chapter list c:[]</li></ol>The index order is used for displaying the dropdown list for selecting a book.
<br />
<br />These text files are loaded via the jQuery.ajax() function which is then parsed as a JavaScript Object. This object is then stored locally via the Lawnchair library which supports various types of storage including a SQLite database. Although Apple's iPhone supports this "WebSQL" format, FireFox doesn't, so I stuck to localStorage aka. "DOM Storage".
<br />
<br />To retrieve a book which is cached, you use the get() function which is a bit unwieldy since it uses callbacks instead of just returning a value:<pre>db.bible = new Lawnchair({table:'bible'});
<br />db.bible.get('2 John', function(r){
<br />db.currentBook = r;
<br />});</pre>The first command basically says I want a new data store or table called "bible" and I use get() to fetch the book object. This r object has the b, i and c[] properties and I can loop through the chapters or the verses in a specific chapter and display them.
<br />
<br />To display the chapter list we need a dropdown list in our HTML:
<br /><textarea cols="60" rows="1"><select id="chapter"></select></textarea>
<br />
<br />Which we then fill with the cool jQuery function each() which loops through our array:<pre>$('#chapter').children().remove();
<br />$.each(db.currentBook.c, function(index, value) {
<br />$('#chapter').append($("").attr("value",index+1).text(index+1));
<br />});</pre>Listing verses in a chapter is also easy:<pre> $('#verseList').empty();
<br />$.each(db.currentBook.c[chapter], function(i,v) {
<br />$('#verseList').append('<span class="verse"><sup>'+(i+1)+'</sup>'+v.t+'</span> ');
<br />});
<br />window.scrollTo(0,1);</pre>
<br />I added the scrollTo command for two reasons: 1) The user should be taken to the top of the screen and 2) That this automatically hides Safari's toolbar on the iPhone which can get in the way.
<br />
<br />The little next ">" and previous "<" buttons also use jQuery eventing:<pre>$('#next,#next1').click(nextChapter);
<br />$('#prev,#prev1').click(prevChapter);</pre>
<br />And I have a function to disable them when we are at the start or end of a chapter:<pre>function showNextPrev() {
<br />$('#prev1, #prev').attr("disabled",db.lastBook.c==1)
<br />$('#next1, #next').attr("disabled",db.lastBook.c==db.currentBook.c.length)
<br />}</pre>To make the app look sexy you should always add a nice icon and include it in your HTML as follows:
<br /><textarea cols="60" rows="1">[link rel="apple-touch-icon" href="./bible.png"]</textarea>
<br />
<br />The <a href="http://articles.sitepoint.com/article/iphone-development-12-tips/">viewport meta tag</a> is also important because it ensures the iPhone browser displays your app in the correct size in any orientation!
<br /><textarea cols="60" rows="1">[meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=0;"]</textarea>
<br />
<br />Finally, I tried the jQuery <a href="http://plugins.jquery.com/project/swipe">swipe plugin</a> because I wanted to be able to swipe forwards and backwards. Unfortunately the swiping doesn't work well and it disabled the ability to scroll up and down so I've removed it but I may come back to it again because that would be awesome.cawoodmhttp://www.blogger.com/profile/02108527908963003806noreply@blogger.com0