Clojurians
#hoplon
<
2016-05-04
>

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

leontalbot00:05:55

@micha: That is weird. Using env lib, I use to have bind KEY_1 to KEY_2, but now KEY_2 has its own value, still, when I do boot make-war, seems KEY_1 var is used for KEY_2.

leontalbot00:05:56

Or maybe I'm wrong...

leontalbot00:05:34

I am wrong...

leontalbot00:05:28

Still can't use heroku var. Brings back default string var from my env.cljs

leontalbot01:05:21

Yes I confirm.

leontalbot01:05:44

It seems I can't read Heroku's environment variable or that defaults in (env/def...) has the priority

alandipert01:05:23

@beatngu13: i think because there isn't an invocation of the target task in the build.boot, and target-by-default is disabled in the template's boot.properties

alandipert01:05:31

@beatngu13: you can do boot prod target to see output

alandipert01:05:46

(we should probably add target to the prod task)

alandipert01:05:03

but yes, for local dev boot dev should be sufficient

alandipert01:05:19

boot prod target would give you files you can FTP/scp etc to host statically somewhere

alandipert01:05:42

@beatngu13: glad you like it btw. bring it with the questions :smile:

levitanong10:05:50

Do you guys know if there’s a size limit for castra’s RPC payloads?

levitanong11:05:40

welcome to the group, @syk0saje!

micha12:05:24

@levitanong: no limit, since the payload is in the POST body

micha12:05:31

why do you ask?

micha12:05:06

i guess there is a practical limit since the body is parsed into objects that must fit in memory

levitanong12:05:49

Oh sorry, I meant response. My team and I seem to be hitting some kind of size limit when the server is trying to send something.

micha12:05:08

that's definitely not good

levitanong12:05:11

We checked the castra source and found a 4096 byte thingy

micha12:05:46

that's the buffer size, i believe

micha12:05:51

for the writer

micha12:05:28

how does the problem manifest?

micha12:05:36

out of memory exception?

levitanong12:05:02

It shows up as a generic server error.

micha12:05:17

does the stack trace have any info?

micha12:05:23

especially the root cause

micha12:05:32

if there are nested exceptions

levitanong12:05:00

