<commit_before>(ns re-frame.subs
 (:require
   [re-frame.db    :refer [app-db]]
   [re-frame.interop :refer [add-on-dispose! debug-enabled? make-reaction ratom?]]
   [re-frame.loggers :refer [console]]
   [re-frame.utils :refer [first-in-vector]]))


;; -- Subscription Handler Lookup and Registration --------------------------------------------------

;; Maps from a query-id to a handler function.
(def ^:private qid->fn (atom {}))

(defn register
  "Registers a subscription handler function for an query id"
  [query-id handler-fn]
  (if (contains? @qid->fn query-id)
    (console :warn "re-frame: overwriting subscription handler for: " query-id))  ;; allow it, but warn. Happens on figwheel reloads.
  (swap! qid->fn assoc query-id handler-fn))


;; -- Subscription cache -----------------------------------------------------
;;
;; De-duplicate subscriptions. If two or more identical subscriptions
;; are concurrently active, we want only one handler running.
;; Two subscriptions are "identical" if their query vectors
;; test "=".
(def ^:private query->reaction (atom {}))

(defn clear-all-handlers!
  "Unregisters all existing subscription handlers"
  []
  (reset! qid->fn {})
  (reset! query->reaction {}))

(defn cache-and-return
  "cache the reaction r"
  [query-v dynv r]
  (let [cache-key [query-v dynv]]
    ;; when this reaction is nolonger being used, remove it from the cache
    (add-on-dispose! r #(do (swap! query->reaction dissoc cache-key)
                            (console :log "Removing subscription: " cache-key)))
    ;; cache this reaction, so it can be used to deduplicate other, identical subscriptions
    (swap! query->reaction assoc cache-key r)

    r))  ;; return the actual reaction

(defn cache-lookup
  ([query-v]
   (cache-lookup query-v []))
  ([query-v dyn-v]
   (get @query->reaction [query-v dyn-v])))


;; -- Subscription cache -----------------------------------------------------

(defn subscribe
  "Returns a Reagent/reaction which contains a computation"
  ([query-v]
   (if-let [cached (cache-lookup query-v)]
     (do (console :log "Using cached subscription: " query-v)
         cached)
     (let [query-id   (first-in-vector query-v)
           handler-fn (get @qid->fn query-id)]
       (console :log "Subscription created: " query-v)
       (if-not handler-fn
         (console :error "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription."))
       (cache-and-return query-v [] (handler-fn app-db query-v)))))

  ([v dynv]
   (if-let [cached (cache-lookup v dynv)]
     (do (console :log "Using cached subscription: " v " and " dynv)
         cached)
     (let [query-id   (first-in-vector v)
           handler-fn (get @qid->fn query-id)]
       (when debug-enabled?
         (when-let [not-reactive (not-empty (remove ratom? dynv))]
           (console :warn "re-frame: your subscription's dynamic parameters that don't implement IReactiveAtom: " not-reactive)))
       (if (nil? handler-fn)
         (console :error "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription.")
         (let [dyn-vals (make-reaction (fn [] (mapv deref dynv)))
               sub (make-reaction (fn [] (handler-fn app-db v @dyn-vals)))]
           ;; handler-fn returns a reaction which is then wrapped in the sub reaction
           ;; need to double deref it to get to the actual value.
           (console :log "Subscription created: " v dynv)
           (cache-and-return v dynv (make-reaction (fn [] @@sub)))))))))

;; -- Helper code for register-pure -------------------

(defn- fmap
  "Returns a new version of 'm' in which f has been applied to each value.
  (fmap inc {:a 4, :b 2}) => {:a 5, :b 3}"
  [f m]
  (into {} (for [[k val] m] [k (f val)])))

(defn- multi-deref
  "derefs a map sequence or a singleton"
  [data]
  (cond
    (map? data)  (fmap deref data)
    (coll? data) (map deref data)
    :else @data))

