How to create worker with elm 0.17 - node.js

For elm 0.16, I just defined some ports
(that are just Signal of data) without main function and
used Signal.map to process data back and forth.
It is just for data processing called from nodejs (not from browser),
so I do not depend on Html module.
Now elm 0.17 has Cmd and Sub instead of Signal,
I cannot figure out how to do the same...
Can anybody give me an simplest example to do data
processing via ports exposed by worker initialization
with elm 0.17?
Here is my simplest example with Elm 0.16...
elm 0.16 code:
module Main where
import Signal
import String exposing (isEmpty, reverse)
-- input ports
port jsToElm : Signal String
-- output ports
port elmToJs : Signal String
port elmToJs
= Signal.map String.reverse jsToElm
javascript (es6) code:
/** main */
const Elm = loadElm('./index.js') // custom function to eval Elm code
const app = Elm.worker( Elm.Main, { jsToElm: ''})
app.ports.jsToElm.send('test')
app.ports.elmToJs.subscribe(( txt ) => {
console.log( txt )
})

Update
There is now a package that gives you the ability to create a worker. See lukewestby/worker
Original Answer
I was able to get a working example by creating an Html.App program. I can't find any way around the requirement of having a main function that results in a program that includes a view function, and I'm not alone in that confusion.
My example here is browser based, hopefully this fits back into your node-based version; I just haven't used the node version before.
Main.elm:
port module Main exposing (..)
import Json.Decode
import Json.Encode
import Html exposing (..)
import Html.App
import String exposing (isEmpty, reverse)
-- input ports
port jsToElm : (String -> msg) -> Sub msg
-- output ports
port elmToJs : String -> Cmd msg
type alias Flags = { jsToElm : String }
main =
Html.App.programWithFlags
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
type alias Model = { text : String }
init : Flags -> (Model, Cmd Msg)
init flags =
(Model flags.jsToElm, Cmd.none)
type Msg
= Reverse String
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Reverse str ->
let reversed = reverse str
in ({ model | text = reversed }, elmToJs reversed)
view : Model -> Html.Html Msg
view model =
text <| "text is: " ++ model.text
subscriptions : Model -> Sub Msg
subscriptions _ =
jsToElm Reverse
index.html:
<script type="text/javascript" src="Main.js"></script>
<script type="text/javascript">
var app = Elm.Main.fullscreen({
jsToElm: 'first'
});
window.setTimeout(function() {
app.ports.jsToElm.send('test');
}, 1);
app.ports.elmToJs.subscribe(function ( txt ) {
console.log( txt )
});
</script>
A couple takeaways:
As you can see, the simple mapping of Signals is gone and there is much more boilerplate for such a small example. The idea is that you set up your subscription to the port, then send a Cmd inside the update function when you want to send information back to Javascript.
This whole Cmd/Sub thing requires a Program and the only way to do that (that I could find) was to include [Html.App]. I have a hunch that in the future, the core Router type can be used to make a "headless" worker, but it seems like for now we're stuck with requiring the Html.App program and having to render a view.
You'll notice the setTimeout call when sending "test" to the port in javascript. I'm not sure why this kludge is necessary but others have seen it before as well

The upgrade guide goes into this, but I'll summarize here with references to your code:
Input Port:
-- 0.16
port jsToElm : Signal String
-- 0.17
port jsToElm : (String -> msg) -> Sub msg
Output Port:
-- 0.16
port elmToJs : Signal String
-- 0.17
port elmToJs : String -> Cmd msg
The new Elm guide explains exactly how to hook all of this up.

Related

How node-modules are implemented in node.js , how function implemention and creating instance are same in socket.io

