Mostrando las entradas con la etiqueta commonjs. Mostrar todas las entradas
Mostrando las entradas con la etiqueta commonjs. Mostrar todas las entradas

2014-10-15

Módulos AMD en sandro

Ayer implementé en sandro módulos AMD, como los de requirejs. Esto debería hacer más fácil el uso de bibliotecas que usen commonjs. Acá les dejo el changeset donde está la implementación (con tests!).

Happy hacking,
Aureliano.

2014-03-30

sandro reboot

Hace ya casi 4 años empecé un proyecto para hacer más fácil hacer páginas web seguras al que llamé sandro. Ahora estoy empezando el mismo proyecto de nuevo, siguiendo con mismas ideas rectoras:

  • Un solo lenguaje en todos lados, JavaScript.
  • Templating client-side y server side compartiendo código.
  • Validación de datos en el cliente y server compartiendo código.
  • Seguro por defecto. El diseño del framework debería hacer que sea más fácil hacer aplicaciones seguras que inseguras.
La primera versión va a estar basada en rhino, igual que la vez anterior, pero va a haber algunos cambios que creo que son importantes:
  • No va a estar enfocado a correr en app-engine.
  • Para mantener la idea de que haya JavaScript en todos lados, el motor de base de datos elegido va a ser mongodb.
  • Para reconciliarme con la naturaleza asíncrona y mono-thread del browser, voy a cambiar el manejo de módulos de commonjs por requirejs en el cliente y una API compatible con requirejs en el server.
Si quieren ir viendo el avance del proyecto, está en un proyecto público de bitbucket.

Espero feedback y, sobre todo, pull requests.

Happy hacking,
Aureliano.

2010-01-10

Implementación de require en Rhino

Hoy estuve hackeando un toque y modifiqué el RhinoServlet que les mostré en este post para poder tener módulos en Javascript. El API que hice es un subset de lo definido en CommonJS. En particular, soporta require pero no los paths relativos de módulos (o sea, los que empiezan con . o ..). También le puse a todo un lock para que pueda usarse en un entorno concurrente, pero si un módulo tarda mucho en cargarse puede llegar a ser un problema.
Lo que me parece más interesante de mi prototipo es que se pueden agregar fácilmente nuevos loaders de módulos de JavasScript. Ya vienen por defecto loaders para cargar desde el classpath y desde la webapp, pero si quieren poder poner sus módulos en /WEB-INF/example, alcanza con agregar una línea en init.js:

require.loaders.push( require.stdLoader(servlet.servletContext, "/WEB-INF/example/"))

Bueno, basta de cháchara, acá está la versión modificada del RhinoServlet:
package aure.jslib;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

public class RhinoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Scriptable initialScope = null;

public void runJs(Context cx, String scriptLocation, Scriptable scope) throws IOException {

InputStream is = this.getClass().getResourceAsStream(scriptLocation);
if( is == null) {
is = this.getServletContext().getResourceAsStream("/WEB-INF/js/" + scriptLocation);
}
Reader jsReader = new InputStreamReader(is);

cx.evaluateReader(scope, jsReader, scriptLocation, 1, null);
}

public static void addJavaObjectToScope(Scriptable scope, String name, Object obj) {
ScriptableObject.putProperty(scope, name, Context.javaToJS(obj, scope) );
}

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
addJavaObjectToScope(scope, "servlet", this);
addJavaObjectToScope(scope, "config", config);

this.runJs(cx, "initRequire.js", scope);
this.runJs(cx, "init.js", scope);

this.initialScope = scope;
} catch (IOException e) {
throw new ServletException(e);
} finally {
Context.exit();
}
}

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Context cx = Context.enter();
try {
Scriptable scope = cx.initStandardObjects();
scope.setParentScope(this.initialScope);

addJavaObjectToScope(scope, "request", request);
addJavaObjectToScope(scope, "response", response);

this.runJs(cx, "start.js", scope);
} finally {
Context.exit();
}
}
}

Noten que toqué el init para que agregue el require y cambié la forma de levantar javascript. Por otro lado este es el javascript en initRequire.js, que tiene la implementación del require:

/* Based on http://www.davidflanagan.com/demos/require.js
* but heavily modified.
*
* Does not support relative paths (yet?).
* Can be invoked by several threads.
*
* Uses the servletContext and the classloader to fetch js modules.
*/
var logger = java.util.logging.Logger.getLogger("sarasa")

var require = function (id) {

try {

require.lock.lock() // Avoid threading issues while loading modules.

if (!require.modules[id]) {

var modText = require.loadMod(id)

var context = {}
var exports = {}
require.modules[id] = exports
var module = {
id : id
}

var f = new Function("require", "exports", "module", modText)
f.call(context, require, exports, module)
}

return require.modules[id]
} catch (x) {
throw new Error("Can't load module: " + id + ": " + x)
} finally {
require.lock.unlock()
}
}

require.loaders = []
require.modules = {}
require.lock = new java.util.concurrent.locks.ReentrantLock()
require.loadMod = function(id) {
var modText = null
var i = 0
while (modText == null && i < require.loaders.length) {
modText = require.loaders[i](id)
i++
}
return modText
}

// Setup loaders for files in the classpath, files in WEB-INF and files in the
// web-app (usually served to the client)
require.stdLoader = function(source, prepend) {
// Support functions
function readFromStream(stream) {
var io = java.io
var reader = new io.BufferedReader( new io.InputStreamReader(stream) )
var stringBuffer = new java.lang.StringBuffer()

var line = ""
while( line = reader.readLine()) {
stringBuffer.append(line)
stringBuffer.append("\n")
}

return stringBuffer.toString()
}

function path(id) {
return prepend + id + ".js"
}

var f = function(id) {
var stream = source.getResourceAsStream(path(id))
return stream ? readFromStream(stream) : null
}

return f

}

require.initStdLoaders = function() {
// classpath loader
require.loaders.push( this.stdLoader(servlet["class"], "/") )

// web-app context loader
require.loaders.push( this.stdLoader(servlet.servletContext, "/") )
}

require.initStdLoaders()

Empecé a hacer esto mirando el código en http://www.davidflanagan.com/demos/require.js pero al final quedó bastante distinto, sobre todo porque tiene la opción de tener muchos loaders.
Las cosas que me quedan para hacer son hacer que acepte paths relativos (que para que sea thread-safe hace falta que haga algunos truquitos) y hacer que en vez de usar
   var f = new Function("require", "exports", "module", modText)
f.call(context, require, exports, module)

para invocar el módulo haga algo usando el API de rhino para poder tener errores donde aparezca el nombre del módulo y el número de línea si hubo algún problema.

Escucho comentarios y sugerencias.

Happy hacking,
Aureliano.