SOLID, Dependency Inversion principle, testing doubles, presenters, decorators, composites, oh my!
Now that I have your attention with a bunch of fancy words, let’s apply them to a real problem. I’ve closed a lot of recent issues for RABL on “limitations” of how RABL supports syntax for multiple variables and conditions. The problem basically boils down to ignoring the SOLID principles.
If you’re not familiar with RABL, it’s a template rendering engine similar to ERB but tuned for structured outputs like JSON and XML.
RABL allows you to have a main object that everything is built around. However, there are pull requests for passing locals and passing objects to allow conditionals within blocks. There’s also a slew of issues that mostly deal with syntax problems for making RABL do things you shouldn’t allow your views to do in the first place.
Here’s an example:
# post_controller.rb def show @post = Post.find(params[:id]) end # show.rabl object @post attributes :title, :body child :author do # root_object is a hack for accessing @post attribute :name => :author_name unless root_object.author == current_user end node :publication_date do |post| if post.status == 'published' post.published_date end end
Presenters, Decorators and Composites
If you’re not sure what the differences between the wrappers are, I highly suggest reading an article by Thoughtbot that covers Decorators compared to Strategies, Composites, and Presenters. I’m going to combine two of those to make a presentable decorator:
# post_presenter.rb class PostPresenter < Draper::Base decorates :post attr_reader :current_user def initialize(post, current_user) super(post) @current_user = current_user end def author_name author.name unless author == @current_user end def publication_date publication_date if status == 'published' end end # post_controller.rb def show @post = PostPresenter.new(Post.find(params[:id]), current_user) end # show.rabl object @post attributes :title, :body, :author_name, :publication_date
Oh my, look at how easy that view is now!
If you don’t want to use Draper, you can just break the Law of Demeter a little and go with a plain object that requires accessing the instance variables through the presenter:
class PostPresenter attr_reader :post, :current_user def initialize(post, current_user) @post = post @current_user = current_user end end
Or you can use four other methods of decorating in Ruby.
Those four examples of decorating though didn’t cover the simple delegates. So you can also use the Delegate class in Rails:
class PostPresenter attr_accessor :post attr_accessor :current_user delegate :title, :body, :author, :publication_date, to: :post def_delegators :@current_user, :name, to: :current_user def initialize(post, current_user) @post = post @current_user = current_user end end
Or in plain Ruby, use Forwardable:
require 'forwardable' class PostPresenter extend Forwardable def_delegators :@post, :title, :body, :author, :publication_date def_delegators :@current_user, :name def initialize(post, current_user) @post = post @current_user = current_user end end
Read more about delegation in Ruby.
Dependency Inversion (the D in SOLID) used for testing
Now that I don’t have logic embedded within my view, I don’t even need to unit test them. I should still have my controller verify that the proper template was rendered and I need some basic level of integration testing but overall, I can skip view rendering.
To test my “RABL”, I actually test my presenter. To test my presenter, I use Dependency Injection to supply my presenter with the objects I need it to test with.
Gregory Brown talks about Dependency Inversion in Issue #23: SOLID Design Principles of Ruby Best Practices. The problem with his examples and my example above is we’re actually demonstrating Dependency Injection and not Inversion. An article on RubySource titled SOLID Ruby: Dependency Inversion Principle does a better job of explaining the differences between the two. It basically boils down to initializing the object with your dependencies versus calling methods with your dependencies passed in. Either way, it’s the same damn thing. You’re abstracting the interface between different implementations which makes it easier to change and test.
Test Doubles (Stubs and Mocks)
This is ultimately the entire point of this article. You have
successfully segregated your dependency on multiple instance variables,
random helper methods and anything else floating around in your scopes.
When a view refers to
bar, you can look at the presenter to know
exactly where that is coming from.
It also makes it possible to use stubs and mocks to easily swap out
parts of the presenter for testing. You don’t have to actually have a
mock(:current_user). You can also really speed up
your tests by not using fixtures or factories to build out your user or
Post.new with method stubs where needed.
RABL Issue #299 asks:
... we want to use current_user in our renderer ... ... it doesn't cover having, say, a current_user method in use ...
Now that’s easy. Pass in whatever
current_user you want to use to your
One last example…
Are you using the Timecop gem to manipulate your clock for testing? Just pass in a Time object:
class PostPublishPresenter include ActionView::Helpers::DateHelper def initialize(published_at) @published_at = published_at end def age(to_when = Time.now.utc) distance_of_time_in_words @published_at, to_when end end >> foo = PostPublishPresenter.new(Time.now.utc - 2.days) >> foo.age => "2 days" >> foo = PostPublishPresenter.new(Time.now.utc - 14.hours) >> foo.age => "about 14 hours" >> foo.age(Time.now.utc - 14.hours) => "less than a minute"
All software by the government is Public Domain.
The Freedom of Information Act (FOIA) gives all citizens the right to request something from the government. Back in 2003, I was working at Dialog Medical and we started selling our informed consent software to hospitals, including the VA. So I filled out my FOIA request and sure enough a month later, I received a CD with a copy of the VA’s EMR - known as VistA. This is the real VistA before that other crappy Vista.
As I dug deeper into VistA and needed help, I turned to the online communities of WorldVistA and Hardhats. These groups have taken the freely available, unrestricted and open copy of our government’s software and they would install it at non-federal government hospitals.
WordVistA lists several adopters and last I heard from people in the field, the installs at the American Somaon government and Indian Health Services are some of the more active installs. I even knew a geeky doctor that installed it at his own small practice.
The government doesn’t take pull requests.
The problem with this approach though is that once you request software via FOIA, you’re basically creating a fork. Someone on the outside of the government has to continually merge in changes from both sides or the government has to switch to an outside source. For VistA, it’s obviously the former since it’s a very large codebase and there are more developers working on it internally than externally.
The open government
I think what Anil Dash really wants to see is the government not only make their code available but to do so openly. To work on their code via GitHub and take in pull requests. It’s very possible to happen but it’s not going to be the White House being petitioned to do so.
Instead, I suggest that Anil Dash reach out to Todd Park, our Fed CTO. Work from the inside out by getting developers to join in on the fun. Come up with a plan to make government jobs as fulfilling and productive to the developer as a startup. I mean, of course government jobs gives you health insurance and sleep but it should also advance your career. Your work should be open, your labor should be rewarded and your options should be limitless.
Have you ever done yard work, cleaned your house or move some furniture by yourself and then all of a sudden someone joins in to help you? Don’t you just love that feeling? Now you know it won’t take as long, you get an extra boost of energy and the sum of your work will be greater than the individual parts.
As I’ve worked in Product and UX, I’ve found that pairing with another designer works on my productivity and thought process the same way it works when I’m developing software. I’m able to simply accomplish more work with another person than if we had both gone off for a couple days and had a meeting afterwards.
Now in some cases it won’t make sense, such as implementing a sketch in Photoshop won’t be as productive when both designers have similar skill sets but it’s definitely possible to pick up a few tricks from each other if you haven’t already established equilibrium. The same can be said for a couple developers wasting away on CSS tweaks for IE. It’s not going to be much more productive with two developers getting frustrated at IE than with one since the process for dealing with it can be mostly trial and error.
It would still be interesting to try pairing with traditional professions. For electricians and plumbers, the process of a journeyman but also having multiple professionals working on the same project is normal. It’s normal to see multiple technicians working on the same problem. But what about accounting? Or marketing?
Maybe I’ll find out one day. I’ll probably need to create a company first though.