const io = require('socket.io')();
// or
const Server = require('socket.io');
const io = new Server();
I am confused with following syntax
1) what is socket.io (is it a class or interface i checked the documention in node.js it showed be interface )
2)If it is interface i think we have to use class for using it (i didn't see any documentation to implement interface in javascript )
3) for understanding i tried the following (created 2 files)
example.js
module.exports = function () {
console.log("welcome to javascript");
}
use.js
let imp = require('./example')()
From following Link i have learned about require [https://nodejs.org/en/knowledge/getting-started/what-is-require/][1]
Now i am confused how socket is implemented and how the following 2 syntax's are equal
If socket is a function we can call by
const io = require('socket.io')();
If it is a class we generally do the following (create a instance and use it )
const Server = require('socket.io');
const io = new Server();
But in the documention they said both the syntax are equal how ?
One is for excuting the module.exports and one is used for instancing a class how both are equal
When you do this:
let x = require('someModule');
The value of x is whatever the module sets the module.exports value to in the module. It can literally be anything. It's entirely up to the module what they assign to that. It is often an object (with properties), but it can also be a function and sometimes it's even a function with properties.
To see what socket.io assigns to module.exports, we can go right to the source where we see:
module.exports = Server;
So, then we go look at what is Server and find this:
function Server(srv, opts){
if (!(this instanceof Server)) return new Server(srv, opts);
if ('object' == typeof srv && srv instanceof Object && !srv.listen) {
opts = srv;
srv = null;
}
opts = opts || {};
this.nsps = {};
this.parentNsps = new Map();
this.path(opts.path || '/socket.io');
this.serveClient(false !== opts.serveClient);
this.parser = opts.parser || parser;
this.encoder = new this.parser.Encoder();
this.adapter(opts.adapter || Adapter);
this.origins(opts.origins || '*:*');
this.sockets = this.of('/');
if (srv) this.attach(srv, opts);
}
From that, we can see that it is a constructor function that can be called as either:
const x = new Server(...);
or as:
const x = Server(...);
So, the answer is as follows:
require('socket.io') gives you a constructor function.
That constructor function can be caller either with new or just as a regular function and it adapts to return the same thing, a new server object.
So, when you do this:
const server = require('socket.io')();
it first gets the exported constructor function and then calls it and assigns the newly created object to the server variable.
1) what is socket.io (is it a class or interface i checked the documentation in node.js it should be interface )
socket.io on the server exports a constructor function for creating a server object. It can be called either as a regular function or with new.
2)If it is interface i think we have to use class for using it (i didn't see any documentation to implement interface in javascript )
It's a function. Javascript doesn't have a specific type called an interface. The socket.io code defines a constructor the older fashioned way (before the class keyword existed) though the outcome is largely the same. It defines a constructor function that, when called will create an object of the desired type.
3) Now i am confused how socket is implemented and how the following 2 syntax's are equal
In Javascript, these two different pieces of code create the same server object:
const io = require('socket.io');
const server = io();
and
const server = require('socket.io')();
The second is just a shortcut that doesn't assign the intermediate result from require('socket.io') to a variable, but rather just calls it directly. It will work like this only when the first function call returns a function. So, the first syntax gets a function, assigns it to the io variable and then calls it. The second syntax gets the function and immediately calls it without assigning it to a variable. In both cases the result of getting the function and calling it ends up in the server variable.

Crash when trying to load a file using Node's repl library

I am getting a crash at the indicated point below when the code attempts to load a file. The file contents are read and displayed on the console. But when the line
app.ports.receiveData.send(data.toString());
is (tried to) execute, the code crashes. I've attached the error message below the code. The JS code here is used to run some Elm code "headless". The app.ports... function call is supposed send data back to the Elm app. (The Elm Code is further down).
JS CODE:
const repl = require('repl');
const fs = require('fs')
// Link to Elm code
var Elm = require('./main').Elm;
var main = Elm.Tool.init();
// Eval function for the repl
function eval(cmd, _, _, callback) {
main.ports.put.subscribe(
function putCallback (data) {
main.ports.put.unsubscribe(putCallback)
callback(null, data)
}
)
main.ports.get.send(cmd)
}
main.ports.sendFileName.subscribe(function(data) {
var path = data
// console.log(path)
fs.readFile(path, { encoding: 'utf8' }, (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data.toString())
// Crash on next line !!!!
app.ports.receiveData.send(data.toString());
})
});
function myWriter(output) {
return output
}
console.log("\nType 'h' for help\n")
repl.start({ prompt: '> ', eval: eval, writer: myWriter});
Elm CODE
Here are the parts the Elm code that are relevant.
This code is called when the user wants to load a file.
loadFileCmd : String -> Cmd msg
loadFileCmd fileName =
sendFileName (E.string <| "./source/" ++ fileName)
These are the ports used to communicate with JS
port get : (String -> msg) -> Sub msg
port put : String -> Cmd msg
port sendFileName : E.Value -> Cmd msg
port receiveData : (E.Value -> msg) -> Sub msg
The get port listens for commands the user gives to the repl and gives these commands to Elm to process. The put port sends data that Elm computes to the repl.
The sendFileName port sends a file path to the repl. The receiveData port listens for the file contents. (But we crash before this can happen).
Here are the subscriptions:
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.batch [ Command.get Input, Command.receiveData ReceiveFileContents ]
ERROR MESSAGE:
repl.js:573
const lines = errStack.split(/(?<=\n)/);
^
TypeError: errStack.split is not a function
at Domain.debugDomainError (repl.js:573:30)
at Domain.emit (events.js:321:20)
at Domain.EventEmitter.emit (domain.js:485:12)
at Domain._errorHandler (domain.js:253:23)
at Object.<anonymous> (domain.js:156:29)
at process._fatalException (internal/process/execution.js:164:29)
From the comments, the answer was to replace the line
app.ports.receiveData.send(data.toString());
with
main.ports.receiveData.send(data.toString());
as the Elm app is named main, not app.

