<commit_before>(ns workflo.macros.screen.om-next
  (:require [om.next :as om]
            [workflo.macros.screen.bidi :as sb]
            [workflo.macros.screen :as scr]
            [workflo.macros.view :refer-macros [defview]]))

;;;; Remember the active screen

(defonce ^:private +active-screen+
  (atom {:screen nil :params nil}))

(defn active-screen
  "Returns the active screen as a map, with :screen storing
   the current screen and :params storing the URL parameters
   for the current screen."
  []
  @+active-screen+)

(defn set-active-screen!
  "Sets the active screen and its parameters."
  [screen params]
  (swap! +active-screen+ assoc
         :screen screen
         :params params))

(defn reload-active-screen!
  "Reloads the implementation of the active screen by replacing
   it with a freshly resolved instance from the screen registry."
  []
  (swap! +active-screen+ update :screen
         (fn [screen]
           (some-> screen :name scr/resolve-screen))))

;;;; Wrapping Om Next parser to handle screen-based routing

(defn read-screen
  "Executes a query agains the active screen."
  [read {:keys [parser query] :as env} _ _]
  {:value (parser (assoc env :screen (:screen (active-screen)))
                  query nil)})

(defn read-navigation
  "Queries the navigation info of the active screen."
  [_ {:keys [screen]} _ _]
  {:value (:navigation screen)})

(defn read-layout
  "Executes all queries for views in the layout of the
   active screen."
  [_ {:keys [parser query screen] :as env} _ _]
  {:value (reduce (fn [res layout-item]
                    (let [[k v] (first layout-item)]
                      (assoc res k {:view (-> screen :layout
                                              k :factory)
                                    :props (parser (dissoc env :path)
                                                   v nil)})))
                  {} query)})

(defn- wrapping-read
  "Wraps a user-provided parser read function for Om Next by
   special-casing queries for screen-based routing information
   (such as :workflo/screen, :workflo/navigation and
   :workflo/layout.)"
  [read env key params]
  (case key
    :workflo/screen     (read-screen read env key params)
    :workflo/navigation (read-navigation read env key params)
    :workflo/layout     (read-layout read env key params)
    (read env key params)))

(defn parser
  "Returns a parser that wraps the provided read function to
   catch queries for screen-based routing information."
  [{:keys [read mutate]}]
  (om/parser {:read (partial wrapping-read read)
              :mutate mutate}))

;;;; Root component

(defonce ^:private +root-component+
  (atom {:factory nil :js? false}))

(defn root-component
  "Returns the root component to be used."
  []
  @+root-component+)

(defn set-root-component!
  "Configures the root component to be used."
  [factory js?]
  (swap! +root-component+ assoc
         :factory factory
         :js? js?))

;;;; Wrapping application

(defn- realize-layout
  "Realizes the results of a layout query by instantiating
   all returned views with their props / query results."
  [layout]
  (zipmap (keys layout)
          (map (fn [{:keys [view props] :as item}]
                 (view (vary-meta props assoc :om-path
                                  (-> item meta :om-path))))
               (vals layout))))

(defview RootWrapper
  [{workflo/screen [workflo [navigation layout]]}]
  (render
   (let [{:keys [workflo/navigation workflo/layout]} screen]
     ((:factory (root-component))
      (if (:js? (root-component))
        #js {:navigation navigation
             :layout (realize-layout layout)}
        {:navigation navigation
         :layout (realize-layout layout)})))))

;;;; Routing

(defn mount-screen
  "Mounts a screen with parameters by setting it as the active
   screen and updating the root wrapper's component query
   according to the screen's layout."
  [app screen params]
  (let [c     (om/app-root (:reconciler (:config app)))
        query [{:workflo/screen
                [:workflo/navigation
                 {:workflo/layout
                  (mapv (fn [[k v]]
                          {k (or (om/get-query (:view v))
                                 [])})
                        (:layout screen))}]}]]
    (set-active-screen! screen params)
    (om/set-query! c {:params params :query query})
    (some-> app :config :screen-mounted
            (apply [app screen params]))))
<commit_msg>Convert property keys to camel-cased strings when passing them to JS<commit_after>(ns workflo.macros.screen.om-next
  (:require [clojure.walk :refer [walk]]
            [om.next :as om]
            [workflo.macros.screen.bidi :as sb]
            [workflo.macros.screen :as scr]
            [workflo.macros.util.string :refer [kebab->camel]]
            [workflo.macros.view :refer-macros [defview]]))

