Fork me on GitHub
#hoplon
<
2017-03-14
>
chromalchemy00:03:06

Cool. @alandipert @micha Thanks for the help!

chromalchemy00:03:36

I've been requiring [hoplon.jquery] explicitly (even in .cljs.hl) , as I seemed to have some issues with dynamic attributes working properly without it. In what cases do you typically require it? What about [cljsjs.jquery]? (...from a hoplon/hoplon-UI elem attrs perspective)

chromalchemy00:03:58

Also. I patterned my project off the Hoplon Blaze repo. It uses Bidi for routing. https://github.com/degree9/blaze/blob/80eaa98a09227ec73709afe56bf4e4ec620088e6/src/blaze/bidi.cljs Now I want to integrate Castra. The Castra template includes Compojure. How do I approach this overlap? Can the default Castra code be easily made to use Bidi instead of Compojure? Or is the routing lib orthagonal to the Castra functionality? I'm pretty fuzzy on how routing libs work 😶

alandipert00:03:40

routing is def. orthogonal to castra

alandipert00:03:05

the server-side of castra is a ring middleware

alandipert00:03:12

but it doesn't 'consume' any routes

chromalchemy00:03:25

Ok. Thanks will try to grok -> munge 🤓

chromalchemy00:03:16

Oops. I see now that my routing is handled by UI. That should simplify integration 🙂 "Ui treats the hash within the address bar as another part of the view. it both presents a visualization of and provides a control for changing the application's underlying view state. this state must itself be persisted elsewhere, typically within a javelin cell containing the application's model. Ui represents routes as values of the form [[:foo :bar] {:baz "barf"}], where the vector in the first position is the path and the map in the second position corresponds the query string. ui reads and writes these values via the :route and :routechanged attributes passed to window when the application is constructed. like any other attribute, :route accepts either a route or, more practically, a formula cell bound to the application model to update the route as the application's state changes. :routechanged accepts a callback that will be invoked with an updated route whenever a users enters a route different for the one being displayed into the address bar.

chromalchemy00:03:52

Does this correspond with the basic ring-middleware setup of Castra?

jjttjj00:03:03

the two are unrelated. Castra doesn't really have anything to do with routing. You can use any other server routing system alongside it. UI's routing is client (ie cljs) only and is kind of geared towards single page apps.

jjttjj00:03:05

so with UI's routing when you click on a link it would change the url to look something like http://app.com/#/clicked/link and then you have a route cell that now changes to "/clicked/link", and then you could have something like a case-tpl that changes based on the value of the route cell to show a different page. but there's no actual server-side routing going on

chromalchemy01:03:17

Got it. So UI's routing is client-side. And Castra is orthoganol to routing in general, and doesn't consume any routes. Does this compojure code in the Castra handler constitute server-side routing then? Or can I just use the UI pattern without requiring Compojure?

(ns app.handler
  (:require
   [castra.middleware              :refer [wrap-castra]]
   [                :as    io]
   [compojure.core                 :refer [defroutes GET]]
   [compojure.route                :refer [resources not-found]]
   [ring.middleware.defaults       :refer [wrap-defaults api-defaults]]
   [ring.middleware.resource       :refer [wrap-resource]]
   [ring.middleware.session        :refer [wrap-session]]
   [ring.middleware.session.cookie :refer [cookie-store]]
   [ring.util.response             :refer [content-type resource-response]]))

(defroutes app-routes
  (GET "/" req
    (-> "index.html"
        (resource-response)
        (content-type "text/html")))
  (resources "/" {:root ""})
  (not-found (or (io/resource "public/404.html")
                 "Oups! This page doesn't exist! (404 error)")))

(def app
  (-> app-routes
      (wrap-castra 'app.api)
      (wrap-session {:store (cookie-store "a 16-byte secret")})
      (wrap-defaults api-defaults)
      (wrap-resource "public")))

jjttjj01:03:12

@chromalchemy Yes that's server side routing, even if you want to use UI's routing, you'll need some way to initially serve up the index page (and having server side fallbacks for 404, etc)

chromalchemy01:03:58

Ok. That's where I was getting confused, server vs client routing concerns. So this is just basic vanilla boilerplate app-loading "routing" setup ..... all the stuff after `(-> app-routes ...) is ring middleware stuff and not "routing"?

jjttjj01:03:21

Yes exactly

chromalchemy01:03:48

Thanks for the feedback. Feel more confident to proceed 😅

jjttjj01:03:21

Have fun 🙂

jumblerg04:03:11

@chromalchemy: @jjttjj's answer is spot-on, but since i happened to notice your inquiry during a caffeinated late-night moment, i'll elaborate a bit. in a typical url, anything before the hash # delimiter is the server's concern, while everything after it is a representation of your client's state. since the left side of the hash is the server while the right represents the client, it is also the discriminator between a one:many relationship.

jumblerg04:03:30

i think it helps to be mindful of the fact that, in many implementations, the server is used for two purposes. first, to serve the client; second, as a service that returns data to the client application. i prefer to separate these out. i typically have the browser download the client from an s3 bucket (deployed via https://github.com/tailrecursion/boot-bucket), while the data is returned from an ec2 instance. my client applications and services live in two distinct projects which produce two different artifacts that get deployed to their two respective places. i use cloudfront's routing (see https://github.com/tailrecursion/boot-front) to unify them behind the same domain without using CORS in production.

jumblerg04:03:14

regardless of the mechanics, you're typically concerned with three parts of two uris:

jumblerg04:03:57

1. the path to load the client before the hash in the client url. you'll typically configure this to be at the root of your domain.

jumblerg04:03:49

2. the fragment used to represent the client's state after the hash in the client url. this can be managed using ui's routing. the basic idea is that the address bar is just another representation/view, albeit a textual one, of your model. just wire up your window's :route attribute in the same way you'd bind any other attribute to a cell and you should be in business. of course, the address bar also serves the dual function of a textual input field as well; it can allows users place the client application in a particular state through the url, and also change application state command-line style... sort of. the :routechanged attribute invokes the function you pass it when this occurs.

jumblerg04:03:44

3. the url called by the client to request data from the server. this is where castra comes in handy: it abstracts away the implementation details of the http protocol, replacing the highly-overloaded http request methods with more granular function calls that can be made directly from clojure.

jumblerg04:03:25

on a somewhat related note, i think we should make a change to ui's routing by moving the route->hash and hash->route utils to the user's side of the window api to give users more control over how they represent their client state in the url fragment. as i recall discussing with @flyboarder some time ago, a developer might prefer to generate the fragment by serializing their application state in some other manner.

jumblerg04:03:57

this is how i think about urls and routing, anyway. hope it helps sort through things.

alandipert16:03:13

@micha how do you feel about making get-session in castra public? or anyone else

alandipert16:03:15

craig found he needed to dig into local storage manually to get it to make a 'raw' castra call so he could just get the promise back

micha16:03:14

yeah makes sense