clojurecore.asyncalephyada

Clojure - use a core.async channel with Yada/Aleph


I am trying to use Clojure manifold library, and in order to understand it, I need wanted to convert a core.async channel into a manifold stream.

I would like to create the equivalent the following using a core.async channel :

 (require '[manifold.stream :as s])
 (s/periodically 100 #(str " ok "))

  ;; Here is what I tried, it fails with an error 500
  (let [ch (chan)]
      (go-loop []
      (>! ch " ok ")
      (<! (timeout 100))
      (recur))
    (s/->source ch))

I am trying to feed a core.async channel into yada. The first code sample, using manifold.stream/periodic works, not the others using core.async. I tried on yada 1.0.0 and 1.1.0-SNAPSHOT.

Using manifold.stream/periodic works :

(def get-stream
  (yada (fn [ctx]
          (-> (:response ctx)
              (assoc :status 202)
              (assoc :body (s/periodically 1000 #(str (System/currentTimeMillis) " ")))))
        {:representations [{:media-type "application/json"
                            :charset    "UTF-8"}
                           {:media-type "application/edn"
                            :charset    "UTF-8"}]}))

Using manifold.stream/->source returns an error 500 :

(def get-stream
  (yada (fn [ctx]
          (-> (:response ctx)
              (assoc :status 202)
              ;; Similar to this : https://github.com/juxt/yada/blob/94f3ee93de155a8513b27e0508608691ed556a55/dev/src/yada/dev/async.clj
              (assoc :body (let [ch (chan)]
                             (go-loop []
                               (>! ch " ok ")
                               (<! (timeout 100))
                               (recur))
                             (s/->source ch)))))
        {:representations [{:media-type "application/json"
                            :charset    "UTF-8"}
                           {:media-type "application/edn"
                            :charset    "UTF-8"}]}))

;; Error on the page :

;; 500: Unknown
;; Error on GET

;; #error {
;;         :cause "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: manifold.stream.async.CoreAsyncSource"
;;         :via
;;         [{:type clojure.lang.ExceptionInfo
;;           :message "Error on GET"
;;           :data {:response #yada.response.Response{:representation {:media-type #yada.media-type[application/json;q=1.0], :charset #yada.charset.CharsetMap{:alias "UTF-8", :quality 1.0}}, :vary #{:media-type}}, :resource #function[backend.routes.examples.ts/fn--57734]}
;;                                                                                                  :at [clojure.core$ex_info invoke "core.clj" 4593]}
;;                                                                     {:type java.lang.IllegalArgumentException
;;                                                                      :message "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: manifold.stream.async.CoreAsyncSource"
;;                                                                      :at [clojure.core$_cache_protocol_fn invoke "core_deftype.clj" 554]}]
;;                                                    :trace

Third attempt, with a core.async channel (different error 500) :

(def get-stream
  (yada (fn [ctx]
          (-> (:response ctx)
          (assoc :status 202)
          (assoc :body (chan)))
        {:representations [{:media-type "application/json"
                            :charset    "UTF-8"}
                           {:media-type "application/edn"
                            :charset    "UTF-8"}]}))

;; Error on the page :

;; 500: Unknown

;; Error on GET

;; #error {
;;         :cause "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: clojure.core.async.impl.channels.ManyToManyChannel"
;;         :via
;;         [{:type clojure.lang.ExceptionInfo
;;           :message "Error on GET"
;;           :data {:response #yada.response.Response{:representation {:media-type #yada.media-type[application/json;q=1.0], :charset #yada.charset.CharsetMap{:alias "UTF-8", :quality 1.0}}, :vary #{:media-type}}, :resource #function[backend.routes.api.subscribe/subscribe$fn--64130]}
;;                                                                                                  :at [clojure.core$ex_info invoke "core.clj" 4593]}
;;                                                                     {:type java.lang.IllegalArgumentException
;;                                                                      :message "No implementation of method: :to-body of protocol: #'yada.body/MessageBody found for class: clojure.core.async.impl.channels.ManyToManyChannel"
;;                                                                      :at [clojure.core$_cache_protocol_fn invoke "core_deftype.clj" 554]}]
;;                                                    :trace

Solution

  • The key error is this:

    "No implementation of method: :to-body of protocol: 
     #'yada.body/MessageBody     
     found for class: manifold.stream.async.CoreAsyncSource"
    

    It reveals that the yada version you are using is trying to coerce a body type it doesn't understand. More recent versions of yada are more permissive about what you can send through to the web-server, and allow anything through that it doesn't know how to transform.