(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{158:function(e,t,n){"use strict";n.r(t),n.d(t,"frontMatter",(function(){return i})),n.d(t,"metadata",(function(){return s})),n.d(t,"rightToc",(function(){return c})),n.d(t,"default",(function(){return u}));var o=n(2),r=n(9),a=(n(0),n(196)),i={title:"The Root Engine",id:"root-engine"},s={id:"root-engine",isDocsHomePage:!1,title:"The Root Engine",description:"Now that we've created a simple node editor with some useful nodes, we need a way to actually run our logic. While you are free to parse your logic graphs any way you see fit, Flume ships with a pre-built engine for running \"root-style\" logic graphs. The root engine is responsible for taking in a root-style graph generated with the Flume node editor, and returning the resolved properties of the root node. Let's take a look at how we can get the root engine up and running.",source:"@site/docs/root-engine.mdx",permalink:"/docs/root-engine",editUrl:"https://github.com/chrisjpatty/flume/edit/master/docs/docs/root-engine.mdx",sidebar:"someSidebar",previous:{title:"Saving Nodes",permalink:"/docs/saving-nodes"},next:{title:"Using With React",permalink:"/docs/using-with-react"}},c=[{value:"Setting up the engine",id:"setting-up-the-engine",children:[]},{value:"Resolving ports",id:"resolving-ports",children:[]},{value:"Resolving nodes",id:"resolving-nodes",children:[]},{value:"Summary",id:"summary",children:[]}],l={rightToc:c};function u(e){var t=e.components,n=Object(r.a)(e,["components"]);return Object(a.b)("wrapper",Object(o.a)({},l,n,{components:t,mdxType:"MDXLayout"}),Object(a.b)("p",null,"Now that we've created a simple node editor with some useful nodes, we need a way to actually run our logic. While you are free to parse your logic graphs any way you see fit, Flume ships with a pre-built engine for running \"root-style\" logic graphs. The root engine is responsible for taking in a root-style graph generated with the Flume node editor, and returning the resolved properties of the root node. Let's take a look at how we can get the root engine up and running."),Object(a.b)("h2",{id:"setting-up-the-engine"},"Setting up the engine"),Object(a.b)("p",null,"To get started, let's create a new file called ",Object(a.b)("inlineCode",{parentName:"p"},"engine.js")," where we'll import ",Object(a.b)("inlineCode",{parentName:"p"},"RootEngine")," from ",Object(a.b)("inlineCode",{parentName:"p"},"flume"),", and our config file from ",Object(a.b)("inlineCode",{parentName:"p"},"config.js")),Object(a.b)("pre",null,Object(a.b)("code",Object(o.a)({parentName:"pre"},{className:"language-js"}),"import { RootEngine } from 'flume'\nimport config from './config'\n\nconst engine = new RootEngine(config)\n\nexport default engine\n")),Object(a.b)("p",null,"Like before, we create a new instance of the RootEngine and set it to a variable that we'll export below. We also imported our config file and provided it to the root engine as the first parameter. In order for the engine to work though, we need to provide it with 2 helper functions: ",Object(a.b)("inlineCode",{parentName:"p"},"resolvePorts"),", and ",Object(a.b)("inlineCode",{parentName:"p"},"resolveNodes"),"."),Object(a.b)("h2",{id:"resolving-ports"},"Resolving ports"),Object(a.b)("p",null,"The first thing we need to do is tell the root engine how to handle the controls for each of our ports. To keep things organized, let's create a new function above our root engine."),Object(a.b)("pre",null,Object(a.b)("code",Object(o.a)({parentName:"pre"},{className:"language-js"}),"import { RootEngine } from 'flume'\nimport config from './config'\n\nconst resolvePorts = (portType, data) => {\n switch (portType) {\n case 'string':\n return data.string\n case 'boolean':\n return data.boolean\n case 'number':\n return data.number\n default:\n return data\n }\n}\n\nconst engine = new RootEngine(config, resolvePorts)\n")),Object(a.b)("p",null,"Let's break this down. ",Object(a.b)("inlineCode",{parentName:"p"},"resolvePorts")," is a function that takes in the type of the port currently being processed, and all of the data from its controls. Then we open a switch statement with the port type, and return the port data for each port type. Because we only have 3 port types, we only have 3 entries in our switch statement. In our case, each of our ports only has one control, and we gave each control the same name as the port, so we can fill this function our pretty easily. In advanced use-cases, ports may have any number of controls, so this function will need to resolve each control, but this work for now."),Object(a.b)("div",{className:"admonition admonition-note alert alert--secondary"},Object(a.b)("div",Object(o.a)({parentName:"div"},{className:"admonition-heading"}),Object(a.b)("h5",{parentName:"div"},Object(a.b)("span",Object(o.a)({parentName:"h5"},{className:"admonition-icon"}),Object(a.b)("svg",Object(o.a)({parentName:"span"},{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"}),Object(a.b)("path",Object(o.a)({parentName:"svg"},{fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"})))),"note")),Object(a.b)("div",Object(o.a)({parentName:"div"},{className:"admonition-content"}),Object(a.b)("p",{parentName:"div"},"This might seem a little unintuitive or redundant at first, but keep in mind that Flume doesn't make many assumptions about how you build or run your logic, so these functions are how you define that behavior."))),Object(a.b)("h2",{id:"resolving-nodes"},"Resolving nodes"),Object(a.b)("p",null,"The last function we need is ",Object(a.b)("inlineCode",{parentName:"p"},"resolveNodes"),". This function tells each node how it should transform its inputs into its outputs. Let's create this function above the engine."),Object(a.b)("pre",null,Object(a.b)("code",Object(o.a)({parentName:"pre"},{className:"language-js"}),"import { RootEngine } from 'flume'\nimport config from './config'\n\n/* ... */\n\nconst resolveNodes = (node, inputValues, nodeType, context) => {\n switch (node.type) {\n case 'string':\n return { string: inputValues.string }\n case 'boolean':\n return { boolean: inputValues.boolean }\n case 'number':\n return { number: inputValues.number }\n case 'user':\n return context.user\n case 'joinText':\n return { joinedText: inputValues.string1 + inputValues.string2 }\n case \"reverseBoolean\":\n return { boolean: !inputValues.boolean }\n default:\n return inputValues\n }\n}\n\nconst engine = new RootEngine(config, resolvePorts, resolveNodes)\n")),Object(a.b)("p",null,"This function looks similar to ",Object(a.b)("inlineCode",{parentName:"p"},"resolvePorts")," but has some key differences. ",Object(a.b)("inlineCode",{parentName:"p"},"resolveNodes")," takes in the current node, the resolved input values, the nodeType (which we're not using in this example), and an object called ",Object(a.b)("inlineCode",{parentName:"p"},"context"),". We create a switch statement using the node type, and return an object representing all of the outputs of that node. You may also notice that we didn't create a function for the ",Object(a.b)("inlineCode",{parentName:"p"},"homepage"),' node. Because we marked this node as the "root" node, the root engine will take care of resolving and returning the values of its inputs for us.'),Object(a.b)("p",null,"Remember, each node can conceptually be thought of as a single function that transforms inputs into outputs. Are you starting to see how easy it can be to expose some powerful programming functions in a visual way?"),Object(a.b)("div",{className:"admonition admonition-note alert alert--secondary"},Object(a.b)("div",Object(o.a)({parentName:"div"},{className:"admonition-heading"}),Object(a.b)("h5",{parentName:"div"},Object(a.b)("span",Object(o.a)({parentName:"h5"},{className:"admonition-icon"}),Object(a.b)("svg",Object(o.a)({parentName:"span"},{xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"}),Object(a.b)("path",Object(o.a)({parentName:"svg"},{fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"})))),"note")),Object(a.b)("div",Object(o.a)({parentName:"div"},{className:"admonition-content"}),Object(a.b)("p",{parentName:"div"},"We'll explain the context object in more detail in the next section, but in short, the context object is how you can pass live data into your engine at runtime. In this case we're using it to get the current user."))),Object(a.b)("h2",{id:"summary"},"Summary"),Object(a.b)("p",null,"In this section we setup a new ",Object(a.b)("inlineCode",{parentName:"p"},"RootEngine"),", and gave it instructions for resolving our ports and nodes. In the next section we'll show how to hook up the root engine to React."))}u.isMDXComponent=!0},196:function(e,t,n){"use strict";n.d(t,"a",(function(){return p})),n.d(t,"b",(function(){return h}));var o=n(0),r=n.n(o);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=r.a.createContext({}),u=function(e){var t=r.a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=u(e.components);return r.a.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.a.createElement(r.a.Fragment,{},t)}},b=r.a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),p=u(n),b=o,h=p["".concat(i,".").concat(b)]||p[b]||d[b]||a;return n?r.a.createElement(h,s(s({ref:t},l),{},{components:n})):r.a.createElement(h,s({ref:t},l))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=b;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var l=2;l