(defn register-pure
  "This fn allows the user to write a 'pure' subscription
  i.e. that is a subscription that operates on the values within app-db
  rather than the atom itself
  Note there are 3 ways this function can be called

    ```(register-pure
         :test-sub<commit_msg>rename fmap to map-vals<commit_after>(ns re-frame.subs
 (:require
   [re-frame.db    :refer [app-db]]
   [re-frame.interop :refer [add-on-dispose! debug-enabled? make-reaction ratom?]]
   [re-frame.loggers :refer [console]]
   [re-frame.utils :refer [first-in-vector]]))


;; -- Subscription Handler Lookup and Registration --------------------------------------------------

;; Maps from a query-id to a handler function.
(def ^:private qid->fn (atom {}))

(defn register
  "Registers a subscription handler function for an query id"
  [query-id handler-fn]
  (if (contains? @qid->fn query-id)
    (console :warn "re-frame: overwriting subscription handler for: " query-id))  ;; allow it, but warn. Happens on figwheel reloads.
  (swap! qid->fn assoc query-id handler-fn))


;; -- Subscription cache -----------------------------------------------------
;;
;; De-duplicate subscriptions. If two or more identical subscriptions
;; are concurrently active, we want only one handler running.
;; Two subscriptions are "identical" if their query vectors
;; test "=".
(def ^:private query->reaction (atom {}))

(defn clear-all-handlers!
  "Unregisters all existing subscription handlers"
  []
  (reset! qid->fn {})
  (reset! query->reaction {}))

(defn cache-and-return
  "cache the reaction r"
  [query-v dynv r]
  (let [cache-key [query-v dynv]]
    ;; when this reaction is nolonger being used, remove it from the cache
    (add-on-dispose! r #(do (swap! query->reaction dissoc cache-key)
                            (console :log "Removing subscription: " cache-key)))
    ;; cache this reaction, so it can be used to deduplicate other, identical subscriptions
    (swap! query->reaction assoc cache-key r)

    r))  ;; return the actual reaction

(defn cache-lookup
  ([query-v]
   (cache-lookup query-v []))
  ([query-v dyn-v]
   (get @query->reaction [query-v dyn-v])))


;; -- Subscription cache -----------------------------------------------------

(defn subscribe
  "Returns a Reagent/reaction which contains a computation"
  ([query-v]
   (if-let [cached (cache-lookup query-v)]
     (do (console :log "Using cached subscription: " query-v)
         cached)
     (let [query-id   (first-in-vector query-v)
           handler-fn (get @qid->fn query-id)]
       (console :log "Subscription created: " query-v)
       (if-not handler-fn
         (console :error "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription."))
       (cache-and-return query-v [] (handler-fn app-db query-v)))))

  ([v dynv]
   (if-let [cached (cache-lookup v dynv)]
     (do (console :log "Using cached subscription: " v " and " dynv)
         cached)
     (let [query-id   (first-in-vector v)
           handler-fn (get @qid->fn query-id)]
       (when debug-enabled?
         (when-let [not-reactive (not-empty (remove ratom? dynv))]
           (console :warn "re-frame: your subscription's dynamic parameters that don't implement IReactiveAtom: " not-reactive)))
       (if (nil? handler-fn)
         (console :error "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription.")
         (let [dyn-vals (make-reaction (fn [] (mapv deref dynv)))
               sub (make-reaction (fn [] (handler-fn app-db v @dyn-vals)))]
           ;; handler-fn returns a reaction which is then wrapped in the sub reaction
           ;; need to double deref it to get to the actual value.
           (console :log "Subscription created: " v dynv)
           (cache-and-return v dynv (make-reaction (fn [] @@sub)))))))))

;; -- Helper code for register-pure -------------------

(defn- map-vals
  "Returns a new version of 'm' in which 'f' has been applied to each value.
  (map-vals inc {:a 4, :b 2}) => {:a 5, :b 3}"
  [f m]
  (into {} (for [[k val] m] [k (f val)])))

(defn- multi-deref
  "derefs a map sequence or a singleton"
  [data]
  (cond
    (map? data)  (map-vals deref data)
    (coll? data) (map deref data)
    :else @data))

(defn register-pure
  "This fn allows the user to write a 'pure' subscription
  i.e. that is a subscription that operates on the values within app-db
  rather than the atom itself
  Note there are 3 ways this function can be called

    ```(register-pure
         :test-sub
{'fraction_non_alphanumeric': 0.17172430016466714, 'fraction_numerical': 0.0011761938367442955, 'mean_word_length': 2.5049464138499586, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, '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': '2846037', 'n_tokens_mistral': 2678, 'n_tokens_neox': 2580, 'n_words': 920}