Calling protected class method from instance method in Ruby - ruby-on-rails

I've been having this bothering recurring theme; let's just say, I have a class which defines an instance method and a protected class method. The instance method must call the class method. In order to do so, I kind of have to break the visibility rule and use the dangerous 'send' function. Something like this:
class Bang
def instance_bang
self.class.send(:class_band)
end
protected
def self.class_bang
puts "bang"
end
end
I find this awful, since the class method should be used inside the class scope, therefore should remain visible and callable within it, right? Is there an alternative way to use class methods in instance methods with needing to rely on the "send" function and therefore not break visibility?
UPDATE:
Following Sergio Tulentsev's response (thx for the correction), I'll update my concern with a code snippet that sums up my concerns of the method visibility being taken into account while still inside the scope where it has been defined.
class Bang
def instance_bang
private_bang = 1
self.private_bang(private_bang)
end
private
def private_bang(p)
puts "bang"
p
end
end
Calling Bang.new.instance_bang will raise an Exception unless you use send on that private_bang call (this time I checked it :) ).

EDIT: Answering the updated question
It is forbidden to call private methods with explicit receiver. You either have to use implicit receiver (private_bang, without self) or use send. Please see my another answer for more information.
By the way, the original question is about calling class instance methods from instance methods. Your clarification doesn't include that. But if that's still true, you have to use self.class.send or make the method public (so that you can use explicit receiver).

Let's consider a private class method (since protected class methods don't make sense).
We know it's possible for an instance to call a private method on itself, as long as it isn't using an explicit receiver (self.call_something_private). It seems you also expect that an instance can call a private class method on its own class, but that is not the case.
Let's look at a way to do this without using send.
The private and protected macros only affect instance methods of the current scope, not class methods. Here are three ways to rewrite your original code:
class Bang
def instance_bang
self.class.class_bang
end
# declare method visibility after
def self.class_bang
puts "bang"
end
private_class_method :class_bang
# inline
private_class_method def self.class_bang
puts "bang"
end
# class scope
class << self
# the private macro works here because we're inside the class scope
private
def class_bang
puts "bang"
end
end
end
So now we want to expose an interface on the class to call class_bang, but only if it's called by an instance of Bang.
class Bang
def instance_bang
self.class.invoke_class_bang(self)
end
class << self
private
def class_bang
puts "bang"
end
public
# we ask the receiver to pass itself as an argument ...
def invoke_class_bang(receiver)
# ... so that we can check whether it's
class_bang if receiver.is_a?(Bang)
end
end
end
That's not a very nice looking solution though. Here's a sneakier way:
class Bang
def initialize
def self.instance_bang() self.class.method(:class_bang).call end
end
class << self
private
def class_bang
puts "bang"
end
end
end

"The class method should be used inside the class scope, therefore should remain visible and callable within it, right?" Yes, that's correct, and that's the behavior Ruby exhibits. (As a point of clarification, instance scope is not "within" class scope. They are, appropriately, separate.)
The non-send solution is to subclass or reopen the class to add a public class method to access the protected class method.

Related

How to improve lisibility/split "fat" class method (inside ruby class/rails model)

as a new Rubyist, I'm running into a recurring problem when it comes to structure my models.
When a method is too long:
I try to refactor to a better/shorter syntax
I try to split some parts into "sub methods"
PROBLEM: I don't know how to split the method properly + whith which tool (private method, modules etc.)
For example:
I need to run Foo.main_class_method
My model looks like this:
class Foo < Applicationrecord
def self.main_class_method
[...] # way too long method with nasty iterations
end
end
I try to split my method to improve lisibility. It becomes :
class Foo < Applicationrecord
def self.main_class_method
[...] # fewer code
self.first_splitted_class_method
self.second_splitted_class_method
end
private
def self.first_splitted_class_method
[...] # some code
end
def self.second_splitted_class_method
[...] # some code
end
end
Result: It works, but I fell like this is not the proper way to do it + I have side effects
expected: splitted_methods are not accessible, except inside main_class_method
got: I can call Foo.first_splitted_class_method since class methods "ignore" Private. splitted_class_methods under Private are not private
Question: Is it an acceptable way to split main_class_method or is it a complete misuse of private method ?
Using private method to split your code:
Possible but not the real solution if the code belongs somewhere else
It's rather about "does it belongs here?" than "does it look nicer?"
To fix the "not private" private class method (original post) :
use private_class_method :your_method_name after you defined it
or right before
private_class_method def your_method_name
[...] # your code
end
If your splitting a class/instance method:
the splitted_method must be the same type(class/instance) as the main_class_method calling it
In the main_method you can call the splitted_method with or without using self.method syntax
class Foo < Applicationrecord
def self.main_class_method
# Here, self == Foo class
# first_splitted_class == class method, I can call self.first_splitted_class_method
self.first_splitted_class_method
# I can also call directly without self because self is implicit
second_splitted_class_method
end
def self.first_splitted_class_method
end
def self.second_splitted_class_method
end
private_class_method :first_splitted_class_method, :second_splitted_class_method
end

