summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/lib/utils/recurrence.js
blob: 8fd26f3e393abc64d71c1657b65db9bdb1c9708a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { uuids } from './uuids';

/**
 * @module recurrence
 */

const instances = {};

/**
 * Create a new unique {@link module:recurrence~RecurInstance|RecurInstance}
 * @returns {module:recurrence.RecurInstance} The newly created {@link module:recurrence~RecurInstance|RecurInstance}
 */
export function create() {
  const id = uuids()[0];
  let handlers = {};
  let count = 0;

  /**
   * @namespace RecurInstance
   * @description A RecurInstance tracks the count of any occurrence as registered by calls to <code>occur</code>.
   * <br /><br />
   * It maintains an internal counter and a registry of handlers that can be arbitrarily assigned by a user.
   * <br /><br />
   * While a RecurInstance isn't specific to any particular use-case, it may be useful for:
   * <br />
   * <ul>
   * <li>Tracking repeated errors across multiple - but not linked - network requests</li>
   * <li>Tracking repeated user interactions (e.g. multiple clicks)</li>
   * </ul>
   * @summary A closure to track repeated occurrences of any arbitrary event.
   * */
  const instance = {
    /**
     * @type {module:uuids~UUIDv4}
     * @description A randomly generated {@link module:uuids~UUIDv4|UUID} for this particular recurrence instance
     * @memberof module:recurrence~RecurInstance
     * @readonly
     * @inner
     */
    get id() {
      return id;
    },
    /**
     * @type {Number}
     * @description The number of times this particular instance of recurrence has been triggered
     * @memberof module:recurrence~RecurInstance
     * @readonly
     * @inner
     */
    get count() {
      return count;
    },
    /**
     * @type {Object}
     * @description The handlers assigned to this recurrence tracker
     * @example
     * myRecurrence.handle( 4, () => console.log( "four" ) );
     * console.log( myRecurrence.handlers ); // {"4": () => console.log( "four" )}
     * @memberof module:recurrence~RecurInstance
     * @readonly
     * @inner
     */
    get handlers() {
      return handlers;
    },
    /**
     * @type {Boolean}
     * @description Delete any internal reference to the instance.
     * <br />
     * Keep in mind that this will only attempt to remove the <strong>internal</strong> reference.
     * <br />
     * If your code maintains a reference to the instance, the regular garbage collector will not free the memory.
     * @memberof module:recurrence~RecurInstance
     * @inner
     */
    free() {
      return delete instances[id];
    },
    /**
     * @description Register a handler to be called when this occurrence is seen <code>onCount</code> number of times.
     * @param {Number} onCount - The number of times the occurrence has been seen to respond to
     * @param {Function} behavior - A callback function to run when the occurrence has been seen <code>onCount</code> times
     * @memberof module:recurrence~RecurInstance
     * @inner
     */
    handle(onCount, behavior) {
      if (onCount && behavior) {
        handlers[onCount] = behavior;
      }
    },
    /**
     * @description Remove the behavior callback handler that would be run when the occurrence is seen <code>onCount</code> times
     * @param {Number} onCount - The count identifier for which to eject the callback handler
     * @memberof module:recurrence~RecurInstance
     * @inner
     */
    eject(onCount) {
      if (onCount) {
        delete handlers[onCount];
      }
    },
    /**
     * @description Register that this occurrence has been seen and trigger any appropriate handlers
     * @memberof module:recurrence~RecurInstance
     * @inner
     */
    occur() {
      count += 1;

      if (typeof handlers[count] === 'function') {
        handlers[count](count);
      }
    },
    /**
     * @description Reset this recurrence instance without destroying it entirely
     * @param {Object} [options]
     * @param {Boolean} [options.currentCount = true] - Whether to reset the count
     * @param {Boolean} [options.handlersList = false] - Whether to reset the list of attached handlers back to an empty state
     * @memberof module:recurrence~RecurInstance
     * @inner
     */
    reset({ currentCount = true, handlersList = false } = {}) {
      if (currentCount) {
        count = 0;
      }

      if (handlersList) {
        handlers = {};
      }
    },
  };

  instances[id] = instance;

  return instance;
}

/**
 * Retrieve a stored {@link module:recurrence~RecurInstance|RecurInstance} by {@link module:uuids~UUIDv4|UUID}
 * @param {module:uuids~UUIDv4} id - The {@link module:uuids~UUIDv4|UUID} of a previously created {@link module:recurrence~RecurInstance|RecurInstance}
 * @returns {(module:recurrence~RecurInstance|undefined)} The {@link module:recurrence~RecurInstance|RecurInstance}, or undefined if the UUID doesn't refer to a known Instance
 */
export function recall(id) {
  return instances[id];
}

/**
 * Release the memory space for a given {@link module:recurrence~RecurInstance|RecurInstance} by {@link module:uuids~UUIDv4|UUID}
 * @param {module:uuids~UUIDv4} id - The {@link module:uuids~UUIDv4|UUID} of a previously created {@link module:recurrence~RecurInstance|RecurInstance}
 * @returns {Boolean} Whether the reference to the stored {@link module:recurrence~RecurInstance|RecurInstance} was released
 */
export function free(id) {
  return recall(id)?.free() || false;
}