dviola has quit [Read error: Connection reset by peer]
diego has joined #ruby
diego is now known as Guest5514
jmcantrell has quit [Quit: WeeChat 4.6.3]
Guest5514 has quit [Read error: Connection reset by peer]
diego has joined #ruby
diego is now known as Guest8434
nyoki has quit [Quit: WeeChat 4.6.3]
ih8u has quit [Remote host closed the connection]
ih8u has joined #ruby
smp has quit [Read error: Connection reset by peer]
Guest8434 has quit [Ping timeout: 260 seconds]
sunyour has quit [Ping timeout: 260 seconds]
smp has joined #ruby
diego has joined #ruby
sunyour has joined #ruby
diego is now known as Guest6261
Vonter has joined #ruby
ih8u has quit [Quit: ih8u]
cata has joined #ruby
cata has quit [Changing host]
cata has joined #ruby
PauloEE has quit [Quit: My Mac has gone to sleep. ZZZzzz…]
Vonter has quit [Ping timeout: 252 seconds]
cata has quit [Quit: WeeChat 4.6.3]
sunyour has quit [Ping timeout: 260 seconds]
sunyour has joined #ruby
Neg127 has joined #ruby
Neg127 has left #ruby [#ruby]
Linux_Kerio has joined #ruby
Tempesta has quit [Quit: See ya!]
_whitelogger has joined #ruby
steinomead has joined #ruby
schne1der has joined #ruby
graaff has quit [Quit: Leaving]
marc_in_space has quit [Ping timeout: 276 seconds]
marc_in_space has joined #ruby
Tempesta has joined #ruby
fantazo has joined #ruby
infinityfye has joined #ruby
pcw has joined #ruby
Linux_Kerio has quit [Ping timeout: 276 seconds]
Linux_Kerio has joined #ruby
Linux_Kerio has quit [Ping timeout: 260 seconds]
jhass_ has joined #ruby
Vonter has joined #ruby
jhass__ has joined #ruby
jhass_ has quit [Remote host closed the connection]
jhass has quit [Remote host closed the connection]
jhass__ is now known as jhass
<rovanion>
How do I map a function across a sequence? In Clojure this would be (map fn seq), e.g. `(map inc [1 2 3])` => [2 3 4]. But Ruby's map doesn't seem to accept functions? `[1 2].map inc` where `def inc(i) = i + 1` throws "`inc': wrong number of arguments (given 0, expected 1)".
<rovanion>
`[1, 2].map &method(:inc)` seems to be a solution...
nakilon has joined #ruby
PauloEE has joined #ruby
PauloEE has quit [Changing host]
PauloEE has joined #ruby
gr33n7007h has quit [Ping timeout: 252 seconds]
gr33n7007h has joined #ruby
Linux_Kerio has joined #ruby
pcw has left #ruby [WeeChat 4.7.0-dev]
gr33n7007h has quit [Ping timeout: 244 seconds]
gr33n7007h has joined #ruby
GreenResponse has joined #ruby
otisolsen70 has joined #ruby
otisolsen70 has quit [Remote host closed the connection]
otisolsen70 has joined #ruby
user71 has joined #ruby
schne1der has quit [Ping timeout: 252 seconds]
sleetdrop has quit [Quit: ZNC 1.8.2+deb2ubuntu0.1 - https://znc.in]
ih8u has joined #ruby
Tempesta has quit [Ping timeout: 240 seconds]
schne1der has joined #ruby
cappy has joined #ruby
rvalue- has joined #ruby
rvalue has quit [Ping timeout: 268 seconds]
Guest6261 has quit [Ping timeout: 272 seconds]
diego has joined #ruby
diego is now known as Guest8072
rvalue- is now known as rvalue
Tempesta has joined #ruby
schne1der has quit [Ping timeout: 245 seconds]
o0x1eef has quit [Quit: Quit]
o0x1eef has joined #ruby
cappy has quit [Quit: Leaving]
<kmad>
rovanion: If you want to be a but more verbose, you can pass a block: [1, 2].map{|i| inc(i)}
inline has quit [Quit: Leaving]
<kmad>
s/but/bit/
rovanion has quit [Quit: WeeChat 3.8]
otisolsen70 has quit [Quit: Leaving]
ih8u2 has joined #ruby
<ih8u2>
what's the best way to update a browser's view after a long-running process in ruby?
<ih8u2>
like the user submits a form, the server starts a thread to do a bunch of processing while showing the user a loading page. when the thread is finished, show the user the content
ih8u has quit [Ping timeout: 245 seconds]
ih8u2 is now known as ih8u
<havenwood>
rovanion: If you come back: [1, 2].map(&:succ)
<o0x1eef>
ih8u: You have a few ways. ActionCable (Rails) | websockets, Server-Side Events, polling. Since you just want to notify the user when something completes, I'd probably choose SSE.
<ih8u>
my app is roda + bootstrap and unpoly
<ih8u>
sorry, i should have specified that up front
<ih8u>
not that rails is irrelevant to me, but anything rails-specific would be
<o0x1eef>
You'd want to emit: 'data: <representation of an event>' to the stream. And then use the EventSource API on the client side to catch the event. I might also consider my own plugin that extended the Streaming plugin so it is more specific to server-side events.
<o0x1eef>
The old school way to do this is polling, where the request returns immediately and then another endpoint provides its status.
<ih8u>
i understood about half of those words
<o0x1eef>
So then maybe polling is the simplest of the bunch. :)
<ih8u>
what i mean is that web dev is not an especially familiar domain for me
<ih8u>
i'm not incapable of learning
<ih8u>
but don't assume i know any jargon or existing best practices
<ih8u>
i don't know what an EventSource API is, for example
<o0x1eef>
I would probably recommend polling given those circumstances, since there's not much new tech in between, mostly just the implementation
schne1der has joined #ruby
<ih8u>
could you give me a rough overview of what that looks like?
<o0x1eef>
And it would likely work reasonably well with unpoly
<o0x1eef>
With the polling approach you will implement an endpoint that runs a task in the background, and the request will return immediately. You would probably include an ID in the response, so you can reference the task in the polling operation. Once the first request is complete, you would setup a function that would poll a different endpoint for the status of the task, and the status could be: pending,
<o0x1eef>
error, complete.
<havenwood>
ih8u: I wrote an SSE plugin for Roda. It works well.
<o0x1eef>
Awesome. Although, I don't think consumers should worry about 'data:', too low level, I'd prefer emit(myevent).
<havenwood>
Here's it sending "video" over the web with thousands of divs, each updating with htmx (could use Unpoly): https://youtu.be/WqYExpMWIUU?t=1528
<havenwood>
o0x1eef: I have another lib that let's you do that, but the spec is so darned simple I don't even mind "data:\n" much.
<havenwood>
o0x1eef: Thought about updating the Roda SSE gem, but wasn't sure on adding the little bit of bulk. Maybe I should?
<havenwood>
o0x1eef: I also wrote a lib I didn't bother to publish that is an SSE browser-like client in Ruby.
<o0x1eef>
I think you're right :) It is a simple abstraction you could add by yourself. But the abstraction leaks a bit with that approach, you have to know the data: syntax and also how to properly terminate the line (double \n).
<o0x1eef>
Cool video btw
<ih8u>
you guys are cool
<havenwood>
o0x1eef: Thanks!
<ih8u>
i sometimes feel like i'm in the presence of wizards
<ih8u>
havenwood: would you say that roda-sse is an appropriate tool for what i want to accomplish?
<ih8u>
in short: user submits some data, server generates an image and shows it to the user
<havenwood>
ih8u: It's a very simple, very effective way to do that, yup. Pair it with Unpoly or HTMX to avoid any Javascript and you're good to go. ;)
<ih8u>
also, is there more thorough documentation anywhere?
<ih8u>
i'm reading the example, but it's not obvious to me what everything is doing
<ih8u>
is hx-ext an htmx thing?
<ih8u>
like up-target in unpoly?
<havenwood>
ih8u: For HTMX they have WebSockets and SSE as hx-ext(ensions).
<o0x1eef>
havenwood: Supercool. Also great content for a presentation. The library still feels a bit low-level. It might be cool to be able to do: stream << Event.new(event: "div", data: "<div></div>") or something like that. Ideally you wouldn't have to know the protocol in and out, and at the same time, if you do, you could drop down to that level.
<havenwood>
o0x1eef: I have already written than interface, just forgot to ship.
<havenwood>
I think it's fair I should make the SSE payload a tad easier. It'll just take a few lines of code, and I've already written it just need to add to the plugin.
<ih8u>
my app is served by puma (i'd never even heard of falcon before just now); are these plugins specific to falcon?
<havenwood>
ih8u: The SSE plugin is general to any Rack 3 app. The WebSocket plugin is specific to Falcon.
<havenwood>
The SSE one works great with Puma.
<havenwood>
Or Falcon, whatever.
<havenwood>
For WebSockets, I wanted to use Async-backed websockets and have connections suspend while they're out for I/O, so Falcon.
<havenwood>
Reminds, me Jeremy Evans told me to update the plugins readme for Roda and I haven't yet. I think I was going to fix these up first and never did.
<havenwood>
o0x1eef: At least it's easy enough to add with only `event`, `data`, `id` and `retry` to handle and just newlines as delimiters.
<ih8u>
i didn't see this in the roda docs for third-party plugins whilst trying to find an answer on my own
<ih8u>
of course, searching would have helped but i didn't even know what the techniques were called, so my results were predictably poor
<ih8u>
can you please explain the significance of `sse-connect="/streaming"`?
<havenwood>
ih8u: It's the URL of the SSE server, as per HTMX.
<ih8u>
is that just to initiate the connection to the 'streaming' endpoint?
<o0x1eef>
Small change but I think it would help. Add to the README as a second example. :)
<havenwood>
ih8u: Yeah, it's an HTMX thing to say where to get the SSE from.
<ih8u>
i see
<ih8u>
i know nothing about htmx
<ih8u>
trying to figure out how to translate this to unpoly
<ih8u>
it doesn't help that i don't really know all that much about unpoly, either
<ih8u>
someone suggested it here for a completely unrelated feature and it made enough sense that i rolled with it
<havenwood>
ih8u: Glancing at Unpoly, I don't see SSE support. I do see polling support.
<ih8u>
ugh
<havenwood>
Looks like you'd so something like `up-poll up-source="/unread-count" up-poll up-interval="10000"` with Unpoly for polling, roughly equivalent to HTMX's SSE `hx-ext="sse" sse-connect="/streaming"` just going to wait then check rather than push when ready.
<havenwood>
ih8u: I'd think Unpoly should work fine with SSE if you want to field it yourself. I just don't see a plugin.
<havenwood>
I'm not an Unpoly user so I'm unsure.
<ih8u>
i'm obviously out of my depth here
<havenwood>
It's dead simple with HTMX.
<ih8u>
i come from more traditional application dev where shit isn't so borked at its core
<ih8u>
anyway
<havenwood>
ih8u: SSE was released with the HTML spec. >.> It's by the W3C. It's an extremely simple spec.
<ih8u>
i'm not opposed to htmx, i just don't know what it is
<havenwood>
ih8u: HTMX is an Unpoly competitor. HTMX has plugins like WebSockets and SSE. Very similar to Unpoly. Unpoly has better modal support.
<ih8u>
especially if i can use it in tandem with unpoly so i don't have to redo work
<havenwood>
ih8u: You can for sure use SSE alongside Unpoly. I just don't see a plugin.
<havenwood>
There are some folk who know Unpoly well here. I'd listen to them on that front.
<ih8u>
don't see a plugin for what?
<havenwood>
So HTMX has a plugin where you can point it at an SSE server then tell it what to do with payloads it receives.
<havenwood>
Unpoly does not. But, you can write a tiny bit of Javascript then use Unpoly to make the modifications, I'd imagine.
<havenwood>
I haven't done it and don't know Unpoly well enough to say how exactly but seems like something you could totally do.
<ih8u>
i would prefer to stay as far away from javascript as possible
<havenwood>
Or just using Unpoly's polling is fine!
<havenwood>
SSE is a little bit slicker, since you send fewer requests and might get it a bit sooner. That said, users probably wouldn't notice a difference.
<ih8u>
what's the core difference between "polling" and "streaming"?
<havenwood>
ih8u: With polling, you're telling the browser "try me again in x seconds" and it "polls" on that interval, sending a GET request.
<havenwood>
ih8u: With SSE, the browser doesn't keep making requests, the server pushes (streams) updates.
<o0x1eef>
Server-Side Events and websockets take you outside the traditional request model where requests are typically short lived, and maintains an open socket for a longer duration. IMO that's where the complexity is, because your application has to think differently, think in the streaming mentality, and that's the biggest shock. IMO anyway :)
<havenwood>
ih8u: So polling with Unpoly, the user is going to keep requesting to /polling and do the update you say when it happens. With HTMX SSE, the browser is going to keep a connection open to /streaming and field anything it streams when it happens.
<ih8u>
i see
<havenwood>
ih8u: Polling: ping, ping, ping, ping, oh a change!
<havenwood>
ih8u: Streaming: nothing, nothing, nothing, nothing, oh a change!
<ih8u>
right, makes sense
<ih8u>
thank you for the explanation
<ih8u>
streaming seems more efficient
<havenwood>
SSE is incredibly simple. Both SSE and polling will typically keep the connection open.
<ih8u>
especially when there's a definite ending
<havenwood>
Long polling helps make polling less inefficient by keeping the connection open.
<havenwood>
You can close the connection for both once the thing happens.'
<havenwood>
ih8u: It takes _very_ little JavaScript to field SSE, and there's a nice built-in way so it's like a line or two of code.
<ih8u>
ideally, when the image is done processing, i'd send it to the client once and that would be it
<havenwood>
I suspect someone familiar could help you get it working with Unpoly pretty easily. Or it is usually absolutely fine to do long polling.
<havenwood>
It's likely happening in your browser all the time and just feels like things updating transparently.
<ih8u>
but it sounds like in both cases the expectation is for the connection to remain active for the lifetime of the page
<o0x1eef>
Polling implies a new request on each ping though, at the very least. It could also imply a new connection. Streaming is built entirely around a single connection.
<havenwood>
ih8u: And both can reopen the connection if if fails.
<havenwood>
ih8u: SSE automatically does, from the browser side.
<ih8u>
is installing htmx just a matter of those two lines in the example header?
<havenwood>
ih8u: Yes.
<havenwood>
HTMX is nice. Quite like Unpoly.
<havenwood>
adam12 introduced me to Unpoly and I believe has used it to great effect.
<havenwood>
I do like that HTMX has a nice WebSocket and SSE plugin and that _just work_.
<ih8u>
yes, i believe he's the one who suggested it to me in the first place
<havenwood>
If you're using it's modal stuff, I don't think it can be beat.
<havenwood>
its*
<ih8u>
i has worked very well for the feature i adopted it for
<ih8u>
i'm far away from a single-page app, though
<havenwood>
Okay, I'm going to get coffee. Can't type.
<havenwood>
ih8u: Or try HTMX. If you need something like Unpoly with SSE or WebSockets it may ring that bell.
<havenwood>
Unless it's very time sensitive, users aren't going to much notice. I wouldn't want to do that thousands of divs over sse with long-polling because you'd be sending thousands of requests per second.
<o0x1eef>
On the other hand, if I'm just checking the status of a background job that's modifying an image or something like that, I'd probably choose polling because it is the simplest choice with the least lift
schne1der has quit [Ping timeout: 265 seconds]
vigumnov has quit [Read error: Connection reset by peer]
victori has joined #ruby
Sheilong has joined #ruby
oznek has joined #ruby
<ih8u>
well, i believe i've got enough of a handle on this to get it working how like using polling with unpoly
<ih8u>
havenwood and o0x1eef: thank you both very kindly for your help
<ih8u>
i learned a lot, and have much to think about moving forward
Guest8072 has left #ruby [WeeChat 4.6.3]
dviola has joined #ruby
<o0x1eef>
You're welcome :)
<ih8u>
is there a way to kill a ruby thread by, like, id or something?
fantazo has quit [Quit: Lost terminal]
pcw has joined #ruby
<havenwood>
ih8u: You can name a thread then find it in its thread group and kill it. Not a normal thing to do, but you can.
<havenwood>
You can store threads in a Hash or whatever and find them by key. Or add them to a ThreadGroup other than the default.
<havenwood>
I've never actually seen someone use thread names to find threads, but you can.
<ih8u>
i'm thinking about this same feature
<ih8u>
if someone navigates away from the page for whatever reason, i want to just kill the thread creating the image
<ih8u>
but that requires me persisting the thread's id somehow
<havenwood>
ih8u: For stuff like this, folk often use a job queue.
<havenwood>
There are thread-backed queues.
<havenwood>
Or db-backed is the norm.
<ih8u>
so when the thread starts, store its id in the db, and if another request comes from the same user, kill the process
pcw has left #ruby [WeeChat 4.7.0-dev]
<havenwood>
ih8u: Not quite. You typically serialize and store the job in the db. Then workers, (can be threads, can be processes) handle the work in the queue.
<havenwood>
ih8u: You can then remove a job and stop the worker various ways, depending on the particular job queue.
<ih8u>
what does "serializing the job" mean?
<ih8u>
to my mind there's got to be a way to cancel the process anyway
<ih8u>
so the user can implicitly or explicitly request it by going back to the previous page to make adjustments or otherwise
<ih8u>
what's the point of finishing a job that will never be seen?
<ih8u>
if it's already finished by then, no harm no foul, but if it's not, just kill it and move on
<havenwood>
ih8u: Serializing the job means following a convention for what worker will be called, like which class and method and what data to pass to it then saving it as something like JSON. The JSON is stored in the DB, and the worker queue takes the jobs and "performs" them in turn.'
<havenwood>
ih8u: You don't need to finish it, and often folk will "cancel" a job and have a long running process check if it has been cancelled.
<havenwood>
ih8u: Yeah, and you can kill a job by its thread too. Or invert it and have the job poll to check if it should stop itself.
<ih8u>
the tip you gave me above works great, byt the way
<ih8u>
i pored over the docs, but i didn't see the solution through the trees
<havenwood>
ih8u: Yeah, should work fine. I'd recommend making a ThreadGroup just for these threads if you're going that way.
<ih8u>
i've never actually done threads in ruby before
<havenwood>
Or other data structures (a Hash) where you maintain the keys can be nicer.
<ih8u>
despite having used it for decades
<ih8u>
it's just never been needed
<havenwood>
ih8u: Rinda::TupleSpace is actually threadsafe along with Queue.
<ih8u>
i've also not been doing web dev with it, so you know
<ih8u>
won't threads naturally queue based on available resources?
<havenwood>
ih8u: I mean for mutating data from a Thread. Like you can push or pop from a Queue from multiple threads at once safely.
<havenwood>
Or you can set Rinda::TupleSpace tuples from multiple threads at once.
<havenwood>
If you can avoid mutating data from Threads, even better.
<ih8u>
these are all completely self-contained processes
<ih8u>
they share no data
<havenwood>
ih8u: A local job setup may look like a Hash that has the jobs. A thread can reference back to that hash to see if it's job is still there, and if it removed (cancelled) stop processing.
<havenwood>
If you want to invert the control.
<havenwood>
Or it works to just kill a thread too. You might want to think about whether the thread should be doing any cleanup.
<havenwood>
Or just die.
<ih8u>
yeah, i feel like in this particular case, simpler is better
<ih8u>
just kill it and move on
<ih8u>
it's a luxury to be this cavalier with threads
<havenwood>
If it's not doing stuff like leaving open file descriptors, should be fine?
<ih8u>
yeah that's my one concern
<ih8u>
but i'm planning on doing everything in0memory to in-database
<havenwood>
ih8u: Use an ensure block in your thread to close the I/O?
<ih8u>
so the thread either gets killed before the blob is written to db or it isn't
<havenwood>
A killed thread will just ignore an ensure.
<havenwood>
hence folk having the thread check if it is cancelled, so it can ensure any cleanup
<havenwood>
if you don't need to clean up, kill away!
<ih8u>
it's can't kill in the middle of an insert, can it?
<havenwood>
ih8u: You can also Thread#raise, which gives you a lot more control over what happens.
<ih8u>
and if so, can postgres withstand that?
<havenwood>
ih8u: A transaction likely rolled back?
<ih8u>
right?
<havenwood>
But yeah, you can definitely kill a thread mid transaction.
<ih8u>
i feel like my danger zones are accounted for by the technology
<ih8u>
rught, but postgres will reject the update, right?
<havenwood>
ih8u: Yeah, that's the transaction rolling back.
<ih8u>
then it's all good
<ih8u>
i'm going to look into Thread#raise, though
<havenwood>
ih8u: You'll probably want to raise rather than kill. Maybe kill ones that don't raise properly.
<havenwood>
Raising lets you rescue and ensure.
<havenwood>
A kill is pretty... abrupt.
<havenwood>
A raise gives you more control. Then if you find you want to persist jobs if the process restarts or that sorta thing, worker queue.
Vonter has quit [Ping timeout: 276 seconds]
<ih8u>
good stuff
<ih8u>
thank you very much
Fusl has quit [Quit: K-Lined]
Fusl has joined #ruby
infinityfye has quit [Read error: Connection reset by peer]
wbooze has quit [Quit: Leaving]
eddof13 has joined #ruby
eddof13 has quit [Client Quit]
user71 has quit [Quit: Leaving]
joako_ has joined #ruby
joako has quit [Ping timeout: 248 seconds]
Furai has quit [Quit: WeeChat 4.6.3]
inline has joined #ruby
sarna has quit [Remote host closed the connection]
sarna has joined #ruby
Sheilong has quit []
ruby[bot] has quit [Remote host closed the connection]
ruby[bot] has joined #ruby
GreenResponse has quit [Quit: Leaving]
Furai has joined #ruby
<o0x1eef>
Probably worth checking out sidekiq (alongside redis) if you want to do background jobs in a web app, it's really common for ruby web apps to handle background work that way