No method error for class variable defined with attr_accessor

I want to define methods dynamically using an array of strings.
Here is a simple piece of code that should achieve that.
class SomeClass
attr_accessor :my_array
def initialize(user, record)
#my_array=[]
end
my_array.each do |element|
alias_method "#{element}?".to_sym, :awesome_method
end
def awesome_method
puts 'awesome'
end
end
When I instantiate this class in the console, I get the following error
NoMethodError (undefined method `each' for nil:NilClass)
What is wrong with this code and how to make it work. any help highly appreciated :)
Edit 1:
What I ultimately want to achieve is to inherit from SomeClass and override my_array in the child class to dynamically define methods with its attributes like so
class OtherClass < SomeClass
my_array = %w[method1 method2 method3]
# Some mechanism to over write my_array.
end
And then use self.inherited to dynamically define methods in child class.
Is there a good way to achieve this?
In your code, you use an instance variable (#my_array) and an attr_accessor over it, and then try to access my_array from class level (that is, from the body of the class definition, outside of any methods). But instance variables only exist at instance level, so it is not available in the class scope.
One solution (the natural one, and the one which you would probably use in other languages) is to use a class variable: ##my_array. But class variables in ruby are a little problematic, so the best solution would be to make use of class instance variables, like that:
class SomeClass
class << self
attr_accessor :my_array
end
#my_array=[]
def initialize(user, record)
end
#my_array.each do |element|
alias_method "#{element}?".to_sym, :awesome_method
end
def awesome_method
puts 'awesome'
end
end
The syntax is a little tricky, so, if you look that up and it still doesn't makes sense, try just reading about scopes and using a regular class variable with ##.
Edit:
Ok, so, after your edit, it became more clear what you are trying to accomplish. A full working example is like follows:
class SomeClass
class << self
attr_accessor :my_array
end
#my_array=[]
def awesome_method
puts 'awesome'
end
def self.build!
#my_array.each do |element|
self.define_method("#{element}?".to_sym){ awesome_method }
end
end
end
class ChildClass < SomeClass
#my_array = %w[test little_test]
self.build!
end
child_instance = ChildClass.new
child_instance.test?
>> awesome
child_instance.little_test?
>> awesome
So, I've made some tweaks on SomeClass:
It does not need an initialize method
I tried to use the inherited hook for this problem. It won't ever work, because this hook is called as soon as "ChildClass < SomeClass" is written, and this must be before you can define something like #my_array = %w[test little_test]. So, I have added a self.build! method that must be called in the child instances so that they build their methods from my_array. This is inevitable, but I think it is also good, because it makes more explicit in the subclasses that you are doing something interesting there.
I think you want "define_method", not "alias_method".
awesome_method in passed in a block, which is ruby's way of doing functional programming.
With that done, ChildClass inherits from SomeClass, and it's instances have the dynamically created methods 'test?' and 'little_test?'.
You need to change my_array to class level accessible, in my case class constant.
class SomeClass
DYNAMIC_METHOD_NAMES = %w(method_a method_b method_C).freeze
def initialize(user, record)
end
DYNAMIC_METHOD_NAMES.each do |element|
alias_method "#{element}?".to_sym, :awesome_method
end
def awesome_method
puts 'awesome'
end
end

Are these just different ways of defining a static method in Ruby? [duplicate]

This Ruby Style Guide tells that is better using self.method_name instead of class method_name. But Why?
class TestClass
# bad
class << self
def first_method
# body omitted
end
def second_method_etc
# body omitted
end
end
# good
def self.first_method
# body omitted
end
def self.second_method_etc
# body omitted
end
end
Are there performance issues?
class << self is good at keeping all of your class methods in the same block. If methods are being added in def self.method form then there's no guarantee (other than convention and wishful thinking) that there won't be an extra class method tucked away later in the file.
def self.method is good at explicitly stating that a method is a class method, whereas with class << self you have to go and find the container yourself.
Which of these is more important to you is a subjective decision, and also depends on things like how many other people are working on the code and what their preferences are.
Generally, class << self is used in metaprogramming to set the class as self for a prolonged period of time. If I'm trying to write 10 methods, I would use it like so:
METHOD_APPENDICES = [1...10]
class << self
METHOD_APPENDICES.each do |n|
define_method("method#{n}") { n }
end
end
This would create 10 methods (method1, method2, method3, etc.) that would just return the number. I would use class << self for clarity in this case because in metaprogramming self is crucial. Littering self. inside there would actually make things less readable.
If you're just defining class methods normally, stick to self.class_method_name because more people are likely to understand it. No need to bring in meta-syntax unless you expect your audience to understand it.
As noted above, both styles seem to be equivalent, however using class << self allows one to mark class methods as private or protected. For example:
class UsingDefSelf
def self.a; 'public class method'; end
private
def self.b; 'public class method!'; end
end
class UsingSingletonClass
class << self
def a; 'public class method'; end
private
def b; 'private class method'; end
end
end
private only affects instance methods. Using the singleton class, we are defining instance methods of that class, which turn into class methods of the containing class!
We can also mark class methods as private with def self:
class UsingDefSelf
def self.a; 'private class method'; end
def self.b; 'private class method!'; end
private_class_method :a, :b
# In Ruby 2.1 there is an alternative syntax
private_class_method def self.c; 'private class method!'; end
end
But we cannot mark them as protected, there is no protected_class_method. (However, since class is the only instance of its singletonclass, private class method and protected class methods are almost the same except their calling syntax is different.)
Also it is less easier than using class << self to mark private class methods, since you have to list all method names in private_class_method or prefix private_class_method to every private class method definition.
I assume that they think self.* is better because you can say for sure, it's a class or instance method, without having to scroll up and seaching this class << self string.
Whichever you want. Both are very clear for what you do. But I think of some recommendations for this.
When there're only one class method to define, Use def self.xxx. Because for defining only one method, increasing indent level probably become cluttering.
When there're more than one class method to define, Use class << self. Because writing def self.xxx, def self.yyy and def self.zzz is certainly repetition. Create a section for these methods.
When all methods in a class are class method, you can use module with module_function instead of class. This let you define module functions just use def xxx.
So far the question and answers only discuss these two options:
class MyClass
def self.method_name
..
end
end
class MyClass
class << self
def method_name
..
end
end
end
There's a third option to consider for class methods:
class MyClass
def MyClass.method_name
..
end
end
It's not popular and is more verbose, but it's the most explicit option.
It's also less confusing if you mix up self behaviour between Python and Ruby.

Pass a symbol to a method and call the corresponding method

In a Rails controller you can pass a symbol to the layout method that corresponds to a method in you controller that will return the layout name like this:
layout :my_method
def my_method
'layout_1'
end
I want to have a similar functionality to likewise pass a symbol to my classes method and that class should call the corresponding function and use its return value, like this
myClass.foo :my_method
def my_method
'layout_1'
end
I've read posts[1] that tell me I need to pass
myClass.foo(method(:my_method))
which I find ugly and inconvenient. How is rails here different allowing to pass just the symbol without any wrapper? Can this be achieved like Rails does it?
[1] How to implement a "callback" in Ruby?
If you want to only pass a :symbol into the method, then you have to make assumptions about which method named :symbol is the one you want called for you. Probably it's either defined in the class of the caller, or some outer scope. Using the binding_of_caller gem, we can snag that information easily and evaluate the code in that context.
This surely has security implications, but those issues are up to you! :)
require 'binding_of_caller'
class Test
def foo(sym)
binding.of_caller(1).eval("method(:#{sym})").call
end
end
class Other
def blork
t = Test.new
p t.foo(:bar)
p t.foo(:quxx)
end
def bar
'baz'
end
end
def quxx
'quxx'
end
o = Other.new
o.blork
> "baz"
> "quxx"
I still don't understand, what is author asking about. He's saying about "callbacks", but only wrote how he wants to pass parameter to some method. What that method(foo) should do - i have no idea.
So I tried to predict it's implementation. On class initialising it gets the name of method and create private method, that should be called somewhere under the hood. It possible not to create new method, but store method name in class variable and then call it somewhere.
module Foo
extend ActiveSupport::Concern
module ClassMethods
def foo(method_name)
define_method :_foo do
send method_name
end
end
end
end
class BaseClass
include Foo
end
class MyClass < BaseClass
foo :my_method
private
def my_method
"Hello world"
end
end
MyClass.new.send(:_foo)
#=> "Hello world"
And really, everything is much clearer when you're not just wondering how it works in rails, but viewing the source code: layout.rb

Extend instance method of model

I'm trying to extend a specific model in my app using railtie. Adding class methods works, but neither does instance methods. I have the following code:
class Railtie
def self.insert
return unless defined?(::ActiveRecord)
::MyApp::MyModel.extend(ModelMethods)
end
end
module ModelMethods
def hello
puts "hello"
end
end
Now, I'm able to call MyModel.hello. But what should I do if i want to add some instance methods? When I try to add them through ::MyApp::MyModel.include(InstanceMethods) it fails with something saying calling a private methods.
include is a private method and cannot have an explicit receiver. You can get around this limitation by using send:
MyModel.send(:include, InstanceMethods)

Resources