None at all. :( it's weird because the error object has an empty serverStack property

micha12:05:00

if you could make a snippet of the full stack trace that would help

levitanong12:05:09

Okay hang on, will do

micha12:05:16

ah that sounds like the conection was dropped by the client

micha12:05:21

or the server closed the connection

micha12:05:49

you do't see an exception in the terminal coming from the web server?

levitanong12:05:39

No exception from the server.

levitanong12:05:52

from the terminal, i mean

levitanong12:05:42

cljs$core$ExceptionInfo {message: "Server Error", data: c…s.c…e.PersistentArrayMap, cause: cljs$core$ExceptionInfo, name: "Error", description: undefined…} "Error: Server Error
    at new cljs$core$ExceptionInfo ()
    at Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 ()
    at cljs$core$ex_info ()
    at castra$core$make_ex ()
    at 
    at 
    at 
    at Object.<anonymous> ()
    at fire ()
    at Object.self.fireWith [as rejectWith] ()” null

levitanong12:05:45

This is a cause of one nesting level only: cljs$core$ExceptionInfo cause : null columnNumber : undefined data : cljs.core.PersistentArrayMap description : undefined fileName : undefined lineNumber : undefined message : "Server Error" name : "Error" number : undefined serverStack : null stack : "Error: Server Error↵ at new cljs$core$ExceptionInfo (http://localhost:8765/index.html.out/cljs/core.js:32838:10)↵ at Function.cljs.core.ex_info.cljs$core$IFn$invoke$arity$3 (http://localhost:8765/index.html.out/cljs/core.js:32914:9)↵ at cljs$core$exinfo (http://localhost:8765/index.html.out/cljs/core.js:32900:26)↵ at castra$core$make_ex (http://localhost:8765/index.html.out/castra/core.js:54:34)↵ at http://localhost:8765/index.html.out/castra/core.js:142:46↵ at http://localhost:8765/index.html.out/castra/core.js:195:56↵ at Object.<anonymous> (http://localhost:8765/index.html.out/castra/core.js:199:3)↵ at fire (http://localhost:8765/index.html.out/jquery.inc.js:1037:30)↵ at Object.self.fireWith [as rejectWith] (http://localhost:8765/index.html.out/jquery.inc.js:1148:7)↵ at Object.deferred.(anonymous function) [as reject] (http://localhost:8765/index.html.out/jquery.inc.js:1237:34)" status : 500

tbrooke12:05:09

@thedavidmeister: I’m looking in to Rethinkdb and Hoplon but I’m not very far with it yet - looks like a good fit - Keep me posted on your progress

thedavidmeister12:05:26

@tbrooke: this is my progress, pretty much nothing yet :stuck_out_tongue:

syk0saje13:05:08

@levitanong: with a curl request, here's the output:

syk0saje13:05:09

HTTP/1.1 500 Server Error Connection: close Server: Jetty(9.2.10.v20150310)

micha13:05:14

@levitanong: we'd need to get the exception to print in the terminal to see

micha13:05:50

try adding middleware to your ring stack to log exceptions

levitanong13:05:08

@micha can you point me to some resource for middlewares? I’m woefully out of my league here :stuck_out_tongue:

levitanong13:05:31

My first guess is to look into handler.clj

levitanong13:05:47

specifically with the castra/wrap-castra stuff

micha13:05:03

presumably an exception is being thrown elsewhere, or you'd get the stack trace in the client

micha13:05:43

put that as the outermost middleware in your stack

levitanong13:05:32

(-> app-routes
      (castra/wrap-castra ‘app.api)
      (castra/wrap-castra-session "a 16-byte secret")
      (d/wrap-defaults d/api-defaults)
      (d/wrap-stacktrace))
like so?

micha13:05:57

(def app
  (-> (foo)
      (bar)
      (wrap-stacktrace)))
or
(def app
  (wrap-stacktrace (bar (foo))))
the two are equivalent

micha13:05:03

exactly yes

levitanong13:05:31

thanks :smile:

levitanong13:05:04

not getting anything in the terminal though

levitanong13:05:21

could it be that it’s not exactly a server thing?

levitanong13:05:13

I’m trying both wrap-stacktrace and wrap-stacktrace-web

levitanong13:05:12

yeah, none of the three seem to be printing anything out in the terminal

levitanong13:05:34

could something else be catching it?

levitanong13:05:54

also, @syk0saje is working on a simple case so you guys can demo it

thedavidmeister14:05:37

how do i get castra to add transit writers?

alandipert14:05:11

@thedavidmeister: i don't think it's possible currently. i think we may want to add some atoms to castra.core that users can swap handlers onto

alandipert14:05:21

then we can pass the atom contents to t/reader and t/writer at https://github.com/hoplon/castra/blob/master/src/castra/core.cljs#L109-L110

thedavidmeister14:05:14

@alandipert: kk, i’ll just try converting datoms to a straight hashmap

thedavidmeister14:05:12

@alandipert: ah, even if i do that, it just fails on my own defrecord

thedavidmeister14:05:29

so is castra not able to handle defrecord and deftype?

micha14:05:58

transit is doing the serialization

micha14:05:15

it doesn't know how to serialize records or deftypes

thedavidmeister14:05:41

but you can normally extend it to work with that right?

micha14:05:54

yeah transit has hooks for that

thedavidmeister14:05:17

mmm, unfortunately i can’t really use castra with my data then :disappointed:

micha14:05:17

not worth the trouble in my experience

alandipert14:05:22

do they have an atom or something?

alandipert14:05:38

if they have an atom of handlers then we don't need to modify castra to fix up the transit function calls

micha14:05:07

you can do the serialization in a separate middleware

micha14:05:19

if you want

thedavidmeister14:05:30

so i can pass castra a string?

micha14:05:36

there is no need to modify castra in any case, because that part is separate

micha14:05:44

no you pass it clojure forms

micha14:05:01

i.e. you deserialize the post body yourself

micha14:05:23

if you pass it a string it would have to deserialize it

thedavidmeister14:05:09

so how do i make middleware to do serialization?

alandipert14:05:35

i think micha's point is that you can use castra to move values around, like strings

micha14:05:44

you can use the :body-keys [:foo] option to castra middleware

micha14:05:00

(wrap-castra :body-keys [:foo] ...)

micha14:05:20

you would wrap that with middleware that assocs the clojure forms onto the request :foo key then

micha14:05:42

so your middleware would read the body string and turn that into clojure

micha14:05:11

there is a wrap-transit middleware you can use

micha14:05:17

or something like that

micha14:05:47

you'd need to add stuff to the client also

thedavidmeister14:05:54

ah yeah i get it

micha14:05:46

it's easier to do this with cljson than with transit

l1sp3r14:05:56

I had a philosophical question, apart from performance, why not have a system which takes a model and a transform function of (model -> dom representation) and re-renders the lot on any model change? What does cell and cell= give you over that model?

micha14:05:34

@l1sp3r: because it's an api inversion, basically

micha14:05:41

the dom is stateful

micha14:05:46

there is no way around that

micha14:05:07

if you destroy and recreate things, youwill need to recreate that statefulness

micha14:05:31

and there are things in javascript-land that are not exposed to your program in any useful way

micha14:05:38

like the internal state of dom elements

micha14:05:50

each dom element has hundreds of properties

micha14:05:58

many of those are managed by the layout engine, etc

micha14:05:20

and there is no way to be notified when they are changed underneath you

micha14:05:49

the way you describe is a good way to manage an immediate mode thing, like canvas

micha14:05:09

but a retained mode rendering surface like the dom cannot be managed that way without a lot of duct tape

micha14:05:33

because you're building a retained mode thing on top of a false immediate mode thing that's on top of a real retained mode thing

l1sp3r14:05:36

understood, you’d need some kind of diffing to work out whats actually novel

micha14:05:48

well the diffing is the api inversion

l1sp3r14:05:49

aka virtual doms

micha14:05:05

and it's not capable

micha14:05:12

that's why you need all the lifecycle protocols

micha14:05:32

you need to have your code inspect the state of the dom elements before they're destroyed and stuff

micha14:05:39

so you can update your own dom

micha14:05:55

and there is of course no way to do that in the general case

micha14:05:11

you can only do it painstakingly via special case in your application code

micha14:05:15

really terrible

l1sp3r14:05:53

i wonder if theres another way...

micha14:05:58

diffing works if the rendering surface does not contain any state

micha14:05:05

like emacs for example

micha14:05:10

it's just a buffer

micha14:05:24

so you can do clever things like diff portions of the array

micha14:05:29

and redraw only what you need

micha14:05:38

because the terminal contains no state

micha14:05:45

it's an immediate mode surfate

micha14:05:53

but the dom is not like that

l1sp3r14:05:58

so if the renderer contained the transform function

l1sp3r14:05:08

and it was given sequence of models

micha14:05:16

the user interferes

micha14:05:30

and torpedoes any immutable sequence of things approach

micha14:05:56

the fact that dom eleemnts can get out of sync with the virtual dom

micha14:05:59

that's the problem

l1sp3r14:05:09

you need listeners generating events

micha14:05:25

which the browser does not provide

micha14:05:42

another, perhaps bigger problem is that if you have this rendering function

micha14:05:53

you now need to pass all the information as arguments

micha14:05:57

down through the tree

l1sp3r14:05:10

its essentially just data transform

l1sp3r14:05:20

a tree transform

micha14:05:27

yeah but that means that you're crippled with the kinds of abstractions you can make

micha14:05:35

because the dom isn't just data

micha14:05:50

if it were then we wouldn't need javascript at all, we'd just use something like php

micha14:05:17

like with hoplon you can make custom dom elements

micha14:05:30

that is extend the capabilities of the dom elements themselves

micha14:05:43

which is different than having a function that works like a macro

micha14:05:54

like imagine if clojure didn't have lambda

micha14:05:59

you can only make macros

micha14:05:06

you could program in that world

micha14:05:14

but it would be far worse than clojure

micha14:05:26

that's what the virtual dom is

l1sp3r14:05:39

right, but even with custom elements, still decomposes to basic html elements

micha14:05:47

how do you mean?

l1sp3r14:05:01

so i say want a treeview

l1sp3r14:05:12

needs to be expressed in terms of divs etc

l1sp3r14:05:23

its not quite polymer

micha14:05:29

well only in the way that lisp needs to decompose to special forms at the bottom

micha14:05:37

i think it's more than polymer

micha14:05:44

since polymer is doing the same thing really

micha14:05:53

just hiding it from you via special hooks in the dev tools

micha14:05:50

the essential difference between vdom and hoplon is that hoplon is fully lisp, so you can use closures and scopes

micha14:05:58

which allows you to make first class abstractions

micha14:05:06

vdom is more like programming with macros

micha14:05:14

you can't form a first-class abstraction with macros

micha14:05:21

you can only make dsls and whatnot

l1sp3r14:05:16

so i have a tree of elements i want to render

l1sp3r14:05:23

i want to modify this tree

l1sp3r14:05:39

my callbacks generate actions

l1sp3r14:05:47

which modify the tree in the root cell

l1sp3r14:05:25

and that generates cascading changes through cells to generate the view

l1sp3r14:05:22

now i want to take out as much logic from the view layer as possible

l1sp3r14:05:38

and my model starts looking very much like a dom

micha14:05:47

yeah i would not do it that way

micha14:05:58

because first of all why is your data in a big tree?

micha14:05:09

lisp programs don't have that form

l1sp3r14:05:12

because my data is a big tree

micha14:05:20

that's highly unusual

micha14:05:52

i can't think of a single time i made a clojure program that had all the data in a single var

micha14:05:11

that's why we have namespaces

micha14:05:34

this big atom approach is basically making a coconut namespace system

l1sp3r14:05:36

i could be editing arbrary clojuRe data structures on the server

micha14:05:49

but without the affordances of lisp's scopes

l1sp3r14:05:51

lists, sets maps, lists of maps etc

micha14:05:25

yes cell contain lists and maps and so on also

micha14:05:31

in hoplon programs

l1sp3r14:05:36

ok lets not worry about the size

l1sp3r14:05:42

i do need to edit trees

micha15:05:09

editing trees is the most difficult thing to do with hoplon

micha15:05:25

but i think it's still superior to the vdom

micha15:05:44

the orgmode thing alan and i made is a tree editing program

l1sp3r15:05:51

ok with the vdom approach, its straightforward, tree in, transform, tree out

syk0saje15:05:04

@levitanong: found that if i commented out wrap-castra-session in handler, the problem goes away

micha15:05:07

right, plus lifecycle protocols, plus no real custom elements, etc

l1sp3r15:05:38

ok… not being difficult, i really like hoplon, just searching for the best answer

micha15:05:17

haha no worries, it's a weird thing

alandipert15:05:26

we have discussed in the past that maybe a vdom is a useful thing to embed in a hoplon program

alandipert15:05:40

similar to the architecture of emacs, it is a retained-mode system in which there are immediate-mode "holes"

micha15:05:43

i'm happy to discuss with skeptics

l1sp3r15:05:02

i think theres a better way than vdoms though

l1sp3r15:05:59

well the vdom diffing that is

l1sp3r15:05:18

ok playing devils advocate, i could build a system out of cells

l1sp3r15:05:43

it takes a dom in and uses cell= to filter and do the diffing

l1sp3r15:05:20

is that an antipattern?

alandipert15:05:45

one of the illuminating things is "objects that know about themselves"

alandipert15:05:01

like the case of the row in a table with a "delete" button

alandipert15:05:20

there are a few ways to do this and they seem all to correspond to different ways of attacking the problem

alandipert15:05:39

the vdom way is manage a namespace of events to virtual objects and to capture all events

alandipert15:05:54

the jquery way is to navigate tot he parent and delete it from the dom

alandipert15:05:24

the hoplon way is to remove the domain object from whatever cell its in

alandipert15:05:11

the cell-based vdom thing would need system #1 probably, virtual objects would need a way to refer to themselves

l1sp3r15:05:37

with #1 i expect its their own path from the root

alandipert15:05:04

could be, would then need to tie in a system for fixing up paths when a thing gets removed or added

alandipert15:05:30

the other part would be managing the dom objects without leaks, so you'd need a pool of them probably

alandipert15:05:40

also a pool for the events you're intercepting

alandipert15:05:50

schemes like this seem to converge on react

alandipert15:05:20

never bad to do experiments tho. who knows, maybe a thing with the affordances of both hoplon and react is possible, just takes a new perspective

alandipert15:05:45

i just took a shower, and was thinking about how awesome browsers could be

alandipert15:05:01

that's really the main hoplon limitation, that browsers aren't more awesome

alandipert15:05:11

they don't have weak references, and the dom attributes aren't cells

alandipert15:05:28

if cells and proper GC were in at the browser level, we'd have an amazing system. lisp-machine like

alandipert15:05:58

instead they spend all their time doing dumbo syntax things in javascript :disappointed:

l1sp3r16:05:10

vdom schemes seem to diff the vdom, i wonder if its better to cache the previous model/vdom result fragment, and just compare the model

alandipert16:05:34

that's definitely how browsers themselves work

alandipert16:05:46

like HTMl rendering engines work like that

l1sp3r16:05:45

if the rendering engine captures the transform function from model -> VDOM should be fairly trivial

l1sp3r16:05:11

in this model the VDOM is just clojurescript data

l1sp3r16:05:22

cells and ratoms are good, but in my instance i think the simplest conceptual model is to regenerate all from data every update

micha16:05:09

you can put an element in a cell

micha16:05:21

a formula cell

micha16:05:27

to try it out and see if you like it

micha16:05:36

(cell= (div ...))

micha16:05:02

that will not do the diffing, but it will regenerate from data each update

micha16:05:22

then you can see the issues that arise with the vdom architecture

micha16:05:46

(defn render [data]
  (div :class (:class data)
    (span (:text data))))

micha16:05:52

then in hoplon page you could do:

micha16:05:22

(html
  (body
    (div :id "mountpoint"
      (cell= (render the-data-cell)))))

flyboarder16:05:38

Any ideas why running a jquery plugin using interop works, but when i try to do the same thing as a do! multimethod, the plugin seems to do nothing? Has anyone else run into this problem before?

flyboarder16:05:59

it’s fairly simple js

levitanong19:05:25

How is validate used?

levitanong19:05:30

Could you also link the plugin documentation?

jumblerg23:05:00

@alandipert: reading your remarks above… i've been wondering if there’s some way to add weak reference emulation for event handlers through the hoplon layer

jumblerg23:05:56

if hoplon exposed an add-event fn or wrapped the existing addEventListener on elements, and could detect when those corresponding elements were attached and detached to the dom (maybe via mutation observers), perhaps it could call removeEventListener for elements as they were detached.

jumblerg23:05:02

i’ve read about frameworks/projects that implemented reference counting schemes to deal with this problem