Skip to main content

How To Setup Vagrant Android/phonegap Build Env

this is a brain dump of something I did and I want documented somewhere.

first install vagrant, I won't go over it.

then do:

vagrant init

then edit the generated Vagrant file, I changed this:

# use ubuntu trusty = "ubuntu/trusty64"

# 1GB or ram
config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"

# provision all the needed packages, I'm using it to build a phonegap app
# that's why I install all the npm stuff
config.vm.provision "shell", inline: <<-SHELL
  sudo apt-get update
  sudo apt-get install -y ant lib32ncurses5 lib32stdc++6 lib32z1 npm build-essential git nodejs-legacy openjdk-7-jdk
  sudo npm install -g "phonegap@3.5.0-0.21.14" bower grunt-cli


vagrant up
vagrant ssh

inside the vm:

tar -xzf android-sdk_r24.0.2-linux.tgz

echo "export PATH=\$PATH:\$HOME/android-sdk-linux/tools:\$HOME/android-sdk-linux/platform-tools:\$HOME/android-sdk-linux/build-tools/19.1.0" >> $HOME/.bashrc

android update sdk -a --no-ui --filter "platform-tools"
android update sdk -a --no-ui --filter "android-19"
android update sdk -a --no-ui --filter "sys-img-armeabi-v7a-android-19"
android update sdk -a --no-ui --filter "build-tools-19.1.0"
android create avd --name myandroid -t "android-19"

I installed the version 19 of the sdk because of project requirements, feel free to install something more up to date, to see what you can install you can run:

android list sdk --extended

Youtube Video to Audio Download

this is a reminder for myself in the future, I tend to watch/listen to talks when I'm doing something like cooking, cleaning.

but when I'm outside I want to take my music player and the talks tend to be on youtube, so normally I search for "youtube to mp3" and there's some ad ridden site that does that (sometimes)

but today I wanted to learn how to do it from the cli and here is the post that explains it so I can come back int he future to copy paste the commands.

first install youtube-dl and ffmpeg:

sudo yum install youtube-dl ffmpeg

then download your video:


then extract the audio from the video:

ffmpeg -i Music\ Theory\ and\ Performance\ Analysis\ with\ Sebastian\ and\ Czerny-06h21nBqwec.webm -strict experimental -acodec vorbis -ab 128k -vn ./output.ogg

then do whatever you want with the video and the audio

Overtone, clojure, jackd, alsa in ubuntu 14.10

ok, this is a small dump of what I did, I'm surely missing some stuff and surely it won't work as is for you, but maybe it does and you are as happy as I'm right now generating noise (not as happy as anyone close to me at this moment).

all the things I think I installed:

sudo apt install alsa-tools alsaplayer-jack alsa-utils pulseaudio-module-jack supercollider jack-tools fftw3 qjackctl openjdk-8-jdk

yes, you can see the desperation there with some stuff that may not make sense, but at least it works..

you need to install leiningen, it's really easy and it has good instructions on the site so I won't go into details, just follow the instructions here:

then I follow the instructions from overtone's wiki here:

before starting our repl and rocking like a hurricane we need to kill pulseaudio and start jack, it sounds easier than it is because pulseaudio just won't stay dead :(

the way I found to make it work was to edit pulseadio client.conf:

sudo vim /etc/pulse/client.conf

uncomment the line (remove the semicolon):

; autospawn = yes

and leaving it like this:

autospawn = no

I added myself to the audio group, not sure if it's required but just in case:

sudo adduser $USER audio

for this to take effect you need to logout and login again, to make sure you have the group, open a terminal and run:


you should see audio between some other groups, if you can't see it try rebooting or replacing $USER with your actual username in the adduser command.

now stop pulseaudio:

pulseaudio --kill

then start jackd, I tried all the combinations I could find on the internet without success, this is the one that worked for me:

jackd -R -d alsa -d hw:1

if that doesn't work try:

jackd -R -d alsa

or try the versions that are recommended on the overtone wiki:

jackd -r -d alsa -r 44100


jackd -r -d alsa -r 44100 -P

you can also try running qjackctl and play with the settings to see if you have luck.

now that we (hopefully) have jackd running, we can start playing with overtone, here is a small dump of a session:

lein new tutorial
cd tutorial

# add [overtone "0.9.1"] to :dependencies
vim project.clj

lein deps
lein repl

