4

I want to hide certain implementations from the main Model methods, because of clean code reasons. I don't want my Model to contain a lot of huge methods, only the clearest and verbose functionalities.

For example:

class SomeModel
  #included stuff
  #fields & attrs

  def modelMethod
      variable = functionality1(functionality2)
      if some condition
        functionality3
      else
        functionality4
      end
  end

Should I put my functionality methods under a private or protected part at the end of the same model file, or should I put them into a helper file?

If I'm right, the codes in the helpers are only used for the View. What is the convention for this?

MattSom
  • 2,097
  • 5
  • 31
  • 53
  • It really depends on your needs. I'd encourage you to create reusable code, think about the code you need and see if you can extract it so that you can use somewhere else, if you can, then create a Class and just create an instance of that Class wherever you use it. The thing I don't like about modules is that it's just a way to organize methods but in the end your classes end up with the same amount of code as if they had those lines of codes in the same class/file. – fanta Jun 07 '17 at 14:43

2 Answers2

6

Having private or protected has nothing to do with the type of cleanup you're trying to do.

This is related to inheritance method visibility/access (though inheritance can obviously be used for reusability).

Methods will depends on reusability. Why not leverage concerns? Say we have SomeModel and want multiple models to implement suspensions.

# app/models/some_model.rb
class SomeModel
  include Suspendable
end

Then add your model concern.

# app/models/concerns/suspendable.rb
module Suspendable
  extend ActiveSupport::Concern

  included do
    has_one :suspension
    scope :active, -> { joins('LEFT OUTER JOIN suspensions').where(suspension: {id: nil} }
  end
end

Or if this only really applies to a single model, but want to keep the model strictly DB manipulations (not Business oriented), then you could have namespaced concerns.

# app/models/concerns/some_model/availability.rb
module SomeModel::Availability
  extend ActiveSupport::Concern

  module ClassMethods
    def availabilities_by_some_logic
    end
  end
end

http://api.rubyonrails.org/v5.0/classes/ActiveSupport/Concern.html

fbelanger
  • 3,522
  • 1
  • 17
  • 32
  • It's a bit hard for me to decide what is strictly business logic and strictly db manipulation. I would only say I want to reduce the size of my functions (20-100 lines to 7-8 lines) and organize out different things somewhere else, as I'm not necessarily interested in every algorithm implementation. – MattSom Jun 13 '17 at 13:46
  • 2
    I know what you mean. In the `Suspendable` example, we are bundling DB manipulation and business logic into a shareable feature. I don't think this is strictly one or the other (confusing right?), but instead functionality segmentation. If it could never be shared, then maybe namespace your concern to that model. Example `app/models/product.rb` might merit a `app/models/concerns/product/payable.rb`. While arguably `payable.rb` might be shared in future releases. – fbelanger Jun 13 '17 at 13:52
  • And should I say `include SomeModel::ClassMethods` to include it into only one model? Is the `::Availability` a must? What is it for here? – MattSom Jun 13 '17 at 15:19
  • You only need to include `SomeModel::Availabiltiy`, not it's `ClassMethods` explicitly. – fbelanger Jun 13 '17 at 15:20
1

If you have a method or set of methods that are used in various models: Rails Concerns

This is different from private/protected and you can have private/protected methods in a concern. This is just how to extract out duplication.

If you have a method that is needed by the model, and only the model (not subclasses of the model, and never called outside the class: private

If you have a method that is needed by the model and its subclasses but not from outside the model: protected

If you need to be able to call the method from outside the class: neither

this answer goes into better detail on those

Michael Gorman
  • 1,077
  • 7
  • 13