00:00:01.979
Thank you so much for being here! Good morning, good afternoon, or wherever people are in the world. I'm Nick, a senior production engineer at Shopify on the Ruby and Rails infrastructure team. I’m incredibly grateful for the encouragement I get at work from my team to research topics like this, and I’m more than grateful to the organizers and everyone who works hard to put on conferences like this. It's been a hard few years, and I'm so happy that we are back at it, talking about Ruby.
00:00:34.800
I have a keen interest in Ruby history and maintain the Past Rubies newsletter, which is somewhat like "This Day in History" for Ruby. I spend a lot of time reviving old gems and old Ruby patterns that I believe should still be in use today. However, the focus for today's talk is web frameworks.
00:01:02.399
In the 2000s, everyone was writing a Ruby web framework. There was a time during the early days of Ruby, particularly when it was difficult to secure a paid job writing it, and there was a tremendous amount of web frameworks coming and going. People debated design patterns, configuration, modularization versus convention, and what the "Ruby way" to write for the web was. Today, I want to have a higher-level discussion about some of these supposedly forgotten frameworks to remind everyone that there was a time when everyone was seriously experimenting with these frameworks, and we should take that same spirit of experimentation into the future.
00:01:44.220
I will achieve this by discussing three frameworks and one standard library gem that I believe have become somewhat forgotten but are worth revisiting. First, let's talk about CGI. For those who aren't familiar with it, the Common Gateway Interface is a set of standards that define how information is exchanged between a web server and a custom script. A simple way to think about it, if you're not familiar, is that you could write your own Ruby framework sitting on top of Rack. By the way, Rack 3.0 dropped yesterday, which adds to the timeliness of this discussion.
00:02:02.100
CGI is a lower-level, old-school alternative that people were using before other methods became ubiquitous. This standard can be installed on a Unix system and defines how your scripts communicate with HTTP requests. Because of this, the Ruby standard library includes a CGI library that people can still use today. In essence, CGI scripts are quite an inheritance from Perl, where they were very popular. When looking at doing this today, you could use raw Ruby. If you have your CGI script set up accordingly, the print statements will work well if you'd like to be really bare-metal. However, once you require the CGI library, you get a lot of beautiful Ruby helpers, such as headers, forms, and a lot more. Many things still look like this—markup or Flex today—and this is quite a Ruby pattern that you might not realize has such a long history.
00:02:46.020
CGI also handles parameters. You might think, 'Nick, this seems a bit intense, would anyone choose to go this route?' Historically, though, CGI scripts were indeed used. Kirk Haynes, who used Iowa and is now the current maintainer of Iowa—a web framework around since 2001—discussed the use of CGI scripts, which were not as popular as tooling started to evolve but remained a valid option competing with other frameworks of the time. My assessment of cgi.rb as a standard library gem is that you can legitimately use it today for experimentation or when writing a lightweight framework, but I'd typically recommend using Rack. You would need to configure it along with a server like Apache, and after that, your limits are defined only by Ruby.
00:03:26.820
However, the feasibility of CGI would mainly be for fun projects, definitely not for a corporate job option unless you're looking to insert some Ruby as a lightweight handler on a server. Moving on, let's discuss some real frameworks that you may or may not have heard of. Our first one is Nitro. I really hope that at least half the audience hasn't heard of this one. Nitro was first released in October 2004 by George Moscovitis and Tram. Back in those days, you could just have your moniker as an engineer and that was sufficient. As a timeline reference, the last release was in 2009, and based on my historical analysis, Nitro peaked just before 2007. However, it did inspire several future web frameworks after it.
00:04:05.280
So what did Nitro have to say about itself? Here’s their description from the documentation: "Nitro provides everything you need to develop professional web applications using Ruby and JavaScript. It redefines rapid application development by providing a clean yet efficient API, a layer of domain-specific languages implemented on top of Ruby, and the most powerful and elegant object-relational mapping solution available. Nitro is Web 2.0 ready, featuring solid support for Ajax, XML, and syndication while remaining standards-compliant." Nitro took itself seriously, as you can tell from its mission statement. But what was the community saying about it?
00:04:33.780
Back in 2006, Nitro was regarded as a serious choice amongst other popular web frameworks at the time and lent itself to a different way of thinking about the web and some of its dependencies. James Britt stated, 'The best part, of course, is that it's not an either-or choice. If you're serious about web development in Ruby, you owe it to yourself to spend some time with Nitro, Iowa, and other frameworks. Ruby gives you choices; pick the best tool for the job.' Given how significant Nitro was, it’s interesting that many in the audience may not have heard about it until now.
00:05:17.220
Now, let's look at some of Nitro's documentation. Nitro had four core dependencies. The first is Raw, which served as the web application middleware. OG functioned as their ODM (Object Data Mapper), while Facets, which is still around, was a very popular Ruby gem that provided a lot of Ruby goodness on top of the standard library back in the day. Lastly, jQuery formed another one of those dependencies—though we now joke about jQuery, back in 2004, it was vital.
00:05:56.880
One remarkable element about Nitro is that it predated Rack, having its own framework implementation set atop CGI. If you want to look at a full Ruby application built without Rack as a dependency, Nitro would be the one to explore. They claimed you could write your app any way you wanted, even transforming automatically into Ruby code or using a traditional MVC pattern. Nitro appears to take the extreme stance of being non-opinionated and leaning more towards the notion of being flexible rather than constrained by convention, as it also supported fragmented and action-based templates that would facilitate componentization.
00:06:36.300
Additionally, Nitro utilized the DRB (Distributed Ruby) library to provide distributed sessions when running applications across a server cluster. They also included scaffolding capabilities to help speed development. With that said, I want to pause here and focus on one dependency that I think is particularly interesting: OG. OG stands for Object Graph and made some bold claims about its features, automatically mapping standard Ruby objects to schemas, offering support for various databases including PostgreSQL, MySQL, and SQL lite. It even promised to reverse-engineer legacy database schemas.
00:07:22.080
OG's syntax was simplified: you basically define a plain old Ruby object, allocate types for your attributes, and it handles mapping to the database along with maintaining any necessary schema evolution. The configuration was straightforward; you would simply pass in a hash to declare database configurations. You could still use your natural Ruby objects for their intended purpose, enhancing them with a dash of metadata for database use.
00:08:05.100
In practice, the OG used the evolve schema method that iterated through your Ruby objects. This made transformations easier by running commands to rename columns, add or remove them. The simplicity raises a question about whether this is feasible or scalable—maybe not, but the conceptual compression it offers is intriguing. However, I cannot confidently say how it operated with validations.
00:08:43.560
Returning to Nitro, it’s concerning that example code heavily referenced in the documentation is not available on GitHub. There's really just four publicly available commits, leaving users with options to dig out older versions from RubyGems.org, which may not be feasible, or they can refer back to pre-RubyGems resources or the Archive.org website to locate the code. The original infrastructure for Nitro is somewhat difficult to run on modern environments.
00:09:25.260
Nevertheless, the original 'Hello World' document was straightforward. Regardless of what you do with all the applications, you could navigate through the directories and controllers. However, despite being noted for its pagination capabilities, many references regarding scaffolding feel absent or unclear due to documentation falling through the cracks. My overall assessment of Nitro is that it has powerful concepts, but it remains heavily restricted by being tied to Ruby 1.8.2. You need specific tooling to be able to use it effectively, and using it on an M1 chip is particularly cumbersome.
00:10:02.580
Now moving on to RubyWaves, a lot of the canonical discussions were through a few talks, so I’ve found and will present some slides to illustrate. This framework was introduced in February 2008 by Dan Yoder at AT&T Interactive, where it seems many Ruby developers were present at that time. But it had a brief life span, being effectively dead by 2009.
00:10:42.779
The resource-oriented architecture they promoted was quite forward-thinking, anticipating a cloud-based shift. RubyWaves relied on a few core dependencies that shaped how it functioned, with the Functor being a major part of its unique architecture. Functor was designed to implement pattern matching in Ruby. It allows you to define certain behaviors based on argument patterns. If the repeater class received different parameter types (like string or integer), it could trigger different methods according to the type of argument passed.
00:11:21.180
Another interesting element is that Functor enables building your matchers using lambdas, expanding the capabilities you could have utilized.
00:11:59.219
The architectural concepts allowed for mainstream Ruby practices of inheritance and mixin as they used standard classes and instance methods directly. Although this setup can get complex as applications grow, it allowed for rich acknowledgment of pattern-based tooling and enhances modularity as well.
00:12:39.779
An integral part of RubyWaves’ design was to facilitate resource delegation. Resources were defined as ordinary classes allowing numerous benefits like modular encapsulation through inheritance. However, it may grow challenging to manage routes effectively, so knowing how to derive the routes through resource management became notably useful.
00:13:17.160
The architectural layers and foundations could be fashioned to suit needs, such as the classic foundation incorporating MVC and other assets tailored based on the developers' priorities. Specifically, auto code presented an advanced form of auto-loading and pattern matching through dependencies that felt very much ahead of its time, yet unfortunately, it remains untouched since 2009.
00:14:01.140
The RubyWaves framework is claimed to accomplish recognizable modularity that was required at that time. However, it never truly took off and failed to gather the essential groundwork to sustain momentum. As development continued in the Ruby community, projects like Merb seemed to outshine those options by offering a more viable alternative.
00:14:38.580
Merv perfectly managed dependencies while RubyWaves seemed to falter among many competing frameworks. Therefore, RubyWaves has become a relic, forging historical notions that may be reflected through new programming eyes today.
00:15:20.400
Finally, let's talk about Remains, widely known for its moderated blend of flexibility while retaining MVC capabilities.
00:15:56.359
It first appeared in November 2006, intended as a middle ground between Merb and another popular Ruby web framework. Remains is modular, yet its structure establishes a standard base of MVC. It drew inspiration from the Nitro framework, known for its lightweight architectural instructions. Allowing dynamic representation without a colossal memory footprint, it lets developers perform tasks efficiently while running in their current environments.
00:16:40.740
Remains encourages simplicity in development, quickly embracing modularity design principles while catering to developer comfort with prevalent patterns. Scaffolding is straightforward—a core part of its ethos—promoting ease of use while maintaining essential functionality.
00:17:20.940
The routes and actions get optimized through dynamic view mappings, allowing easy association of actions to their respective views. Such simplicity tied to powerful foundations encourages effective project growth, while clever architecture recognizes intention through an organized controller.
00:18:00.660
Additionally, remains leverages Tani as a foundational contribution allowing pure Ruby code rendering, bringing forward the idea of using straightforward syntax while utilizing the time-tested Ruby language.
00:18:39.840
Focusing on benchmarks comparing competing template engines, Remains shows significant enzymatic performance. Combined with innovative rendering strategies, Tani has the potential of delivering a speedy interface, often returning faster than established engines like ERB, Haml, and Slim.
00:19:23.340
In conclusion, I hope to have inspired you today to explore Ruby's web application side further and consider the old frameworks while innovating your projects. The very legacy of Ruby frameworks encourages creativity, and I look forward to seeing what new ideas emerge from your discoveries. Thank you so much for having me. Please connect with me! This is my Twitter handle, the best way to get a hold of me. I'm incredibly grateful to be here and appreciate your time.