I am totally new to CLIPS and I would like to know how could we call a defrule inside another defrule.
For example if one rule does a regular job like printing or swapping, and another rule wants to call it after doing pattern-matching itself; how could the second rule call the first one in its pattern-matching part?
Thank you
Chain rules by asserting a fact from one rule that activates another.
CLIPS (6.31 6/12/19)
CLIPS>
(defrule start
=>
(assert (r1)))
CLIPS>
(defrule rule-1
(r1)
=>)
CLIPS> (watch rules)
CLIPS> (watch activations)
CLIPS> (watch facts)
CLIPS> (agenda)
0 start: *
For a total of 1 activation.
CLIPS> (run)
FIRE 1 start: *
==> f-1 (r1)
==> Activation 0 rule-1: f-1
FIRE 2 rule-1: f-1
CLIPS>
Related
I'm trying to use the React Simulate function to simulate a mouseDown event for testing.
(defn mouse-down [node]
((.-mouseDown(.-Simulate ReactTestUtils) node (clj->js {:button 0})))
js translation:
ReactTestUtils.Simulate.mouseDown(node, {button: 0})
Nothing I've tried has resulted in an invocation of the mousedown listener--but when the listener is there when i try it in the browser where it works. It's just in the simulation.
What am I missing?
There are a couple mistakes in the Syntax here and your parens don't match. Generally it becomes easier if you reorganize the code a bit and -> can help there. As mentioned in the comment you want to use .mouseDown instead of .-mouseDown. I opted to use #js instead of clj->js since that is more optimal for static js objects such as this.
(defn mouse-down [node]
(-> (.-Simulate ReactTestUtils)
(.mouseDown node #js {:button 0})))
You can also make this a little more readable depending on where ReactTestUtils is coming from. I'm assuming its from the react-dom package which you just required.
;; in your ns
(:require ["react-dom/test-utils" :as ReactTestUtils])
;; which would allow
(defn mouse-down [node]
(ReactTestUtils/Simulate.mouseDown node #js {:button 0}))
;; or if you are on the latest CLJS version
(:require ["react-dom/test-utils$Simulate" :as sim])
;; and then
(defn mouse-down [node]
(sim/mouseDown node #js {:button 0}))
I've crawled through some of the web and video documentation for routing. But I'm failing to get Dynamic UI Routing working for a simple set of pages.
root.cljs
(ns ui.root)
;; ...
(defsc Index [this props]
{:query [:index]
:ident (fn [] [:id :index])
:route-segment ["index"]
:initial-state {}}
(h3 "Index"))
(defsc Landing [this props]
{:query [:landing]
:ident (fn [] [:id :landing])
:route-segment ["landing"]
:initial-state {}}
(h3 "Landing"))
(defsc Settings [this props]
{:query [:settings]
:ident (fn [] [:id :settings])
:route-segment ["settings"]
:initial-state {}}
(h3 "Setting"))
(dr/defrouter TopRouter [this {:keys [current-state] :as props}]
{:router-targets [Game Settings Landing Index]
:initial-state (fn [{:keys [current-state]}]
{:current-state current-state})}
(case current-state
:pending (dom/div "Loading...")
:failed (dom/div "Failed!")
(dom/div "No route selected.")))
(def ui-top-router (comp/factory TopRouter))
(defsc Root [this {:keys [router] :as props}]
{:query [{:router (comp/get-query TopRouter)}]
:ident (fn [] [:id :root])
:initial-state (fn [_]
{:top-router (comp/get-initial-state TopRouter {:current-state :pending})
:index {:id 1}
:landing {:id 1}
:settings {:id 1}})
:componentDidMount (fn [_] (log/info "Root Initial State /" (prim/get-initial-state Root {})))}
(log/info "Root Props /" this props)
(ui-top-router router {:current-state :pending}))
client.cljs
(ns client)
...
(app/mount! #app root/Root "app" {:initialize-state? true
:foo :bar})
Q: Initial load gives this output. How do we pass props into the Root component? I expect to see at least {:foo :bar}.
INFO [ui.root:81] - Root Props / [object Object] {}
INFO [ui.root:53] - TopRouter Props / {:current-state nil, :route-factory #object[cljs.core.MetaFn], :pending-path-segment nil, :route-props nil}
INFO [ui.root:77] - Root Initial State / nil
Q: If this is my initial state, is the :query and :ident right? And do they (:query + :ident) correspond to the :route-segment ? Do they need to?
{:index {:id 1}
:landing {:id 1}
:settings {:id 1}}
Q: How do we kick off the initial route? Calling this fails with the below message.
(dr/change-route app ["index"])
INFO [com.fulcrologic.fulcro.rendering.ident-optimized-render:146] - Optimized render failed. Falling back to root render.
>> UPDATE <<
I was able to get a working Fulcro Root :initial-state, and :query and :ident on child components.
On initial load, the router fails with this.
INFO [beatthemarket.ui.root:61] - TopRouter Props / {:current-state nil, :route-factory #object[cljs.core.MetaFn], :pending-path-segment nil, :route-props {:index/id 1, :index/text "Index Text"}}
core.cljs:159 ERROR [com.fulcrologic.fulcro.routing.dynamic-routing:410] - will-enter for router target beatthemarket.ui.root/Index did not return a valid ident. Instead it returned: [:index/id nil]
core.cljs:159 ERROR [com.fulcrologic.fulcro.routing.dynamic-routing:410] - will-enter for router target beatthemarket.ui.root/Index did not return a valid ident. Instead it returned: [:index/id nil]
browser.cljs:25 shadow-cljs: WebSocket connected!
browser.cljs:25 shadow-cljs: REPL session start successful
core.cljs:159 INFO [com.fulcrologic.fulcro.algorithms.indexing:104] - component beatthemarket.ui.root/Index's ident ([:index/id nil]) has a `nil` second element. This warning can be safely ignored if that is intended.
So a command like (dr/change-route app (dr/path-to root/Index)) fails with this.
react_devtools_backend.js:6 ERROR [com.fulcrologic.fulcro.routing.dynamic-routing:410] - will-enter for router target beatthemarket.ui.root/Index did not return a valid ident. Instead it returned: [:index/id nil]
These are my client.cljs and root.cljs look like this.
I think your Root initial state should be calling (comp/get-initial-state Index). You have an initial state set on Index but it's different than the initial state that Root gives.
Also, a big part of Fulcro (and React) is that you build a tree of components and a tree of data and they need to match.
The way you have it here, there's no connection between "Root" and "Index" because Root only renders (ui-top-router router). You're getting the data for Index by having a query for {:root/index (comp/get-query Index)} but you're not creating the connection between Root and Index by having Root call Index and pass in that data. You need a (ui-index index) inside Root.
And if you do that, then in that (ui-index index) call, index will take the value that you're setting with :initial-state. That's why you'll also need to update initial-state to call comp/get-initial-state so that you can get the :index/id 1 value that you're setting in the Index component's initial state.
(defsc Index [this {:index/keys [id text]}]
{:query [:index/id :index/text]
:ident [:index/id :index/id]
:route-segment ["index"]
:initial-state {:index/id 1
:index/text :param/text}}
(h3 text))
(defsc Root [this {:root/keys [router index landing game settings]}]
{:query [{:root/router (comp/get-query TopRouter)}
{:root/index (comp/get-query Index)}
{:root/landing (comp/get-query Landing)}
{:root/game (comp/get-query Game)}
{:root/settings (comp/get-query Settings)}]
:initial-state {:root/router {}
:root/index {:text "Index Text"}
:root/landing {:text "Landing Text"}
:root/game {:text "Game Text"}
:root/settings {:text "Settings Text"}}}
(when router
(dom/div (ui-top-router router))))
After all of that is addressed, here's the next thing you might be interested in.
You probably won't always want to hard-code index/id 1. You might need to fetch data from a server in order to have anything to render.
That's where :will-enter and "deferred routing" come into play. See the example below and the docs at http://book.fulcrologic.com/#_router_rendering_of_a_deferred_ui.
(defsc Person [this {:ui/keys [modified?]
:person/keys [id name]
:address/keys [city state]
:as props}]
{:query [:ui/modified? :person/id :person/name :address/city :address/state]
:ident :person/id
:route-segment ["person" :person/id]
:route-cancelled (fn [{:person/keys [id]}]
(log/info "Routing cancelled to user " id))
:allow-route-change? (fn [this {:ui/keys [modified?]}]
(when modified?
#?(:cljs (js/alert "You cannot navigate until the user is not modified!")))
(not modified?))
:will-enter (fn [app {:person/keys [id] :as route-params}]
(log/info "Will enter user with route params " route-params)
;; be sure to convert strings to int for this case
(let [id (if (string? id) (edn/read-string id) id)]
(dr/route-deferred [:person/id id]
#(df/load app [:person/id id] Person
{:post-mutation `dr/target-ready
:post-mutation-params {:target [:person/id id]}}))))}
I guess you first have to fix the Ident problem with your Index component.
Does routing work for the other components?
Is there a way to dynamically ask for bindings in another package, and by dynamically i mean by not knowing the exact name of a binding in some package. A concrete case would be:
As in package B, i know there exists a package A which has a certain class and i extract all direct slots of that class by (in LispWorks):
(setq direct-slots (mapcar #'slot-definition-name
(class-direct-slots (class-of class-in-package-A))))
Now i want to bind those slots to some values using MAPCAR:
(mapcar #'(lambda (slot) (list slot
(funcall slot class-in-package-A)))
direct-slots)
This doesn't work since i am in package B and need package precision for the call to (funcall slot class-in-package-A), packageA::slot is obviously wrong. Is there a function for this which searchs for a certain symbol in a package?
If you have a slot-name and want to get the value of the named slot in some object, use slot-value:
(mapcar (lambda (slot-name)
(slot-value some-object slot-name))
slot-names)
Slot names are symbols, and they will not magically lose their package if you happen to “be” in a different package. I think your confusion is that you are thinking about accessors, but those are a different thing (they use something like slot-value internally).
CL-USER> (defpackage #:foo
(:use #:cl))
#<PACKAGE "FOO">
CL-USER> (defpackage #:bar
(:use #:cl #:sb-mop)) ; in SBCL
#<PACKAGE "BAR">
CL-USER> (in-package #:foo)
#<PACKAGE "FOO">
FOO> (defclass afoo ()
((a :initarg :a)
(b :initarg :b)))
#<STANDARD-CLASS FOO::AFOO>
FOO> (in-package #:bar)
#<PACKAGE "BAR">
BAR> (mapcar #'slot-definition-name
(class-direct-slots (find-class 'foo::afoo)))
(FOO::A FOO::B)
BAR> (let ((slot-names (mapcar #'slot-definition-name
(class-direct-slots (find-class 'foo::afoo))))
(obj (make-instance 'foo::afoo
:a 1
:b 2)))
(mapcar (lambda (slot-name)
(slot-value obj slot-name))
slot-names))
(1 2)
In general, you should be using accessors in “user” code, and you should know which accessors exist for a given object. It also shouldn't matter for user code whether something is a direct slot.
Suppose having
(def defining-list `(def one 1))
How can I evaluate defining-list so that one becomes 1 ?
(in clojurescript)
EDIT:
I will give an idea of the broader image and what I am trying to accomplish here to avoid falling into an X/y problem.
I am trying to use cljsjs/material-ui from cljsjs package
Instead of defining each time a react component to use it as following:
(def app-bar
(r/adapt-react-class (aget js/MaterialUI (name :AppBar)))
I would like to define all the components from an array of tags:
(def material-ui-tags '[AppBar Avatar Backdrop])
So I was thinking if it's possible to do this without the usage of a macro as I found this
Something like:
(doseq [component material-ui-tags]
`(def ~(symbol (->kebab-case component)) (r/adapt-react-class (aget js/MaterialUI ~(name component)))))
But the above does only create a list of defs, I would like to evaluate these. In clojure eval would do the trick.
With reagent, you can use :> as shorthand for adapt-react-class as documented in https://github.com/reagent-project/reagent/blob/master/docs/InteropWithReact.md
Also, you can use dot notation with js/ and I think in shadow-cljs or cljs above 1.9.854 you can require to import the symbol instead of using aget.
In your case, it would be something like:
(ns example.core
(:require [MaterialUI]))
(defn component-two []
[:> MaterialUI/AppBar {:some-prop "some-value"}
[:div "children-here"]])
(defn component-two []
;; If the require above doesn't work
[:> js/MaterialUI.AppBar {:some-prop "some-value"}
[:div "children-here"]])
To do what you wanted using def, you either need eval or macro. Eval is not ideal as Jared Smith explained in the comment.
The example that you linked from reagent-material-ui uses macro. Invoking a macro actually performs expansion and then evaluation. So your code needs to be something like this:
clj file
(def material-ui-tags '[AppBar Avatar Backdrop])
(defmacro adapt-components []
(for [component material-ui-tags]
`(def ~(symbol (->kebab-case component)) (reagent.core/adapt-react-class (aget js/MaterialUI ~(name component))))))
cljs file
(adapt-components) ;; your defs will be available below this line
(defn my-component []
[app-bar ...])
I'm trying to work out how to get sessions and flash working in Google App Engine. Could someone provide a clear example using either Ring or Sandbar? I think I have sandbar working, specifically it doesn't tell me that Var sandbar.stateful-session/sandbar-flash is unbound and when I dump the handler I get :flash and :session though I'm not certain if that is a sandbar session or a ring one. For completeness I will mention that I am using the latest versions of appengine-magic, ring, hiccup and sandbar. There do not appear to be any incompatibilities or issues.
So a clear example preferably with use of flash-put!, flash-get, session-put! and session-get.
I don't usually like answering my own questions, however in this case I'll make an exception because:
a) There isn't a lot of easy to understand examples out there.
b) It would be nice to have a quick working example for others to use.
Note: appengine-magic is not required here, this will also work with normal ring sessions
Code
;; Place in a file called session.clj in the example project
(ns example.session
"Works with the normal ring sessions
allowing you to use side-effects to manage them")
(declare current-session)
(defn wrap-session! [handler]
(fn [request]
(binding [current-session (atom (or (:session request) {}))]
(let [response (handler request)]
(assoc response :session #current-session)))))
(defn session-get
([k] (session-get k nil))
([k default] (if (vector? k)
(get-in #current-session k)
(get #current-session k default))))
(defn session-put!
([m]
(swap! current-session (fn [a b] (merge a m)) m))
([k v]
(swap! current-session (fn [a b] (merge a {k b})) v)))
(defn session-pop! [k]
(let [res (get #current-session k)]
(swap! current-session (fn [a b] (dissoc a b)) k)
res))
(defn session-delete-key! [k]
(swap! current-session (fn [a b] (dissoc a b)) k))
(defn session-destroy! []
(swap! current-session (constantly nil)))
;; Place in a file called core.clj in the example project
(ns example.core
(:use compojure.core
[ring.middleware.keyword-params :only [wrap-keyword-params]]
[ring.middleware.session :only [wrap-session]]
[ring.middleware.session.cookie :only [cookie-store]]
[example session])
(:require [appengine-magic.core :as ae]))
(declare current-session)
(defroutes example-app-routes
(GET "/" _
(fn [req]
(let [counter (session-get :counter 0)]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (str "started: " counter)})))
(GET "/inc" _
(fn [req]
(let [counter (do
(session-put! :counter (inc (session-get :counter 0)))
(session-get :counter))]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (str "incremented: " counter)}))))
(def example-app-handler
(-> #'example-app-routes
wrap-keyword-params
wrap-session!
(wrap-session {:cookie-name "example-app-session"
:store (cookie-store)})))
(ae/def-appengine-app example-app #'example-app-handler)
How to use it
Navigating to http://127.0.0.1:8080/inc increments the counter in the session and http://127.0.0.1:8080/ will display the value of counter in the session.
wrap-session! is not required for sessions to work, just using
(wrap-session {:cookie-name "example-app-session"
:store (cookie-store)})
will give you working functional sessions. However I wanted to manage my sessions with side-effects and wrap-session! provides that functionality. To use flash like functionality, simply use session-put! to put a value into the session and then use session-pop! to remove it.
Hope that's helpful.
If you want to use the Sessions provided by GAE, you can use the following
https://gist.github.com/1095841
to include a ring like session in your requests, but based on GAE session support.
If you want stateful sessions on top of this, you can use the stateful session API provided by Sandbar
https://github.com/brentonashworth/sandbar