<commit_before>
import callAction from './callAction';
import { ACTION_TYPE, INTROSPECTION_TYPE } from './types';
import SimpleGraphController from './SimpleGraphController';

const notFoundValue = {};


export default class Flambeau {
  /**
   * Creates a new Flambeau store
   * @param  {graph controller} graph The object graph that reducers are attached to.
   */
  constructor(graph = new SimpleGraphController()) {
    this.resources = {};
    this.graph = graph;
    this.actionSetIDsToActionFunctions = {};
    this.listeners = [];
  }

  /**
   * Attach a set of reducer functions.
   *
   * @param  {String} id      The unique identifier for this particular reducer
   * @param  {Object} reducer The set of reducer functions, most likely made using `import * as ...`
   * @param  {Object} props   The unique attributes to customize the reducer
   */
  attachReducer(id, reducer, props) {
    this.resources[id] = {
      reducer,
      props
    };

    this.graph = this.graph.setUp(id, {
      state: reducer.getInitialState(props)
    });
  }

  /**
   * Attach multiple reducers, with ids as keys, and reducer function sets as values.
   *
   * @param  {Object} idToReducers An object mapping identifiers to reducer function sets
   * @param  {Object} idToProps    (Optional) the unique attributes for each particular reducer
   */
  attachReducers(idToReducers, idToProps = {}) {
    Object.keys(idToReducers).forEach(id => {
      this.attachReducer(id, idToReducers[id], idToProps[id]);
    });
  }

  dispatch({ actionSetID, actionID, payload }) {
    let idsChanged = {};

    Object.keys(this.resources).forEach(resourceID => {
      const resource = this.resources[resourceID];

      const result = callAction({
        responder: resource.reducer,
        type: ACTION_TYPE,
        initialValue: this.graph.get(resourceID),
        props: resource.props,
        payload,
        notFoundValue,
        actionSetID,
        actionID
      });

      if (result !== notFoundValue) {
        this.graph = this.graph.set(resourceID, result);
        idsChanged[resourceID] = true;
      }
    });

    this.listeners.forEach((callback) => {
      callback(idsChanged);
    });
  }

  /**
   * Add actions that will be connected to reducers.
   *
   * @param  {Object} actionSets An object mapping action set identifiers to action functions
   */
  registerActionSets(actionSets) {
    Object.keys(actionSets).forEach(actionSetID => {
      this.actionSetIDsToActionFunctions[actionSetID] = actionSets[actionSetID];
    });

    delete this.allConnectedActionSets;
  }

  /**
   * Get a set of actions, connected to this store ready to dispatch to the reducers.
   *
   * @param  {String} actionSetID The unique identifier for the action set
   * @return {Object}             The connected action functions
   */
  getConnectedActionSet(actionSetID) {
    const sourceActionFunctions = this.actionSetIDsToActionFunctions[actionSetID];
    let connectedActionFunctions = {};

    Object.keys(sourceActionFunctions).forEach(actionID => {
      if (actionID === 'introspection') {
        return;
      }

      const sourceActionFunction = sourceActionFunctions[actionID];

      connectedActionFunctions[actionID] = (payload) => {
        // Synchronous, immediately dispatched
        if (sourceActionFunction.length <= 1) {
          const result = sourceActionFunction(payload);
          // Allow empty action declarations
          if (typeof result !== 'undefined') {
            payload = result;
          }
          this.dispatch({ actionSetID, actionID, payload });
        }
        // Asychronous, delegates the dispatching
        else {
          const allActionSets = this.getAllConnectedActionSets();
          const currentActionSet = allActionSets[actionSetID];

          const getConsensus = ({ viewpointID, payload, combine, booleanOr = false, booleanAnd = false }) => {
            if (booleanOr) {
              combine = (combined, current) => {
                return combined || current;
              };
            }
            else if (booleanAnd) {
              combine = (combined, current) => {
                return combined && current;
              };
            }

            return Object.keys(this.resources).reduce((combinedValue, resourceID, i) => {
              const resource = this.resources[resourceID];
              let currentValue = callAction({
                responder: resource.reducer,
                type: INTROSPECTION_TYPE,
                initialValue: this.graph.get(resourceID),
                defaultValue: notFoundValue,
                props: resource.props,
                actionID: viewpointID,
                actionSetID,
                payload,
                notFoundValue
              });

              if (i === 0) {
                combinedValue = currentValue;
              }
              else {
                combinedValue = combine(combinedValue, currentValue);
              }

              return combinedValue;
            }, undefined);
          }

          sourceActionFunction(payload, { currentActionSet, allActionSets, getConsensus });
        }
      };
    });

    return connectedActionFunctions;
  }

