Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: split up model building, extract contexts #147

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: split up model building, extract contexts
  • Loading branch information
erdos committed Apr 21, 2023
commit 6e0b879767d8f7965e8fbb30d8c6dc772dd9601f
117 changes: 55 additions & 62 deletions src/stencil/model.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
[stencil.model.numbering :as numbering]
[stencil.types :refer [->FragmentInvoke ->ReplaceImage]]
[stencil.postprocess.images :refer [img-data->extrafile]]
[stencil.util :refer [unlazy-tree assoc-if-val eval-exception]]
[stencil.util :refer [unlazy-tree eval-exception]]
[stencil.model.relations :as relations]
[stencil.model.common :refer [unix-path ->xml-writer resource-copier]]
[stencil.functions :refer [call-fn]]
[stencil.model.style :as style
:refer [expect-fragment-context! *current-styles*]]
[stencil.cleanup :as cleanup]))
[stencil.model.style :as style :refer [expect-fragment-context!]]
[stencil.cleanup :as cleanup]
[stencil.postprocess.fragments :as fragments]))

(set! *warn-on-reflection* true)

Expand All @@ -36,9 +36,6 @@
(def extra-relations
#{rel-type-footer rel-type-header rel-type-slide})

;; all insertable fragments. map of id to frag def.
(def ^:private ^:dynamic *all-fragments* nil)

;; set of already inserted fragment ids.
(def ^:private ^:dynamic *inserted-fragments* nil)

Expand Down Expand Up @@ -104,7 +101,7 @@
@*inserted-fragments*])]
(swap! *inserted-fragments* into fragments)
{:xml result
:fragment-names fragments
:fragment-names fragments ;; set of fragment names this model used
:writer (->xml-writer result)})))


Expand All @@ -122,46 +119,44 @@
(defn- eval-template-model [template-model data functions fragments]
(assert (:main template-model) "Should be a result of load-template-model call!")
(assert (some? fragments))
(binding [*current-styles* (atom (:parsed (:style (:main template-model))))
numbering/*numbering* (::numbering (:main template-model))
*inserted-fragments* (atom #{})
*extra-files* (atom #{})
*all-fragments* (into {} fragments)]
(let [evaluate (fn [m]
(let [result (eval-model-part m data functions)
fragment-names (set (:fragment-names result))]
(cond-> m
(style/with-template-styles template-model
(numbering/with-template-numberings template-model
(fragments/with-fragments-context fragments
(binding [*inserted-fragments* (atom #{})
*extra-files* (atom #{})]
(let [evaluate (fn [m]
(let [result (eval-model-part m data functions)
;; names of fragments
fragment-names (set (:fragment-names result))]
(cond-> m

;; create a rels file for the current xml
(and (seq @*extra-files*) (nil? (::path (:relations m))))
(assoc-in [:relations ::path]
(unix-path (file (.getParentFile (file (::path m)))
"_rels"
(str (.getName (file (::path m))) ".rels"))))
(and (seq @*extra-files*) (nil? (::path (:relations m))))
(assoc-in [:relations ::path]
(unix-path (file (.getParentFile (file (::path m)))
"_rels"
(str (.getName (file (::path m))) ".rels"))))

;; add relations if any
(seq @*extra-files*)
(update-in [:relations :parsed] (fnil into {})
(for [relation @*extra-files*
:when (or (not (contains? relation :fragment-name))
(contains? fragment-names (:fragment-name relation)))]
[(:new-id relation) relation]))
(seq @*extra-files*)
(update-in [:relations :parsed] (fnil into {})
(for [relation @*extra-files*
:when (or (not (contains? relation :fragment-name))
(contains? fragment-names (:fragment-name relation)))]
[(:new-id relation) relation]))

;; relation file will be rendered instead of copied
(seq @*extra-files*)
(update-in [:relations] dissoc :source-file)

:finally (assoc :result result))))]
(-> template-model
(update :main evaluate)
(update-in [:main :headers+footers] (partial mapv evaluate))
(seq @*extra-files*)
(update-in [:relations] dissoc :source-file)

(cond-> (-> template-model :main :style)
(assoc-in [:main :style :result] (style/file-writer template-model)))))))
:finally (assoc :result result))))]
(-> template-model
(update :main evaluate)
(update-in [:main :headers+footers] (partial mapv evaluate)))))))))


(defn- model-seq [model]
(let [model-keys [:relations :headers+footers :main :style :content-types :fragments ::numbering :result]]
(let [model-keys [:relations :headers+footers :main :style :content-types :fragments :stencil.model.numbering/numbering :result]]
(tree-seq map? (fn [node] (flatten (keep node model-keys))) model)))


Expand Down Expand Up @@ -190,7 +185,7 @@
(for [m (model-seq evaled-template-model)
:when (:relations m)
:let [src-parent (delay (file (or (:source-folder m)
(.getParentFile (file (:source-file m))))))
(.getParentFile (file (:source-file m))))))
path-parent (some-> m ::path file .getParentFile)]
relation (vals (:parsed (:relations m)))
:when (not= "External" (::mode relation))
Expand Down Expand Up @@ -222,29 +217,27 @@

(defmethod eval/eval-step :cmd/include [function local-data-map {frag-name :name}]
(assert (map? local-data-map))
(assert (string? frag-name))
(expect-fragment-context!
(if-let [fragment-model (get *all-fragments* frag-name)]
(let [;; merge style definitions from fragment
style-ids-rename (-> fragment-model :main :style :parsed (doto assert) (style/insert-styles!))

relation-ids-rename (relations/ids-rename fragment-model frag-name)
relation-rename-map (into {} (map (juxt :old-id :new-id)) relation-ids-rename)

;; evaluate
evaled (eval-template-model fragment-model local-data-map function {})

;; write back
get-xml (fn [x] (or (:xml x) @(:xml-delay x)))
evaled-parts (->> evaled :main :result
(get-xml)
(extract-body-parts)
(map (partial relations/xml-rename-relation-ids relation-rename-map))
(map (partial style/xml-rename-style-ids style-ids-rename)))]
(swap! *inserted-fragments* conj frag-name)
(run! add-extra-file! relation-ids-rename)
[{:text (->FragmentInvoke {:frag-evaled-parts evaled-parts})}])
(throw (eval-exception (str "No fragment for name: " frag-name) nil)))))
(let [fragment-model (fragments/fragment-by-name frag-name)

;; merge style definitions from fragment
style-ids-rename (-> fragment-model :main :style :parsed (doto assert) (style/insert-styles!))

relation-ids-rename (relations/ids-rename fragment-model frag-name)
relation-rename-map (into {} (map (juxt :old-id :new-id)) relation-ids-rename)

;; evaluate
evaled (eval-template-model fragment-model local-data-map function {})

;; write back
get-xml (fn [x] (or (:xml x) @(:xml-delay x)))
evaled-parts (->> evaled :main :result
(get-xml)
(extract-body-parts)
(map (partial relations/xml-rename-relation-ids relation-rename-map))
(map (partial style/xml-rename-style-ids style-ids-rename)))]
(swap! *inserted-fragments* conj frag-name)
(run! add-extra-file! relation-ids-rename)
[{:text (->FragmentInvoke {:frag-evaled-parts evaled-parts})}]))


;; replaces the nearest image with the content
Expand Down
7 changes: 6 additions & 1 deletion src/stencil/model/numbering.clj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
(def ^:dynamic *numbering* nil)


(defmacro with-template-numberings [template-model body]
`(binding [numbering/*numbering* (::numbering (:main ~template-model))]
~body))


(defn- find-node [tree predicate]
(when (map? tree)
(if (predicate tree)
Expand Down Expand Up @@ -79,7 +84,7 @@

(defn assoc-numbering [model dir]
(->> (main-numbering dir (:stencil.model/path model) (:relations model))
(assoc-if-val model :stencil.model/numbering)))
(assoc-if-val model ::numbering)))

(defn style-def-for [id lvl]
(assert (string? id))
Expand Down
7 changes: 7 additions & 0 deletions src/stencil/model/style.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

(set! *warn-on-reflection* true)

(declare file-writer)
(def rel-type
"Relationship type of style definitions in _rels/.rels file."
"http:https://schemas.openxmlformats.org/officeDocument/2006/relationships/styles")
Expand All @@ -20,6 +21,12 @@
;; throws error when not invoked from inside fragment context
(defmacro expect-fragment-context! [& bodies] `(do (assert *current-styles*) ~@bodies))

(defmacro with-template-styles [template-model body]
`(binding [*current-styles* (atom (:parsed (:style (:main ~template-model))))]
(let [modified-model# ~body]
(if (-> modified-model# :main :style)
(assoc-in modified-model# [:main :style :result] (file-writer modified-model#))
modified-model#))))

(defn- parse
"Returns a map where key is style id and value is style definition."
Expand Down
13 changes: 13 additions & 0 deletions src/stencil/postprocess/fragments.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@
[stencil.util :refer :all]))


;; all insertable fragments. map of id to frag def.
(def ^:dynamic *all-fragments* nil)

(defmacro with-fragments-context [fragments-map body]
`(binding [*all-fragments* (into {} ~fragments-map)] ~body))

(defn fragment-by-name
"Get fragment model by name in current context or throw exception."
[frag-name]
(assert (string? frag-name))
(or (get *all-fragments* frag-name)
(throw (eval-exception (str "No fragment for name: " frag-name) nil))))

(defn- remove+up
"Removes current node and moves pointer to parent node."
[loc]
Expand Down
4 changes: 2 additions & 2 deletions src/stencil/spec.clj
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

(s/def ::main (s/keys :req [:stencil.model/path]
:opt-un [:stencil.model/headers+footers ::result] ;; not present in fragments
:opt [::numbering]
:opt [:stencil.model.numbering/numbering]
:req-un [::source-file
::executable
::style
Expand All @@ -63,7 +63,7 @@

(s/def ::parsed any?)

(s/def :stencil.model/numbering (s/nilable (s/keys :req [:stencil.model/path]
(s/def :stencil.model.numbering/numbering (s/nilable (s/keys :req [:stencil.model/path]
:req-un [::source-file ::parsed])))

(s/fdef stencil.model/load-template-model
Expand Down