I would like to write specific components of my frontend using Purescript's Halogen.
For example, I would like to create a registration form using Halogen. It would look something like below:
module RegistrationForm where
import Prelude
...
-- | The state of the application
newtype State = State { email :: String, password :: String }
derive instance genericState :: Generic State
instance showState :: Show State where show = gShow
instance eqState :: Eq State where eq = gEq
initialState :: State
initialState = State { email: "", password: "" }
-- | Inputs to the state machine
data Input a = FormSubmit a
| UpdateEmail String a
| UpdatePassword String a
| NoAction a
type AppEffects eff = HalogenEffects (ajax :: AJAX, console :: CONSOLE | eff)
ui :: forall eff . Component State Input (Aff (AppEffects eff))
ui = component render eval
where
render :: Render State Input
render (State state) = H.div_
[ H.h1_ [ H.text "Register" ]
, ...
]
eval :: Eval Input State Input (Aff (AppEffects eff))
eval (NoAction next) = pure next
eval (FormSubmit next) = do
...
eval (UpdateEmail email next) = do
...
eval (UpdatePassword password next) = do
...
runRegistrationForm :: Eff (AppEffects ()) Unit
runRegistrationForm = runAff throwException (const (pure unit)) $ do
{ node: node, driver: driver } <- runUI ui initialState
appendTo ".registration" node
Similarly, I have a LoginForm module that handles logging a user into the application.
I'm wondering how to go about organizing my source code, building my source code, and calling my Purescript code from Javascript?
Currently, my source code is organized like the following:
$ cd my-application/
$ tree
.
├── bower.json
├── README.md
├── site/
│ └── index.html
├── src/
│ ├── LoginForm.purs
│ └── RegistrationForm.purs
└── test/
└── Test.purs
However, since I don't have a Main.purs, I can't do any of the following to build my source code:
$ pulp build --to site/app.js
$ pulp browserify --to site/app.js
$ pulp server
It would be nice to be able to build my purescript code into logical javascript files. For instance, src/LoginForm.purs could be built as site/loginForm.js, and src/RegistrationForm.purs could be built as site/registrationForm.js.
Then, I could include loginForm.js and registrationForm.js in my actual html pages as need-be.
Pulp doesn't really cover this use case, it's only intended for apps where there is a single Main.
I'd suggest using a gulp setup to achieve this, using a gulpfile something like this:
"use strict";
var gulp = require("gulp"),
purescript = require("gulp-purescript"),
webpack = require("webpack-stream");
var sources = [
"src/**/*.purs",
"bower_components/purescript-*/src/**/*.purs",
];
var foreigns = [
"src/**/*.js",
"bower_components/purescript-*/src/**/*.js"
];
gulp.task("make", function() {
return purescript.psc({
src: sources,
ffi: foreigns
});
});
var mkBundleTask = function (name, main) {
gulp.task("prebundle-" + name, ["make"], function() {
return purescript.pscBundle({
src: "output/**/*.js",
output: "tmp/js/" + name + ".js",
module: main,
main: main
});
});
gulp.task("bundle-" + name, ["prebundle-" + name], function () {
return gulp.src("tmp/js/" + name + ".js")
.pipe(webpack({
resolve: { modulesDirectories: ["node_modules"] },
output: { filename: name + ".js" }
}))
.pipe(gulp.dest("site/js"));
});
return "bundle-" + name;
};
gulp.task("bundle", [
mkBundleTask("loginForm", "LoginForm"),
mkBundleTask("registrationForm", "RegistrationForm")
]);
gulp.task("default", ["bundle"]);
That might not be quite right, but I extracted it from how we do things with SlamData so it's definitely along the right lines.
This is possible with pulp, using the --to and --main options:
pulp build --main LoginForm -O --to site/loginForm.js
pulp build --main RegistrationForm -O --to site/registrationForm.js
Of course, the LoginForm and RegistrationForm modules will both need to export a value called main for this to work.
Related
How can I generate a sha256-RSA-signed JWT token in a Karate (https://github.com/karatelabs/karate) feature file?
https://github.com/karatelabs/karate/issues/1138#issuecomment-629453412 has a nice recipee for doing such for a HMAC-SHA256 (or "HmacSHA256" in Java lingo) token, i.e. using symmetric/shared secret crypto.
But we need asymmetric crypto and the RS256 algo (see RS256 vs HS256: What's the difference? for background)...
OK, think I figured it out :-).
Big thanks to the generous souls providing all the necessary info here:
JWT generation in Karate, but with HmacSHA256: https://github.com/karatelabs/karate/issues/1138#issuecomment-629453412
Signing with sha256 RSA signature in Java: https://www.quickprogrammingtips.com/java/how-to-create-sha256-rsa-signature-using-java.html
So the following is an example Karate feature file using
an RS256 JWT token (put in the x-jwt header)
mTLS (i.e. using a client certificate for mutual TLS)
To do this one needs to make use of Karate's JavaScript and Java-interop capabilities.
This is our setup to make it work:
0 $ tree
.
├── karate-config.js
├── karate.jar
├── secrets
│ ├── client-cert-keystore.p12
│ ├── client-cert.pem
│ ├── client-cert_private-key.pem
│ ├── rsa-4096-cert.pem
│ ├── rsa-4096-private.pem
│ └── rsa-4096-public.pem
└── test.feature
1 directory, 9 files
We'll use the private key rsa-4096-private.pem (keep it secret!) of our rsa-4096-* files to create the signed token.
So the essential files for the JWT parts are
rsa-4096-private.pem for creating the JWT
rsa-4096-public.pem for verifying the token/signature, that's what the api/service/server would do with your JWT token (i.e. this file's not needed/used in our feature file). You can try verifying a resulting token with e.g. https://jwt.io/.
Sidenote: public/private key pairs can be generated with e.g. openssl.
As a bonus this example contains using a client certificate and mTLS (which httpbin probably gracefully ignores). If you don't need this you can simply strip the configure ssl... line and the client_cert_keystore_pass stuff from the karate config file and the command line.
Karate feature file:
# test.feature
Feature: Simple test
Background:
# Several helper functions for creating a RS256 signed JWT token.
# Graciously adapted from:
# JWT generation in Karate, but with HmacSHA256:
# https://github.com/karatelabs/karate/issues/1138#issuecomment-629453412
# Signing with sha256 RSA signature in Java:
# https://www.quickprogrammingtips.com/java/how-to-create-sha256-rsa-signature-using-java.html
* def b64encode_bytes =
"""
function(bytes) {
// Base64-encode `bytes`.
// Returns bytes.
var encoder = Java.type('java.util.Base64')
.getUrlEncoder()
.withoutPadding()
return new java.lang.String(encoder.encode(bytes))
}
"""
# Base64-encode `str`, encodes str to UTF-8 and base64-encodes it.
# Returns bytes.
* def b64encode_str = function(str) {return b64encode_bytes(str.getBytes("UTF-8"))}
* def strip_key_header_footer_ws =
"""
function(key_text) {
// Strip -----BEGIN ... header + footer and all newline characters.
// Returns UTF-8-encoded bytes.
// Need string object for replaceAll method.
var key_text_str = new java.lang.String(key_text)
var key_str = key_text_str
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("\r", "")
.replaceAll("\n", "")
return key_str.getBytes('UTF-8')
}
"""
* def sha256rsa_sign =
"""
function(bytes, privateKey) {
var decoder = Java.type('java.util.Base64')
.getDecoder()
var PKCS8EncodedKeySpec = Java.type(
'java.security.spec.PKCS8EncodedKeySpec')
var spec = new PKCS8EncodedKeySpec(decoder.decode(privateKey))
var kf = Java.type('java.security.KeyFactory').getInstance("RSA")
var signature = Java.type('java.security.Signature')
.getInstance("SHA256withRSA")
signature.initSign(kf.generatePrivate(spec))
signature.update(bytes)
var signed = signature.sign()
return signed
}
"""
* def generate_jwt_sha256rsa =
"""
function(payload) {
// Generate JWT from given `payload` object (dict).
// Returns SHA256withRSA-signed JWT token (bytes).
var header_encoded = b64encode_str(
JSON.stringify({alg: "RS256", typ: "JWT"}))
var payload_encoded = b64encode_str(JSON.stringify(payload))
var data_to_sign = header_encoded + '.' + payload_encoded
var signature = b64encode_bytes(
sha256rsa_sign(data_to_sign.getBytes("UTF-8"), privateKey)
)
var token = data_to_sign + '.' + signature
return token
}
"""
# enable X509 client certificate authentication with PKCS12 file
* configure ssl = { keyStore: 'secrets/client-cert-keystore.p12', keyStoreType: 'pkcs12', keyStorePassword: '#(client_cert_keystore_pass)' }
# get private key for JWT generation and API key
* def privateKeyContent = read('secrets/rsa-4096-private.pem')
* def privateKey = strip_key_header_footer_ws(privateKeyContent)
# generate JWT
* def jwt = generate_jwt_sha256rsa({iss: "ExampleApp", exp: "1924902000"})
# put all needed API access credential in the header
* headers { x-jwt: '#(jwt)'}
* url 'https://httpbin.org'
Scenario Outline: get anything
Given path '/anything/<anything_id>'
When method get
Then status 200
Examples:
| anything_id |
| 1 |
Karate config file:
// karate-config.js
function fn() {
//var http_proxy = java.lang.System.getenv('http_proxy');
var client_cert_keystore_pass = java.lang.System.getenv(
'CLIENT_CERT_KEYSTORE_PASS');
// setup connection
karate.configure('connectTimeout', 5000);
karate.configure('readTimeout', 5000);
//karate.configure('proxy', http_proxy);
var config = {
client_cert_keystore_pass: client_cert_keystore_pass
};
return config;
}
As noted you won't need the client_cert_keystore_pass stuff unless you want mTLS. Also, you probably won't need the timeout configurations. I've tested behind a proxy so this also contains some additional config support for http_proxy (commented, left in for educational purposes). Adapt to your tastes.
Run it:
0 $ CLIENT_CERT_KEYSTORE_PASS="$PASSWORD" java -jar karate.jar -o /tmp/karate-out test.feature
17:34:41.614 [main] INFO com.intuit.karate - Karate version: 1.2.1.RC1
17:34:42.076 [main] DEBUG com.intuit.karate.Suite - [config] karate-config.js
17:34:43.942 [main] DEBUG com.intuit.karate - key store key count for secrets/client-cert-keystore.p12: 1
17:34:44.535 [main] DEBUG com.intuit.karate - request:
1 > GET https://httpbin.org/anything/1
1 > x-jwt: eyJhbGciO...
1 > Host: httpbin.org
1 > Connection: Keep-Alive
...
---------------------------------------------------------
feature: test.feature
scenarios: 1 | passed: 1 | failed: 0 | time: 1.7300
---------------------------------------------------------
17:34:46.577 [main] INFO com.intuit.karate.Suite - <<pass>> feature 1 of 1 (0 remaining) test.feature
Karate version: 1.2.1.RC1
======================================================
elapsed: 4.74 | threads: 1 | thread time: 1.73
features: 1 | skipped: 0 | efficiency: 0.36
scenarios: 1 | passed: 1 | failed: 0
======================================================
HTML report: (paste into browser to view) | Karate version: 1.2.1.RC1
file:///tmp/karate-out/karate-reports/karate-summary.html
===================================================================
0 $
Note that I'm by no means a Karate expert nor a JavaScript or Java programmer. So this might well not be your idiomatic Karate/JS/Java code. ;-)
I have a very compact ReasonReact reducer component, which has a component, initialState, reducer, action, and render function as follows:
type actions =
| DoNothing;
let component = ReasonReact.reducerComponent("ShowHide");
let make = (~name, _children) => {
...component,
initialState: () => 0, /* here, state is an `int` */
reducer: (action, state) =>
switch action {
| DoNothing => ReasonReact.NoUpdate;
},
render: (self) => {
let greeting =
"Hello: " ++ name ++ ". You've clicked the button " ++ string_of_int(self.state) ++ " time(s)!";
<div></div>
}
};
I am trying to render in my app.re file using the ReactDOMRe.renderToElementWithId function:
<div id = "RenderShowHide"></div>
ReactDOMRe.renderToElementWithId(<ShowHide name="hello" />, "RenderShowHide")
However, the Reason/Bucklescript compiler is complaining as follows:
This has type:
(ReasonReact.reactElement, string) => unit
But somewhere wanted:
at <anonymous>actElement
However, I am having a difficulty what an actElement is. Any suggestions as to what an actElement is, and how I can go around fixing the above, would be more than appreciated. Thank you.
I tried the repo you posted: https://github.com/CharlieGreenman/reason-react-razroo
npm install
bsb -make-world
I got the following error message:
We've found a bug for you!
/Users/yawar/src/reason-react-razroo/src/app.re 16:9-40
14 ┆ </div>
15 ┆ <p className="App-intro">
16 ┆ ReactDOMRe.renderToElementWithId(<ShowHide name="hello"/>, "index")
17 ┆ (ReasonReact.stringToElement("To get started, edit"))
18 ┆ <code> (ReasonReact.stringToElement(" src/app.re ")) </code>
This has type:
(ReasonReact.reactElement, string) => unit
But somewhere wanted:
ReasonReact.reactElement
It looks like something in your build tooling was swallowing part of your error message. The main problem is on l. 16; if you get rid of that line the error will go away. If you want to render the ShowHide component, then change the line to just the literal JSX, not the call to ReactDOMRe.renderToElementWithId.
I have two more general recommendations; try to stick with the bsb-provided React skeleton project unless you are expert-level, because it's way simpler and much better supported:
bsb -init my-project -theme react
And, try to post the entire error message for future errors, starting from the 'We've found a bug for you!' line. That will help diagnose a lot.
I have a chrome packaged app that also includes a PNaCl/NaCl C++ module, as well as some data files which the NaCl module needs to read in. However, I am not able to get it to read in the files.
I set it up according to all the documentation and official examples that I could find, as well as the answer to: How to include a data file in a chrome app for a native client module to read
The nacl_io demo that comes with the SDK is able to do this, but it is in C, not C++.
I came up with a simple example, which I'll post below. When you press the button on the page, the NaCl module should load the first character of test.txt and show it. As of now, it always just responds with "-100" (the error value I put in), meaning that it could not open the file, rather than with the first character of the file.
Can anyone suggest some changes that would allow it to work correctly and load the file?
In order to run it, on the Mac at least, I use this command, with all the files in the ./file-test dir:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --load-and-launch-app=./file-test
Note that if you try to use this, you will most likely need to change the NACL_SDK_ROOT path in the makefile.
file_test.cc
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "nacl_io/nacl_io.h"
#include "sys/mount.h"
class FileTestInstance : public pp::Instance {
public:
explicit FileTestInstance(PP_Instance instance) : pp::Instance(instance)
{
// initialize nacl file system
nacl_io_init_ppapi(instance, pp::Module::Get()->get_browser_interface());
// mount the http root at /http
mount("", "/http", "httpfs", 0, "");
}
virtual ~FileTestInstance() {}
// Receive message from javascript
virtual void HandleMessage(const pp::Var& var_message) {
// Open and load from the file
int c;
FILE *file;
file = fopen("/http/test.txt", "r");
if (file) {
c = getc(file);
fclose(file);
} else {
c = -100;
}
// Send message to JavaScript
pp::Var var_reply(c);
PostMessage(var_reply);
}
};
class FileTestModule : public pp::Module {
public:
FileTestModule() : pp::Module() {}
virtual ~FileTestModule() {}
virtual pp::Instance* CreateInstance(PP_Instance instance) {
return new FileTestInstance(instance);
}
};
namespace pp {
Module* CreateModule() {
return new FileTestModule();
}
} // namespace pp
index.html
<!DOCTYPE html>
<html>
<head>
<title>File Test</title>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<h1>File Test</h1>
<input type="button" id="test" name="test" value="Test" />
<p><b>Output:</b><p>
<div id="output">
</div>
<p>
<div id="listener">
<embed id="file_test" width=0 height=0 src="file_test.nmf" type="application/x-pnacl" />
</div>
</p>
</body>
</html>
script.js
// outgoing messages
function postMessage(message) {
var nacl_module = document.getElementById('file_test')
nacl_module.postMessage(message);
}
// incoming messages
function handleMessage(message_event) {
var outputDiv = document.getElementById('output');
outputDiv.textContent = message_event.data;
}
// button action
function buttonClicked() {
postMessage("file");
}
// set up
function init() {
// add listener to nacl module
var listener = document.getElementById('listener');
listener.addEventListener('message', handleMessage, true);
// add action to button
document.getElementById("test").onclick = buttonClicked;
}
window.onload = init;
main.js
/**
* Listens for the app launching then creates the window
*/
chrome.app.runtime.onLaunched.addListener(function() {
// Center window on screen.
var screenWidth = screen.availWidth;
var screenHeight = screen.availHeight;
var width = 600;
var height = 600;
chrome.app.window.create('index.html', {
id: "File-TestID",
bounds: {
width: width,
height: height,
left: Math.round((screenWidth-width)/2),
top: Math.round((screenHeight-height)/2)
}
});
});
file_test.nmf
{
"program": {
"portable": {
"pnacl-translate": {
"url": "file_test.pexe"
}
}
}
}
Makefile
#
# Get pepper directory for toolchain and includes.
#
# If NACL_SDK_ROOT is not set, then assume where it can be found.
#
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
NACL_SDK_ROOT ?= $(abspath $(dir $(THIS_MAKEFILE))../../nacl_sdk/pepper_33)
# Project Build flags
WARNINGS := -Wno-long-long -Wall -Wswitch-enum -pedantic -Werror
CXXFLAGS := -pthread -std=gnu++98 $(WARNINGS)
#
# Compute tool paths
#
GETOS := python $(NACL_SDK_ROOT)/tools/getos.py
OSHELPERS = python $(NACL_SDK_ROOT)/tools/oshelpers.py
OSNAME := $(shell $(GETOS))
RM := $(OSHELPERS) rm
PNACL_TC_PATH := $(abspath $(NACL_SDK_ROOT)/toolchain/$(OSNAME)_pnacl)
PNACL_CXX := $(PNACL_TC_PATH)/bin/pnacl-clang++
PNACL_FINALIZE := $(PNACL_TC_PATH)/bin/pnacl-finalize
CXXFLAGS := -I$(NACL_SDK_ROOT)/include -I$(NACL_SDK_ROOT)/include/pnacl
LDFLAGS := -L$(NACL_SDK_ROOT)/lib/pnacl/Release -lppapi_cpp -lppapi -lnacl_io
#
# Disable DOS PATH warning when using Cygwin based tools Windows
#
CYGWIN ?= nodosfilewarning
export CYGWIN
# Declare the ALL target first, to make the 'all' target the default build
all: file_test.pexe
clean:
$(RM) file_test.pexe file_test.bc
file_test.bc: file_test.cc
$(PNACL_CXX) -o $# $< -O2 $(CXXFLAGS) $(LDFLAGS)
file_test.pexe: file_test.bc
$(PNACL_FINALIZE) -o $# $<
test.txt
AAAA
From Sam Clegg on the native-client-discuss list:
"I think the main problem you have is that you are trying to use nacl_io on the main thread. nacl_io, like the blocking PPAPI interfaces on which it is mostly based, will only work on background threads where blocking calls are allowed. See:
https://developer.chrome.com/native-client/devguide/coding/nacl_io."
"Try running your code on a separate thread. One easy way to do this is to use the ppapi_simple library."
Using this advice, and also looking at the examples using_ppapi_simple, flock, and earth, that are included with the SDK, I was able to make a working version:
file_test.cc
#include <stdio.h>
#include "sys/mount.h"
#include <ppapi/cpp/var.h>
#include "ppapi_simple/ps_main.h"
#include "ppapi_simple/ps_event.h"
#include "ppapi_simple/ps_interface.h"
int file_test_main(int argc, char* argv[]) {
PSEventSetFilter(PSE_ALL);
// mount the http root at /http
mount("", "/http", "httpfs", 0, "");
while (true) {
PSEvent* ps_event;
// Consume all available events
while ((ps_event = PSEventWaitAcquire()) != NULL) {
// handle messages from javascript
if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) {
// Convert Pepper Simple message to PPAPI C++ vars
pp::Var var_message(ps_event->as_var);
// process the message if it is a string
if (var_message.is_string()) {
// get the string message
std::string message = var_message.AsString();
// handle message
if (message == "file") {
// Open and load from the file
int c;
FILE *file;
file = fopen("/http/test.txt", "r");
if (file) {
c = getc(file);
fclose(file);
} else {
c = -100;
}
// Send response back to JavaScript
pp::Var var_reply(c);
PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), var_reply.pp_var());
}
}
}
PSEventRelease(ps_event);
}
}
return 0;
}
/*
* Register the function to call once the Instance Object is initialized.
* see: pappi_simple/ps_main.h
*/
PPAPI_SIMPLE_REGISTER_MAIN(file_test_main)
In addition, it is necessary to add -lppapi_simple to LDFLAGS in Makefile.
It would also be possible to do this handling the threads oneself, rather than using ppapi_simple, which can be seen in nacl_io_demo which is included with the SDK.
I'm fairly new to Scala and need to build a really simple command line parser which provides something like the following which I created using JRuby in a few minutes:-
java -jar demo.jar --help
Command Line Example Application
Example: java -jar demo.jar --dn "CN=Test" --nde-url "http://www.example.com" --password "password"
For usage see below:
-n http://www.example.com
-p, --password set the password
-c, --capi set add to Windows key-store
-h, --help Show this message
-v, --version Print version
Scallop looks like it will do the trick, but I can't seem to find a simple example that works! All of the examples I've found seem to be fragmented and don't work for some reason or other.
UPDATE
I found this example which works, but I'm not sure how to bind it into the actual args within the main method.
import org.rogach.scallop._;
object cmdlinetest {
def main(args: Array[String])
val opts = Scallop(List("-d","--num-limbs","1"))
.version("test 1.2.3 (c) 2012 Mr Placeholder")
.banner("""Usage: test [OPTION]... [pet-name]
|test is an awesome program, which does something funny
|Options:
|""".stripMargin)
.footer("\nFor all other tricks, consult the documentation!")
.opt[Boolean]("donkey", descr = "use donkey mode")
.opt("monkeys", default = Some(2), short = 'm')
.opt[Int]("num-limbs", 'k',
"number of libms", required = true)
.opt[List[Double]]("params")
.opt[String]("debug", hidden = true)
.props[String]('D',"some key-value pairs")
// you can add parameters a bit later
.args(List("-Dalpha=1","-D","betta=2","gamma=3", "Pigeon"))
.trailArg[String]("pet name")
.verify
println(opts.help)
}
}
Well, I'll try to add more examples :)
In this case, it would be much better to use ScallopConf:
import org.rogach.scallop._
object Main extends App {
val opts = new ScallopConf(args) {
banner("""
NDE/SCEP Certificate enrollment prototype
Example: java -jar demo.jar --dn CN=Test --nde-url http://www.example.com --password password
For usage see below:
""")
val ndeUrl = opt[String]("nde-url")
val password = opt[String]("password", descr = "set the password")
val capi = toggle("capi", prefix = "no-", descrYes = "enable adding to Windows key-store", descrNo = "disable adding to Windows key-store")
val version = opt[Boolean]("version", noshort = true, descr = "Print version")
val help = opt[Boolean]("help", noshort = true, descr = "Show this message")
}
println(opts.password())
}
It prints:
$ java -jar demo.jar --help
NDE/SCEP Certificate enrollment prototype
Example: java -jar demo.jar --dn CN=Test --nde-url http://www.example.com --password password
For usage see below:
-c, --capi enable adding to Windows key-store
--no-capi disable adding to Windows key-store
--help Show this message
-n, --nde-url <arg>
-p, --password <arg> set the password
--version Print version
Did you read the documentation? It looks like all you have to do is call get for each option you want:
def get [A] (name: String)(implicit m: Manifest[A]): Option[A]
It looks like you might need to provide the expected return type in the method call. Try something like this:
val donkey = opts.get[Boolean]("donkey")
val numLimbs = opts.get[Int]("num-limbs")
If you're just looking for a quick and dirty way to parse command line arguments, you can use pirate, an extremely barebones way to parse arguments. Here is what it would look like to handle the usage you describe above:
import com.mosesn.pirate.Pirate
object Main {
def main(commandLineArgs: Array[String]) {
val args = Pirate("[ -n string ] [ -p string ] [ -chv ]")("-n whatever -c".split(" "))
val c = args.flags.contains('c')
val v = args.flags.contains('v')
val h = args.flags.contains('h')
val n = args.strings.get("n")
val p = args.strings.get("p")
println(Seq(c, v, h, n, p))
}
}
Of course, for your program, you would pass commandLineArgs instead of "-n whatever -c".
Unfortunately, pirate does not yet support GNU style arguments, nor the version or help text options.
I'm writing a (ever larger) set of unit tests using Coffeescript and node.js. I build the files using the coffee "watch" option (-w)
coffee -w -b -c -o web/ src/
My problem is that running the unit tests takes 20 secs (I'm assuming for the compile to .js).
If possible, I'd like automatically run the unit tests on a (compiled .js) file change, which would eliminate the long wait for the results.
My current Cakefile:
fs = require 'fs'
{print} = require 'sys'
{spawn, exec} = require 'child_process'
build = (watch, callback) ->
if typeof watch is 'function'
callback = watch
watch = false
options = ['-c', '-b', '-o', 'web', 'src']
options.unshift '-w' if watch
coffee = spawn 'coffee', options
coffee.stdout.on 'data', (data) -> print data.toString()
coffee.stderr.on 'data', (data) -> print data.toString()
coffee.on 'exit', (status) -> callback?() if status is 0
task 'test', 'Run the test suite', ->
build ->
require.paths.unshift __dirname + "/lib"
{reporters} = require 'nodeunit'
process.chdir __dirname
reporters.default.run ['test']
Take a look at the Cakefile for my connect-assets project: https://github.com/adunkman/connect-assets/blob/master/Cakefile
It's a bit more complex than sstephenson's (which I assume your example is derived from), but it shows how you can watch a directory of files for changes and respond to those changes by re-running tests.