;;;; Remember the active screen

(defonce ^:private +active-screen+
  (atom {:screen nil :params nil}))

(defn active-screen
  "Returns the active screen as a map, with :screen storing
   the current screen and :params storing the URL parameters
   for the current screen."
  []
  @+active-screen+)

(defn set-active-screen!
  "Sets the active screen and its parameters."
  [screen params]
  (swap! +active-screen+ assoc
         :screen screen
         :params params))

(defn reload-active-screen!
  "Reloads the implementation of the active screen by replacing
   it with a freshly resolved instance from the screen registry."
  []
  (swap! +active-screen+ update :screen
         (fn [screen]
           (some-> screen :name scr/resolve-screen))))

;;;; Wrapping Om Next parser to handle screen-based routing

(defn read-screen
  "Executes a query agains the active screen."
  [read {:keys [parser query] :as env} _ _]
  {:value (parser (assoc env :screen (:screen (active-screen)))
                  query nil)})

(defn read-navigation
  "Queries the navigation info of the active screen."
  [_ {:keys [screen]} _ _]
  {:value (:navigation screen)})

(defn read-layout
  "Executes all queries for views in the layout of the
   active screen."
  [_ {:keys [parser query screen] :as env} _ _]
  {:value (reduce (fn [res layout-item]
                    (let [[k v] (first layout-item)]
                      (assoc res k {:view (-> screen :layout
                                              k :factory)
                                    :props (parser (dissoc env :path)
                                                   v nil)})))
                  {} query)})

(defn- wrapping-read
  "Wraps a user-provided parser read function for Om Next by
   special-casing queries for screen-based routing information
   (such as :workflo/screen, :workflo/navigation and
   :workflo/layout.)"
  [read env key params]
  (case key
    :workflo/screen     (read-screen read env key params)
    :workflo/navigation (read-navigation read env key params)
    :workflo/layout     (read-layout read env key params)
    (read env key params)))

(defn parser
  "Returns a parser that wraps the provided read function to
   catch queries for screen-based routing information."
  [{:keys [read mutate]}]
  (om/parser {:read (partial wrapping-read read)
              :mutate mutate}))

;;;; Root component

(defonce ^:private +root-component+
  (atom {:factory nil :js? false}))

(defn root-component
  "Returns the root component to be used."
  []
  @+root-component+)

(defn set-root-component!
  "Configures the root component to be used."
  [factory js?]
  (swap! +root-component+ assoc
         :factory factory
         :js? js?))

;;;; Wrapping application

(defn- realize-layout
  "Realizes the results of a layout query by instantiating
   all returned views with their props / query results."
  [layout]
  (zipmap (keys layout)
          (map (fn [{:keys [view props] :as item}]
                 (view (vary-meta props assoc :om-path
                                  (-> item meta :om-path))))
               (vals layout))))

(defn camel-cased-prop-map
  "Convert a Clojure map with Om Next properties to a map
   where all keys are camel-cased strings that can be accessed
   like object properties in JS/React."
  [m]
  (walk (fn [[k v]]
          [(kebab->camel (name k))
           (cond-> v (map? v) camel-cased-prop-map)])
        identity
        m))

(defview RootWrapper
  [{workflo/screen [workflo [navigation layout]]}]
  (render
   (let [navigation (:workflo/navigation screen)
         layout     (realize-layout (:workflo/layout screen))
         root-info  (root-component)]
     ((:factory root-info)
      (if (:js? root-info)
        #js {:navigation (camel-cased-prop-map navigation)
             :layout (camel-cased-prop-map layout)}
        {:navigation navigation
         :layout layout})))))

;;;; Routing

(defn mount-screen
  "Mounts a screen with parameters by setting it as the active
   screen and updating the root wrapper's component query
   according to the screen's layout."
  [app screen params]
  (let [c     (om/app-root (:reconciler (:config app)))
        query [{:workflo/screen
                [:workflo/navigation
                 {:workflo/layout
                  (mapv (fn [[k v]]
                          {k (or (om/get-query (:view v))
                                 [])})
                        (:layout screen))}]}]]
    (set-active-screen! screen params)
    (om/set-query! c {:params params :query query})
    (some-> app :config :screen-mounted
            (apply [app screen params]))))

{'fraction_non_alphanumeric': 0.11658957106812448, 'fraction_numerical': 0.0, 'mean_word_length': 2.3146341463414632, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3822504', 'n_tokens_mistral': 2835, 'n_tokens_neox': 2686, 'n_words': 966}