PDA

View Full Version : Using SilkJS ORM and ExtJS Grids Together



mschwartz
12 Dec 2011, 7:13 AM
SilkJS is a very fast JavaScript based HTTP server (http://silkjs.org) that was designed to work with ExtJS. All code for the server is written in JavaScript, including almost the entirety of the server itself. You can check out the server's github repository: http://github.com/mschwartz/SilkJS and the repository for this demo at http://github.com/mschwartz/SilkJS-ext.

The SilkJS Install.js script creates a /usr/share/SilkJS directory with the HTTP server, library files, and default documentRoot. It also installs the main HTTP server in /usr/local/bin/httpd-silk.js.

Let's examine the SilkJS-ext repository first.

config.js:


Config.mysql = {
host: 'localhost',
user: 'mschwartz',
passwd: '',
db: 'ext3'
};
Config.documentRoot = 'docroot';


This file overrides the default Config object, specifying MySQL connection information, and a documentRoot for this example.

The actual Server-Side JavaScript code is implemented in < 60 lines of code, in Server.js:


SQL = new MySQL();
SQL.connect();


Schema.add({
name: 'Users',
fields: [
{ name: 'userId', type: 'int', autoIncrement: true, defaultValue: 0 },
{ name: 'username', type: 'varchar', size: 64, header: 'User Name', width: 128, autoExpand: true, editable: true },
{ name: 'password', type: 'varchar', size: 64, header: 'Password', serverOnly: true, editable: true },
{ name: 'email', type: 'varchar', size: 128, header: 'E-Mail Address', width: 128, editable: true },
{ name: 'created', type: 'int', defaultValue: function() { return parseInt(new Date().getTime() / 1000, 10); }, header: 'Created', width: 150, format: 'DateTime', editable: false }
],
primaryKey: 'userId',
indexes: [
'username',
'email'
]
});


function main_action() {
res.write([
'<!doctype html>',
'<html>',
' <head>',
' <title>Schema / ExtJS Demo</title>',
' <link rel="stylesheet" type="text/css" href="/ext-3.4.0/resources/css/ext-all.css" />',
' </head>',
' <body>',
' <script type="text/javascript" src="/ext-3.4.0/adapter/ext/ext-base.js"></script>',
' <script type="text/javascript" src="/ext-3.4.0/ext-all-debug.js"></script>',
' <script type="text/javascript" src="/client/Ext.ux.SchemaGrid.js"></script>',
' <script type="text/javascript" src="/client/ViewPort.js"></script>',
' <script type="text/javascript">',
' Schemas = ' + Json.encode(Schema.getSchemaExtJs()) + ';',
' </script>',
' </body>',
'</html>'
].join('\n'));
res.stop();
}
function Server_action() {
switch (req.data.method) {
case 'listUsers':
Json.success(Schema.list('Users', {}, function(o) {
o = Schema.clean(o);
}));
case 'editUser':
var example = Json.decode(req.data.example);
example.userId = example.userId || 0;
Schema.putOne('Users', example);
Json.success();
case 'deleteUsers':
var examples = Json.decode(req.data.examples);
forEach(examples, function(example) {
Schema.remove('Users',example);
});
Json.success();
}
}


The first thing it does is create a global MySQL object, named SQL. This SQL object is used by the ORM to generate queries. With the ORM, you rarely have to generate any queries yourself, and even then, the ORM does most of the work.

The Schema.add() method is called to install a "Schema" in the ORM. The Schema is defined as a JavaScript object. The definition above shows only a part of what's possible. The name member is the name of the table in the database. The fields array is an array of field definitions. The primaryKey member is the primary key of the table, and the indexes array is an array of additional indexes to be created for the table.

For the fields array, each item is an object with a few required members and some optional ones. The required ones are name, and type. If type is 'varchar', then size is also required. Only one of the fields may be autoIncrement: true, and is typically the primaryKey of the table as well. This autoIncrement field is also typically the ID field of ExtJS DataStores on the client-side.

The defaultValue member may be an expression or a function. The defaultValue member is used to set a default value for the field when creating a new entry in the database. As you can see in the example above, we have a 'created' field of type 'int' (a Unix-style timestamp) that has a defaultValue of a function that creates a Unix timestamp for the current time.

One additional members may be specified. Note the password field is marked serverOnly: true - this is to specify that the value of this field should not be sent to the client (we'll see this later).

Every other member of a field definition is ignored by the ORM, currently, and can be used for your application for whatever purposes you like. In this case, I specify information that will help us generate ExtJS Grid columns, and form fields for editing records. For example, username is autoExpand: true, and fields with header: 'some string' will show in the grids, etc.

The SilkJS HTTP server will call main_action() (if it is defined) if / is the specified URL. Here we simply write a typical HTML document to the browser. The output HTML looks like this:






<!doctype html>



<html>



<head>



<title>Schema / ExtJS Demo</title>



<link rel="stylesheet" type="text/css" href="/ext-3.4.0/resources/css/ext-all.css (http://localhost:9090/ext-3.4.0/resources/css/ext-all.css)" />



</head>



<body>



<script type="text/javascript" src="/ext-3.4.0/adapter/ext/ext-base.js (http://localhost:9090/ext-3.4.0/adapter/ext/ext-base.js)"></script>



<script type="text/javascript" src="/ext-3.4.0/ext-all-debug.js (http://localhost:9090/ext-3.4.0/ext-all-debug.js)"></script>



<script type="text/javascript" src="/client/Ext.ux.SchemaGrid.js (http://localhost:9090/client/Ext.ux.SchemaGrid.js)"></script>



<script type="text/javascript" src="/client/ViewPort.js (http://localhost:9090/client/ViewPort.js)"></script>



<script type="text/javascript">



Schemas = {"Users":{"name":"Users","fields":[{"name":"userId","type":"int","autoIncrement":true,"defaultValue":0},{"name":"username","size":64,"header":"User Name","width":128,"autoExpand":true,"editable":true},{"name":"password","size":64,"header":"Password","serverOnly":true,"editable":true},{"name":"email","size":128,"header":"E-Mail Address","width":128,"editable":true},{"name":"created","type":"int","header":"Created","width":150,"format":"DateTime","editable":false}],"primaryKey":"userId"}};



</script>



</body>



</html>


The time to execute the main_action() method is sub 1ms on my Core i5 760 CPU (2.8GHz). The entire page consists of 29 requests and about 1.5MB, and is fully loaded within .5 seconds (this is server and browser running on the same machine).

Of interest is the script tag with the 'Schemas = ' statement that makes our ORM schemas also available on the client side. Notice that the Schema class has a method called getSchemaExtJS() that translates our internal Schema format to one that is friendly to ExtJS. ALL Schemas defined will be present in this Schema object on the client side - each Schema is accessed via Schema[name] or Schema.name. For example, Schema.Users in our case.

Last in Server.js is the Server_action() method. This function is called by the HTTP server when /Server is the URI. The pattern is main_action() for /, foo_action() for /foo, bar_action() for /bar, and so on. If an action() method returns, the normal HTTP processing will occur - static files will be sent to the client, etc. We call res.stop() in main_action() to end the request so the normal processing does not occur.

The Server_action() method processes POST data from the client, which will be done via Ext.Ajax.request() or the internal DataStore load() logic. The params passed to Server_action() will contain at least one argument, 'method.' Server_action() switches on method and provides the basic CRUD type operations: listUsers, editUser, and deleteUser.

The listUsers method demonstrates how the ORM automatically generates lists suitable for use with ExtJS DataStores. The code is very short (3 lines) and executes very fast. The Schema.list() method takes 3 arguments: the name of the Schema/table, an "example" of what to list, and a function to call with each item of the result. In this case, we call Schema.clean(o), which removes all the serverOnly: true fields from the result. Thus the password field is not sent over the wire.

Json.success() takes an object and sends it to the client as a JSON string, with success: true added. This is so the sent JSON string conforms to what ExtJS DataStores expect. The Json.success() method calls res.stop(), so it is the last thing executed for the request.

The editUser method is passed a JSON encoded "example" and will either create a new record in the database from that example, or will update an existing record. Before I go on, I better explain the concept of examples and query by example.

The concept of Query by Example implemented in SilkJS' ORM is loosely based upon the concept described on this WWW page:


http://en.wikipedia.org/wiki/Query_by_Example.

All records in SilkJS and ExtJS are effectively just JavaScript objects, each member is a column/value pair from the database. But not every field from the database must exist in these objects! A partial record of this form is what I call an "example." If you call Schema.find(example), it will do a SELECT query based upon the fields you do specify in the example. For example, Schema.find({ username: '%msch%' }) will return an array of records from the DB that have a username with 'msch' somewhere in it. Schema.putOne({ username: 'mschwartz' }) will store a new record in the database with defaultValues for the other fields and username set to 'mschwartz'. The only way I can explain examples is with examples! :)

So editUser expects the client to build an example and send it over. If editing an existing record, the example will have a userId, and the record will be updated by Schema.putOne(). If adding a new record, the example will have no userId or a userId set to 0.

The deleteUsers method expects an array of examples. It calls Schema.remove(example) on each element of the array, which deletes the specified rows from the DB. Schema.remove() can be dangerous if you're not careful about the examples you give it. In this case, the array of examples would be some multiple selections on the grid on the client side.

The client-side ExtJS code is in docroot/client and accessed via /client/whatever paths in the HTML. In this case, we have just two files, Viewport.js and Ext.ux.SchemaGrid.js.

ViewPort.js is rather simple:



Ext.onReady(function() {
var items = [];
for (var i in Schemas) {
var schema = Schemas[i];
items.push({
xtype: 'ext-ux-schemagrid',
title: i,
schema: schema,
method: 'list'+i
});
}


new Ext.Viewport({
layout: 'fit',
items: [
{
xtype: 'tabpanel',
activeTab: 0,
items: items
}
]
})
});


It loops through the global Schemas and creats an items array of Panel configs, one Schema Grid per panal. The Ext.ViewPort is simply a Ext.TabPanel with one tab per panel.

The real work on the client is done by Ext.ux.SchemaGrid.js:


Ext.ux.SchemaGrid = Ext.extend(Ext.grid.GridPanel, {
initComponent: function() {
var me = this,
schema = me.initialConfig.schema,
fields = schema.fields,
id = Ext.id();


var store = new Ext.data.JsonStore({
root: 'list',
totalProperty: 'count',
idProperty: schema.primaryKey,
url: '/Server',
baseParams: {
method: me.initialConfig.method,
},
fields: fields,
listeners: {
load: function() {
me.updateToolbar();
}
}
});
var sm = new Ext.grid.RowSelectionModel({
singleSelect: false,
listeners: {
rowselect: function() {
me.updateToolbar();
},
rowdeselect: function() {
me.updateToolbar();
}
}
});
var columns = [],
fieldHash = {};
var autoExpandColumn = undefined;
for (var i in fields) {
var field = fields[i];
fieldHash[field.name] = field;
if (field.header && !field.serverOnly) {
if (field.autoExpand) {
autoExpandColumn = field.name;
}
columns.push({
header: field.header,
id: field.name,
format: field.format,
dataIndex: field.name,
width: field.width,
renderer: me.renderField
});
}
}
var config = {
sm: sm,
store: store,
columns: columns,
autoSizeColumns: true,
autoFill: true,
stripeRows: true,
trackMouseOver: true,
loadMask: true,
autoExpandColumn: autoExpandColumn,
buttonId: id
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
me.tbar = new Ext.Toolbar({
items: [
{
text: 'Add',
id: 'add-'+id,
handler: function() {
me.editRecord({});
}
},
{
text: 'Edit',
id: 'edit-'+id,
handler: function() {
me.editRecord(me.getSelectionModel().getSelected().data);
}
},
{
text: 'Delete',
id: 'delete-'+id,
handler: function() {
me.deleteRecords();
}
}
]
});
me.bbar = new Ext.PagingToolbar({
store: store,
pageSize: 25,
displayInfo: true,
displayMsg: 'Displaying ' + me.initialConfig.schema.name + '{0} - {1} of {2}',
beforePageText: 'Page',
emptyMsg: 'Nothing to Display'
});
Ext.ux.SchemaGrid.superclass.initComponent.apply(me, arguments);
me.on({
'render': function() {
store.reload();
}
});
me.fieldHash = fieldHash;
},
updateToolbar: function() {
var me = this,
id = this.buttonId;
var selections = me.getSelectionModel().getSelections();
if (selections.length) {
Ext.getCmp('delete-'+id).enable();
if (selections.length == 1) {
Ext.getCmp('edit-'+id).enable();
}
else {
Ext.getCmp('edit-'+id).disable();
}
}
else {
Ext.getCmp('delete-'+id).disable();
Ext.getCmp('edit-'+id).disable();
}
},
editRecord: function(record) {
var me = this,
id = Ext.id(),
schema = me.initialConfig.schema,
fields = schema.fields,
primaryKey = schema.primaryKey;
record = record || {};
var items = [];
for (i in fields) {
var field = fields[i];
if (field.header && field.editable) {
items.push({
xtype: 'textfield',
fieldLabel: field.header,
anchor: '100%',
id: field.name + '-' + id,
name: field.name,
value: record[field.name]
})
}
}
var dialog = new Ext.Window({
title: record[primaryKey] ? 'Edit Record' : 'Add Record',
width: 640,
height: 480,
modal: true,
layout: 'fit',
items: [
{
xtype: 'form',
frame: true,
labelWidth: 150,
items: items
}
],
buttonAlign: 'center',
buttons: [
{
text: 'OK',
handler: function() {
for (var i=0, len = items.length; i<len; i++) {
var cmp = Ext.getCmp(items[i].id);
if (cmp) {
record[items[i].name] = cmp.getValue();
}
}
Ext.Ajax.request({
url: '/Server',
params: {
method: 'editUser',
example: Ext.encode(record)
},
success: function(response) {
dialog.close();
me.store.reload();
}
});
}
},
{
text: 'Cancel',
handler: function() {
dialog.close();
}
}
]
});
dialog.show();
},
deleteRecords: function() {
var me = this,
records = me.getSelectionModel().getSelections(),
len = records.length;
var examples = [];
for (var i=0; i<len; i++) {
examples.push(records[i].data);
}
Ext.Ajax.request({
url: '/Server',
params: {
method: 'deleteUsers',
examples: Ext.encode(examples)
},
success: function(response) {
me.store.reload();
}
});
},
renderField: function(value, p, r) {
var me = this,
format = me.format;
switch (format) {
case 'DateTime':
return new Date(value*1000).toString();
default:
return value;
}
}
});


Ext.reg('ext-ux-schemagrid', Ext.ux.SchemaGrid);


I don't want to go into great detail about how this custom component works, but I will explain some of the things it does. I figure you all know how ExtJS works...




SchemaGrid extends Ext.grid.GridPanel and takes as additional config options a Schema, and a method. Those are set in the ViewPort.js code.

In the initComponent method, the URL of the JsonStore is set to /Server (so Server_action() is called on the SilkJS side), and baseParams method is set to the SchemaGrid's config.method, listUsers in our case. The JsonStore's fields are directly used from the Schema's fields array (no muss, no fuss!).

After setting up the SelectionModel, the code loops through the Schema's fields and generates column definitions for the grid. Those fields that have a header member and are not serverOnly will have columns displayed. The renderer for each column is a single method in the SchemaGrid class. The default action for our renderer is to render the created/timestamp in human readable format. An autoExpand column may be specified in the Schema definition, and it is handled automatically by this code.

When the JsonStore is loaded or the user clicks on items in the grid, the updateToolbar() method is called to conditionally enable and disable the edit and delete buttons. Basically, you can only edit if ONE row is selected, and you can't delete unless at least one row is selected.

The editRecord() method dynamically creates a form in a modal dialog window. If a record is passed in, the dialog title says "Edit record" otherwise "Add record." The dialog/form can be used to add new records (click the add button) or edit existing records (select a record, click edit button).

When the OK button is clicked on the dialog, the record (EXAMPLE) is updated with the form fields' values and the Ext.Ajax.request() made to call editUser to update the database.

The deleteRecords() method is called to delete one or more selected records in the grid. It creates the array of examples and calls Ext.Ajax.request() to invoke deleteUsers on the server side.

So let's see it in action!

The attached snapshot1.png is the application when it first starts.

The attached snapshot2.png is the application after I click the add button.

The attached snapshot3.png is the application after I've added a few records.

The attached snapshot4.png is the application after I've selected the bbb record and clicked edit.

mschwartz
12 Dec 2011, 7:20 AM
So what's so neat about this?

If you edit the Schema definition at the top of Server.js, those changes will immediately be reflected in the database when you either restart the server or the browser makes a new request (reload the browser, click the reload button in the paging toolbar).

What do I mean? If you change the size of password field to 128, the ORM will submit the appropriate ALTER TABLE statements to MySQL to change the size of the field. If you add a field to the fields array, it will be added to the table in the DB as well. If you delete a field, it will be deleted in the DB. If you rename a field, it will be renamed in the DB.

If you add a second Schema definition at the top of Server.js, the table will be created next access. If you reload your browser, you will see TWO tabs, one for each table, and the CRUD operations work on each.

Outside of the Schema definitions, which are compact in their own right, and the main_action() method that spews out the HTML for the app, the entire CRUD operations on the server side consist of under 20 lines of code.

The entire demo took me under 1 hour to create, all but 2-3 minutes was spent writing the client-side code.

edykstra
17 Feb 2013, 8:04 AM
Hey mschwartz,

I have used ExtJS a lot for complex desktop (RIA) type apps. These projects have been Rails on the server side. I see a LOT of code duplication to define models etc. on each side.

I have a new project being planed, and I will be doing both the server and client side code. While I can poke around a bit in Rails, there is MUCH for me to learn still, and I am not sure I want to climb that learning curve. The time may be better spent learning NodeJS and related technologies.

The app will have Responsive and Mobile interfaces for 'consumers' and a very complex RIA 'webtop' in ExtJS for 'administrators'. There will also be a very scaled down mobile interface for 'administrators' too.

I don't see the need for 'real-time' via sockets or Meteor etc., but some of the requirements make me think that is a possibility. Mostly, I see for those few instances, I could use ExtJS polling initially, but that may not scale well server side if this app becomes popular.

So, I guess I am asking if you could give an update to this thread. Is SilkJS still the way to go? Does it allow common model code for client/server side with ExtJS 4+? Any other suggestions working with ExtJS and server side JS?

Thanks!

Eric

mschwartz
17 Feb 2013, 2:23 PM
Hey mschwartz,

I have used ExtJS a lot for complex desktop (RIA) type apps. These projects have been Rails on the server side. I see a LOT of code duplication to define models etc. on each side.

I have a new project being planed, and I will be doing both the server and client side code. While I can poke around a bit in Rails, there is MUCH for me to learn still, and I am not sure I want to climb that learning curve. The time may be better spent learning NodeJS and related technologies.

The app will have Responsive and Mobile interfaces for 'consumers' and a very complex RIA 'webtop' in ExtJS for 'administrators'. There will also be a very scaled down mobile interface for 'administrators' too.

I don't see the need for 'real-time' via sockets or Meteor etc., but some of the requirements make me think that is a possibility. Mostly, I see for those few instances, I could use ExtJS polling initially, but that may not scale well server side if this app becomes popular.

So, I guess I am asking if you could give an update to this thread. Is SilkJS still the way to go? Does it allow common model code for client/server side with ExtJS 4+? Any other suggestions working with ExtJS and server side JS?

Thanks!

Eric

Hey Eric,

I'm absolutely using SilkJS with the ORM to implement the server side for ExtJS4.

https://github.com/mschwartz/SilkJS-extjs4

This demo implements Ext.ux.SchemaGrid class. The Grid implements a toolbar with CRUD buttons (new, edit, delete), and a popup window with form in it to edit records. The grid columns, form fields, and the database schema (table definitions) are all defined on the server side. If you edit the server side code and add columns, delete columns, etc., the database is altered (alter table) accordingly and you'll see new columns and form fields on the client side to match.

I've started with that demo to create two different ExtJS projects in the past few months.

If you have any issues getting any of this to run, PM me.

As for polling, I measure 0-4ms for some pretty complicated short poll requests using SilkJS on a micro type virtual machine instance. In theory, you should be able to get at least 250 clients polling at 1/second, 500 at 2/second, etc. Scale it like you would apache (server farm behind load balancer) and you can do a huge number of clients.

The http://silkjs.net/ WWW site is entirely served by SilkJS.

Cheers

natedsaint
18 Jun 2013, 10:13 AM
This... is awesome. =P~

One of our devs recently went to Fluent and everyone was talking about how great SilkJS was looking. After attempting to build something similar to this and getting flustered with all the dependency issues with NodeJS, this is going to get forked as soon as I have a minute.

Looking at the silkJS stuff I've seen, is there any planned support for a something like a freetds system to connect to a db like sybase?

edykstra
18 Jun 2013, 10:30 AM
NatedSaint,

I'll let Mike answer your specific question - but I thought I would chime in with some things I've learned since I last posted in this thread.

Overall - SilkJS is too new to have 'all the bells and whistles' that other frameworks have. But, really, that is a moot point, because you can use pretty much any library anyways. Mike has designed it extremely well, because there is a ton of power without complexity. Those 2 characteristics usually conflict. Not so in SilkJS.

Same goes for 'Best Practices' for ... say ... MVC. There is no documentation on how to do 'MVC' because there is no need. You are not constrained at all - and can arrange the folders/files the way you want to support your preference for MVC - or whatever.

It would be nice if there were more 'tools'. For example, in a Rails app, I am used to deploying with a simple; 'cap staging deploy:migrations'. Still no worries - I wrote my own using SilkJS. It isn't ready to post to GitHub, but will be soon.

I don't know Rails well at all. I know Node.js even less. But what I do know is that with SilkJS, I was productive very quickly. It only took a few days. It is a very shallow learning curve. Any road-blocks I hit, Mike helped me with very quickly.

SilkJS may currently have a small following, and that raises a concern that it is too obscure to support in the future if you need to assign a new developer to it etc. But again that is a moot point, because it is all JavaScript (except for the 'glue' to the OS) and a shallow learning curve like I mentioned. So, anyone can pick it up fast. Further, SilkJS is essentially a 'wrapper' around the Google V8 Javascript engine. So, Mike has been able to highly leverage a gazillion man-hours that Google invested and will continue to support. Smart. Sweet.

I was planning on learning Rails soon. Then Node. Now I am skipping both and just using SilkJS.

Cheers,

Eric

mschwartz
18 Jun 2013, 10:48 AM
This... is awesome. =P~

One of our devs recently went to Fluent and everyone was talking about how great SilkJS was looking. After attempting to build something similar to this and getting flustered with all the dependency issues with NodeJS, this is going to get forked as soon as I have a minute.

Looking at the silkJS stuff I've seen, is there any planned support for a something like a freetds system to connect to a db like sybase?

Thanks Eric for his validation that SilkJS does work. I'd add that the WWW server is written in JavaScript. The whole thing is ~1500 lines - hardly a lot to figure out.

I honestly never heard of freetds. However, I just found their site and looked at the documentation and it really shouldn't be difficult to implement the glue (between JS and C++) to make all that callable from JavaScript code.

The MySQL glue (using libmysqlclient) took me me less than a day. I implemented an Oracle API as well. https://github.com/mschwartz/SilkJS-Oracle. It's a self-installing plugin for SilkJS. See the build.js script for details.

The real trick with Oracle was the implementation of my ORM for it. The idea being that you use the ORM and you get JavaScript like objects with "some" backing store. The ORM being mostly transparent about using either MySQL or Oracle to actually store the objects. There were some portability issues, like Oracle having seriously restricted column name lengths and that sort of thing.

Unfortunately, I have no real experience with Sybase or MS SQL. It would be one thing to implement the glue, and a very different one to test it :) Being an open source project, though, help from the community makes it possible.

natedsaint
18 Jun 2013, 10:53 AM
Thanks for the prompt reply!

The reason I asked in the first place is that I've been debating writing some fast unit testing that actually hits our DB, but our back end is an interesting set of configurations (mod_perl and Sybase) although the front end is Ext 4. Thus, I've been trying to find a way to create JavaScript tests that run on the server with minimal success. I tried with node and a freetds library, but it had a ton of dependencies that wouldn't work with our configs.

I'll see if I can figure out what all needs to be done to tie the pieces together. I've just forked everything from github so if nothing else I can see what I can break on my own server!

mschwartz
18 Jun 2013, 10:58 AM
You can trivially invoke a perl program from SilkJS using process.exec().

http://silkjs.net/documentation/builtin/process#process-exec

Function: process.execSynopsisvar output = process.exec(command_line);
Execute a Unix command, returning the output of the command (it's stdout) as a JavaScript string.
DescriptionThis function calls popen() with the specified command line and reads its output until end of file. Under the hood, a fork() and exec() is performed which is not particularly fast. Still it can be useful to execute shell commands.
Arguments

{string} command_line - a Unix command to execute
Returns

{string} output - the output of the command executed.

Back to top (http://silkjs.net/documentation/builtin/process#)
Function: process.exec_result();Synopsisvar result = process.exec_result);
Returns the exit code of the last process.exec() call.
NOTE: The value is the low-order 8 bits of the argument the program run by exec() passed to _exit(2) or exit(3).
Returns

{int} result - exit code



Thanks for the prompt reply!

The reason I asked in the first place is that I've been debating writing some fast unit testing that actually hits our DB, but our back end is an interesting set of configurations (mod_perl and Sybase) although the front end is Ext 4. Thus, I've been trying to find a way to create JavaScript tests that run on the server with minimal success. I tried with node and a freetds library, but it had a ton of dependencies that wouldn't work with our configs.

I'll see if I can figure out what all needs to be done to tie the pieces together. I've just forked everything from github so if nothing else I can see what I can break on my own server!

natedsaint
18 Jun 2013, 11:03 AM
That's actually something I hadn't considered...
I could potentially just write a thin layer to hit the database and pass what I need. The real issue here is that I was seeing if I could get lift from developing a back-end ORM that allows us to create some schema definitions once rather than twice. While I could potentially have the perl just output the JSON-encoded return from the db, I'd still ultimately have to do the work there.

I'll look into how complex it is to get some "glue" built for communication between a SilkJS server and the db itself, but when it comes to c++ I'm hopeless :))

natedsaint
19 Jun 2013, 3:57 PM
I got it all set up but it looks like this repo (SilkJS-extjs4) is a little out of date. I'm getting a ton of errors whenever I try to start it. Originally, I got this:



4766 configbuiltin/include.js:61: Could not compile config.js
ReferenceError: Config is not defined
throw "Could not compile " + fn + "\n" + e.toString();
^
no stack trace available


So I noticed Config.mysql was being set immediately on line 10 of config.js, so I initialized it first, but then I got this:



4857 config
/usr/local/silkjs/modules/Schema.js:165: ReferenceError: Server is not defined
Server.addOnStart({name: 'Schemas', func: onStart});
^
ReferenceError: Server is not defined
at /usr/local/silkjs/modules/Schema.js:165:9
at /usr/local/silkjs/modules/Schema.js:908:6
at /usr/local/silkjs/modules/Schema.js:912:2
at /usr/local/silkjs/modules/Schema.js:915:2
at runScript (/usr/local/silkjs/builtin/require.js:12:20)
at require (/usr/local/silkjs/builtin/require.js:116:31)
at Server.js:11:11




I'd love to help out here but it looks like my lack of fundamental understanding of your framework is getting in my way. It seems as though the Schema require expects a global Server object but I don't see how or where that would have been initialized, etc. The README is completely empty, but I've run into this problem with a lot of node projects as well, my assumption is your code is out of date from the latest SilkJS build.

In either case this all looks promising! I'm looking forward to working with it!

mschwartz
19 Jun 2013, 4:05 PM
did you?

sudo make install

to install silkjs

Then you git clone the SilkJS-extjs4 repo somewhere else. I have something like:

/home/mschwartz/src/SilkJS
/home/mschwartz/src/SilkJS-extjs4

In the SilkJS-extjs4 directory is a config.js where you override the default configuration options installed with SilkJS.

Edit that. Make sure you have the db and credentials created in MySQL to match what you have in config.js.

In the SilkJS-extjs4 directory:

$ http-silk.js ./server.js

And then point your browser at http://localhost:9090/

mschwartz
19 Jun 2013, 4:08 PM
I realize it may not be obvious, but the simplest "server.js" type file you can make looks something like:


Config.documentRoot = 'path_to_your_document_root';


Then you put your files in "path_to_your_document_root" like you would with apache.

So assuming:

/home/mschwartz/src/SilkJS-test (where I make a test project)
/home/mschwartz/src/SilkJS-test/server.js (the one Config line above, documentRoot='docroot')
/home/mschwartz/src/SilkJS-test/docroot/ (directory for documentRoot)

Then inside docroot/, index.html served like apache would, docroot/foo/bar.html would be served for the url http://localhost/foo/bar.html, etc.

natedsaint
19 Jun 2013, 4:26 PM
Hmm, so in your example, your http-silk.js is the server file you've created?

I installed silkJS from source using github, checked out your source, and then ran this from the command-line:

silkjs server.js

(again, I'm thinking from the node model, so my assumption was server.js was the wrapper script for everything)

So now my assumption is that I need to make a wrapper script (in your example http-silk.js) and then feed it your specific server config?

mschwartz
20 Jun 2013, 5:33 AM
Hmm, so in your example, your http-silk.js is the server file you've created?

I installed silkJS from source using github, checked out your source, and then ran this from the command-line:

silkjs server.js

(again, I'm thinking from the node model, so my assumption was server.js was the wrapper script for everything)

So now my assumption is that I need to make a wrapper script (in your example http-silk.js) and then feed it your specific server config?

Here's a way to think about it.

SilkJS is like Perl in some respects. It's a command line program (written in C/C++). You give it a script on the command line and it loads it, runs it, then exits. The difference is SilkJS uses v8 and JavaScript as the language vs. the Perl language.

You can write a WWW server in pure Perl. I've done it at least once back in the day...

http-silk.js is a WWW server written in pure JavaScript. It's a program loaded and run by the silkjs program. The JavaScript code never exits because there's a while (true) loop at the end.

httpd-silk.js takes the name of a script on the command line that is the www application you want to run. You can name this script server.js, foo.js, myOwnGoodness.js or whatever. Your app script is going to inherit the basic things that http-silk.js provides. Like the global Config object. You can override the defaults in Config by assigning to it. It's just plain old JavaScript :)

httpd-silk.js uses what is called shebang. The first line of the script is #!/usr/local/bin/silkjs. This is not valid JavaScript, but the silkjs program replaces #! with // before running the first JavaScript file on the command line, effectively turning it into a valid comment.

The effect of shebang is that the Unix command line interpreter (bash, csh, whatever) sees the #! on the first line and passes the rest of the script to the program specified (/usr/local/bin/silkjs).

So you can use shebang in any JavaScript (for silkjs) you want to be able to run. That is, http-silk.js can be run:

$ http-silk.js

This is identical to:

$ /usr/local/bin/silkjs /usr/local/bin/http-silk.js

natedsaint
20 Jun 2013, 5:46 AM
I apologize for not making it clear enough.

I unfortunately have enough years of experience with Perl that I'm very familiar with the she-bang at this point. I think this issue is that my path was not set up correctly to use your http-server.js script included with SilkJS without calling the full path, and I was trying to figure out how things work directly.

So ultimately I think this works the way I expected (you call the interpreter and feed it the appropriate scripts) but I didn't realize when you built this particular setup it was harnessed through http-server.js. Looking at your server script this could be done as a one-off in another way, so I think I get it all now!

I'm tied up with something else at the moment but I'm excited about getting in there and messing with this later. Thanks for your prompt feedback! I wish any of my other threads was this productive in this forum!

mschwartz
20 Jun 2013, 6:07 AM
I apologize for not making it clear enough.

I unfortunately have enough years of experience with Perl that I'm very familiar with the she-bang at this point. I think this issue is that my path was not set up correctly to use your http-server.js script included with SilkJS without calling the full path, and I was trying to figure out how things work directly.

So ultimately I think this works the way I expected (you call the interpreter and feed it the appropriate scripts) but I didn't realize when you built this particular setup it was harnessed through http-server.js. Looking at your server script this could be done as a one-off in another way, so I think I get it all now!

I'm tied up with something else at the moment but I'm excited about getting in there and messing with this later. Thanks for your prompt feedback! I wish any of my other threads was this productive in this forum!

If you do:

$ sudo make install

in your SilkJS directory, where you built silkjs per the directions, then http-silk.js, silkjs, etc., should be on your path if you have /usr/local/bin on your path.

SilkJS installs itself in /usr/local/silkjs, so it's trivial to remove.

Other than /usr/local/silkjs, it creates a softlink in /usr/local/bin to the silkjs binary and to http-silk.js (which is actually linked to /usr/local/silkjs/httpd/main.js)

Hopefully that helps you fix the path issues.

natedsaint
20 Jun 2013, 5:03 PM
Bah, my pathing was screwed up by a bad config. I updated and re-sourced all my shell files and it's working now! Very interesting stuff.

Now that 4.2 has gone GPL I'm debating trying some things out with it. I'll update when I get something working. Thanks again!