This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-08
Channels
- # announcements (2)
- # beginners (75)
- # cider (2)
- # clj-kondo (8)
- # cljs-dev (4)
- # clojure (69)
- # clojure-europe (4)
- # clojure-nl (6)
- # clojure-uk (1)
- # clojurescript (17)
- # clojureverse-ops (1)
- # cursive (7)
- # lsp (7)
- # malli (10)
- # missionary (2)
- # off-topic (16)
- # pathom (4)
- # polylith (5)
- # portkey (1)
- # reitit (4)
- # rewrite-clj (1)
- # shadow-cljs (5)
- # spacemacs (3)
- # specter (3)
- # xtdb (14)
is there a better idiom of “move n parts from vector 1 to vector 2?” than:
(let [[drawn remaining] (split-at 5 (get-in game [:corp :deck]))]
(-> game
(update-in [:corp :hand] #(into [] (apply conj % drawn)))
(assoc-in [:corp :deck] (into [] remaining))))
I'm assuming there's a shuffled deck, from which a hand of 5 is drawn from the top. If that is the case, we can do:
(let [num-cards 5
deck (-> game :corp :deck)]
(-> game
(assoc-in [:corp :hand] (take num-cards deck))
(assoc-in [:corp :deck] (drop num-cards deck))))
I think @UEENNMX0T’s original code adds the five drawn cards to the existing hand -- your code would replace it.
I think (update-in [:corp :hand] into drawn)
is all that is needed?
I'll echo @U051MHSEK’s question about the vector representation -- does it need to be ordered? That would simplify things.
The deck has to stay ordered. This is a game like Magic the Gathering where effects can reveal cards from the top or bottom or order cards before they’re placed inside etc
And in both sides, I want to avoid any laziness because these changes should happen immediately
But either way, into drawn
looks like it will maintain the hand’s original type, so thanks @U04V70XH6
Ick, I misread the code... into drawn
is right.
I don't think lazy sequences will be problematic though, in pure game logic.
That’s very possible. I have been bitten by laziness not doing what I expect a couple of times and am now probably unnecessarily wary of it
Effects and lazy operations don't mix well at all... (map ignite-rocket (lazy-seq [rocket1 rocket2 rocket3]))
. The problem here isn't the lazy-seq, it's that map is lazy.
Possibly a very dumb question: I’m writing a clojurescript/reagent app, and I want to have persistent state across sessions. In clojure I can just spit and slurp to disk, or have a database. But googling around I don’t see a way to easily do either of these from the react app itself. I could create a light web server which will take care of this, but that seems quite heavy for what I want to do. Is there a conventional way to do this without creating a second program?
Presumably this wouldn’t suitable for the use cases where, say, you would normally use a SQLite database though?
But yeah, this isn’t durable in any way. For example Safari has an opt-out setting that will clear LocalStorage every couple of weeks.
Here’s what I’ll suggest: If you’re unsure, just use LocalStorage and get going. It’s a very minimal time investment and doesn’t require any kind of server op. As soon as you need a server, add that and think about ops. YAGNI
And there is https://github.com/replikativ/konserve for a k/v interface to IndexedDB
Provided I have some Java interface IFoo, which extends an ILifeCycle with onInit
, onStart
etc etc which will be called at the appropriate time by some runtime framework; what would be an idiomatic way to not run everything via callbacks (in callbacks in callbacks)?
I was thinking promises or a once-and-done channel, but have not seen a lot of chatter on working with callback-oriented java apis
you can push messages into a queue, sure, but that just turns a "callback api that might be synchronous" into a definitely asynchronous thing
maybe there is a better answer with knowing more specifics of the code, but based on your description it doesn't sound like it
You can make that blocking again if you want.
(let [init (promise)
start (promise)]
(.onInit life-cycle #(deliver init %))
(.onStart life-cycle #(deliver start %)))
that presumes that they are the ones calling .onInit - which would be sync regardless if they are making a blocking call
I mean register a callback that delivers to a promise. Now your code can just block on the promise.
I'm assuming .onInit
here is not the call to call the callback, but how to register a callback for the onInit event.
Now in your code you can do:
(do-some-stuff "that don't care if init has happened or not")
(do-something-that-depenps-on-init @init)
This assumes you'd be using threads, or that you don't care to have your single thread block once init is needed by some task. If you don't want to use threads, do the same thing but use a channel, and execute your tasks in GO blocks.
(let [init (promise-chan)
start (promise-chan)]
(.onInit life-cycle #(>!! init %))
(.onStart life-cycle #(>!! start %)))
(go (do-something-that-depends-on-init (<! init)))
the "in callbacks in callbacks" part isn't super relevant I don't think in that situation
I think its you provide a reified ILifeCycle which implements onInit and the framework will call the onInit. That's still fine for what I'm proposing, just you'd have to do:
(let [init (promise)]
(reify
ILifeCycle
(onInit [_this event]
(deliver init event))))
But ya, I'm making assumptions. If they don't even control the callback, then nothing can be done.This was all very useful folks, thanks! The thing with promise-chan seems to do the trick. (For context; the framework that actually invokes these callbacks has its own thread pool, and is A-okay with blocking calls, so my code will only be ugly, not ugly+broken 🙂 )
FYI, putting in a promise-chan will never block. It's a dropping buffer of 1. The first put always succeeds, the following puts are dropped.
Unless I am doing something Very Wrong, my IFoo will only be initialised and started once (each), but thank you for clarifying
How would I write a spec for a vector of two ints, where the second element in the vector must be greater than the first? So [1 2]
is valid but [1 1]
is not.
(s/and (s/tuple int? int?) #(> %2 %1))
@U064X3EF3 Is it also possible with s/coll-of
, or does the predicate there only act on individual items in the passed collection?
If I am building a banking app using clojure, can I apply uniform access controls to prod UI and prod repl ( 2 factor authentication, session time out)? Does the ability to repl and change code or run code under the guise of production support activity open a security hole and let me circumvent all change control processes?
No more then if you could push that logic normally? And also inspect that running application. But I would defer to someone who has given this more thought.
Having access control and audit trail and being compliant of all regulatory requirements is my first concern.. In non repl apps, I have access controls upto linux terminal. And I got key stroke auditing in bash repl. But in case of clojure I need access controls upto jvm.. I haven't tried JMX much. It was more of SSL public key based auth
It depends what kind of REPL you want. If you enable a local REPL only, where the REPL only allows connection from the machine itself. Then you can interact with it by ssh-ing into the machine, and if you have access control and ssh logging you'll get it for the REPL as well.
If you use a remote REPL, then its a matter of your REPL, there might be a nREPL middleware for this, but I don't know of one that already exists which adds access control and logging.
I mean, you can likely step up the repl connection such that it's auditable.
Auditing isn't bashes responsibility either, I haven't done either so I can't speak to the gritty details.
We got different tools for key stroke logging. Not done by bash..
Couldn't those be applied to a repl?
Prob move this to a security channel
if I can change the code running in production then I can install all kinds of nasty things that go around normal channels. I'm interested how this is solved
I don't disagree, I'm just not sure why that's unique to a repl. If I can update the app though any process I might be able to install nasty things. To be sure that it was less secure, I would have to know the existing security. Interesting topic, not sure I can chime in with anything else really.
The process can only run what its user has permissions to do. But the way this is handled is by securing the connection, the way that people allow ssh to prod machines for example, same thing
Repl to live app is 50% of the USP of clojure?
A REPL is how Clojure code runs in any environment. It's a process that gives Clojure it's dynamic runtime on top of the JVM. A connection is required to access that REPL, either a terminal UI or a protocol such as nRepl or Socket Repl to connect an editor. When developing Clojure apps then I recommend a Repl connection 100% of the time. If 100% uptime was required, then a repl could be used to change a system without any downtime. I haven't had a need for a REPL connection to a Live production environment, as I would deploy new versions rather than change the running system. With effective load balancing then this gives a very high uptime of the overall service. If there is a business or technical need to live update a running system in production, then the system could be deployed with an active REPL connection. This should be secure, e.g. over an SSH connection with specific IP subnet or VPN connection. A REPL connection would give you access to change and add anything you wish. So with great power comes great responsibility... For an audit trail, a log capturing the messages over the REPL connection could be captured. For example all the nRepl messages.
And if you really want a "limited" REPL, take a look at projects like clojail
-- but it's really hard to lockdown a REPL since you can call any code already in your program which could cause DB updates etc, unless you so severely limit the REPL to make those namespaces off-limits -- and then you might as well not bother with a REPL.
If you deploy JAR files with AOT compiled code and direct linking enabled, that makes it much more difficult to apply live updates via a REPL (we've had to have that discussion at work: we decided fast startup for rolling upgrades in the cluster was more important than the ability to apply live patches via the REPL for most apps -- since our deployment pipeline is automated and we can roll new builds to production in about 15 minutes if we want to -- but we have one internal app that we run from source so we can apply live patches via a REPL without downtime, it isn't clustered).
Thank you John and Sean.
That's a random number I threw
A read only repl login that can inspect data but is disabled for code changes with 2 factor authentication and key stroke logging, and session time out, is what is needed
The read only part will probably not be possible. The best you can do is use SCI to setup a REPL within the SCI environment, and make sure you only expose to SCI APIs that are read only. But I wouldn't say that SCI has been pen tested or anything like that before, so who knows if there are some vulnerabilities to abuse in this scheme. But you can lock down access to the REPL by simply only allowing local connections through SSH, and securing your SSH connection.
Take a look at direct linking for some kind of “read-only”ness. Existing non-dynamic vars can’t be modified at runtime.
Or lockable namespaces? Like schemas in db
We disable db function /stored proc execution for prod support people in our prod Oracle dbs
A REPL is how Clojure code runs in any environment. It's a process that gives Clojure it's dynamic runtime on top of the JVM. A connection is required to access that REPL, either a terminal UI or a protocol such as nRepl or Socket Repl to connect an editor. When developing Clojure apps then I recommend a Repl connection 100% of the time. If 100% uptime was required, then a repl could be used to change a system without any downtime. I haven't had a need for a REPL connection to a Live production environment, as I would deploy new versions rather than change the running system. With effective load balancing then this gives a very high uptime of the overall service. If there is a business or technical need to live update a running system in production, then the system could be deployed with an active REPL connection. This should be secure, e.g. over an SSH connection with specific IP subnet or VPN connection. A REPL connection would give you access to change and add anything you wish. So with great power comes great responsibility... For an audit trail, a log capturing the messages over the REPL connection could be captured. For example all the nRepl messages.