Fork me on GitHub

greetings, how can we build fulcro-inspect-electron from source? I'm trying to make a fulcro+electron template out of the inspect project, and running npm dist on the shells/electron folder runs ok but trying to run the app throws an error:

Uncaught Exception:
Error: ENOENT: no such file or directory, open '/Users/mping/Devel/workspace/fulcro-inspect/shells/electron/dist/mac/'


There is a video on youtube…see Fulcro Optimization video


it’s specifically about working on Inspect using the electron version


it seems I missed some kind of build step of shadow cljs


@mping looks like you are trying to run the electron-main build in the browser?


@thheller I'm trying to do a build


yes. there should be 2 builds. the main part running in node, the renderer part running in the browser


it looks like you are running main code in the browser?


I just did a checkout and tried to do an electron build


> yarn run shadow-cljs compile electron-main electron-renderer
> cd shells/electron/
> yarn dist


you probably can't use dist with compile


I'm not familiar with the repo. I think you are just supposed to run electron in the dir without dist?


I can run dev just fine, it's just doing the final electron binary and running that fails


yarn dist does the electron build thing


ah. then you need to run yarn run shadow-cljs release electron-main electron-renderer probably


watch and compile are both dev mode builds


I started from the fulcro-template and changed it to use websockets but now I am struggling to make the login process work as I can't change the session once the connection is set. I have the feeling I am doing something wrong, any pointers?


See Sente reconnect, which I think I made a wrapper for in fulcro websockets


Indeed you did. If I understood right, to keep using login as a defmutation i should change the SessionStore directly since the session is immutable and them reconnect from the client to get the new session data.


Maybe change response-updating-session to do that... but I am under the impression that I didn't understand it because the solution seems weird to me.


so, websockets once connected is, well, connected. This means the request in the ring session is that original request. Login does not change that, so if you shove stuff in the session you’ll still be seeing the old request map from the original…reconnect lets that be rebuilt


so, even if the session STORE has the data…that request map will still be the old one from the original request


there is no trip through Ring once connected


(unless you reconnect)


Yeah, I got the reconnect part, just wondering the best way to change the data on the session.


@thheller that's it, thanks!


Is there a simple way to essentially cascade delete related data on an entity in the app db? I know about remove-ident* and I also saw some of the sweep/missing functions in the com.fulcrologic.fulcro.algorithms.merge namespace. In this case I have an entity with foreign keys to some data in the app DB. The data might remain in the server-side DB, but I at least want to GC the related data from the app DB since it usually isn't needed for rendering once the entity that references it is removed from the UI. Right now I'm just doing a few swaps manually on the client state atom inside a delete mutation which works, but I'm not sure if there is an easier method without me rolling my own. Moreover, I saw an old issue ( related to this and wasn't sure if anything had changed since then. Thanks.


@twicebaked So: 1. GC is probably an “early optimization” in most cases…apps are short-lived and have tons of space. 2. If you really need it (sometimes you do), then yes, a new ns called normalized-state in algorithms will have some helpers.


I’m actually about to do a release upgrading Fulcro to use guardrails instead of ghostwheel…so if it isn’t in the current version, it’ll be out in a moment.


that function has a “cascade” option


There is another function I’m toying with (commented out for the moment in that same file)….currently slated to be called remove-edge, but that is a bit of a misnomer…not sure about the API/name, so it isn’t released. Another possible GC (not written) could look at a root set of some kind and try to remove “unreferenced” things, but that is impossible to do without some more info from the end user…so it would likely not get used given (1) above + complexity.


Couldn’t this possibly be done by (perhaps optionally) making the graph bidirected during normalization? I.e, a contains :b/id […] and b contains :a/id […]. That would make the cost of the check upon removing the logical tree beginning with an a (for example) that of count on the b’s :a/id for the bs contained within its (the a’s) :b/id. You’d pay an extra charge upfront for mutually inserting the references, but it would be a constant overhead, whereas the complexity of a GC-like algorithm would vary with the total number of bs in the DB in order to check if any are referencing an a other than the a we want to remove.


it’s not a cost in CPU: it’s that you cannot do local analysis on just the db to know the answers to GC questions…you’re in a distributed system, and the DB is not like RAM GC…just because something isn’t linked to it now means nothing to GC (you might be about to route back to it/re-link it to the graph). For example, you’ve loaded a list of people and are curretnly on the screen to edit one. None of the others are reachable in the current UI graph…but you might not want to GC those.


So, GC is mostly an exercise for the app dev, since only you know your local data model.


For a GC, that would be marking and timing I guess, which would both be user-driven. FWIW, making the graph bidirectional won’t help you with those decisions either. As the owner of the app, you still have to say at some point “remove this thing iff no one is referencing it” in both cases.


The only difference being that bidirectionality behaves a bit more like an analogy to reference counting.


Agree with #1, however in this case, the screen where this data appears is what the user has open 99% of the time. Further, a lot of data gets created with opening/closing things dynamically. I think therefore it seems justified.


Regarding #2, sounds great and will save me some time in several places + eliminate some boiler plate. Thanks so much.


Released 3.0.6 of Fulcro. Adds normalized state helpers, and converts spec checking over to guardrails from ghostwheel.


very cool, will be giving it a try later in the week once I make some progress on my current tasks


I’m moving all fulcrologic libraries away from Ghostwheel to Guardrails. Guardrails is a small library that is a fork of ghostwheel that strips out the things I didn’t want, and adds in a few things that I did. The overall usefulness (to me) of co-located specs with functions is being able to check those at runtime as a rough type system (which also helps greatly when testing). Feel free to use it in your own projects:


of course it can still emit proper specs to the spec registry, so generative testing is not affected…but you can turn on the checking without also generating the side-effect to the registry if all you want is the runtime arg/ret check.


Also, the instrumentation can be configured to not throw, meaning an errant spec can be tolerated (it won’t crash your program)


This prevents libraries with bad specs from completely breaking the utility of the library


yeah I'm a fan of co-located specs


I use them quite a bit in some other languages like Erlang and Elixir (see dialyzer)


My long-term desire would be to also add in optional static analysis…think: convert the code into an assembly-like structure like core-async go does, and then use the specs to trace data like a type checker does and be able to report errors during compile of the function…I can dream, can’t I? 😜


looks a bit like this which should be readable even if you don't know the language

defmodule QuietCalculator do
  @spec add(number, number) :: number
  def add(x, y), do: make_quiet(LousyCalculator.add(x, y))

  @spec make_quiet(LousyCalculator.number_with_remark) :: number
  defp make_quiet({num, _remark}), do: num


you can compose more complex specs out of specs and types defined elsewhere which is nice, though clojure spec is better in many ways overall


it does have a fair amount of tooling for analysis, testing, and checking to give you reports in various ways/points


Isn’t co-located specs a thing in core.spec.alpha2?


not that I know of


Okay, I must be remembering something else