Oh my dear Ruby, son of Lisp, Perl, Eiffel and Smalltalk, time has passed but I think you still are such an amazing creature. Sure, you need to resolve some issues. Everbody does. Some really intelligent doctors have stated that you are sick, that you might be dying, others are not quite sure about that:
Good clarification about the previous post: Rubinius seeks to modernize, not bury, the Ruby language
Let's start with some graphs. Everybody loves graphs!
Programming language popularity indexes
As reported by the TIOBE Index, Ruby has descended one position on its ranking during this year. On January 2013, Ruby was the eleventh most used language and is now the twelfth.
TIOBE ratings for Ruby
According to the Popularity of Programming Language index (PYPL), Ruby is the ninth most popular programming language. PYPL is created by analyzing how often language tutorials are searched on Google.
According to RedMonk Programming Language Rankings, at this moment, Ruby is the seventh most popular language based on correlations between GitHub’s and Stack Overflow’s rankings.
Other popularity indicators
Let's move onto other indicators.
Ruby conferences registered by lanyrd per year:
The absolute number of conferences kept on growing, however the growth speed diminished.
The number of modules seems to have kept growing at about the same speed for the last years.
Ruby's lines of code
Ohloh reports that the Ruby language number of lines has grown at a steady speed.
Interest over time reported by Google Trends:
However the interest over time registered by Google seems to have plunked.
These statistics show that even though Ruby is not as popular as it was a few years ago, it's still a big player.
So far, we have seen some numbers. Now it's a good time to check other indicators that account for liveliness of Ruby.
Let me mention a few interesting tools created in Ruby apart from Rails and other web frameworks such as Sinatra or Padrino. I think this might give an idea about Ruby's usage. Homebrew, coded in Ruby, is the simplest and more useful package manager for Mac OS X. Even if I prefer other alternatives, created in Nodejs, such as Docpad, Jekyll, which is coded in Ruby, was one of the main reasons for the popularity of static page generators. Discourse, the best discussion platform. Artoo is a micro-framework for robotics. I don't know a heck about it. Ruby also has got the two most used IT automation tools that exist: Chef and Puppet. These are two really big players that won't disappear anytime soon and it doesn't look as though they want to move away from Ruby.
Regarding books, Ruby always had great and fascinating books such as:
And now we got Ruby under a microscope. One of the latest and greatest books about Ruby that keeps up with this tradition of precious books.
Last but not least, let's see some Ruby talks:
As we can see, very different pieces of software are crafted with Ruby and Ruby developers seem to be attracted by very different things. From my point of view these are only examples that show how much life there is on the Ruby world. In my humble opinion Ruby is not going to die anytime soon. I think that, while Ruby's hype is dead, the language and its great community have a great future.
Ruby's hardest problem
This doesn't mean there are no problems on Ruby. Ruby seems to be in the same order of magnitude in terms of speed than Python, PHP, and Perl but in general Ruby it is not the fastest kid on the block. Even Matsumoto, its creator, admits it!
Yukihiro Matsumoto details the past, present, and future of the popular programming language, calling mobile 'the way to go'
InfoWorld: Are there any limitations with developing Ruby applications?
Matsumoto: Well, in some cases, performance could be the limitation. For example, Twitter was originally written in Ruby, but it has now has billions of users so, it's larger, its core [is now] on top of the JVM. It was originally running on C Ruby, my Ruby. [With Twitter's JVM-based program], the program is written in Scala and Clojure.
I think Matsumoto represents very well the self-critical spirit of the Ruby community. But it would be great if we could sidestep the performance problem. One way to do it is with concurrency and parallelism.
Some say that:
Both Python and Ruby have full support for multi-threading. There are some implementations (e.g. CPython, MRI, YARV) which cannot actually run threads in parallel, but that's a limitation of those specific implementations, not the language. This is similar to Java, where there are also some implementations which cannot run threads in parallel, but that doesn't mean that Java is single-threaded. Note that in both cases there are lots of implementations which can run threads in parallel: PyPy, IronPython, Jython, IronRuby and JRuby are only few of the examples. Are languages like python and ruby single threaded unlike say java?
But come on, that's pure theory. Almost everybody uses MRI, the main Ruby implementation. I think that the main reason is that it's reliable. However the issue with MRI is that it:
has something called a global interpreter lock (GIL). It's a lock around the execution of Ruby code. This means that in a multi-threaded context, only one thread can execute Ruby code at any one time. So if you have 8 threads busily working on a 8-core machine, only one thread and one core will be busy at any given time. The GIL exists to protect Ruby internals from race conditions that could corrupt data. There are caveats and optimizations, but this is the gist. Nobody understands the GIL:
With GIL or without it (read 2012: The Year Rubyists Learned to Stop Worrying and Love Threads) it's pretty clear that:
the single-core nature of the canonical Ruby interpreter, MRI (the "Matz Ruby Interpreter"), is limiting Ruby’s potential applications.
Parallelism is a Myth in Ruby. While we wait for this to get solved for MRI we can check out why critics of Rails have it all wrong (and Ruby's bright multicore future) and learn how to overcome this problem:
Rubinius is an implementation of Ruby designed for concurrency using native threads to run Ruby code on all the CPU cores. It also has a low-pause generational garbage collector, LLVM-based just-in-time (JIT) native machine code compiler. Pretty badass piece of technology. It's mainly implemented on Ruby. Chicken or the egg?. But it has a big downside: Rubinius currently implements MRI Ruby version 1.8.7. MRI Ruby 1.8.7 was released on April 2008, that's more than 5 years ago. From what I have read Rubinius is working on implementing the upcoming MRI 2.1 release.
JRuby runs your Ruby code on the JVM, and that's why we get real threading. Latest version of JRuby is compatible with MRI Ruby 1.8.7 and 1.9.3. If you haven't got enough links yet start reading Concurrency in JRuby. JRuby: Insights from Six Years in Production and The Future of JRuby by Charles Nutter and Thomas Enebo
Also you got Visualizing Garbage Collection in Rubinius, JRuby and Ruby 2.0 that will be useful for any of the three implementation we mentioned.
Even if it's a synthetic benchmark, this comparison between MRI 1.9.3, MRI 2.0.0, MRI 2.1.0dev JRuby 1.7.4 and Rubinius 2.0.0 has some interesting information.
Again I think it's a good idea to hear what Matsumoto thinks:
InfoWorld: What's your perspective on alternative Ruby implementations just as JRuby and Rubinius?
Matsumoto: I don't see any problem about other implementations just because the diversity is very sound, the healthy things they have. And actually Ruby, the language, is very good for productivity but the programming environment differs from application to application. For example, some clients require very stable and multicore applications on top of the JVM. In that kind of field, JRuby works better than my Ruby, actually, which is called C Ruby. For most of the cases, C Ruby is good for Web applications. But in certain situations, JRuby and maybe Rubinius are a better fit for a particular requirement.
We need a concurrency model!
Despite having real concurrency or being able to run code in many cores, doesn't mean it's easy to do so:
Why Johnny Can’t Write Multithreaded Programs
Introductory multithreading materials explain what threads are. Then they launch into discussions of how to make those threads work together in various ways, such as controlling access to shared data with locks and semaphores, and perhaps controlling when things happen with events. There’s detailed discussion of condition variables, memory barriers, critical sections, mutexes, volatile fields, and atomic operations. You’re given examples of how to use those low level constructs to do all manner of systems level things. By the time a programmer is halfway through that material, she thinks she knows how to use those primitives in her applications. After all, if you understand how to use something at the systems level, using it at the application level should be trivial, right?
This is like teaching a teenager how to build an internal combustion engine from discrete parts and then, without the benefit of any driving instruction, setting him behind the wheel of a car and turning him loose on the roads. The teenager understands how the car works internally, but he has no idea how to drive it from point A to point B.
Knowing how threads work at the systems level is mostly irrelevant to understanding how to use them in an application program. I’m not saying that programmers shouldn’t know how things work under the hood, just that they shouldn’t expect that knowledge to be directly applicable to the design or implementation of a business application. After all, knowing the details of the intake, compression, combustion, and exhaust cycle doesn’t help you in getting from home to the grocery store and back.
Introductory multithreading textbooks (and computer science courses) shouldn’t be teaching those low level constructs. Rather, they should concentrate on common classes of problems and show developers how to use higher level constructs to solve those problems. For example, a large number of business applications are in concept extremely simple programs: They read data from one or more input devices, apply some arbitrarily complex processing to that data (perhaps querying some other stored data in the process), and then output the results.
Red and Green Callbacks
What’s even more difficult to understand is errors. Errors in multi-threaded callback code with shared memory is something that would give me an extremely large headache.
The best concurrency model I know in the Ruby land is the one provided by the family of Celluloid. Celluloid, with the help of its sons DCell - this is the best one - and Celluloi-io, tries to help you achieve the nirvana described by these two famous quotes:
"I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages" - Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"
"Objects can message objects transparently that live on other machines over the network, and you don't have to worry about the networking gunk, and you don't have to worry about finding them, and you don't have to worry about anything. It's just as if you messaged an object that's right next door." -Steve Jobs describing the NeXT Portable Distributed Object system
Why I like Ruby
Time to land on Earth. We have spent too much time floating on the sky. I have never explained why I use and like Ruby.
In comparison with other dynamic type languages it has an awesome set of tools as rubocop, ruby-lint and reek that help you analyze your code. Furthermore it has the best REPL that I am aware of: pry. Watch REPL driven development with Pry and Pry, The Good Parts! to learn how it can help you speed up your development.
On the other hand, as I mentioned before, it's not as fast as C or Java but Ruby core developers are doing an excellent work on that area. For example, check the history of improvements to the Ruby garbage collector.
Even though I sometimes think that it's Perl-inherited attitude of having more than one way to do the same thing is overwhelming (for some reason I need to reread Understanding Ruby Blocks, Procs and Lambdas every month or so), in general I really like its syntax and semantics. I think it's a very expressive and intuitive language. In my opinion how good a language is is not defined only by semantics, syntax and performance. I am not an academic. I have not even finished university.
The Master, The Expert, The Programmer
Programming is a very new discipline, so there’s not too many master programmers out there. What’s worse is that the few people I would consider masters aren’t very exemplary of the software profession and art. They are typically professors who never write anything under a deadline and are given complete artistic freedom to develop whatever they want.
I am not saying this in any mean way. I have huge respect for professors and researchers. But I am not one. I code stuff on a deadline. That's why for me the community and the tools that it has created are really important. They are the salt and pepper of what I am cooking. This is why I love Ruby. It has a lot of salt and pepper. I particularly like Rails, the most known Ruby framework, and its cut down version Rails API, because of the following features:
Debunking the Node.js Gish Gallop
Handled at the middleware layer:
Reloading: Rails applications support transparent reloading. This works even if your application gets big and restarting the server for every request becomes non-viable.
Development Mode: Rails application come with smart defaults for development, making development pleasant without compromising production-time performance.
Test Mode: Ditto test mode.
Logging: Rails applications log every request, with a level of verbosity appropriate for the current mode. Rails logs in development include information about the request environment, database queries, and basic performance information.
Security: Rails detects and thwarts IP spoofing attacks and handles cryptographic signatures in a timing attack aware way. Don't know what an IP spoofing attack or a timing attack is? Exactly.
Parameter Parsing: Want to specify your parameters as JSON instead of as a URL-encoded String? No problem. Rails will decode the JSON for you and make it available in params. Want to use nested URL-encoded params? That works too.
Conditional GETs: Rails handles conditional GET, (ETag and Last-Modified), processing request headers and returning the correct response headers and status code. All you need to do is use the stale? check in your controller, and Rails will handle all of the HTTP details for you.
Caching: If you use dirty? with public cache control, Rails will automatically cache your responses. You can easily configure the cache store. HEAD requests: Rails will transparently convert HEAD requests into GET requests, and return just the headers on the way out. This makes HEAD work reliably in all Rails APIs.
Handled at the ActionPack layer:
Resourceful Routing: If you're building a RESTful JSON API, you want to be using the Rails router. Clean and conventional mapping from HTTP to controllers means not having to spend time thinking about how to model your API in terms of HTTP.
URL Generation: The flip side of routing is URL generation. A good API based on HTTP includes URLs (see the GitHub gist APIfor an example).
Header and Redirection Responses: head :nocontent and redirectto userurl(currentuser) come in handy. Sure, you could manually add the response headers, but why?
Caching: Rails provides page, action and fragment caching. Fragment caching is especially helpful when building up a nested JSON object. Basic, Digest and Token Authentication: Rails comes with out-of-the-box support for three kinds of HTTP authentication.
Instrumentation: Rails 3.0 added an instrumentation API that will trigger registered handlers for a variety of events, such as action processing, sending a file or data, redirection, and database queries. The payload of each event comes with relevant information (for the action processing event, the payload includes the controller, action, params, request format, request method and the request's full path).
Generators: This may be passé for advanced Rails users, but it can be nice to generate a resource and get your model, controller, test stubs, and routes created for you in a single command.
Plugins: Many third-party libraries come with support for Rails that reduces or eliminates the cost of setting up and gluing together the library and the web framework. This includes things like overriding default generators, adding rake tasks, and honoring Rails choices (like the logger and cache backend).
These features are priceless when a deadline is near cutting your head like a guillotine. I think these features are partly the reason Why Targeter moved from NodeJS to RoR. This doesn't mean I don't like Nodejs, but that discussion is beyond the scope of this post.
For example, this week, a client asked me to create a button on the admin site so that they could get the emails from the users of the application. It took me less than ten minutes to do it:
invitations_emails = InvitationRequest.pluck(:email) user_emails = User.pluck(:email) content = (invitations_emails + user_email).to_csv send_data content, filename: "emails.csv"
However all this magic ain't always great, in particular when you have performance issues or library-related bugs. But in most cases it will save you from reinventing the wheel.
In this same line devise (authentication), paperclip (file attachment library that helps you treat files as any other attribute), kaminari (paginator), geocoder (really simple and complete geocoding solution), koala (Facebook library supporting the Graph and REST API), aws-sdk (self-explanatory), tire (API and DSL for the Elasticsearch search engine) or other alternatives that you can find in The Ruby Toolbox will make you cry of joy if your non techincal boss ask you to add a new functionality in a couple of hours.
Also, cucumber and rspec will be of great utility to check that the commit you did on Monday morning while you are really sleepy won't destroy everything. I also wanted to mention RVM - the greatest version manager of any language I know - and Ruby Gems. After Nodejs great npm package manager, this is the best one I know.
At last, I wanted to show you watir-webdriver. I bet you can't find a better api to drive a browser. This is a perfect example of how rich and simple Ruby libraries are:
require 'watir-webdriver' b = Watir::Browser.new b.goto 'bit.ly/watir-webdriver-demo' b.text_field(:id => 'entry_0').set 'your name' b.select_list(:id => 'entry_1').select 'Ruby' b.select_list(:id => 'entry_1').selected? 'Ruby' b.button(:name => 'submit').click b.text.include? 'Thank you'
I have not mentioned before that Ruby is also an excellent language for creating prototypes or for doing IT related work like dealing with files and parsing strings.
Finally, if you need to create a webpage or a REST API, specially when you don't need real-time stuff, with limited time and money I can't currently imagine a better replacement for Ruby and Rails.
I won't deny that Ruby has lost some momentum. But that doesn't mean the language is dead. I think it has matured and that it is a great tool to add to your arsenal as a developer.
frycicle explained it really well in a few words.
Shoot, in terms of libraries, language design, and community, Ruby has it all. It's won't scale to 10 website levels. And that is ok for it's use. They usually have to make super specialized software anyways.
Even accepting that that Ruby is not dying, we could ask ourselves why hasn't Ruby won?. The answer is that:
Sarah Mei: this is a game where there is no winning turns out, but there is losing, there is definently losing. [...] The best thing that you can do for Ruby is go learn something else and then come back.
@bascule daily amazed at the number of ruby people I come across in the scala and Go worlds too. Ruby opens your mind.— Paul Lamb (@PaulLamb) January 23, 2014
That's why you should learn some Python, Go, Rust, Erlang, Scala, Haskell, Clojure or any Lisp dialect. Oh, and keep an eye on Julia and Elixir!
Part III of these series will be about Python.