Encapsulation and Global State in Rails

Rails makes many of our day to day tasks much easier, but sometimes this ease comes at a cost. Consistently across applications we see a lack of encapsulation and the shared global state of controllers and views to be one of the biggest sources of complexity and bugs. In this episode Chris and Josh dive into the causes, and solutions to this complexity in hopes of building easier to maintain Rails apps.


This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/encapsulation-and-global-state-in-rails

interesting video. I like the clear examples in the show notes. Thanks!

I refactored a partial _sections.html.erb that was accessing instance variables (@products and @behandelingen)

now I pass in those objects as locals:

    <%= render partial: "section", collection: sections, locals: { products: @products, behandelingen: @behandelingen } %>

Is this ok?

thanks for your advice,

Anthony

Hi @acandael, yup, I think that is a nice improvement. Purposefully went pretty strong on the “don’t access instance variables in partials topic” in the video since it is both a source of trouble, and easy to avoid.

1 Like

Do you have any opinions on the global state when using rails as the API layer in your architecture ? I really try to avoid global state there as well and some of these tips apply but you might have some other advice :slight_smile:

@novarac23 In APIs specifically, things like flash, session, and cookies don’t make sense, so there’s momentum towards reducing global state already. Many of the same principles, like avoiding instance variables in controllers and favoring methods returning correctly scoped information, still apply.

I think you’re missing an attr_reader :users in this class.

class SubscriberCounter
  def initialize(users:)
    @users = users
  end

  def count
    users.where.not(subscription: nil)
  end
end

But on another note, I like the idea of passing in just the users that are needed, but if you’re passing in the list of all users and then whittling it down, wouldn’t that be an extreme waste of cpu time? Probably doesn’t matter for users, but for whatever your business actually does it could be very bad.

Unless you’re relying on passing a lazy collection reference, but that seems like it’s violating some rule about passing native things instead of complex objects/hashes/references (but I don’t know the name for this rule or if it’s even a rule).

What am I missing?

Btw, I really like this article even though I had complaints. I’m definitely going to change how I do use my before filters and I love the idea of passing locals. This is really nice!

Any thoughts about how to control other types of global state like Rails.application.config? That’s one that I’ve been trying to figure out how to test recently.

The users passed in is an ActiveRecord_Relation. There’s no performance cost because the query isn’t executed until count is called, but perhaps it would be better named users_relation to make this clearer.

Makes sense to me. Thanks!