How to pass data from an native application (AdiIRC, an IRC client) to my Chrome extension?

I use Chrome 79 and and Nodejs 13, in windows 10.
I also use an IRC client, AdiIRC, to connect to some IRC channel. This program can call external programs with arguments via its scripting language (via Run and $exec ).
So, what I want to do is, probably using Native Messaging, whenever a specific message appears in the IRC channel,
it to be passed as argument from AdiIRC to my Chrome extension, most probably via something like: Run \path\to\some_executable.exe %message
where "some_executable.exe" would send the wanted message data (using stdout) to the extension .
So, my question is, how can I pass data from AdiIRC to my Chrome extension? Is it possible?
Am I entirely missing something regarding the Native Messaging concept, and so, what I want to do is possible via a different way??
Below is my effort, i.e. my Chrome extension and a native messaging host.
Note: in Google's Native Messaging documentation, at some point is says that:
Chrome starts each native messaging host in a separate process and
communicates with it using standard input (stdin) and standard output
(stdout).
So, from what I understand from that,
in order to be able to pass data from AdiIRC to my extension whenever needed, I need AdiIRC to manually launch another instance of NativeMessaging.js .
So, to test my theory, I launched node "C:\MDN\app\NativeMessaging.js" manually, outside of Chrome (after loading my extension in Chrome), and tried entering random test text inside it, but unfortunately they are not received in my extension's background.html page|Console .
background.js (of my Chrome extension) :
var port = chrome.runtime.connectNative('my_messaging_host');
port.onMessage.addListener((message) => {
console.log("Received: " + message);
});
Native Messaging host setup:
install-host.bat
REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\my_messaging_host" /ve /t REG_SZ /d "%~dp0my_messaging_host.json" /f
my_messaging_host.json
{
"name": "my_messaging_host",
"description": "Example host for native messaging",
"path": "C:\\MDN\\app\\my_messaging_host_win.bat",
"type": "stdio",
"allowed_origins": [
"chrome-extension://(my extension's hash)/"
]
}
my_messaging_host_win.bat
node "C:\MDN\app\NativeMessaging.js"
NativeMessaging.js ( NodeJS code from MDN )
#!/usr/local/bin/node
process.stdin.on('readable', () => {
var input = [];
var chunk;
while (chunk = process.stdin.read()) {
input.push(chunk);
}
input = Buffer.concat(input);
var msgLen = input.readUInt32LE(0);
var dataLen = msgLen + 4;
if (input.length >= dataLen) {
var content = input.slice(4, dataLen);
var json = JSON.parse(content.toString());
handleMessage(json);
}
});
function sendMessage(msg) {
var buffer = Buffer.from(JSON.stringify(msg));
var header = Buffer.alloc(4);
header.writeUInt32LE(buffer.length, 0);
var data = Buffer.concat([header, buffer]);
process.stdout.write(data);
}
process.on('uncaughtException', (err) => {
sendMessage({error: err.toString()});
});
Possible addition to NativeMessaging.js, in case it's to be run with an argument:
e.g. via node NativeMessaging.js messageToSend
sendMessage(process.argv[2]);

