CAPÍTULO V : PRESENTACIÓN ANALÍTICA DE RESULTADOS
5.1 Caracterización de los actores
5.1.5 El gobierno local
Cycle.js is a web application framework, but rxdn is not a web application; it is an OpenFlow controller written for Node.js. While the languages are the same and the APIs are similar, there are significant differences between a web application running in a web browser and a server application running in Node.js. Therefore, Cycle.js cannot be used directly, as it makes many assumptions about the code running in a browser and its primary focus is on efficiently writing HTML to the DOM. Therefore, a tailored structure was created based on the core concepts of Cycle.js to be more applicable to a server framework which accepts connections from clients (in this case, switches) and implements behavior based on a set of modules.
The primary abstractions provided by Cycle.js are the runfunction, the separation of logic into referentially transparent functions composed by a singlemainfunction, and the separation of side effects (e.g., network communication) into a set of drivers. The runfunction is called with main and the set of drivers as its arguments. Each driver is a function which takes an Observable called a “sink” and returns an Observable called a “source.” The main function takes the set of sources as an argument passed fromrun and returns a set of sinks. These sets are objects where the key is equal to the name of the driver and the value is the Observable corresponding to that driver. Finally, the runfunction ties the sources input and sinks output of main together to the sink input and source output of each driver. This is represented in Figure 39, specifically with the main function in a cycle with the DOM driver, which is responsible in Cycle.js for writing HTML to the DOM, the primary output to the user (this driver is specific to a web application and is not present in rxdn).
These abstractions clearly delineate the location and structure of each piece of functionality. It also removes much of the confusion of using RxJS directly. For
Figure 39. Cycle.js structure with DOM driver [73]
example, RxJS provides Observables, but does not inherently provide answers to questions like the following:
• At what point should an Observable be subscribed to? • When should an Observable be unsubscribed?
• Should a given Observable be created as cold, hot, or multicast?
These questions may be straightforward for a simple application that merely requests some data from a server and displays it, but can become confusing in a large framework.
This structure creates a cycle, which at first seems to present a chicken-and-egg problem: what doesruncall first, themainfunction or each of the driver functions, and how can the outputs of one function be given as the inputs to another function when the first function has not yet been called? Simplifying drivers into a single function, the pseudocode could be:
This cannot work, as sourcesis referenced before it is created. In more mathematical terms, this could be written
a = f (b) (2)
b = g(a) (3)
When combined, this yields a = f (g(a)). By understanding thatsources andsinksare
not single-assignment variables, but Observables which represent a stream of events, it becomes clear that intermediate or proxy Observables can be used to tie together these function input and output streams (where p represents a proxy):
p = (initialize) (4)
a = f (p) (5)
b = g(a) (6)
p = f (b) (7)
Or in pseudocode,
let sinks = main(sources) let sources = drivers(sinks)
This problem is solved by Cycle.js (and rxdn) with the use of Subjects. A Subject is a class which implements both the Observer and Observable interfaces. This means it can both subscribe to an Observable and itself be subscribed to. As all inputs and outputs for these functions are Observables, the Subject class can be used to create a set of proxies which act as intermediaries to tie these functions together. The actual runused in rxdn is clear and concise and is shown in Figure 40. The interfaces defined in rxdn are also clear and concise, and help in understanding the runfunction; these are shown in Figure 41.
1 import {Subscription, ReplaySubject} from "rxjs";
2 import {Collection, Component, Drivers} from "./interfaces"; 3
4 interface SubjectCollection {
5 [name: string]: ReplaySubject<any>; 6 }
7
8 function makeProxies(drivers: Drivers): SubjectCollection { 9 const proxies: SubjectCollection = {};
10 const names = Object.keys(drivers);
11 names.forEach(name => { proxies[name] = new ReplaySubject(1); });
12 return proxies;
13 } 14
15 function callDrivers(drivers: Drivers, proxies: Collection): Collection { 16 const sources: Collection = {};
17 const names = Object.keys(drivers); 18 names.forEach(name => {
19 let source = drivers[name](proxies[name]);
20 sources[name] = source; 21 }); 22 return sources; 23 } 24 25 function subscribeAll(
26 sinks: Collection, proxies: SubjectCollection 27 ): Subscription {
28 const subscription = new Subscription(); 29 const names = Object.keys(sinks);
30 names.forEach(name => { 31 subscription.add(sinks[name].subscribe(proxies[name])); 32 }); 33 return subscription; 34 } 35
36 export function run(main: Component, drivers: Drivers): Subscription { 37 const proxies = makeProxies(drivers);
38 const sources = callDrivers(drivers, proxies);
39 const {sinks} = main(sources);
40 const subscription = subscribeAll(sinks, proxies);
41 return subscription;
1 import {Observable} from "rxjs"; 2
3 /** A collection of Observables indexed by key */ 4 export interface Collection {
5 [name: string]: Observable<any>;
6 } 7 8 /**
9 * A Component is a function which accepts a source of Observables
10 * indexed by key (a Collection) and returns sources as inputs to 11 * composed Components and sinks as inputs to Drivers. A Component 12 * should not create side-effects, as this is the function of a Driver. 13 */
14 export interface Component {
15 (sources: Collection): {sources: Collection, sinks: Collection}; 16 }
17 18 /**
19 * A Driver is a function which takes an Observable (a Sink) and returns 20 * an Observable (a Source).
21 * The Driver is the place to acquire events or data from external sources 22 * and to create side-effects.
23 */
24 export interface Driver<Sink, Source> {
25 (sink: Observable<Sink>): Observable<Source>; 26 }
27
28 /** A collection of Drivers indexed by key */ 29 export interface Drivers {
30 [name: string]: Driver<any, any>; 31 }