Sorry for the late reply. Some others have responded and I mostly agree, but I will add where I can.
>is it ok to use Procs for example for extraction of block methods for cleanliness in refactors?
As others have said, use Lambdas via `->() { do_thing }` over Procs if you want to pass them around. They are the "true" way to use first-class functions.
Also, if you want to pass methods around, you can do so, just be aware that they carry everything you might expect with them: they are called with the receiver still being the object they came from. Callbacks are uncommon in Ruby, but I've seen a few instances of passing methods of the current object into some other as a callback, e.g.
some_interactor.do_complex_thing(
order_id: 123,
on_success: method(:succeeded),
on_failure: method(:failed),
)
Then #do_complex_thing can use `on_success.call` and `on_failure.call`, which is nice when you want to stub these in unit tests without resorting to stubbing/mocking. You can just pass lambdas (first-class functions) as the value for :on_success and :on_failure kwargs in tests.
>Also, is there any particular Rails convention to place collections of useful procs?
No convention for a "place" for these. I generally dislike how Rails got people into using directory names for the "kind" of a module/class instead of actually mirroring the architecture. This is kind of why.
If what you really desire is a big bag of pure functions in the global scope, you should still at least put them inside of a namespace (a Ruby module). For example, if you wanted a big bag of type coercions, you might put them into a module:
module CommonCoercions
class << self
def currency
->(value) { do_things }
end
def coordinates
->(value) { do_things }
end
end
end
However, this isn't necessarily very efficient. Each call to CommonCoercions.currency is going to instantiate a new Lambda instance! You could alleviate this by caching the value in a singleton-scope instance variable:
module CommonCoercions
class << self
def currency
@currency ||= ->(value) { do_things }
end
def coordinates
@coordinates ||= ->(value) { do_things }
end
end
end
Even then, this isn't really common in Ruby. You might as well just have these be singleton methods outright (aka "class methods"):
module CommonCoercions
class << self
def currency(value)
do_things
end
def coordinates(value)
do_things
end
end
end
If you
really want to pass them around as first-class functions, you can grab a reference to that function like so:
CommonCoercions.method(:currency)
And then use them via `.call()` the same as any other lambda if you want to pass them around.
However, if you want to use them only locally within a particular class, in a Rails project you are better off just calling them directly on the module, or even delegating so that the method is available in the class as though it were a local instance method:
module CommonCoercions
def self.coerce_currency(value)
do_things
end
end
class SomeAction
delegate :coerce_currency, to: CommonCoercions
def some_method(params)
coerce_currency(params.fetch(:amount))
end
end