Websockets with Snap won't bind in production

please help me with something:
I want to implement websockets-snap in production but they seem that won't take the address. I have the following code:
the js inside index.html file
function createChatSocket() {
if(window.location.host == '') {
/* Running on localhost */
return new WebSocket('ws://35.197.208.147/ws/console/');
} else {
/* Running in "production" */
return new WebSocket('wss://jaspervdj.be/websockets/example/chat/');
}
}
the window.location.host is not empty, it is the ip
the server.hs file
app :: Snap ()
app = Snap.route
[ ("", Snap.ifTop $ Snap.serveFile "./shorts/index.html")
, ("console", console)
]
....
....
....
--------------------------------------------------------------------------------
console :: Snap ()
console = do
state <- liftIO $ newMVar newServerState
WS.runWebSocketsSnap $ application state
No matter what i do, it will always return the websockets found on wss://jaspervdj.be/websockets/example/chat/ and not my server. What can i do?
Thanks
That's a JavaScript issue, not a Haskell one. The window.location.host will never be empty. Therefore, you always end up with the websocket to jaspervdj.be.

Serving client side Jade templates

I want to use Jade templates at the client side with Backbone. How can I do that?
For now, I have successfully configured Backbone (Marionette) to compile Jade templates for use in its Views:
Marionette.TemplateCache.prototype.compileTemplate = (tmplStr) ->
console.log "jade stuff: ", jade.compile(tmplStr)
return jade.compile(tmplStr)
The "problem" is: I am currently writing templates like:
script(type="text/template", id="tmplMainView")
| h1= title
| p= content
Notice the pipes (|) those are to prevent Jade from trying to interpret/parse them on server side. How can I eliminate those?
UPDATE
Perhaps I can use the jade --client flag ... but it gives a single compiled function: for example
h1= title
Becomes
function anonymous(locals, attrs, escape, rethrow, merge) {
attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge;
var buf = [];
with (locals || {}) {
var interp;
buf.push('<h1>');
var __val__ = title
buf.push(escape(null == __val__ ? "" : __val__));
buf.push('</h1>');
}
return buf.join("");
}
That means I have to have 1 Jade/compiled JS for each template? How might I use it? Also I think many JS files is a slow way to work? But since template functions are all named anonymous, how can I then concat or somehow work with them effectively?
Check the ClientJade project.
From their site:
clientjade is a command line tool to compile your jade templates into client side templates for use in the browser. It will automatically include everything you need to render the templates, no need to include jade.js or runtime.js.
$ clientjade test1.jade test2.jade > templates.js
And then include template.js file in your html. To render the templates, just make a call like this:
//jade.render(domNode, templateName, data);
jade.render(document.getElementById('test1'), 'test1', { name: 'Bob' });
After looking at Jadify and ClientJade, and encountering a few problems along the way ... (perhaps its just somethings I missed out), I decided to explore simply compiling the templates on server side.
I defined a Node module (used by ExpressJS) which does the compilation and returns the compiled JS source (which I served with /js/templates.js).
fs = require "fs"
jade = require "jade"
async = require "async"
# done callback will be passed (source, err)
exports.compile = (done, templatesDir) ->
js = "var Templates = {}; \n\n"
# get all files in templates directory
fs.readdir templatesDir, (err, files) ->
# keep only ".jade" files
jadeFiles = files.filter (file) ->
file.substr(-5) == ".jade"
# function to compile jade templates (appending to js source)
compileTmpl = (file, doneCompile) ->
# "test.jade" becomes "test"
key = file.substr(0, file.indexOf("."))
filePath = templatesDir + file
fs.readFile filePath, (err, src) ->
# store js function source into Templates.{key}
js += "Templates." + key + " = " + jade.compile(src, { debug: false, client: true }).toString() + "; \n\n"
doneCompile(err)
# foreach jadeFile, compile template, then write templates.js file
async.forEach jadeFiles, compileTmpl, (err) ->
done(js, err)
And I use the precompiled templates on client side by including the templates.js and use templates like:
Templates.tmpl1()
Templates.tmpl2({ something: "Hello world", ... })
More on https://coderwall.com/p/hlojkw

Resources