Fork me on GitHub
#hoplon
<
2016-08-28
>
greenhorse14:08:29

@flyboarder could you explain what you mean by "dynamic scope" model

flyboarder18:08:35

@greenhorse: sure, so what I have done is broken my application down into a set of DSL's (blaze is a blogging DSL) these define a bunch of vars with ^:dynamic meta data on them, this lets me bind a new ref to this var at runtime, what this allows me to do is program against these vars without them containing a real reference (they can contain a default), in my index page I use bindings to tie these together, which means anything can be removed or replaced by adjusting the bindings instead of recoding all the references

flyboarder18:08:21

It also allows me to break up my application into libraries, as long as the libraries implement the dynamic refs, they are unaware of each other, and do not rely on each other, even tho many of them may reference my database for example

flyboarder18:08:42

I hope that helps

flyboarder18:08:07

Oh it might be worth mentioning that bindings allow 'pass through' between functions, so I don't need to directly pass a reference through my functions, they just know about the refs based on the thread binding

flyboarder18:08:41

So if I know my database will give me a formula cell I can work with that, and then just swap the binding when I change database providers

flyboarder18:08:25

No recoding needed, all my libraries assume that the db provider will give me a cell with the correct data

micha18:08:57

i use this pattern to establish per-component scopes

flyboarder18:08:44

@micha probably has a better technical answer than I do

flyboarder18:08:59

As to how this model works

micha18:08:24

there are lots of useful things about dynamic scopes

greenhorse18:08:22

This is all for multi-threaded programs only ?

flyboarder18:08:55

@greenhorse: no, works in the browser too, where there is only one thread

micha18:08:06

the browser is a multithreaded environment from the point of view of overall program design

micha18:08:19

because you have asynchronous processes

flyboarder18:08:44

^yes but you need to be aware of moving things across threads in browser

micha18:08:21

this is the login page for https://app.adzerk.com

micha18:08:39

you'll see the with-form etc. macros

micha18:08:50

those are setting up dynamic bindings

micha18:08:27

inside of that scope there are various other nested components whose attributes get their default values from the dynamic scope

micha18:08:42

so you don't need to pass the data down via arguments

micha18:08:09

this means that a nested component can be inside another component that doesn't know aobut the data

micha18:08:11

like in the example, the form/validation-message component gets its configuration from dynamic scope, which gives it access to the workflow state machine for instance

micha18:08:44

and also it gets the specific field for which it will display validation messages from the with-field macro

micha18:08:53

i mean from the dynamic bindings set by that macro

micha18:08:10

so the code is very clean and composable

greenhorse18:08:36

could you show how form/validation-message component looks inside ?

micha18:08:51

notice how the :state attribute can be overridden, but it gets its default value from the dynamic scope

micha18:08:31

the *input-error* binding is made by the form/with-field macro

greenhorse18:08:38

can I take a look at the with-field macro also ? 🙂 or just a general idea what it does?

micha18:08:56

those are just really some syntax sugar around cljs.core/binding that i personally like

micha18:08:47

the with-field macro doesn't really do much

micha18:08:35

oh sorry, my mistake

micha18:08:39

it does do some things

flyboarder18:08:12

@micha do the bindings need to exist in a namespace to be able to set them in binding? I noticed yours don't have namespaces

micha18:08:51

they're in the same namespace at compile time, they're in the cljs file that defines the same namespace in cljs

micha18:08:21

the ui.form namespace in cljs, in the ui/form.cljs file

micha18:08:28

in the other gist

micha18:08:21

eg it will generate code like (clojure.core/binding [ui.form/*input-data* ...

micha18:08:25

which is correct

micha19:08:12

expanding macros at compile time in clojure to generate code that runs in clojurescript later is confusing sometimes

flyboarder19:08:00

yeah, I read @alandipert post on cljs macro’s the other day, that was one of the light bulbs you gave me was that macro’s didn’t work the way in cljs I was expecting.

flyboarder19:08:27

im taking too much clj and expecting it to be the same in cljs

flyboarder19:08:48

@greenhorse I hope your question got answered 😛

greenhorse19:08:44

@flyboarder it probably got answered. Unfortunately it's above my current level. But I kinda understand the idea. Tried to follow the code but it's not easy (for me). The code itself looks beautiful though. Great job!

flyboarder19:08:27

examples are all micha’s stuff, i was inspired 🙂

micha19:08:48

this is a large application that we'll be maintinaing for a long time, so it pays to make it nice 🙂

flyboarder19:08:24

im my code I provide the option to also pass in local state everywhere as an alternative, so if the bindings don’t work or get confusing for whatever reason you can just ignore it, like click handlers for example

micha19:08:40

also it helps with the PTSD we get from working on the legacy C# asp .net application

micha19:08:33

yeah that's an important point, you need to be able to override all the dynamic things via direct attributes on the elements when you want to

micha19:08:21

that's another example, you can see all the forms use the same components throughout the application

micha19:08:15

the data table stuff is a little crazy because we're tangled up with bootstrap stuff

micha19:08:05

it's a temporary thing, we're planning to redesign how that works soon anyway

micha19:08:41

that namespace is an entire view, a datatable of all advertisers in your account with ability to create, modify, and delete them