-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
extend self
and module_function
are not the same
#556
Comments
Per the ruby documentation:
|
There is another difference that might matter:
(from http://idiosyncratic-ruby.com/8-self-improvement.html#advantages-of-extend-self) |
Can someone from maintainers give an answer on this question? Why do you need to prefer one functionality over another. How does it go together? |
I'm still hoping to know why as well. |
Confusing me too |
I don't see any situation where
Is there any other situation where you would want to take a module method like A: You create a local method
B: You def self vs extend self vs module_functionusing
|
@kke but what if I need |
@ifokeev you shouldn't need instance methods in the module. Private module methods can probably be achieved with Edit: nope, use |
@kke show me, please, the correct way of using |
@ifokeev You need to use private_class_method in addition. module Bedtime
def now?
after_midnight?
end
module_function :now?
def after_midnight?
Time.now.hour < 12
end
module_function :after_midnight?
private_class_method :after_midnight?
def work_in_the_morning?
(0..4).cover?(Time.now.wday)
end
module_function :work_in_the_morning?
private_class_method :work_in_the_morning?
end
class Test
include Bedtime
def print
puts now?
end
end
# Test.new.print => works
# Test.new.now? => private method now? called
# Test.now? => undefined method 'now?'
# Bedtime.after_midnight? => private method after_midnight? called
# Bedtime.now? => works Btw, FileUtils in stdlib has defined |
@kke your code doesn't look good. That's why I prefer |
And your code doesn't work correctly :) You can write it a bit shorter: module Foo
private_class_method def bar(foo)
puts "yo #{foo}"
end
module_function :bar
end Maybe: module PrivateModuleMethod
def module_private(sym)
module_function sym
private_class_method sym
end
end
module Foo
extend PrivateModuleMethod
module_private def foo(bar)
puts bar
end
end I suppose that with some trickery it could even be But that's another story. The story here is that |
After 14 months, this issue has not attracted the attention of any maintainers, so I'm closing it. Thanks to everyone who provided feedback. I think if someone were to summarize the discussion here, a useful PR could be written. |
The maintainers are extremely busy. PR welcome! |
@jaredbeck @bbatsov Should not this rule be removed? |
This cop implies that `extend self` and `module_function` behave the same way, but they actually handle reflections and private method visibility in slightly different ways. See here for more info: https://idiosyncratic-ruby.com/8-self-improvement.html Issue against Rubocop: rubocop/ruby-style-guide#556
Discussion here: rubocop/ruby-style-guide#556
I wouldn't mind resurrecting this thread. tldnr: I would reverse that rule, i.e. favor I ❤️
|
@bbatsov did you get a chance to read this? |
Just now. While I no longer remember the original reasoning I do recall it was a combination of several factors:
Looking at the examples, however, it seems that many people are discussing modules that go beyond a pure utility module where |
Well, in my experience that has rarely been the case. 😆
Yeah, that's what I meant in the second point. Btw, these days you can also use module_function def dataflow(*inputs, &block)
dataflow_with(Concurrent.global_io_executor, *inputs, &block)
end |
Yep, that was the point. 😄 As remarked earlier I didn't expect people would be including utility modules, as those are typically used stand-alone. Obviously, if also plan to re-use some methods in classes the |
Cool. Yeah, are we ready to turn this discussion into a PR, and add an explanation to this rule? |
I blindly followed some RuboCop changes that were actually breaking things. I found this thread enlightening: rubocop/ruby-style-guide#556 Especially this comment: rubocop/ruby-style-guide#556 (comment) > module_function does not work with attr_accessor
I have a case with modules inheritance, without classes. module BaseSpecHelper
def environment_specs_coefficients
{
-> { ENV['CI'] } => 1,
-> { RUBY_PLATFORM == 'java' } => 3,
-> { Gem::Platform.local.os == 'darwin' } => 1
}
end
extend self
end
# some specs using `BaseSpecHelper.environment_specs_coefficients`
module CLISpecHelper
include BaseSpecHelper
def environment_specs_coefficients
super.merge(
lambda do
RUBY_PLATFORM == 'java' && ENV['CI'] && is_a?(Filewatcher::Spec::ShellWatchRun)
end => 2
)
end
extend self
end
# some specs for CLI I don't need classes here, just scoped static methods. I'd be glad to resolve it with So, |
I agree with the above. I have an issue with Module B including Module A. I then have Class A including Module B. See this example: module ModuleA
private_class_method
module_function
def insert
puts "inserting"
end
end
module ModuleB
include ModuleA
private_class_method
module_function
def select
puts "selecting"
end
end
class ClassA
include ModuleB
# lets consider this a base class
def common_base_class_method
puts 'base class'
end
end
class ClassB
include ClassA
def run
select
insert #fails here with NoMethodError!!!
end
end I can't use Module A function in Class A with module_function. I need extend self. If you're trying to prevent people from creating 2 copies of methods, move this rule to RuboCop-performance, please. I think there's enough of an argument that Please undo this or provide a way to disable this in the rubocop.yml file. |
You can do it: Style/ModuleFunction:
Enable: false Or even do inline a code: # rubocop:disable Style/ModuleFunction
extend self
# rubocop:enable Style/ModuleFunction |
@AlexWayfer Here you go! module BaseSpecHelper
module_function
def environment_specs_coefficients
1
end
end
module CLISpecHelper
extend BaseSpecHelper
module_function
def environment_specs_coefficients
super + 1
end
end BaseSpecHelper.environment_specs_coefficients # => 1
CLISpecHelper.environment_specs_coefficients # => 2 I'm not insisting to burn Still, I believe both |
@pirj: Your example is error prone:
Which ones are they? It's one datapoint, but I've survived all my Ruby programming years so far without |
Again - I'll clarify the this guideline about One thing is clear - we need more precises wording at this point. :-) |
and this is intentional. Just tested on 2.5 and 3.0. I was frightened this might have changed at some point of time. The purpose of this is to pass |
Sorry, forgot that they would be private. So a method from class Anything
include CLISpecHelper
def foo
environment_specs_coefficients
end
end
Anything.new.foo # => no super defined |
It's not clear to me what use case there is. For example, are there any example of uses of If they are truly meant to be used directly, and only directly, why not define them as singleton methods directly? module Example
class << self
def call_me_directly
#...
end
end
end |
Can't say about RuboCop, but: https://github.com/rails/rails/blob/291a3d2ef29a3842d1156ada7526f4ee60dd2b59/activesupport/lib/active_support/security_utils.rb#L25 and its usage https://github.com/rails/rails/blob/291a3d2ef29a3842d1156ada7526f4ee60dd2b59/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L378 Well, it can probably be replaces with There's a subtle difference: module A
module_function
def a
1
end
end
class C
include A
def b
a
end
end
> C.new.a
NoMethodError: private method `a' called for #<C:0x000055a9491a4b98>
> C.new.b
=> 1
module B
extend self
def a
1
end
end
class D
include B
end
> D.new.a
=> 1 And yes. If The point here is that And yes, module A
class << self
def a
1
end
end
end
class C
include A
def b
a
end
end
C.new.a # => undefined method `a'
C.new.b # => undefined local variable or method `a' Revenons a nos moutons
I guess in the light of the recent discoveries this statement still has a chance to stand. While a different one, something like "if you design your public API to both allow to access your module's instance methods by directly calling them on a module, and to be able to call them on a class when this method is included, use |
This comment has been minimized.
This comment has been minimized.
@pirj it's a bit complex use-case, but I found a difference (in wrong real-life behavior without any error), here is an example: https://replit.com/@AlexWayfer/InfiniteLividTrees The difference of my example from your is additional usage method ( So, I still see the difference between |
Another example with constants, which is pretty hard to reproduce with
|
there's some debate about the value of this style guide rule at rubocop/ruby-style-guide#556. it seems in our case like using `module_function` is going to be prohibitively hard, and i think the longer term desire is to remove both `extend Forwardable` and `extend self`, expecting callers to just call `#config` directly. the behavior around defaults needs to be cleaned up to make this possible, but it seems like a bad plan to bend this implementation around (unhelpful?) style rules for code we'd rather work on removing.
* move config to a Configurable class the older style config setup is incompatible with Rails 6, preventing support for Hyrax 4. this is a minimal pass at extracting the config and keeping the current API. * disable rubocop Style/ModuleFunction for Bulkrax module there's some debate about the value of this style guide rule at rubocop/ruby-style-guide#556. it seems in our case like using `module_function` is going to be prohibitively hard, and i think the longer term desire is to remove both `extend Forwardable` and `extend self`, expecting callers to just call `#config` directly. the behavior around defaults needs to be cleaned up to make this possible, but it seems like a bad plan to bend this implementation around (unhelpful?) style rules for code we'd rather work on removing.
extend self
andmodule_function
are not the same. The guide says:Unfortunately,
extend self
keeps the instance methods public, butmodule_function
makes them private.Is making them private the point?
The text was updated successfully, but these errors were encountered: