Skip to content

Instantly share code, notes, and snippets.

@jtrussell
Last active December 26, 2015 00:29
Show Gist options
  • Save jtrussell/7064150 to your computer and use it in GitHub Desktop.
Save jtrussell/7064150 to your computer and use it in GitHub Desktop.
Proof of concept for pushing external method files back into 4D in real time while integrating with the vc-framework-v13 component.

Prepare your environment

  • Install Node.js
  • Install the grunt command line interface by opening a terminal and running npm install -g grunt-cli
  • Install the vc-framework-v13 component to your database
    • Follow the vc-framework-v13 usage instructions to get an export of host database's methods
    • IMPORTANT This strategy will kick off an infinite loop if you ultimately run the watch process on the same directory that vc-framework-v13 writes to. There are two options - either copy the vc_source contents somewhere else and watch that directory or disable the vc-framework-v13 component. For now we'll assume you have disabled the component after the initial export.
  • Copy the contents of On_Web_Connection_HOOK.4Dmethod above into a new method in your host database with the name On_Web_Connection_HOOK. If use a different name you'll want to update the grunt task validate_install.
    • Call the On_Web_Connection_HOOK method from the database method On Web Connection and pass it $1, and $2. I.e.:
// Inside On Web Connection
C_TEXT($1,$2)
On_Web_Connection_HOOK($1;$2)
  • Create a new directory called 4d-sync next to the location where the vc-framework-v13 component dumped your method definitions
  • Copy the package.json and gruntfile.js files below into your new 4d-sync directory
  • Make sure 4D and 4D HTTP Server are running
    • If your HTTP server is binding to an IP/hostname other than 'localhost' change the variable HOSTNAME_4D at the top of 4d-sync/gruntfile.js accordingly
    • If your HTTP Server is listening on a port other than 80 change the variable PORT_4D at the top of 4d-sync/gruntfile.js accordingly
  • Open a terminal and cd into 4d-sync then run the command: npm install && npm test

Doing useful things

Make sure 4D and 4D HTTP Server are running. Then open a terminal window and cd into the 4d-sync directory. Run the command npm 4d_sync. If no errors are displayed go forth and edit the files in vc_source, your changes will be synced back to 4D while the terminal window remains open.

Caveats and Current Limitations

The most common issue you may encounter is when trying to watch too many files, particularly on OSX the limit on opened files is low. See here for info on how to mitigate this.

Right now there is no built in fault tolerance or "offline" support. If 4D goes down or stops accepting HTTP requests your changes will not be synced.

There are no security checks - anyone who can see the IP:PORT you listen on can make changes to your methods.

This uses a naive approach for creating method paths from file names. If your 4D method has a complex path you may not be able to sync changes to it.

This is only meant as a proof of concept. It will alter your application and could possibly cause harm, there isn't much code here I'd suggest reading through to get a sense for what's happening before running it. please limit your use to a demo databases and always backup your work.

module.exports = function(grunt) {
// Edit these if your 4D HTTP server isn't listening on localhost:80
var HOSTNAME_4D = '192.168.1.170'
, PORT_4D = '80'
, PROTOCOL_4D = 'http'
, REST_ENDPOINT_4D = PROTOCOL_4D + '://' + HOSTNAME_4D + ':' + PORT_4D + '/_alter_method';
// Where the 4D method text files are in relation to this file
var VC_SOURCE = '../vc_source/**/*.txt';
var path = require('path')
, request = require('request');
grunt.initConfig({
watch: {
methods_4d: {
files: [VC_SOURCE]
}
}
});
grunt.event.on('watch', function(action, filepath) {
var methodName = getMethodNameFromFilePath(filepath)
, opts = {
method: 'deleted' === action ? 'DELETE' : 'POST',
uri: REST_ENDPOINT_4D + '/' + methodName
};
if('deleted' !== action) {
opts.body = grunt.file.read(filepath);
}
request(opts, function(err, response, body) {
if(err) {
grunt.log.error('Method ' + methodName + ' could not be ' + action);
} else {
grunt.log.ok('Method ' + methodName + ' was ' + action);
}
});
});
var getMethodNameFromFilePath = function(filepath) {
return path.basename(filepath, '.txt');
};
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('validate_install', function() {
var done = this.async();
var callback = function(err, response, body) {
if(err || response.statusCode !== 200) {
grunt.fail.fatal('Web hook appears to not be installed');
} else {
grunt.log.ok('Web hook install validated');
}
done();
};
request({
method: 'GET',
uri: REST_ENDPOINT_4D + '/On_Web_Connection_HOOK'
}, callback);
});
};
C_TEXT($1;$2;$url;$headerAndBody)
$url:=$1
$headerAndBody:=$2
If ($url="/_alter_method/@")
C_TEXT($methodPath)
$methodPath:=Replace string($url;"/_alter_method/";"")
C_TEXT($action)
Case of
: ($headerAndBody="GET @")
$action:="READ"
: ($headerAndBody="POST @")
$action:="UPDATE"
: ($headerAndBody="PUT @")
$action:="UPDATE"
: ($headerAndBody="DELETE @")
$action:="DELETE"
End case
C_TEXT($methodCode)
If ($action="READ")
METHOD GET CODE($methodPath;$methodCode)
WEB SEND TEXT($methodCode)
End if
If ($action="UPDATE")
WEB GET HTTP BODY($methodCode)
METHOD SET CODE($methodPath;$methodCode)
METHOD GET CODE($methodPath;$methodCode)
WEB SEND TEXT($methodCode)
End if
If ($action="DELETE")
// ... ?
End if
End if
{
"name": "4d-sync",
"description": "Simple strategy for sending 4D method text files back into a 4D database",
"version": "0.0.1",
"author": "Justin Russell <jus.russell@gmail.com>",
"private": true,
"main": "gruntfile.js",
"dependencies": {
"grunt": "~0.4.1",
"grunt-contrib-watch": "~0.5.3",
"request": "~2.27.0"
},
"scripts": {
"test": "grunt validate_install"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment