mirror of
https://github.com/status-im/pluto.git
synced 2025-02-24 16:38:16 +00:00
Moved to uniform types usage
This commit is contained in:
parent
229393d91e
commit
ead2d95b27
@ -57,8 +57,10 @@
|
||||
(defn parse [m]
|
||||
(reader/parse {:capacities {:components html/components
|
||||
:queries #{:random-boolean}
|
||||
:hooks {:main {:hook hook :properties {:view :view}}}
|
||||
:events {:log
|
||||
:hooks {:main
|
||||
{:hook hook
|
||||
:properties {:view :view}}}
|
||||
:events {:alert
|
||||
{:permissions [:read]
|
||||
:value []}}}}
|
||||
m))
|
||||
|
@ -4,14 +4,14 @@
|
||||
:documentation "Nothing. Just see a text with dynamic random color."}
|
||||
|
||||
hooks/main.demo
|
||||
{:view views/main}
|
||||
{:view [views/main]}
|
||||
|
||||
views/main
|
||||
(let [{name :name} properties]
|
||||
[view {}
|
||||
[button {:on-click [:alert {:value name}]}
|
||||
"Hello"]
|
||||
(let [{cond? :cond?} (query [:random-boolean])]
|
||||
(let [{cond? :cond?} [:random-boolean]]
|
||||
(if cond?
|
||||
[text {:style {:color "green"}}
|
||||
name]
|
||||
|
@ -1,11 +1,10 @@
|
||||
(ns pluto.components.html
|
||||
(:require [re-frame.core :as re-frame]))
|
||||
(ns pluto.components.html)
|
||||
|
||||
(defn view [props & content]
|
||||
(into [:div props] content))
|
||||
|
||||
(defn button [{:keys [on-click]} & content]
|
||||
(into [:button {:on-click #(re-frame/dispatch on-click)}] content))
|
||||
(into [:button {:on-click on-click}] content))
|
||||
|
||||
(defn text [props & content]
|
||||
(into [:span props] content))
|
||||
@ -14,9 +13,9 @@
|
||||
:value view
|
||||
:description ""
|
||||
:examples []}
|
||||
'button {:properties {}
|
||||
'button {:properties {:on-click :event}
|
||||
:value button
|
||||
:examples []}
|
||||
'text {:properties {:on-click :event}
|
||||
'text {:properties {}
|
||||
:value text
|
||||
:examples []}})
|
||||
|
@ -10,12 +10,13 @@
|
||||
# Activate
|
||||
* based on hooks, inject views / trigger events"
|
||||
(:refer-clojure :exclude [read])
|
||||
(:require [clojure.set :as set]
|
||||
[clojure.spec.alpha :as spec]
|
||||
(:require [clojure.set :as set]
|
||||
[clojure.spec.alpha :as spec]
|
||||
[clojure.tools.reader.edn :as edn]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.hooks :as hooks]
|
||||
[pluto.utils :as utils]))
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.hooks :as hooks]
|
||||
pluto.reader.views
|
||||
[pluto.utils :as utils]))
|
||||
|
||||
(defn reader-error [ex]
|
||||
(errors/error ::errors/reader-error (:ex-kind (ex-data ex))
|
||||
@ -99,16 +100,16 @@
|
||||
|
||||
(defn ^:export parse
|
||||
"Parse an extension definition map as encapsulated in :data key of the map returned by read.
|
||||
`opts` is a map defining:
|
||||
`ctx` is a map defining:
|
||||
* `capacities` a map of valid supported capacities (hooks, queries, events)
|
||||
|
||||
Returns a map defining:
|
||||
* :data a map of meta and parsed hooks
|
||||
* :permissions a vector of required permissions
|
||||
* :errors a vector of errors maps triggered during parse"
|
||||
[opts m]
|
||||
(let [errors (validate opts m)]
|
||||
[ctx m]
|
||||
(let [errors (validate ctx m)]
|
||||
(errors/merge-results
|
||||
(parse-meta ('meta m))
|
||||
(hooks/parse opts m)
|
||||
(hooks/parse ctx m)
|
||||
{:errors errors})))
|
||||
|
@ -1,51 +1,47 @@
|
||||
(ns pluto.reader.blocks
|
||||
(:require [clojure.walk :as walk]
|
||||
[re-frame.core :as re-frame]
|
||||
[pluto.reader.destructuring :as destructuring]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.reference :as reference]
|
||||
[pluto.reader.permissions :as permissions]
|
||||
[re-frame.core :as re-frame]))
|
||||
[pluto.reader.types :as types]))
|
||||
|
||||
(defmulti parse
|
||||
"Parse a block element. Return hiccup data."
|
||||
(fn [_ [type]] type))
|
||||
|
||||
(defn resolve-query [[_ [sub-keyword sub-args :as sub]]]
|
||||
(when-let [o (re-frame/subscribe sub)]
|
||||
@o))
|
||||
(defn resolve-query [query]
|
||||
(let [{:keys [data]} (types/resolve {} {} :query query)]
|
||||
(data)))
|
||||
|
||||
(defn- query? [binding-value]
|
||||
(and (list? binding-value)
|
||||
(= 'query (first binding-value))))
|
||||
(vector? binding-value))
|
||||
|
||||
(defn resolve-binding-value [v]
|
||||
;; TODO resolve query statically
|
||||
(cond
|
||||
(query? v) (resolve-query v)
|
||||
(not (list? v)) v))
|
||||
|
||||
(defn resolve-binding-key [k v]
|
||||
(cond
|
||||
(symbol? k) k
|
||||
:else (:data (destructuring/destructure k v))))
|
||||
(if (symbol? k)
|
||||
k
|
||||
;; TODO handle errors
|
||||
(:data (destructuring/destructure k v))))
|
||||
|
||||
(defn assoc-binding [m k v]
|
||||
(let [resolved-value (resolve-binding-value v)]
|
||||
(let [o (resolve-binding-key k resolved-value)]
|
||||
(cond
|
||||
(symbol? o)
|
||||
(if (symbol? o)
|
||||
(assoc m o resolved-value)
|
||||
:else
|
||||
(merge m o)))))
|
||||
|
||||
(defn resolve-bindings [env]
|
||||
(reduce-kv assoc-binding {} env))
|
||||
|
||||
(defn let-block [{:keys [env]} child]
|
||||
(cond
|
||||
(coll? child) (walk/prewalk-replace (resolve-bindings env) child)))
|
||||
(coll? child) (walk/prewalk-replace (reduce-kv assoc-binding {} env) child)))
|
||||
|
||||
(defn properties? [o]
|
||||
(and (reference/reference? o) (= 'properties (reference/reference->symbol o))))
|
||||
(= 'properties o))
|
||||
|
||||
(defn inject-new-bindings [m v]
|
||||
(cond
|
||||
@ -64,40 +60,20 @@
|
||||
{:errors [(errors/error ::errors/invalid-destructuring-format [k av])]})))))
|
||||
|
||||
(defn bindings->env [bindings]
|
||||
(cond
|
||||
(odd? (count bindings))
|
||||
(if (odd? (count bindings))
|
||||
{:errors [(errors/error ::errors/invalid-destructuring-format bindings)]}
|
||||
:else
|
||||
(reduce-kv merge-bindings {} (apply hash-map bindings))))
|
||||
|
||||
(defn- restrict-get-in [path {:keys [read]}]
|
||||
(when-not (permissions/allowed-path? path read)
|
||||
(errors/error ::errors/forbidden-read-path path)))
|
||||
|
||||
(defn- restrict-queries
|
||||
"Parse time enforcement of valid queries, checks that query
|
||||
is exposed via host platform + enforces valid query vectors for
|
||||
`:get-in` query."
|
||||
[{:keys [permissions queries]} env]
|
||||
(keep (fn [[_ env-value]]
|
||||
(when (query? env-value)
|
||||
(let [[_ [sub-name sub-args]] env-value]
|
||||
(if (not (contains? queries sub-name))
|
||||
(errors/error ::errors/query-not-exposed sub-name)
|
||||
(when (= :get-in sub-name)
|
||||
(restrict-get-in sub-args permissions))))))
|
||||
env))
|
||||
|
||||
(defmethod parse 'let [{:keys [capacities]} [_ bindings & body]]
|
||||
(let [{:keys [data errors]} (bindings->env bindings)
|
||||
query-errors (restrict-queries capacities data)]
|
||||
(errors/merge-errors
|
||||
(let [{:keys [data errors]} (bindings->env bindings)]
|
||||
;; TODO fail if some symbol are not defined in the env
|
||||
;; TODO resolve query references only once, error if unknown
|
||||
(merge
|
||||
{:data
|
||||
(let [child (last body)]
|
||||
[let-block {:env data} child])}
|
||||
(concat errors query-errors))))
|
||||
(when errors
|
||||
{:errors errors}))))
|
||||
|
||||
(defn when-block [{:keys [test]} body]
|
||||
;; TODO warning if test is not of boolean type
|
||||
|
@ -1,20 +1,22 @@
|
||||
(ns pluto.reader.destructuring
|
||||
(:refer-clojure :exclude [destructure])
|
||||
(:require [clojure.walk :as walk]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.reference :as reference]
|
||||
[pluto.reader.permissions :as permissions]
|
||||
[re-frame.core :as re-frame]))
|
||||
|
||||
(defn symbol-afer-as? [bindings idx]
|
||||
(and (pos? idx) (= :as (nth bindings (dec idx)))))
|
||||
(:require [pluto.reader.errors :as errors]))
|
||||
|
||||
(declare destructure-assoc destructure-seq)
|
||||
|
||||
(defn indexed-bindings [bindings]
|
||||
(into {} (map-indexed vector bindings)))
|
||||
(defn- valid-bindings-form? [o]
|
||||
(or (symbol? o) (vector? o) (map? o) (= :as o)))
|
||||
|
||||
(defn merge-seq-bindings [bindings s m idx value]
|
||||
(defn- seq-bindings-size [bindings]
|
||||
(let [size (count bindings)]
|
||||
(if (some #{:as} bindings)
|
||||
(- size 2)
|
||||
size)))
|
||||
|
||||
(defn- symbol-afer-as? [bindings idx]
|
||||
(and (pos? idx) (= :as (nth bindings (dec idx)))))
|
||||
|
||||
(defn- merge-seq-bindings [bindings s m idx value]
|
||||
(cond
|
||||
(or (= :as value) (= '_ value)) m
|
||||
(symbol-afer-as? bindings idx) (assoc-in m [:data value] s)
|
||||
@ -23,26 +25,17 @@
|
||||
(map? value) (errors/merge-results m (destructure-assoc value (nth s idx)))
|
||||
(sequential? value) (errors/merge-results m (destructure-seq value (nth s idx)))))
|
||||
|
||||
(defn seq-bindings-size [bindings]
|
||||
(let [size (count bindings)]
|
||||
(if (some #{:as} bindings)
|
||||
(- size 2)
|
||||
size)))
|
||||
|
||||
(defn valid-bindings-form? [o]
|
||||
(or (symbol? o) (vector? o) (map? o) (= :as o)))
|
||||
(defn- valid-seq-format? [bindings s]
|
||||
(and (sequential? bindings)
|
||||
(every? valid-bindings-form? bindings)
|
||||
(<= (seq-bindings-size bindings) (count s))))
|
||||
|
||||
(defn destructure-seq [bindings s]
|
||||
(cond
|
||||
(or
|
||||
(not (sequential? bindings))
|
||||
(not (every? valid-bindings-form? bindings))
|
||||
(> (seq-bindings-size bindings) (count s)))
|
||||
{:errors [(errors/error ::errors/invalid-destructuring-format {:type :sequential :data bindings})]}
|
||||
:else
|
||||
(reduce-kv #(merge-seq-bindings bindings s %1 %2 %3) {} (indexed-bindings bindings))))
|
||||
(if (valid-seq-format? bindings s)
|
||||
(reduce-kv #(merge-seq-bindings bindings s %1 %2 %3) {} (into {} (map-indexed vector bindings)))
|
||||
{:errors [(errors/error ::errors/invalid-destructuring-format {:type :sequential :data bindings})]}))
|
||||
|
||||
(defn merge-assoc-bindings [s m k v]
|
||||
(defn- merge-assoc-bindings [s m k v]
|
||||
(cond
|
||||
(vector? v) (assoc-in m [:data k] (or ((first v) s) (second v)))
|
||||
(symbol? k) (assoc-in m [:data k] (v s))
|
||||
@ -51,16 +44,19 @@
|
||||
(map? k) (errors/merge-results m (destructure-assoc k (v s)))
|
||||
(sequential? k) (errors/merge-results m (destructure-seq k (v s)))))
|
||||
|
||||
(defn- valid-assoc-format? [bindings]
|
||||
(and (map? bindings)
|
||||
(every? valid-bindings-form? (keys bindings))))
|
||||
|
||||
(defn destructure-assoc [bindings s]
|
||||
(cond
|
||||
(or
|
||||
(not (map? bindings))
|
||||
(not (every? valid-bindings-form? (keys bindings))))
|
||||
{:errors [(errors/error ::errors/invalid-destructuring-format {:type :assoc :data bindings})]}
|
||||
:else
|
||||
(reduce-kv #(merge-assoc-bindings s %1 %2 %3) {} bindings)))
|
||||
(if (valid-assoc-format? bindings)
|
||||
(reduce-kv #(merge-assoc-bindings s %1 %2 %3) {} bindings)
|
||||
{:errors [(errors/error ::errors/invalid-destructuring-format {:type :assoc :data bindings})]}))
|
||||
|
||||
(defn destructure
|
||||
"Given a pattern and an associated data structure, return a map of either:
|
||||
* :data, a map of extracted symbol / value pairs
|
||||
* :errors, a vector of errors encountered during the destructuring"
|
||||
[bindings s]
|
||||
(cond
|
||||
(sequential? s) (destructure-seq bindings s)
|
||||
|
@ -12,20 +12,17 @@
|
||||
::invalid-type-value
|
||||
::invalid-sequential-type
|
||||
::invalid-assoc-type
|
||||
::missing-property-name
|
||||
::missing-property-value
|
||||
::invalid-reference
|
||||
::invalid-destructuring-format
|
||||
::missing-keys
|
||||
::unknown-reference
|
||||
::unknown-component
|
||||
::unknown-component-property
|
||||
::invalid-view
|
||||
::invalid-block
|
||||
::unsupported-test-type
|
||||
::forbidden-read-path
|
||||
::forbidden-write-path
|
||||
::query-not-exposed
|
||||
::invalid-event-handler
|
||||
::event-not-exposed})
|
||||
::query-not-exposed})
|
||||
|
||||
(spec/def ::value any?)
|
||||
|
||||
|
@ -1,104 +1,13 @@
|
||||
(ns pluto.reader.hooks
|
||||
(:require [clojure.string :as string]
|
||||
[clojure.set :as set]
|
||||
[pluto.reader.destructuring :as destructuring]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.reference :as reference]
|
||||
[pluto.reader.views :as views]))
|
||||
(:require [clojure.string :as string]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.types :as types]))
|
||||
|
||||
(defprotocol Hook
|
||||
"Encapsulate hook lifecycle."
|
||||
(hook-in [this id properties cofx] "Hook it into host app.")
|
||||
(unhook [this id properties cofx] "Remove extension hook from app."))
|
||||
|
||||
(defmulti resolve-property
|
||||
(fn [{:keys [type]} _ _ _]
|
||||
(cond
|
||||
(keyword? type) type
|
||||
(:one-of type) :set
|
||||
(set? type) :subset
|
||||
(map? type) :map
|
||||
(vector? type) :vector)))
|
||||
|
||||
(defn inject-properties [m properties]
|
||||
(if-let [ps (get-in m [:env 'properties])]
|
||||
(let [{:keys [data errors]} (destructuring/destructure ps properties)]
|
||||
(errors/merge-errors
|
||||
{:data
|
||||
(-> (update m :env dissoc 'properties)
|
||||
(update :env merge data))}
|
||||
errors))
|
||||
{:data m}))
|
||||
|
||||
(defn hiccup-with-properties [h properties]
|
||||
(if (vector? h)
|
||||
(let [[tag & properties-children] h
|
||||
[props children] (views/resolve-properties-children properties-children)
|
||||
{:keys [data]} (when properties
|
||||
(inject-properties props properties))]
|
||||
(apply conj (if data [tag data] [tag])
|
||||
(map #(hiccup-with-properties % properties) children)))
|
||||
h))
|
||||
|
||||
(defmethod resolve-property :view [def hook opts m]
|
||||
(let [{:keys [data] :as m} (reference/resolve m def hook)
|
||||
{:keys [data errors]} (views/parse opts data)]
|
||||
;; TODO Might fail at runtime if destructuring is incorrect
|
||||
(errors/merge-errors (when data {:data (fn [o] (hiccup-with-properties data o))})
|
||||
(concat errors (:errors m)))))
|
||||
|
||||
(defn- resolve-capacities-value [key error-key capacities {:keys [name optional?]} hook]
|
||||
(if-let [value (get hook name)]
|
||||
(if-let [component ((get capacities key) value)]
|
||||
{:data component}
|
||||
{:errors [(errors/error error-key value)]})
|
||||
(when-not optional?
|
||||
{:errors [(errors/error ::errors/invalid-type-name name)]})))
|
||||
|
||||
(defmethod resolve-property :component [def hook {:keys [capacities]} _]
|
||||
(resolve-capacities-value :components ::errors/unknown-component capacities def hook))
|
||||
|
||||
(defmethod resolve-property :event [def hook {:keys [capacities]} m]
|
||||
(resolve-capacities-value :events ::errors/event-not-exposed capacities def hook))
|
||||
|
||||
(defmethod resolve-property :query [def hook {:keys [capacities]} _]
|
||||
(resolve-capacities-value :queries ::errors/query-not-exposed capacities def hook))
|
||||
|
||||
(defn- resolve-property-value [f {:keys [name optional?]} hook]
|
||||
(if-let [o (get hook name)]
|
||||
(if (f o)
|
||||
{:data o}
|
||||
{:errors [(errors/error ::errors/invalid-type-value o)]})
|
||||
(when-not optional?
|
||||
{:errors [(errors/error ::errors/invalid-type-name name)]})))
|
||||
|
||||
(defmethod resolve-property :string [def hook _ _]
|
||||
(resolve-property-value string? def hook))
|
||||
|
||||
(defmethod resolve-property :keyword [def hook _ _]
|
||||
(resolve-property-value keyword? def hook))
|
||||
|
||||
(defmethod resolve-property :set [def hook _ _]
|
||||
(resolve-property-value (-> def :type :one-of) def hook))
|
||||
|
||||
(defmethod resolve-property :subset [def hook _ _]
|
||||
(resolve-property-value #(set/subset? % (:type def)) def hook))
|
||||
|
||||
(declare parse-properties)
|
||||
|
||||
(defn map->properties [m]
|
||||
(reduce-kv #(conj %1 {:name %2 :type %3}) [] m))
|
||||
|
||||
(defmethod resolve-property :map [def hook opts m]
|
||||
(parse-properties (map->properties (:type def)) ((:name def) hook) opts m))
|
||||
|
||||
(defmethod resolve-property :vector [def hook opts m]
|
||||
(let [props (map->properties (first (:type def)))]
|
||||
(apply errors/merge-results-with #(conj (vec %1) %2) (map #(parse-properties props % opts m) ((:name def) hook)))))
|
||||
|
||||
(defmethod resolve-property :default [{:keys [type]} _ _ _]
|
||||
{:errors [(errors/error ::errors/invalid-type type)]})
|
||||
|
||||
(defn hook? [s]
|
||||
(= "hooks" (namespace s)))
|
||||
|
||||
@ -111,35 +20,20 @@
|
||||
(defn root-id [s]
|
||||
(keyword (first (string/split (name s) #"\."))))
|
||||
|
||||
(defn properties [opts s]
|
||||
(reduce-kv #(conj %1 {:name %2 :type %3}) []
|
||||
(get-in opts [:capacities :hooks s :properties])))
|
||||
(defn parse-hook [ctx ext hook v]
|
||||
(types/resolve ctx ext (:properties hook) v))
|
||||
|
||||
(defn normalize-property [{:keys [name] :as prop}]
|
||||
(let [normalized-name (keyword (string/replace (clojure.core/name name) "?" ""))]
|
||||
(assoc prop
|
||||
:name normalized-name
|
||||
:optional? (not= name normalized-name))))
|
||||
|
||||
(defn parse-properties [props v opts m]
|
||||
(reduce #(let [{:keys [data errors]} (resolve-property (normalize-property %2) v opts m)]
|
||||
(errors/merge-errors (if data (assoc-in %1 [:data (:name (normalize-property %2))] data) %1) errors))
|
||||
{} props))
|
||||
|
||||
(defn parse-hook [hook v opts m]
|
||||
(parse-properties (map->properties (:properties hook)) v opts m))
|
||||
|
||||
(defn parse [opts m]
|
||||
(defn parse [ctx ext]
|
||||
(reduce-kv (fn [acc hook-key data]
|
||||
(let [hook-id (local-id hook-key)
|
||||
hook-root (root-id hook-key)
|
||||
hook (get-in opts [:capacities :hooks hook-root])
|
||||
{:keys [data errors]} (parse-hook hook data opts m)]
|
||||
hook (get-in ctx [:capacities :hooks hook-root])
|
||||
{:keys [data errors] :as m} (parse-hook ctx ext hook data)]
|
||||
(errors/merge-errors
|
||||
(-> acc
|
||||
(assoc-in [:data :hooks hook-root hook-id :parsed] data)
|
||||
(assoc-in [:data :hooks hook-root hook-id :hook-ref] hook))
|
||||
errors)))
|
||||
{}
|
||||
(select-keys m (hooks m))))
|
||||
(select-keys ext (hooks ext))))
|
||||
|
||||
|
@ -5,44 +5,30 @@
|
||||
(defn reference?
|
||||
"Return true if argument is a reference"
|
||||
[o]
|
||||
(symbol? o))
|
||||
(and (vector? o)
|
||||
(let [c (count o)]
|
||||
(or (= 1 c) (= 2 c)))
|
||||
(symbol? (first o))))
|
||||
|
||||
(defn reference->symbol
|
||||
"Return the symbol pointed by the reference
|
||||
|
||||
```clojure
|
||||
(= 'some.ref (reference->name 'views/some.ref))
|
||||
(= 'some.ref (reference->symbol ['views/some.ref]))
|
||||
```"
|
||||
[o]
|
||||
(when (reference? o)
|
||||
o))
|
||||
|
||||
(def ns->type {"views" :view "queries" :query "events" :event})
|
||||
|
||||
(defn reference->type
|
||||
"Return the type of a reference
|
||||
|
||||
```clojure
|
||||
(= :view (reference->type 'views/some.ref))
|
||||
```"
|
||||
[o]
|
||||
(when (reference? o)
|
||||
(when-let [ns (namespace (reference->symbol o))]
|
||||
(get ns->type ns))))
|
||||
(first o)))
|
||||
|
||||
(defn resolve
|
||||
"Resolve a reference defined by a hook
|
||||
|
||||
```clojure
|
||||
(= {:data \"view\"} (resolve {'views/id \"view\"} {:name :view :type :view} {:view 'views/id}))
|
||||
(= {:data \"view\"} (resolve {'views/id \"view\"} ['views/id]))
|
||||
```"
|
||||
[m {:keys [name type optional?]} hook]
|
||||
(let [ref (get hook name)]
|
||||
(if ref
|
||||
(if (= type (reference->type ref))
|
||||
(if-let [o (get m (reference->symbol ref))]
|
||||
{:data o}
|
||||
{:errors [(errors/error ::errors/missing-property-value name)]})
|
||||
{:errors [(errors/error ::errors/invalid-type type)]})
|
||||
(when-not optional?
|
||||
{:errors [(errors/error ::errors/missing-property-name type)]}))))
|
||||
[ext value]
|
||||
(if-let [s (reference->symbol value)]
|
||||
(if-let [o (get ext s)]
|
||||
{:data o}
|
||||
{:errors [(errors/error ::errors/unknown-reference {:value s})]})
|
||||
{:errors [(errors/error ::errors/invalid-reference {:value value})]}))
|
||||
|
@ -4,6 +4,7 @@
|
||||
(:refer-clojure :exclude [resolve])
|
||||
(:require [clojure.string :as string]
|
||||
[clojure.set :as set]
|
||||
[re-frame.core :as re-frame]
|
||||
[pluto.reader.errors :as errors]))
|
||||
|
||||
(defmulti resolve
|
||||
@ -11,7 +12,7 @@
|
||||
Returns a map of either:
|
||||
* data with the resolved data
|
||||
* errors encapsulating all errors generated during resolution"
|
||||
(fn [ctx type value]
|
||||
(fn [ctx ext type value]
|
||||
(cond
|
||||
(keyword? type) type
|
||||
(:one-of type) :one-of
|
||||
@ -22,33 +23,29 @@
|
||||
(defn invalid-type-value [type value]
|
||||
(errors/error ::errors/invalid-type-value {:type type :value value}))
|
||||
|
||||
(defmethod resolve :string [_ _ value]
|
||||
(defmethod resolve :string [_ _ _ value]
|
||||
(if (string? value)
|
||||
{:data value}
|
||||
{:errors [(invalid-type-value :string value)]}))
|
||||
|
||||
(defmethod resolve :keyword [_ _ value]
|
||||
(defmethod resolve :keyword [_ _ _ value]
|
||||
(if (keyword? value)
|
||||
{:data value}
|
||||
{:errors [(invalid-type-value :keyword value)]}))
|
||||
|
||||
(defmethod resolve :subset [_ type value]
|
||||
(println ">>" value type (not (nil? value)) (set/subset? value type))
|
||||
(defmethod resolve :subset [_ _ type value]
|
||||
(if (and (not (nil? value)) (set/subset? value type))
|
||||
{:data value}
|
||||
{:errors [(invalid-type-value :subset value)]}))
|
||||
|
||||
(defmethod resolve :one-of [_ {:keys [one-of]} value]
|
||||
(defmethod resolve :one-of [_ _ {:keys [one-of]} value]
|
||||
(if-let [o (one-of value)]
|
||||
{:data o}
|
||||
{:errors [(invalid-type-value :one-of value)]}))
|
||||
|
||||
(defmethod resolve :default [_ value _]
|
||||
{:errors [(errors/error ::errors/invalid-type {:value value})]})
|
||||
|
||||
(defmethod resolve :sequence [ctx type value]
|
||||
(defmethod resolve :sequence [ctx ext type value]
|
||||
(if (and (vector? type) (= 1 (count type)) (map? (first type)))
|
||||
(apply errors/merge-results-with #(conj (vec %1) %2) (map #(resolve ctx (first type) %) value))
|
||||
(apply errors/merge-results-with #(conj (vec %1) %2) (map #(resolve ctx ext (first type) %) value))
|
||||
{:errors [(errors/error ::errors/invalid-sequential-type {:type type :value value})]}))
|
||||
|
||||
(def ^:private sentinel ::sentinel)
|
||||
@ -59,18 +56,28 @@
|
||||
:name normalized-name
|
||||
:optional? (not= name normalized-name)}))
|
||||
|
||||
(defn- resolve-property [ctx m {:keys [name optional? value]} type]
|
||||
(defn- resolve-property [ctx ext m {:keys [name optional? value]} type]
|
||||
(if (not= sentinel value)
|
||||
(let [{:keys [data errors]} (resolve ctx type value)]
|
||||
(let [{:keys [data errors]} (resolve ctx ext type value)]
|
||||
(errors/merge-errors
|
||||
(if data (assoc-in m [:data name] data) m)
|
||||
errors))
|
||||
(if-not optional?
|
||||
{:errors [(errors/error ::errors/invalid-type-value {:type type :value value})]}
|
||||
{:data {}})))
|
||||
(if optional?
|
||||
(update m :data #(if (empty? %) {} %))
|
||||
(assoc m :errors [(errors/error ::errors/invalid-type-name name)]))))
|
||||
|
||||
(defmethod resolve :assoc [ctx type value]
|
||||
(defmethod resolve :assoc [ctx ext type value]
|
||||
(if (map? type)
|
||||
(reduce-kv #(resolve-property ctx %1 (property %2 value) %3)
|
||||
(reduce-kv #(resolve-property ctx ext %1 (property %2 value) %3)
|
||||
{} type)
|
||||
{:errors [(errors/error ::errors/invalid-assoc-type {:type type :value value})]}))
|
||||
|
||||
(defmethod resolve :event [_ _ _ value]
|
||||
{:data #(re-frame/dispatch value)})
|
||||
|
||||
(defmethod resolve :query [ctx _ _ value]
|
||||
{:data #(when-let [o (re-frame/subscribe value)]
|
||||
@o)})
|
||||
|
||||
(defmethod resolve :default [_ _ type value]
|
||||
{:errors [(errors/error ::errors/invalid-type (merge {:type type} (when value {:value value})))]})
|
||||
|
@ -1,10 +1,12 @@
|
||||
(ns pluto.reader.views
|
||||
(:require [clojure.spec.alpha :as spec]
|
||||
[pluto.reader.blocks :as blocks]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.permissions :as permissions]
|
||||
[pluto.utils :as utils]
|
||||
[re-frame.core :as re-frame]))
|
||||
(:require [clojure.spec.alpha :as spec]
|
||||
[pluto.reader.blocks :as blocks]
|
||||
[pluto.reader.destructuring :as destructuring]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.permissions :as permissions]
|
||||
[pluto.reader.reference :as reference]
|
||||
[pluto.reader.types :as types]
|
||||
[pluto.utils :as utils]))
|
||||
|
||||
;; TODO Distinguish views (can contain blocks, symbols) validation
|
||||
;; from hiccup validation (view after parsing) that are pure hiccup
|
||||
@ -22,63 +24,56 @@
|
||||
:attrs map?
|
||||
:children (spec/* ::form)))
|
||||
|
||||
;; TODO add all possible event handlers
|
||||
(def ^:private event-handler->selector
|
||||
{:on-press (constantly true)
|
||||
:on-change #(.-text (.-nativeEvent %))})
|
||||
|
||||
(declare parse)
|
||||
|
||||
(defn parse-hiccup-children [opts children]
|
||||
(reduce #(let [{:keys [data errors]} (parse opts %2)]
|
||||
(defn parse-hiccup-children [ctx ext children]
|
||||
(reduce #(let [{:keys [data errors]} (parse ctx ext %2)]
|
||||
(errors/merge-errors (update %1 :data conj data)
|
||||
errors))
|
||||
{:data []} children))
|
||||
|
||||
(defn resolve-component [{:keys [components]} o]
|
||||
(defn component? [o]
|
||||
(symbol? o))
|
||||
|
||||
(defn- resolve-component [ctx o]
|
||||
(cond
|
||||
(fn? o) o
|
||||
(symbol? o) (:value (get components o))))
|
||||
(symbol? o) (get-in ctx [:capacities :components o :value])))
|
||||
|
||||
(defn- event? [prop-value]
|
||||
(and (list? prop-value)
|
||||
(= 'event (first prop-value))))
|
||||
(defmulti resolve-default-component-properties (fn [property value] property))
|
||||
|
||||
(defn- create-event-handler [re-frame-event selector]
|
||||
(fn [event-value]
|
||||
(re-frame/dispatch (conj re-frame-event (selector event-value)))))
|
||||
(defmethod resolve-default-component-properties :style [_ value]
|
||||
{:data value})
|
||||
|
||||
(defn- resolve-component-properties [component {:keys [permissions events] :as capacities} properties]
|
||||
; (println ":" properties (get-in capacities [:components component :properties]))
|
||||
; TODO validate component properties based on capacities
|
||||
(reduce (fn [acc [k v]]
|
||||
(if (contains? event-handler->selector k)
|
||||
(if (not (event? v))
|
||||
(update acc :errors conj (errors/error ::errors/invalid-event-handler v))
|
||||
(let [[_ [event-name event-args] :as re-frame-event] v]
|
||||
(cond
|
||||
(defmethod resolve-default-component-properties :default [_ value]
|
||||
nil)
|
||||
|
||||
(not (contains? events event-name))
|
||||
(update acc :errors conj (errors/error ::errors/event-not-exposed event-name))
|
||||
(defn- resolve-component-property [ctx ext component k v]
|
||||
(or (resolve-default-component-properties k v)
|
||||
(if-let [type (k (get-in ctx [:capacities :components component :properties]))]
|
||||
(types/resolve ctx ext type v)
|
||||
{:errors [(errors/error ::errors/unknown-component-property {:component component :property k})]})))
|
||||
|
||||
(not (permissions/allowed-path? event-args (:write permissions)))
|
||||
(update acc :errors conj (errors/error ::errors/forbidden-write-path event-args))
|
||||
(defn- resolve-property [ctx ext component k v]
|
||||
(if (component? component)
|
||||
(resolve-component-property ctx ext component k v)
|
||||
{:data v}))
|
||||
|
||||
:else
|
||||
(update acc :data assoc k (create-event-handler re-frame-event
|
||||
(get event-handler->selector k))))))
|
||||
(update acc :data assoc k v)))
|
||||
{:data {}
|
||||
:errors []}
|
||||
properties))
|
||||
(defn- resolve-component-properties [ctx ext component properties]
|
||||
(reduce-kv (fn [acc k v]
|
||||
(let [{:keys [data errors]} (resolve-property ctx ext component k v)]
|
||||
(-> (update acc :data assoc k data))))
|
||||
{:data {}
|
||||
:errors []}
|
||||
properties))
|
||||
|
||||
(defn resolve-properties-children [[properties? & children]]
|
||||
(defn- resolve-properties-children [[properties? & children]]
|
||||
[(and (map? properties?) properties?)
|
||||
(if (map? properties?)
|
||||
children
|
||||
(cons properties? children))])
|
||||
|
||||
(defn parse-hiccup-element [{:keys [capacities] :as opts} o]
|
||||
(defn- parse-hiccup-element [ctx ext o]
|
||||
(let [explain (spec/explain-data ::form o)]
|
||||
(cond
|
||||
;; TODO Validate views, not hiccup
|
||||
@ -90,10 +85,10 @@
|
||||
(vector? o)
|
||||
(let [[element & properties-children] o
|
||||
[properties children] (resolve-properties-children properties-children)
|
||||
component (resolve-component capacities element)
|
||||
component (resolve-component ctx element)
|
||||
{:keys [data errors]} (when properties
|
||||
(resolve-component-properties element capacities properties))]
|
||||
(cond-> (let [m (parse-hiccup-children opts children)]
|
||||
(resolve-component-properties ctx ext element properties))]
|
||||
(cond-> (let [m (parse-hiccup-children ctx ext children)]
|
||||
;; Reduce parsed children to a single map and wrap them in a hiccup element
|
||||
;; whose component has been translated to the local platform
|
||||
(update m :data #(apply conj (if data [(or component element) data]
|
||||
@ -102,12 +97,42 @@
|
||||
(nil? component) (errors/accumulate-errors [(errors/error ::errors/unknown-component element)])
|
||||
(seq errors) (errors/accumulate-errors errors))))))
|
||||
|
||||
(defn parse [opts o]
|
||||
(defn parse [ctx ext o]
|
||||
(cond
|
||||
(list? o)
|
||||
(let [{:keys [data errors]} (blocks/parse opts o)]
|
||||
(let [{:keys [data errors]} (blocks/parse ctx o)]
|
||||
(if errors
|
||||
{:errors errors}
|
||||
(parse opts data)))
|
||||
(parse ctx ext data)))
|
||||
:else
|
||||
(parse-hiccup-element opts o)))
|
||||
(parse-hiccup-element ctx ext o)))
|
||||
|
||||
(defn- inject-properties [m properties]
|
||||
(if-let [ps (get-in m [:env 'properties])]
|
||||
(let [{:keys [data errors]} (destructuring/destructure ps properties)]
|
||||
(errors/merge-errors
|
||||
{:data
|
||||
(-> m
|
||||
(update :env dissoc 'properties)
|
||||
(update :env merge data))}
|
||||
errors))
|
||||
{:data m}))
|
||||
|
||||
(defn- hiccup-with-properties [h properties]
|
||||
(if (vector? h)
|
||||
(let [[tag & properties-children] h
|
||||
[props children] (resolve-properties-children properties-children)
|
||||
{:keys [data]} (when properties
|
||||
(inject-properties props properties))]
|
||||
(apply conj (if data [tag data] [tag])
|
||||
(map #(hiccup-with-properties % properties) children)))
|
||||
h))
|
||||
|
||||
(defmethod types/resolve :view [ctx ext type value]
|
||||
(let [{:keys [data errors]} (reference/resolve ext value)]
|
||||
(if data
|
||||
(let [{:keys [data errors]} (parse ctx ext data)]
|
||||
;; TODO Might fail at runtime if destructuring is incorrect
|
||||
(errors/merge-errors (when data {:data (fn [o] (hiccup-with-properties data o))})
|
||||
(concat errors (:errors ext))))
|
||||
{:errors errors})))
|
||||
|
@ -1,6 +1,6 @@
|
||||
(ns pluto.storage.ipfs
|
||||
(:require [clojure.string :as string]
|
||||
[pluto.storage :as storage]))
|
||||
[pluto.storage :as storage]))
|
||||
|
||||
(defn result [xhr]
|
||||
(let [status (.-status xhr)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
(ns pluto.storages
|
||||
(:require [clojure.string :as string]
|
||||
[pluto.storage :as storage]
|
||||
(:require [clojure.string :as string]
|
||||
[pluto.storage :as storage]
|
||||
[pluto.storage.http :as http]
|
||||
[pluto.storage.gist :as gist]
|
||||
[pluto.storage.ipfs :as ipfs]))
|
||||
|
@ -11,19 +11,19 @@
|
||||
(is (= {:errors [{:pluto.reader.errors/type :pluto.reader.errors/invalid-destructuring-format
|
||||
:pluto.reader.errors/value '[1 2]}]}
|
||||
(blocks/bindings->env '[1 2])))
|
||||
(is (= {:data '{a 1}} (blocks/bindings->env '[[a] [1]]))))
|
||||
(is (= {:data '{x 1}} (blocks/bindings->env '[{x :x} {:x 1}]))))
|
||||
|
||||
(deftest let-block
|
||||
(testing "parse"
|
||||
(is (= {:data [blocks/let-block {:env {'s "Hello"}} 's]}
|
||||
(blocks/parse {} '(let [s "Hello"] s))))
|
||||
(is (= {:data [blocks/let-block {:env '{{a :a} (query [:aa])}} 'a]}
|
||||
(blocks/parse {:capacities {:queries #{:aa}}} '(let [{a :a} (query [:aa])] a))))
|
||||
(is (= {:data [blocks/let-block {:env '{{a :a} [:aa]}} 'a]}
|
||||
(blocks/parse {:capacities {:queries #{:aa}}} '(let [{a :a} [:aa]] a))))
|
||||
|
||||
(is (= {:data [blocks/let-block {:env '{a {:b 1} b 1}} 'b]}
|
||||
(blocks/parse {} '(let [{a :a} {:a {:b 1}} {b :b} a] b))))
|
||||
(is (= {:data [blocks/let-block {:env '{x 1 {a :a} (query [:aa 1])}} 'a]}
|
||||
(blocks/parse {:capacities {:queries #{:aa}}} '(let [x 1 {a :a} (query [:aa x])] a))))
|
||||
(is (= {:data [blocks/let-block {:env '{x 1 {a :a} [:aa {:x x}]}} 'a]}
|
||||
(blocks/parse {:capacities {:queries #{:aa}}} '(let [x 1 {a :a} [:aa {:x x}]] a))))
|
||||
|
||||
(is (= {:data [blocks/let-block {:env {'s "Hello"}} ['test {} 's]]}
|
||||
(blocks/parse {} (list 'let ['s "Hello"] ['test {} 's]))))
|
||||
@ -37,4 +37,4 @@
|
||||
(deftest let-block-resolution
|
||||
(is (= ['test {} 1] (blocks/let-block {:env {'a 1}} ['test {} 'a])))
|
||||
#_
|
||||
(is (= ['test {} 1] (blocks/let-block {:env '{{a :a} (query [:aa])}} '[test {} a]))))
|
||||
(is (= ['test {} 1] (blocks/let-block {:env '{{a :a} [:aa]}} '[test {} a]))))
|
||||
|
@ -1,8 +1,8 @@
|
||||
(ns pluto.reader.destructuring-test
|
||||
(:refer-clojure :exclude [destructure])
|
||||
(:require [clojure.test :refer [is deftest testing]]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.destructuring :as destructuring]))
|
||||
(:require [clojure.test :refer [is deftest]]
|
||||
[pluto.reader.destructuring :as destructuring]
|
||||
[pluto.reader.errors :as errors]))
|
||||
|
||||
(deftest destructure-seq
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-destructuring-format ::errors/value {:data [1] :type :sequential}}]}
|
||||
|
@ -2,83 +2,8 @@
|
||||
(:require [clojure.test :refer [is deftest testing]]
|
||||
[pluto.reader.blocks :as blocks]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.hooks :as hooks]))
|
||||
|
||||
#_
|
||||
(deftest resolve-property
|
||||
|
||||
(testing "View"
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/missing-property-name
|
||||
:pluto.reader.errors/value :view}]}
|
||||
(hooks/resolve-property {:type :view :name :view}
|
||||
{}
|
||||
{:capacities {:components {'text :text}}}
|
||||
{'views/id ['text {} ""]})))
|
||||
(is (= [:text {} ""]
|
||||
((:data (hooks/resolve-property {:type :view :name :view}
|
||||
{:view 'views/id}
|
||||
{:capacities {:components {'text :text}}}
|
||||
{'views/id ['text {} ""]})) {})))
|
||||
(is (= [blocks/let-block {:env {'value "test"}}
|
||||
[:text {} 'value]]
|
||||
((:data (hooks/resolve-property {:type :view :name :view}
|
||||
{:view 'views/id}
|
||||
{:capacities {:components {'text :text}}}
|
||||
{'views/id '(let [{value :value} properties] [text {} value])})) {:value "test"}))))
|
||||
(testing "Component"
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/invalid-type-name
|
||||
:pluto.reader.errors/value :component}]}
|
||||
(hooks/resolve-property {:type :component :name :component}
|
||||
{}
|
||||
{:capacities {}}
|
||||
{})))
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/unknown-component
|
||||
:pluto.reader.errors/value 'selector}]}
|
||||
(hooks/resolve-property {:type :component :name :component}
|
||||
{:component 'selector}
|
||||
{:capacities {:components {}}}
|
||||
{})))
|
||||
(is (= {:data "selector"}
|
||||
(hooks/resolve-property {:type :component :name :component}
|
||||
{:component 'selector}
|
||||
{:capacities {:components {'selector "selector"}}}
|
||||
{}))))
|
||||
(testing "Event"
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/invalid-type-name
|
||||
:pluto.reader.errors/value :event}]}
|
||||
(hooks/resolve-property {:type :event :name :event}
|
||||
{}
|
||||
{:capacities {}}
|
||||
{})))
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/event-not-exposed
|
||||
:pluto.reader.errors/value :set-in}]}
|
||||
(hooks/resolve-property {:type :event :name :event}
|
||||
{:event :set-in}
|
||||
{:capacities {:events #{}}}
|
||||
{})))
|
||||
(is (= {:data :set-in}
|
||||
(hooks/resolve-property {:type :event :name :event}
|
||||
{:event :set-in}
|
||||
{:capacities {:events #{:set-in}}}
|
||||
{}))))
|
||||
(testing "Query"
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/invalid-type-name
|
||||
:pluto.reader.errors/value :query}]}
|
||||
(hooks/resolve-property {:type :query :name :query}
|
||||
{}
|
||||
{:capacities {}}
|
||||
{})))
|
||||
(is (= {:errors [{:pluto.reader.errors/type ::errors/query-not-exposed
|
||||
:pluto.reader.errors/value :get-in}]}
|
||||
(hooks/resolve-property {:type :query :name :query}
|
||||
{:query :get-in}
|
||||
{:capacities {:queries #{}}}
|
||||
{})))
|
||||
(is (= {:data :get-in}
|
||||
(hooks/resolve-property {:type :query :name :query}
|
||||
{:query :get-in}
|
||||
{:capacities {:queries #{:get-in}}}
|
||||
{})))))
|
||||
[pluto.reader.hooks :as hooks]
|
||||
[pluto.reader.views :as views]))
|
||||
|
||||
(defn- hooks [properties]
|
||||
{:main {:properties properties}})
|
||||
@ -86,9 +11,9 @@
|
||||
(deftest parse
|
||||
(is (= [:text {} ""]
|
||||
((get-in (hooks/parse {:capacities {:hooks (hooks {:view :view})
|
||||
:components {'text :text}}}
|
||||
:components {'text {:value :text}}}}
|
||||
{'views/main ['text {} ""]
|
||||
'hooks/main.a {:view 'views/main}})
|
||||
'hooks/main.a {:view ['views/main]}})
|
||||
[:data :hooks :main :a :parsed :view]) {})))
|
||||
(let [app-hooks (hooks {:name :string
|
||||
:id :keyword})]
|
||||
@ -100,16 +25,16 @@
|
||||
:id :keyword}}))))
|
||||
(testing "Optional property"
|
||||
(let [app-hooks (hooks {:name :string :id :keyword})]
|
||||
(is (= {:data {:hooks {:main {:a {:parsed {:name "name"}
|
||||
(is (= {:data {:hooks {:main {:a {:parsed {:name "name"}
|
||||
:hook-ref (:main app-hooks)}}}}
|
||||
:errors [{::errors/type ::errors/invalid-type-name
|
||||
::errors/value :id}]}
|
||||
(hooks/parse {:capacities {:hooks app-hooks}}
|
||||
{'hooks/main.a {:name "name"}}))))
|
||||
{'hooks/main.a {:name "name"}}))))
|
||||
(is (= {:name "name"}
|
||||
(get-in (hooks/parse {:capacities {:hooks (hooks {:name :string :id? :keyword})}}
|
||||
{'hooks/main.b {:name "name"}})
|
||||
[:data :hooks :main :b :parsed])))
|
||||
{'hooks/main.b {:name "name"}})
|
||||
[:data :hooks :main :b :parsed])))
|
||||
(is (= {:name "name"
|
||||
:id :keyword}
|
||||
(get-in (hooks/parse {:capacities {:hooks (hooks {:name :string :id? :keyword})}}
|
||||
|
@ -1,36 +1,28 @@
|
||||
(ns pluto.reader.reference-test
|
||||
(:refer-clojure :exclude [resolve])
|
||||
(:require [clojure.test :refer [is deftest testing]]
|
||||
(:require [clojure.test :refer [is deftest]]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.reference :as reference]))
|
||||
|
||||
(deftest reference?
|
||||
(is (false? (reference/reference? "")))
|
||||
(is (true? (reference/reference? 'test)))
|
||||
(is (true? (reference/reference? 'views/id))))
|
||||
(is (false? (reference/reference? 'test)))
|
||||
(is (true? (reference/reference? ['views/id])))
|
||||
(is (true? (reference/reference? ['views/id {}])))
|
||||
(is (false? (reference/reference? ['views/id {} {}])))
|
||||
(is (false? (reference/reference? ["views/id" {}]))))
|
||||
|
||||
(deftest reference->symbol
|
||||
(is (= nil (reference/reference->symbol "")))
|
||||
(is (= 'test (reference/reference->symbol 'test)))
|
||||
(is (= 'views/id (reference/reference->symbol 'views/id))))
|
||||
|
||||
(deftest reference->type
|
||||
(is (= nil (reference/reference->type "")))
|
||||
(is (= nil (reference/reference->type 'test)))
|
||||
(is (= :view (reference/reference->type 'views/id))))
|
||||
(is (= 'test (reference/reference->symbol ['test])))
|
||||
(is (= 'views/id (reference/reference->symbol ['views/id {}]))))
|
||||
|
||||
(deftest resolve
|
||||
(is (= {:errors [{::errors/type ::errors/missing-property-value
|
||||
::errors/value :view}]}
|
||||
(reference/resolve {} {:name :view :type :view} {:view 'views/id})))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type
|
||||
::errors/value :view}]}
|
||||
(reference/resolve {'events/id "events"} {:name :view :type :view} {:view 'events/id})))
|
||||
(is (= {:errors [{::errors/type ::errors/missing-property-name
|
||||
::errors/value nil}]}
|
||||
(reference/resolve {'events/id "events"} {} {:event 'events/id})))
|
||||
(is (= {:errors [{::errors/type ::errors/missing-property-name
|
||||
::errors/value :view}]}
|
||||
(reference/resolve {'views/id "view"} {:name :view :type :view} {})))
|
||||
(is (= {:errors [{::errors/type ::errors/unknown-reference
|
||||
::errors/value {:value 'views/id}}]}
|
||||
(reference/resolve {} ['views/id])))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-reference
|
||||
::errors/value {:value ""}}]}
|
||||
(reference/resolve {'views/id "view"} "")))
|
||||
(is (= {:data "view"}
|
||||
(reference/resolve {'views/id "view"} {:name :view :type :view} {:view 'views/id}))))
|
||||
(reference/resolve {'views/id "view"} ['views/id]))))
|
||||
|
@ -6,87 +6,87 @@
|
||||
|
||||
(deftest resolve-primitive
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type
|
||||
::errors/value {:value :unknown}}]}
|
||||
(types/resolve {} :unknown nil)))
|
||||
::errors/value {:type :unknown}}]}
|
||||
(types/resolve {} {} :unknown nil)))
|
||||
(testing "String"
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :string :value nil}}]}
|
||||
(types/resolve {} :string nil)))
|
||||
(types/resolve {} {} :string nil)))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :string :value :value}}]}
|
||||
(types/resolve {} :string :value)))
|
||||
(types/resolve {} {} :string :value)))
|
||||
(is (= {:data "value"}
|
||||
(types/resolve {} :string "value"))))
|
||||
(types/resolve {} {} :string "value"))))
|
||||
(testing "Keyword"
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :keyword :value nil}}]}
|
||||
(types/resolve {} :keyword nil)))
|
||||
(types/resolve {} {} :keyword nil)))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :keyword :value "value"}}]}
|
||||
(types/resolve {} :keyword "value")))
|
||||
(types/resolve {} {} :keyword "value")))
|
||||
(is (= {:data :value}
|
||||
(types/resolve {} :keyword :value))))
|
||||
(types/resolve {} {} :keyword :value))))
|
||||
(testing "Subset"
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :subset :value nil}}]}
|
||||
(types/resolve {} #{"a" "b" "c"} nil)))
|
||||
(types/resolve {} {} #{"a" "b" "c"} nil)))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :subset :value "value"}}]}
|
||||
(types/resolve {} #{"a" "b" "c"} "value")))
|
||||
(types/resolve {} {} #{"a" "b" "c"} "value")))
|
||||
(is (= {:data #{"a"}}
|
||||
(types/resolve {} #{"a" "b" "c"} #{"a"}))))
|
||||
(types/resolve {} {} #{"a" "b" "c"} #{"a"}))))
|
||||
(testing "One of"
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :one-of :value nil}}]}
|
||||
(types/resolve {} {:one-of #{:one :two :three}} nil)))
|
||||
(types/resolve {} {} {:one-of #{:one :two :three}} nil)))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :one-of :value :for}}]}
|
||||
(types/resolve {} {:one-of #{:one :two :three}} :for)))
|
||||
(types/resolve {} {} {:one-of #{:one :two :three}} :for)))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :one-of :value "one"}}]}
|
||||
(types/resolve {} {:one-of #{:one :two :three}} "one")))
|
||||
(types/resolve {} {} {:one-of #{:one :two :three}} "one")))
|
||||
(is (= {:data :one}
|
||||
(types/resolve {} {:one-of #{:one :two :three}} :one)))))
|
||||
(types/resolve {} {} {:one-of #{:one :two :three}} :one)))))
|
||||
|
||||
(deftest resolve-sequential
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-sequential-type
|
||||
::errors/value {:type [:string] :value ["value"]}}]}
|
||||
(types/resolve {} [:string] ["value"])))
|
||||
(types/resolve {} {} [:string] ["value"])))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-sequential-type
|
||||
::errors/value {:type [{:name :string} {:name :string}] :value ["value"]}}]}
|
||||
(types/resolve {} [{:name :string} {:name :string}] ["value"])))
|
||||
(types/resolve {} {} [{:name :string} {:name :string}] ["value"])))
|
||||
(is (= {:data [{:name "name"}
|
||||
{:name "name"}]}
|
||||
(types/resolve {} [{:name :string}]
|
||||
[{:name "name"}
|
||||
{:name "name"}])))
|
||||
(types/resolve {} {} [{:name :string}]
|
||||
[{:name "name"}
|
||||
{:name "name"}])))
|
||||
(is (= {:data [{:name "name" :scopes [{:scope :one}]}
|
||||
{:name "name" :scopes [{:scope :two}]}]}
|
||||
(types/resolve {} [{:name :string :scopes [{:scope {:one-of #{:one :two :three}}}]}]
|
||||
[{:name "name" :scopes [{:scope :one}]}
|
||||
{:name "name" :scopes [{:scope :two}]}]))))
|
||||
(types/resolve {} {} [{:name :string :scopes [{:scope {:one-of #{:one :two :three}}}]}]
|
||||
[{:name "name" :scopes [{:scope :one}]}
|
||||
{:name "name" :scopes [{:scope :two}]}]))))
|
||||
|
||||
(deftest resolve-assoc
|
||||
(is (= {:data {:name "value"}}
|
||||
(types/resolve {} {:name :string} {:name "value"})))
|
||||
(types/resolve {} {} {:name :string} {:name "value"})))
|
||||
(is (= {:data {:name "value"}}
|
||||
(types/resolve {} {:name? :string} {:name "value"})))
|
||||
(types/resolve {} {} {:name? :string} {:name "value"})))
|
||||
(is (= {:data {}}
|
||||
(types/resolve {} {:name? :string} {})))
|
||||
(types/resolve {} {} {:name? :string} {})))
|
||||
(is (= {:errors [{::errors/type ::errors/invalid-type-value
|
||||
::errors/value {:type :string :value nil}}]}
|
||||
(types/resolve {} {:name? :string} {:name nil})))
|
||||
(types/resolve {} {} {:name? :string} {:name nil})))
|
||||
(is (= {:data {}}
|
||||
(types/resolve {} {:name? :string} {:extra "value"})))
|
||||
(types/resolve {} {} {:name? :string} {:extra "value"})))
|
||||
(is (= {:data {:scopes [{:scope :one}]}}
|
||||
(types/resolve {} {:scopes [{:scope {:one-of #{:one :two :three}}}]}
|
||||
{:scopes [{:scope :one}]})))
|
||||
(types/resolve {} {} {:scopes [{:scope {:one-of #{:one :two :three}}}]}
|
||||
{:scopes [{:scope :one}]})))
|
||||
(is (= {:data {:name "hello"
|
||||
:children [{:name "name" :scopes [{:scope :one}]}
|
||||
{:name "name" :scopes [{:scope :two}]}]}}
|
||||
(types/resolve {} {:name? :string
|
||||
:children [{:name :string :scopes [{:scope {:one-of #{:one :two :three}}}]}]}
|
||||
{:extra "value"
|
||||
:name "hello"
|
||||
:children [{:name "name" :scopes [{:scope :one}]}
|
||||
{:name "name" :scopes [{:scope :two}]}]}))))
|
||||
(types/resolve {} {} {:name? :string
|
||||
:children [{:name :string :scopes [{:scope {:one-of #{:one :two :three}}}]}]}
|
||||
{:extra "value"
|
||||
:name "hello"
|
||||
:children [{:name "name" :scopes [{:scope :one}]}
|
||||
{:name "name" :scopes [{:scope :two}]}]}))))
|
||||
|
@ -1,12 +1,16 @@
|
||||
(ns pluto.reader.views-test
|
||||
(:refer-clojure :exclude [resolve])
|
||||
(:require [clojure.test :refer [is deftest testing]]
|
||||
[pluto.reader.blocks :as blocks]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.views :as views]))
|
||||
[pluto.reader.types :as types]
|
||||
[pluto.reader.views :as views]))
|
||||
|
||||
(deftest parse-hiccup-children
|
||||
(is (= {:data (list [:text {} ""])} (views/parse-hiccup-children {:capacities {:components {'text :text}}}
|
||||
(list ['text {} ""])))))
|
||||
(is (= {:data (list [:text {} ""])}
|
||||
(views/parse-hiccup-children {:capacities {:components {'text {:value :text}}}}
|
||||
{}
|
||||
(list ['text {} ""])))))
|
||||
|
||||
(defn- first-error-type [m]
|
||||
(::errors/type (first (:errors m))))
|
||||
@ -16,17 +20,18 @@
|
||||
(is (= ::errors/invalid-view (first-error-type (views/parse {} {}))))
|
||||
#_
|
||||
(is (= ::errors/invalid-view
|
||||
(first-error-type (views/parse {:capacities {:components {'text :text}}} ['text "Hello"]))))
|
||||
(first-error-type (views/parse {:capacities {:components {'text {:value :text}}}} ['text "Hello"]))))
|
||||
#_
|
||||
(is (= ::errors/invalid-view
|
||||
(first-error-type (views/parse {:capacities {:components {'text :text}}} ['text {} []]))))
|
||||
(first-error-type (views/parse {:capacities {:components {'text {:value :text}}}} ['text {} []]))))
|
||||
#_
|
||||
(is (= {:data ['text {} "Hello"]
|
||||
:errors (list {::errors/type ::errors/unknown-component ::errors/value 'text})}
|
||||
(views/parse {} ['text {} "Hello"])))
|
||||
(is (= {:data [:text {} "Hello"]}
|
||||
(views/parse {:capacities {:components {'text :text}}} ['text {} "Hello"])))
|
||||
(views/parse {:capacities {:components {'text {:value :text}}}} {} ['text {} "Hello"])))
|
||||
(is (= {:data [:text {} "Hello"]}
|
||||
(views/parse {:capacities {:components {'text :text}}} ['text {} "Hello"])))
|
||||
(views/parse {:capacities {:components {'text {:value :text}}}} {} ['text {} "Hello"])))
|
||||
(is (= {:data [:view
|
||||
[:text {} "Hello"]
|
||||
[blocks/let-block
|
||||
@ -36,8 +41,9 @@
|
||||
[:text {:style {:color "green"}} "World?"]
|
||||
[:text {:style {:color "red"}} "World?"]]]]}
|
||||
(views/parse {:capacities {:queries #{:random-boolean}
|
||||
:components {'text :text
|
||||
'view :view}}}
|
||||
:components {'text {:value :text}
|
||||
'view {:value :view}}}}
|
||||
{}
|
||||
'[view
|
||||
[text {} "Hello"]
|
||||
(let [cond? (query [:random-boolean])]
|
||||
@ -48,5 +54,10 @@
|
||||
"World?"]))])))
|
||||
(testing "Properties"
|
||||
(is (= {:data [:text {} "Hello"]}
|
||||
(views/parse {:capacities {:components {'text :text}}} ['text {} "Hello"])))))
|
||||
(views/parse {:capacities {:components {'text {:value :text}}}} {} ['text {} "Hello"])))))
|
||||
|
||||
(deftest resolve
|
||||
(is (= [:text "Hello"] ((:data (types/resolve {:capacities {:components {'text {:value :text}}}} {'views/main ['text "Hello"]} :view ['views/main])) {})))
|
||||
(is (= {:errors [{::errors/type ::errors/unknown-reference,
|
||||
::errors/value {:value 'views/unknown}}]}
|
||||
(types/resolve {:capacities {:components {'text {:value :text}}}} {'views/main ['text "Hello"]} :view ['views/unknown]))))
|
||||
|
@ -2,8 +2,8 @@
|
||||
(:refer-clojure :exclude [read])
|
||||
(:require [clojure.test :refer [is deftest]]
|
||||
[pluto.reader :as reader]
|
||||
[pluto.reader.errors :as errors]
|
||||
[pluto.reader.blocks :as blocks]))
|
||||
[pluto.reader.blocks :as blocks]
|
||||
[pluto.reader.errors :as errors]))
|
||||
|
||||
(deftest read
|
||||
(is (= {:data nil} (reader/read "")))
|
||||
@ -69,27 +69,27 @@
|
||||
((get-in m [:data :hooks :main :a :parsed :view]) {}))
|
||||
|
||||
(deftest parse-blocks
|
||||
(is (= [blocks/let-block {:env {'s "Hello"}} [:text {} 's]]
|
||||
(is (= [blocks/let-block {:env {'s "Hello"}} '[text {} s]]
|
||||
(view (reader/parse default-capacities
|
||||
(extension {'views/main (list 'let ['s "Hello"] ['text {} 's])
|
||||
'hooks/main.a {:view 'views/main}})))))
|
||||
(is (= [blocks/when-block {:test 'cond} [:text {} ""]]
|
||||
'hooks/main.a {:view ['views/main]}})))))
|
||||
(is (= [blocks/when-block {:test 'cond} '[text {} ""]]
|
||||
(view (reader/parse default-capacities
|
||||
(extension {'views/main (list 'when 'cond ['text {} ""])
|
||||
'hooks/main.a {:view 'views/main}})))))
|
||||
'hooks/main.a {:view ['views/main]}})))))
|
||||
(is (= {:data {'meta default-meta
|
||||
:hooks {:main {:a {:parsed nil
|
||||
:hook-ref (:main default-hooks)}}}}
|
||||
:errors (list {::errors/type ::errors/unsupported-test-type ::errors/value "string"})}
|
||||
(reader/parse default-capacities (extension {'views/main (list 'when "string" ['text {} ""])
|
||||
'hooks/main.a {:view 'views/main}})))))
|
||||
'hooks/main.a {:view ['views/main]}})))))
|
||||
|
||||
(deftest parse
|
||||
(is (= (list {::errors/type ::errors/unknown-component ::errors/value 'text})
|
||||
(:errors (reader/parse {:capacities {:hooks default-hooks}}
|
||||
(extension {'views/main ['text {} "Hello"]
|
||||
'hooks/main.a {:view 'views/main}})))))
|
||||
(is (= [:text {} "Hello"]
|
||||
'hooks/main.a {:view ['views/main]}})))))
|
||||
(is (= '[text {} "Hello"]
|
||||
(view (reader/parse default-capacities
|
||||
(extension {'views/main ['text {} "Hello"]
|
||||
'hooks/main.a {:view 'views/main}}))))))
|
||||
'hooks/main.a {:view ['views/main]}}))))))
|
||||
|
Loading…
x
Reference in New Issue
Block a user