Have you ever written a non-trivial "real time" app in Ruby? I have (https://github.com/stevegraham/slanger). I think Ruby is actually very well suited to event driven apps. Eventmachine is a very mature library providing asynchronous I/O based on the same pattern as Node. Ruby also has fibers as a native language feature, allowing you to write asynchronous code that looks synchronous, i.e. no nested callback hell, and consequently this makes it a lot easier to write tests for.
Comparing Node to Rails is also absolute nonsense. Rails is a web framework and Node is much lower level than that. Rails is essentially a suite of DSLs for building web applications. Of course there are costs associated with that amount of abstraction.
It's really not turning into anything, Rails had response streaming back in the days, the whole stack has response streaming (as long as the underlying server handles it and there's no broken middleware anywhere) and most microframeworks have response streaming.
There's nothing special to response streaming.
> since everything in node.js is non-blocking.
Not in your wildest dreams, IO is non-blocking and that's about it. Try a bit computation in a node response flow and see your concurrency disappear. If you're looking for "everything is non-blocking", Node most definitely isn't going to cut it.
> There are so many ways to shoot yourself in the foot if you develop large realtime systems in Ruby
Just as there are in Node. And Ruby has eventmachine which works rather well.
Choosing a monolithic framework doesn't encourage separation of concerns and clean interfaces. It also makes scaling horizontally more difficult.
So many ruby gems are built around rails instead of being built as separate discrete libraries. This means that once Rails get really long in the tooth (it has already started to) that it's going to take a bunch of libraries that could have been timeless along with it had they not been so deeply integrated with Rails' architectural features.
The fact that a document like this exists... https://github.com/radar/guides/blob/master/sprockets/sprock...
This is a detailed guide to the internal workings of Sprockets. Hopefully
with this information, somebody else besides Josh Peek, Sam Stephenson,
Yehuda Katz and (partially) myself can begin to understand how Sprockets works.
... that connects so many parts of rails isn't a good sign.node.js, or more specifically npm, got things really right by making the inclusion of libraries really trivial and easy to separate concerns.
I can't begin to imagine the complications with trying to do proper async around a framework that wasn't designed from the ground up to be async. I'd expect that you'll often have to dig into the internals of Rails to discover where something is functioning synchronously where you didn't expect it to. Don't get me wrong, I love the opportunity to dig into other people's code that I rely on, but that is only fun with libraries, not frameworks. When digging around in frameworks you often spend tons of time having to investigate glue code between parts to even understand the part you need to understand.
When Rails starts including a messaging stack, job server, and more, then I'd be right behind you in your complaint.
Actually, Rails 4 does include a job queue:
https://github.com/rails/rails/commit/adff4a706a5d7ad18ef053...
However, I'm not complaining. ;)
First of all, there aren't really any decent solutions for the problem in Ruby. There's a few projects with great potential, but nothing really mature - when I needed to do this, I went with cramp.in, which hasn't been updated in over 6 months now sadly. Node.js would unequivocally have been the better tool for the job, but I didn't want to learn Javascript or Node for this one-off.
Secondly, even if there were mature Ruby equivalents to Node.js, or no impediment to me using Node, I can still see a lot of benefit to doing this in Rails. Personally I think it makes sense to be able to send events down to the clients and hook into the same models and business code you already have in place in the Rails app. (For certain use-cases, for example a project that's in Rails, is mostly a "regular" web app, but you want to give live updates for certain model changes, such as messages, ticket changes, new blog posts, whatever - obviously if the app is totally oriented around live functionality then node would probably be a smarter choice)
(I apologise for using the term "Node.js" as if its another web framework, I know that's not entirely accurate but I don't know enough about it to write more accurately!)
Even ignoring other frameworks, Rails has supported streaming through various APIs since at least the 2.x days.
In Rails 2, you would pass a Proc to the render method giving direct access to the response.
Rails 3 changed the API to any Enumerable assigned to self.response_body, as described in the article.
Rails 4 gets yet another API. It may be arguably cleaner, but the end functionality remains the same as it has always been.
Uh? Rack has supported streaming since the beginning, and Sinatra has had special support on top of that since 1.3.
> Secondly, even if there were mature Ruby equivalents to Node.js
> I can still see a lot of benefit to doing this in Rails.
Don't dismiss the drawbacks. Such as self-DOS-ing. Your Rails application can only have as many clients total as you have workers if they all keep a permanent connection.
The shift to single-page, JS-driven applications and large numbers of dynamic updates requires a different set of design priorities and the performance characteristics of Ruby itself are more problematic in this environment.
In many cases it makes more sense to use something like Go or Node or Erlang that was designed to handle these kinds of loads from the ground up.
For websockets-based applications, check out Cramp. And note that as both Rails and Cramp are Rack-based, you can combine them.
"Go or Node or Erlang"
One of these things is not like the others...
As opposed to doing I/O flow control in an asynchronous, callback-driven system? Have you ever heard of the "slow consumer problem"?
Not only can you build realtime systems with threads, by using synchronous I/O you'll be taking advantage of all the flow control TCP has to offer, instead of unboundedly filling up write buffers.
I think the modern web stack does need an easy way to do realtime communication, and as a casual Rails engineer since pre 1.0, I'm happy to see this get support out of the box.
How is developing "large realtime systems" in Node any better?
It's a thrown together library on top of V8, in a language that doesn't even have concurrency primitives.
Doing non-blocking stuff in Node is like powering your car by pedalling. It works and takes you places, but it misses the point of needing a car in the first place.
Whenever I see someone trying to cobble together their own framework by prepackaging a bunch of node.js libraries I see someone who is doing it wrong.
You shoot yourself in the foot in node.js when you treat it like rails. You shoot yourself in the foot with rails when you try to do anything outside the scope of problems for which it was designed.
For the perplexed: Node isn't a web framework.
I also find any Node/Rails comparison's pretty silly honestly, they're very different tools, and though you can do the same thing with each, I wouldn't.
For the perplexed: it doesn't matter.
Take it to mean "Node+whatever" vs Rails. Or even "raw Node + totally custom js framework on top" vs Rails.
I wrote this thing called Celluloid and I can assure you this isn't true. Ruby has "abort_on_exception" for threads, but the default is most assuredly false.
"Good luck debugging concurrency bugs."
Good luck debugging concurrency bugs in a callback-driven system!
I'm talking about CPU instruction level crashes, not language level crashes. Things like writing to an invalid memory address or heap corruption.
> Good luck debugging concurrency bugs in a callback-driven system!
Actually I already mentioned concurrency bugs in evented systems in the article.
So what you're trying to say is if the entire virtual machine crashes, you lose all running threads.
Would this be analogous to what PHP does if you being writing a response without output buffering?
Erm... it pushes the response to the client, not the server, and only pushes after a normal HTTP request.
And of course, it also ties up a huge amount of server resources (total number of clients = total number of workers, since each client permanently ties up a connection forever). Phusion Passenger's docs recommend 8 workers/GB RAM, hope you expect users.
If you're looking for a SignalR equivalent in Ruby, you need EventMachine.