  /**
   * Get multiple actions sets, connected to this store ready to dispatch to the reducers.
   *
   * @param  {Array} actionSetIDs The unique identifiers of the action sets.
   * @return {Object}             The action set identifiers to connected action function sets.
   */
  getConnectedActionSets(actionSetIDs) {
    return actionSetIDs.reduce((connectedActionSets, actionSetID) => {
      connectedActionSets[actionSetID] = this.getConnectedActionSet(actionSetID);
      return connectedActionSets;
    }, {});<commit_msg>Fix getConsensus() for unimplemented introspection functions<commit_after>
import callAction from './callAction';
import { ACTION_TYPE, INTROSPECTION_TYPE } from './types';
import SimpleGraphController from './SimpleGraphController';

const notFoundValue = {};


export default class Flambeau {
  /**
   * Creates a new Flambeau store
   * @param  {graph controller} graph The object graph that reducers are attached to.
   */
  constructor(graph = new SimpleGraphController()) {
    this.resources = {};
    this.graph = graph;
    this.actionSetIDsToActionFunctions = {};
    this.listeners = [];
  }

  /**
   * Attach a set of reducer functions.
   *
   * @param  {String} id      The unique identifier for this particular reducer
   * @param  {Object} reducer The set of reducer functions, most likely made using `import * as ...`
   * @param  {Object} props   The unique attributes to customize the reducer
   */
  attachReducer(id, reducer, props) {
    this.resources[id] = {
      reducer,
      props
    };

    this.graph = this.graph.setUp(id, {
      state: reducer.getInitialState(props)
    });
  }

  /**
   * Attach multiple reducers, with ids as keys, and reducer function sets as values.
   *
   * @param  {Object} idToReducers An object mapping identifiers to reducer function sets
   * @param  {Object} idToProps    (Optional) the unique attributes for each particular reducer
   */
  attachReducers(idToReducers, idToProps = {}) {
    Object.keys(idToReducers).forEach(id => {
      this.attachReducer(id, idToReducers[id], idToProps[id]);
    });
  }

  dispatch({ actionSetID, actionID, payload }) {
    let idsChanged = {};

    Object.keys(this.resources).forEach(resourceID => {
      const resource = this.resources[resourceID];

      const result = callAction({
        responder: resource.reducer,
        type: ACTION_TYPE,
        initialValue: this.graph.get(resourceID),
        props: resource.props,
        payload,
        notFoundValue,
        actionSetID,
        actionID
      });

      if (result !== notFoundValue) {
        this.graph = this.graph.set(resourceID, result);
        idsChanged[resourceID] = true;
      }
    });

    this.listeners.forEach((callback) => {
      callback(idsChanged);
    });
  }

  /**
   * Add actions that will be connected to reducers.
   *
   * @param  {Object} actionSets An object mapping action set identifiers to action functions
   */
  registerActionSets(actionSets) {
    Object.keys(actionSets).forEach(actionSetID => {
      this.actionSetIDsToActionFunctions[actionSetID] = actionSets[actionSetID];
    });

    delete this.allConnectedActionSets;
  }

  /**
   * Get a set of actions, connected to this store ready to dispatch to the reducers.
   *
   * @param  {String} actionSetID The unique identifier for the action set
   * @return {Object}             The connected action functions
   */
  getConnectedActionSet(actionSetID) {
    const sourceActionFunctions = this.actionSetIDsToActionFunctions[actionSetID];
    let connectedActionFunctions = {};

    Object.keys(sourceActionFunctions).forEach(actionID => {
      if (actionID === 'introspection') {
        return;
      }

      const sourceActionFunction = sourceActionFunctions[actionID];

      connectedActionFunctions[actionID] = (payload) => {
        // Synchronous, immediately dispatched
        if (sourceActionFunction.length <= 1) {
          const result = sourceActionFunction(payload);
          // Allow empty action declarations
          if (typeof result !== 'undefined') {
            payload = result;
          }
          this.dispatch({ actionSetID, actionID, payload });
        }
        // Asychronous, delegates the dispatching
        else {
          const allActionSets = this.getAllConnectedActionSets();
          const currentActionSet = allActionSets[actionSetID];

          const getConsensus = ({ introspectionID, payload, combine, booleanOr = false, booleanAnd = false }) => {
            if (booleanOr) {
              combine = (combined, current) => {
                return combined || current;
              };
            }
            else if (booleanAnd) {
              combine = (combined, current) => {
                return combined && current;
              };
            }

            const combinedValue = Object.keys(this.resources).reduce((combinedValue, resourceID, i) => {
              const resource = this.resources[resourceID];
              const currentValue = callAction({
                responder: resource.reducer,
                type: INTROSPECTION_TYPE,
                initialValue: this.graph.get(resourceID),
                props: resource.props,
                actionID: introspectionID,
                actionSetID,
                payload,
                notFoundValue
              });

              if (currentValue !== notFoundValue) {
                if (combinedValue === notFoundValue) {
                  return currentValue;
                }
                else {
                  return combine(combinedValue, currentValue);
                }
              }

              return combinedValue;
            }, notFoundValue);

            if (combinedValue !== notFoundValue) {
              return combinedValue;
            }
          }

          sourceActionFunction(payload, { currentActionSet, allActionSets, getConsensus });
        }
      };
    });

    return connectedActionFunctions;
  }

  /**
   * Get multiple actions sets, connected to this store ready to dispatch to the reducers.
   *
   * @param  {Array} actionSetIDs The unique identifiers of the action sets.
   * @return {Object}             The action set identifiers to connected action function sets.
   */
  getConnectedActionSets(actionSetIDs) {
    return actionSetIDs.reduce((connectedActionSets, actionSetID) => {
      connectedActionSets[actionSetID] = this.getConnectedActionSet(actionSetID);
      return connectedActionSets;
    }, {});
{'fraction_non_alphanumeric': 0.09762330692563251, 'fraction_numerical': 0.0002555583950932788, 'mean_word_length': 2.319197059655075, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '456971', 'n_tokens_mistral': 3135, 'n_tokens_neox': 3044, 'n_words': 792}