# inside the clojure repl
user=> (use '
user=> (definst foo [] (saw 220))
#<instrument: foo>
user=> (foo)
#<synth-node[loading]: user/foo 35>
user=> (kill 35)

when you are done you can stop jackd from the ui or from the shell however you started it and start again pulseaudio:

pulseaudio --start

some additional note, when I'm using jackd I can't set the volume from the media keys or the sound mixer in the top planel, I run:


press F6, select the output I'm using and change it with the keys (ESC to close).

hope it's useful for someone.

Making a Chat App with Erlang, Rebar, Cowboy and Bullet

this is a continuation of the post about erlang, cowboy and rebar.

let' start by adding the bullet dep:

{sub_dirs, ["rel"]}.

{deps, [
    {cowboy, "1.0.0", {git, "", {tag, "1.0.0"}}},
    {bullet, "0.4.1", {git, "", {tag, "0.4.1"}}}

now get the deps:

./rebar get-deps

add bullet as a dependency on

{application, disrupt,
  {description, ""},
  {vsn, "1"},
  {registered, []},
  {applications, [
  {mod, { disrupt_app, []}},
  {env, []}

register our bullet handler with cowboy:

start(_StartType, _StartArgs) ->
    {ok, ChannelPid} = disrupt_channel:new(),
    Dispatch = cowboy_router:compile([
        {'_', [
               {"/chat", bullet_handler, [{handler, disrupt_chat_handler}, {channel, ChannelPid}]},
               {"/ui/[...]", cowboy_static, {priv_dir, disrupt, "assets",
                                             [{mimetypes, cow_mimetypes, all}]}}
    {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
        {env, [{dispatch, Dispatch}]}

the important lines are:

{ok, ChannelPid} = disrupt_channel:new(),

were we create a new chat channel that will be passed to all disrupt_chat_handler instances here:

{"/chat", bullet_handler, [{handler, disrupt_chat_handler}, {channel, ChannelPid}]},

also note that I changed the path of the ui to /ui/... instead of it being at the root.

add the js libs we need to use bullet (yes, I could use bower, but let's keep it simple):

mkdir priv/assets/vendor
mkdir priv/assets/js
cp deps/bullet/priv/bullet.js priv/assets/vendor
wget -O priv/assets/vendor/jquery.js

we create a really simple chat page at index.html:

<!doctype html>
  <meta charset="utf-8">
  <script src="vendor/jquery.js"></script>
  <script src="vendor/bullet.js"></script>
  <script src="js/app.js"></script>
   body{ font-family: helvetica; color: #333; background-color: #fefefe;
    margin-left: 25%; width: 50%; }
   p, textarea{ padding: 0; }
   p, button, textarea{ width: 100%; margin: 1em 0; }
   label{ float: left; width: 45%; }
   input{ width: 50%; float: right; }
   input, textarea{ border: 1px solid #ddd; }
  <label for="input">Nickname</label>
  <input type="text" id="nick" value="anonymous"/>
  <textarea id="output" cols="80" rows="25"></textarea>
  <label for="input">Input</label>
  <input type="text" id="input"/>
  <button id="send">Send</button>

add code to handle the chat app on priv/assets/js/app.js:

/*globals $, document, window*/
function disruptApp(document, window, $) {
    'use strict';
    var input = document.getElementById('input'),
        output = document.getElementById('output'),
        nickInput = document.getElementById('nick'),
        send = document.getElementById('send'),


    function sendMessage(text) {
        var nick = getNick();
        connection.send(nick + ': ' + text);

    function onSendClicked() {
        var text = input.value.trim();

        if (text !== '') {

        input.value = '';

    function getNick() {
        var nick = nickInput.value.trim();

        if (nick === '') {
            return 'anonymous';
        } else {
            return nick;

    function notify(text) {
        var date = (new Date()).toLocaleString();
        output.innerHTML = output.innerHTML + '[' + date + '] ' + text + '\n';

    function onData(data) {

    send.addEventListener('click', onSendClicked);

    function start(url, options, notify, onData) {
        var connection = $.bullet(url, options);

        connection.onopen = function(){

        connection.onclose = connection.ondisconnect = function(){

        connection.onmessage = function(e){
            if ( === 'pong'){
            } else {

        connection.onheartbeat = function(){

        return connection;

    connection = start('ws://localhost:8080/chat', {}, notify, onData);

document.addEventListener("DOMContentLoaded", function() {
    'use strict';
    disruptApp(document, window, $);

we need a simple pubsub mechanism for channels, I won't explain it here in detail but you can read about erlang's gen_event behaviour which is the one that does all the work:


-export([new/0, subscribe/2, unsubscribe/2, send/2]).

-export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3,
%% API

new() -> gen_event:start_link().

subscribe(Channel, Pid) ->
    gen_event:add_handler(Channel, {disrupt_channel, Pid}, [Pid]).

unsubscribe(Channel, Pid) ->
    gen_event:delete_handler(Channel, {disrupt_channel, Pid}, [Pid]).

send(Channel, Event) ->
    gen_event:notify(Channel, Event).

-record(state, {pid}).
%% callbacks
init([Pid]) -> {ok, #state{pid=Pid}}.

handle_event(Msg, State=#state{pid=Pid}) ->
    Pid ! Msg,
    {ok, State}.

handle_call(_, State) -> {ok, ok, State}.

handle_info(_, State) -> {ok, State}.

code_change(_OldVsn, State, _Extra) -> {ok, State}.

terminate(_Reason, _State) -> ok.

the important part from the code above is the fact that we store the pid of the bullet handler on the gen_event instance so we can send the message back when we get notified:

handle_event(Msg, State=#state{pid=Pid}) ->
    Pid ! Msg,
    {ok, State}.

the bullet handler for the chat channel is:


-export([init/4, stream/3, info/3, terminate/2]).

-record(state, {channel}).

init(_Transport, Req, Opts, _Active) ->
    io:format("channel init ~p~n", [Opts]),
    {channel, ChannelPid} = lists:keyfind(channel, 1, Opts),
    disrupt_channel:subscribe(ChannelPid, self()),
    {ok, Req, #state{channel=ChannelPid}}.

stream(<<"ping">>, Req, State) ->
    io:format("ping received~n"),
    {reply, <<"pong">>, Req, State};

stream(Data, Req, State=#state{channel=ChannelPid}) ->
    io:format("message received ~s~n", [Data]),
    disrupt_channel:send(ChannelPid, {msg, self(), Data}),
    {ok, Req, State}.

info({msg, _Sender, Data}, Req, State) ->
    io:format("msg received ~p~n", [Data]),
    {reply, Data, Req, State}.

terminate(_Req, #state{channel=ChannelPid}) ->
    io:format("unsubscribing from channel~n"),
    disrupt_channel:unsubscribe(ChannelPid, self()),

we subscribe on init, unsubscribe on terminate, when we receive a ping message we reply it only to the sender with pong, if we receive something else we send the message to the channel so it gets sent to all subscribers, that get the message on the info function where they send it to the browsers.


rm -rf rel/disrupt && ./rebar compile generate


./rel/disrupt/bin/disrupt console

open http://localhost:8080/ui/index.html in two or more browsers and chat!


Serving Static Files with Erlang, Cowboy and Rebar, (raw material)

the "serving static files from a server" market needs some disruption, let's tackle that problem with some erlang.

create the folder:

mkdir disrupt
cd disrupt

get rebar:

chmod u+x rebar

generate app:

./rebar create-app appid=disrupt

add dependencies:

vim rebar.config

the file should contain the following code:

{deps, [
    {cowboy, "1.0.0", {git, "", {tag, "1.0.0"}}}

this tells to get cowboy 1.0.0 from git as dependency, let's fetch the dependencies:

./rebar get-deps

now let's serve some static files, open src/disrupt_app.erl, change the start function so it looks like this:

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        {'_', [
            {"/[...]", cowboy_static, {priv_dir, disrupt, "assets",
                [{mimetypes, cow_mimetypes, all}]}}
    {ok, _} = cowboy:start_http(http, 100, [{port, 8080}], [
        {env, [{dispatch, Dispatch}]}

the code above is taken from the static example from cowboy adapted to our needs:

docs here:

now make the priv/assets folder:

mkdir -p priv/assets

and put some content in the index file:

echo "hello static world" > priv/assets/index.html

let's make a release for the project, first create the release files:

mkdir rel
cd rel
../rebar create-node nodeid=disrupt
cd ..

edit rel/reltool.config, the line:

{lib_dirs, []},

should change to:

{lib_dirs, ["../deps"]},

add the line:

sub_dirs, ["rel"]}.

to the top of the rebar.config file

if not you get this error:

Command 'generate' not understood or not applicable

yay helpfulness!

now we are one confusing error closer to our goal, but now we get:

ERROR: generate failed while processing /home/mariano/tmp/disrupt/rel: {'EXIT',{{badmatch,{error,"disrupt: Missing application directory."}},

only because I've seen some other erlang projects I decided that this may be the solution:

mkdir -p apps/disrupt
cd apps/disrupt
ln -s ../../src
ln -s ../../ebin
ln -s ../../priv
cd ../..

we try again to make the release and we get the error again, nice!

for no apparent reason that the fact that I saw it on some other project, change the line on rel/reltool.config:

{lib_dirs, ["../deps"]},


{lib_dirs, ["../deps", "../apps"]},

trying again:

./rebar compile generate


let's try running it:

./rel/disrupt/bin/disrupt console

and what do we get? well, a crash! \o/:

Exec: /home/mariano/tmp/disrupt/rel/disrupt/erts-5.10.4/bin/erlexec -boot /home/mariano/tmp/disrupt/rel/disrupt/releases/1/disrupt -mode embedded -config /home/mariano/tmp/disrupt/rel/disrupt/releases/1/sys.config -args_file /home/mariano/tmp/disrupt/rel/disrupt/releases/1/vm.args -- console
Root: /home/mariano/tmp/disrupt/rel/disrupt
Erlang R16B03 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
=INFO REPORT==== 29-Aug-2014::11:49:32 ===
    application: disrupt
    exited: {bad_return,
    type: permanent
{"Kernel pid terminated",application_controller,"{application_start_failure,disrupt,{bad_return,{{disrupt_app,start,[normal,[]]},{'EXIT',{undef,[{cowboy_router,compile,[[{'_',[{\"/[...]\",cowboy_static,{priv_dir,disrupt,\"assets\",[{mimetypes,cow_mimetypes,all}]}}]}]],[]},{disrupt_app,start,2,[{file,\"src/disrupt_app.erl\"},{line,13}]},{application_master,start_it_old,4,[{file,\"application_master.erl\"},{line,269}]}]}}}}}"}

Crash dump was written to: erl_crash.dump
Kernel pid terminated (application_controller) ({application_start_failure,disrupt,{bad_return,{{disrupt_app,start,[normal,[]]},{'EXIT',{undef,[{cowboy_router,compile,[[{'_',[{"/[...]",cowboy_static

with all my time reading erlang crashes I see an undef there, it seems the app cant find cowboy_router:compile

but I have it on my deps..

well, let's put it on src/ applications for no other reason that I've seen people do that, the file should look like this:

{application, disrupt,
  {description, ""},
  {vsn, "1"},
  {registered, []},
  {applications, [
  {mod, { disrupt_app, []}},
  {env, []}

see the cowboy as last element on the applications list? that's what you should add.

let's try again:

./rebar compile generate

and I get:

<some output removed here>

==> rel (generate)
ERROR: generate failed while processing /home/mariano/tmp/disrupt/rel: {'EXIT',{{badmatch,{error,"Application cowboy is used in release \"disrupt\" and cannot be excluded"}},

"Application cowboy is used in release "disrupt" and cannot be excluded"

who told you to exclude it?

let's apply a technic we already used a lot before, let's make up reasons for what it may be failing, it says exclude there and I've seen a lot of exclude and include atoms in rel/reltool.config, maybe it's that?

let's layer some other technic I use a lot, let's try to make that file look as similar as another one I've seen that works, in this case it's the reltool.config file generated by riak_core rebar template:

ok, that one seems to have less stuff than ours, let's start commenting everything that looks different until we reach this point:

{sys, [
    {lib_dirs, ["../deps", "../apps"]},
        %{erts, [{mod_cond, derived}, {app_file, strip}]},
        %{app_file, strip},
        {rel, "disrupt", "1",
        {rel, "start_clean", "",
        {boot_rel, "disrupt"},
        {profile, embedded},
        %{incl_cond, exclude},
        {excl_archive_filters, [".*"]}, %% Do not archive built libs
    {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)",
        {excl_app_filters, ["\.gitignore"]},
        {app, sasl,   [{incl_cond, include}]},
        %{app, stdlib, [{incl_cond, include}]},
        %{app, kernel, [{incl_cond, include}]},
        {app, disrupt, [{incl_cond, include}]}

{target_dir, "disrupt"}.

{overlay, [
    {mkdir, "log/sasl"},
        {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
        {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
        {copy, "files/disrupt", "bin/disrupt"},
        {copy, "files/disrupt.cmd", "bin/disrupt.cmd"},
        {copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
        {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
        {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
        {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}

trying again:

rm -rf rel/disrupt
./rebar compile generate


now let's try running it:

./rel/disrupt/bin/disrupt console

the output we get:

Exec: /home/mariano/tmp/disrupt/rel/disrupt/erts-5.10.4/bin/erlexec -boot /home/mariano/tmp/disrupt/rel/disrupt/releases/1/disrupt -mode embedded -config /home/mariano/tmp/disrupt/rel/disrupt/releases/1/sys.config -args_file /home/mariano/tmp/disrupt/rel/disrupt/releases/1/vm.args -- console
Root: /home/mariano/tmp/disrupt/rel/disrupt
Erlang R16B03 (erts-5.10.4) [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)

that's what I would call a non crashing system, let's see if it works:

curl http://localhost:8080/index.html

we get:

hello static world


and that's how you serve a static file with erlang, cowboy and rebar :)

How To Build Twister Distributed Microblog on Ubuntu 13.10 (Saucy)

today I found out about twisterd and I wanted to give it a try (and reserve my username ;) so I tried and it took a while to get all the dependencies right, to avoid you the pain here is the guide.

take into account the comments in the script.

just a console dump, it should work just by pasting the commands in order:

mkdir twister
cd twister
sudo apt-get update
sudo apt-get install libssl-dev libdb-dev libdb++-dev libminiupnpc-dev libboost-all-dev build-essential git autoconf libtool
git clone
git clone
mkdir ~/.twister
mv twister-html ~/.twister/html
cd twister-core/libtorrent

# NOTE: the following command will fail with an error about boost, ignore
# it and run the following commands

./configure --enable-logging --enable-debug --enable-dht --with-boost-libdir=/usr/lib/x86_64-linux-gnu/
cd ../src
make -f makefile.unix
./twisterd -daemon -rpcuser=user -rpcpassword=pwd -rpcallowip=

BTW I'm @mariano :)

Install Immutant 1.0 full as a service on debian/ubuntu

Small dump with instructions to install immutant as a service on debian, ubuntu or derivatives.

I adapted the init script that comes with immutant to work on debian:

# install needed packages
sudo apt-get install wget unzip openjdk-7-jdk daemon

# create a dir to download files
mkdir ~/soft
cd ~/soft

# download immutant 1.0 full


# create user that will run the service
sudo adduser jboss-as

# create required folders
sudo cp -r immutant-1.0.0-full/jboss /usr/share/jboss-as/
sudo mkdir -p /var/log/jboss-as /var/run/jboss-as/

# set permissions
sudo chown -R jboss-as.jboss-as /var/log/jboss-as /var/run/jboss-as /usr/share/jboss-as/

# get and install init.d script
sudo wget -O /etc/init.d/jboss-as-standalone
sudo chmod u+x /etc/init.d/jboss-as-standalone

# register the service
update-rc.d jboss-as-standalone default

# start it
sudo /etc/init.d/jboss-as-standalone start

Viaje de navidad - Alghero

Cuando contamos que nos íbamos a Italia alguien dijo que en Serdegna estaba bueno Alghero.


Resultó que Alghero queda en la otra punta de la isla y que la isla no es algo así como pequeña.


Ida en colectivo el 24 de Diciembre, vuelta en tren el 26 (feriado).

La ciudad muy linda, la parte que da al mar esta muy buena.


Ahí el 25 como no teníamos nada que hacer decidimos caminar hasta las grutas de Neptuno, pero dado que era feriado no había ni medio de transporte y estábamos casi seguros que estaba cerrada.


Luego de 4 horas de caminar una media maratón (21 Km) por la ruta llegamos para enfrentarnos con la esperada realidad, las grutas estaban cerradas.

Durante la ida habíamos decidido, dado nuestro estado, intentar hacer dedo a la vuelta.


Primer intento... exitoso! nos volvimos con un italiano que vivía en una ciudad cercana hablando mi cocoliche aprendido días antes de un libro germanamente titulado "Italienish für Dummies".

En un principio nos iba a llevar hasta un pueblo cercano ya que le quedaba a contramano, pero se copó y nos llevo hasta la entrada de Alghero.


A la vuelta a las 6 de la mañana en la estación de trenes donde no había trenes (por el feriado) y donde el reloj de la estación no andaba (?) esperamos el colectivo con dos norteamericanas, que luego volvimos a encontrar en la estación del siguiente pueblo, que luego volvimos a encontrar en el tren a Cagliari, que luego volvimos a encontrar en la fila para abordar el vuelo...


En el avión les pregunto si por casualidad iban a Stuttgart así compartíamos ticket de tren, pero ya como lo dijo Heisenberg, al intentar intervenir en este proceso de coincidencias las interrumpí, iban a Freiburg.


Llegados al aeropuerto fui identificado como un potencial terrorista* y me solicitaron amablemente vaya a una fila donde todos los seres "aleatoriamente" seleccionados eran idénticos a mi (no solo notado por mi sino por el primero en la fila quien dijo "somos todos iguales!") excepto una blonda alemana que fue introducida en el set para romper el patrón** intentando así evitar que nuestras mentes inferiores identificaran el patrón.


Luego del chequeo de pasaporte nos damos con que habíamos perdido el ultimo colectivo a la ciudad mas cercana con estación de tren, por suerte unos amigos (Lucas y Liza) nos habían ido a buscar de semi sorpresa para invitarnos a cenar.

Y así termina el primer tramo del viaje de receso festivo invernal.


desde aca hasta alla

* esta parte la deduje yo

** esta parte también deducida por quien escribe

Viaje de navidad - Cagliari

Vengo un poquito atrasado con los posts, creo que se llama fiaca (o mas modernamente procrastinacion)


Para navidad buscamos con pato los lugares que no conozcamos, que sean templados y no caros que pudiéramos ir.


La búsqueda devolvió pocas opciones, una de las cuales nos pareció interesante, ir a la isla de serdenia.


El primer destino del viaje fue cagliari, donde nos hospedamos en un hotel con una vista muy linda pero bastante alejado de la ciudad y con alternativas de transporte publico reducidas, lo que nos "motivo" a caminar como unos desgraciados.


Lo mejor de la ciudad es el centro histórico, lo 'peor' es que no parece estar muy preparada para el turismo, puede haber sido porque fuimos en invierno pero era época de navidad así que debería ser un punto alto del invierno.


Dado todo el tiempo que paso la única anécdota que me acuerdo es ir a un restaurant donde el duenio era de ahí pero había hecho un intercambio en espania y había conocido a un marplatense.


El resultado es que nos atendieron re bien, le tuvimos que pedir que el ultimo plato nos traiga solo uno porque estábamos llenos y incluso con un solo plato no lo pudimos terminar.


Al final pago un shot para todos los que estaban en la barra "por los argentinos" y nos hizo un descuento porque no comimos todo :D.

clj-rhino - easy api to use rhino from clojure

a nice wrapper to handle rhino from clojure

project home:




the java api for rhino is not really nice


you can see the tests for some usage, here are some REPL examples:

user=> (require '[clj-rhino :as js])
user=> (def sc (js/new-safe-scope))
user=> (js/eval sc "1 + 1")
user=> (js/eval sc "a = 1 + 1")
user=> (js/get sc "a")
user=> (js/get sc "b")
#<UniqueTag org.mozilla.javascript.UniqueTag@172897f: NOT_FOUND>
user=> (js/undefined? (js/get sc "b"))
user=> (js/get sc "b" :w00t?)
user=> (js/defined? (js/get sc "b"))
user=> (js/set! sc "b" 42)
user=> (js/defined? (js/get sc "b"))
user=> (js/get sc "b" :w00t?)
user=> (js/eval sc "a = {name: 'spongebob'}")
#<NativeObject [object Object]>
user=> (js/get-in sc [:a :name])
user=> (js/get-in sc [:a :age])
#<UniqueTag org.mozilla.javascript.UniqueTag@172897f: NOT_FOUND>
user=> (js/get-in sc [:a :age] :dont-know)
user=> (def compiled-fun (js/compile-function sc "function (a, b) { return a + b; }" :filename "foo.js"))
user=> (js/set! sc "add" compiled-fun)
user=> (js/eval sc "add(1, 3)")


it seems the clojure people under this circumstances say something like:

Copyright © 2013 marianoguerra

Distributed under the Eclipse Public License, the same as Clojure.