Fork me on GitHub
#hoplon
<
2016-12-22
>
peterromfeld07:12:43

how about:

(defn wrap-castra [handler & [opts & more :as namespaces]]
  (let [{:keys [body-keys state-fn on-error] :as opts} (when (map? opts) opts)
        nses (if opts more namespaces)
        head {"X-Castra-Tunnel" "transit"}
        seq* #(or (try (seq %) (catch Throwable e)) [%])
        vars (fn [] (->> nses (map seq*) (mapcat #(apply select-vars %)) set))]
    (fn [req]
      (if-not (castra-req? req)
        (handler req)
        (binding [*print-meta*    true
                  *pre*           true
                  *request*       req
                  *session*       (atom (:session req))
                  *validate-only* (= "true" (get-in req [:headers "x-castra-validate-only"]))]
          (let [h (headers req head {"Content-Type" "application/json"})
                fn&args (expression body-keys req)
                f #(do (csrf!) (do-rpc (vars) fn&args))
                d (try (response body-keys req {:result (f) :state (when state-fn (state-fn))})
                       (catch Throwable e
                         (when on-error (on-error fn&args e))
                         (response body-keys req {:error (ex->clj e)})))]
            {:status 200, :headers h, :body d, :session @*session*}))))))

puzzler08:12:36

Is there a general article somewhere about the best kind of architecture to build a hoplon front-end with? For example, the react-based communities seem to favor sharding out pieces of some "central atom of truth" to the different components. You could do that with javelin as well, but most of the hoplon demos I've looked at seem to favor having each component rely on its own cells, and then some formula cell computes the overall state from the individual cells. I would appreciate seeing a discussion of the trade-offs, and what approaches scale the best.

dm308:12:05

I understand the current consensus is in building layers of view elements at different abstraction levels in the application, e.g. div -> popup -> settings-panel, each with a set of parameter cells which capture all the needed interactions and render values. Also Micha showcased his form-machines a few times - an additional abstraction which captures the workflow (as opposed to view elements) - https://gist.github.com/micha/622a62d6a52ff419e5ac. I don't think there's the best solution for storing the overall state of the application, some people use Datascript, some multiple cells, some a single cell...

alandipert15:12:37

@puzzler a useful thing to recognize about cells is that in many ways, the cell graph is a single atom

alandipert15:12:50

in the sense that cells can update consistently

alandipert15:12:06

the difference is cells can be anonymous, which named places in a global atom-map cannot be

peterromfeld15:12:21

any news on castra middleware on-error option?

peterromfeld15:12:34

i can make a pull request if you ok with it

flyboarder16:12:38

@peterromfeld: that would probably be a good idea 👍

jumblerg17:12:20

@micha: got a few re castra error handling? ^^^

micha17:12:47

how about this evening?

micha17:12:07

need to ship some things before end of day today

jumblerg17:12:22

ok, @peterromfeld, hold off a bit first until micha can weigh in.

jumblerg17:12:46

a pr would be much appreciated, but we might want to review castra's approach to error handling as a group first so we can maximize your contribution.

peterromfeld17:12:38

ill just do pr tomorrow, and if something is to your dislike you can reply in the pr

jumblerg17:12:11

ok, many thanks!

peterromfeld17:12:13

also if you guy have time this we or the week after

peterromfeld17:12:52

i need to help debug some flash (css get loaded second time) because we maybe dont include 3rd party js lib the way hoplon wants it

jumblerg17:12:33

happy to help

peterromfeld17:12:52

im about to sleep now, need to work in few hours lol 😉

peterromfeld17:12:08

thats why we or i have free whole week next week

micha17:12:41

i have some free time next week also, maybe we can do some things

jumblerg18:12:18

@peterromfeld, @dm3, @flyboarder: i'm actually thinking that error handling should be factored out into an entirely separate castra middleware.

jumblerg18:12:50

this way, anything on the server can throw an ex-info.

jumblerg18:12:47

and this middleware, as the outer wrapper, can handle all the errors in a uniform way to both ensure they are all a) logged properly and b) returned to the client.

jumblerg18:12:37

@alandipert: this also comes into play with panoply, where our call to github's oath api might return:

2016-12-22 12:51:55.005:WARN:oejs.ServletHandler:qtp1392923222-100: /
clojure.lang.ExceptionInfo: The code passed is incorrect or expired. {:error "bad_verification_code", :error_description "The code passed is incorrect or expired.", :error_uri ""}
	at clojure.core$ex_info.invokeStatic(core.clj:4617)
	at clojure.core$ex_info.invoke(core.clj:4617)
	at panoply.backend.github$guard.invokeStatic(github.clj:10)
	at panoply.backend.github$guard.invoke(github.clj:7)
	at panoply.backend.github$get_access_token.invokeStatic(github.clj:13)
	at panoply.backend.github$get_access_token.invoke(github.clj:12)
	at panoply.backend.github$wrap_token$fn__7532.invoke(github.clj:34)
	at castra.middleware$wrap_castra_session$fn__1465.invoke(middleware.clj:98)
	at clojure.lang.Var.invoke(Var.java:379)
	at ring.middleware.reload$wrap_reload$fn__7011.invoke(reload.clj:38)
	at clojure.lang.Var.invoke(Var.java:379)
	at tailrecursion.clojure_adapter_servlet.impl$service.invokeStatic(impl.clj:137)
	at tailrecursion.clojure_adapter_servlet.impl$service.invoke(impl.clj:135)
	at clojure.lang.Var.invoke(Var.java:383)
	at tailrecursion.ClojureAdapterServlet.service(ClojureAdapterServlet.java:40)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:816)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:583)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1113)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1047)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:119)
	at org.eclipse.jetty.server.Server.handle(Server.java:517)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:302)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:242)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:238)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
	at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:57)
	at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:213)
	at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:147)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
	at java.lang.Thread.run(Thread.java:745)

jumblerg18:12:22

(i added code to throw an ExceptionInfo instead of failing silently).

alandipert18:12:36

so you're saying to opt in, you just place it above original castra in your middleware stack?

alandipert18:12:44

"around" it i guess

jumblerg18:12:55

yeah, as the outermost layer.

jumblerg18:12:25

maybe it also has a config option so that stack traces are removed when in production mode.

jumblerg18:12:05

(from the response sent back to the client, not the log)

flyboarder18:12:58

@jumblerg: 👌👍 agreed

dm319:12:57

yep, but castra middleware then needs to throw the exception for this to work, no?

alandipert20:12:07

it could know its being wrapped by looking at a dynamic variable...

alandipert20:12:29

but then, maybe it makes more sense just to add a 2nd castra-something middleware that does the new thing?