Getting method names from ActionController extension - ruby-on-rails

I have an extension to ActiveRecord's class ActionController:
module MyExtension
def my_method
"default implementation"
end
end
ActionController::Base.class_eval { include MyExtension }
All my controllers inherit from ApplicationController, which inherits from ActionController::Base. I want to add to and override the methods in MyExtension in the inherited class
class MyController < ApplicationController
module MyExtension
def my_method
"overridden implementation"
end
def my_new_method
"added method implementation"
end
end
end
I then want to look up the methods defined in MyExtension for a specific Controller class dynamically
controller = Kernel.const_get("MyController")
methods_in_my_extension = Kernel.const_get("MyController::MyExtension").public_instance_methods
Obviously, this does not work, since there are no MyController::MyExtension constant defined.
In a more general sense, what I want to achieve is:
Group methods to implement default behavior for classes inheriting from ActionController::Base
Be able to override and add to those methods in the class definition
Dynamically get a list of the grouped methods for a specific class
Edit: In answer to Deefour about why I want to do this:
I am creating a CLI menu to wrap around the controllers. It takes input from the user to call controller methods. The menu collects the methods directly from the controllers and thereby defines valid commands/input.
What I want is to define a new level of encapsulation, a new access specifier, a subset of the public methods, which is accessible to the CLI menu.
I could achieve this by just creating a prefix for all menu-available methods, but I am also trying to get a grip on the inner workings on ruby.

I figured it out:
The ActionControllerExtension:
module MyExtension
module MyDefaultImplementations
def my_method
"default implementation"
end
end
def get_selected_methods
default = MyDefaultImplementations.public_instance_methods
added = Kernel.const_get(self.class.name).const_get("MyImplementation").public_instance_methods
return (default + added).uniq
end
end
ActionController::Base.class_eval { include MyExtension }
The Controller:
class MyController < ApplicationController
module MyImplementation
def my_method
"overridden implementation"
end
def my_new_method
"added method implementation"
end
end
include MyImplementation
end
That seems to be working for now. If anyone has any alternate/better approaches I would love to see them. Also if there are any major caveats or conflicts with convention, let me know.

Related

Loading module into User Model in Rails

Trying to make available the methods I have stored in a Module which is located in app/models (side note: not sure if this is the correct place for modules?).
Module:
module MyModule
class MyClass
def some_method
# do something
end
end
end
User Model:
class User < ApplicationRecord
include MyModule
def another_method
some_method
end
end
I am getting a NoMethodError:
NoMethodError (undefined method 'some_method' for #<User:0x00007f6a3ce452c0>
You seem to have missunderstood what what modules and classes do in Ruby. In Ruby a module is simply an object that wraps a set of methods and constants.
A module can extend other modules, classes and objects and can be included in classes thus implementing multiple inheritance. Modules in Ruby fill the role that traits, namespaces and singletons do in other languages.
Classes are actually modules (Module is part of the ancestors chain of Class) with the key difference that you can make instances of a class and that class can inherit from a single other class and cannot extend other objects or be included.
The code example here actually doesn't make sense. If you want to declare a method that will be available to classes that include a module you want to declare it in the module itself:
module MyModule
def some_method
# do something
end
end
When you then call User#another_method it will look in the ancestors chain of the User class until it finds the method which is defined in MyModule.
module MyModule
class MyClass
def some_method
# do something
end
end
end
Will actually definte the class MyClass with an instance method that is only available to instances of MyClass. The only thing that the module does here is change the module nesting so that the class is defined in MyModule instead of the global namespace.
If you want to mix in a method from a method into your class then just put the methods directly in the module (without an intermediate class).
Module:
module MyModule
def some_method
# do something
end
end
User Model:
class User < ApplicationRecord
include MyModule
def another_method
some_method
end
end
Have a look at this answer, you need to instantiate your Class first. Or if you want to
class User < ApplicationRecord
include MyModule
def another_method
my_instance = MyClass.new
my_instance.some_method
end
end
As for a place to store your Module, have a look at this guide about service objects, it gave me some inspiration when it comes to different modules.

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.

Using methods defined in a module with class methods in Ruby

I have a small problem that I can't quite get my head around. Since I want to reuse a lot of the methods defined in my Class i decided to put them into an Helper, which I can easily include whenever needed. The basic Class looks like this:
class MyClass
include Helper::MyHelper
def self.do_something input
helper_method(input)
end
end
And here is the Helper:
module Helper
module MyHelper
def helper_method input
input.titleize
end
end
end
Right now I can't call "helper_method" from my Class because of what I think is a scope issue? What am I doing wrong?
I guess that is because self pointer inside of do_something input is InternshipInputFormatter, and not the instance of InternshipInputFormatter. so proper alias to call helper_method(input) will be self.helper_method(input), however you have included the Helper::MyHelper into the InternshipInputFormatter class as an instance methods, not a singleton, so try to extend the class with the instance methods of the module as the signelton methods for the class:
class InternshipInputFormatter
extend Helper::MyHelper
def self.do_something input
helper_method(input)
end
end
InternshipInputFormatter.do_something 1
# NoMethodError: undefined method `titleize' for 1:Fixnum
As you can see, the call has stopped the execution inside the helper_method. Please refer to the document to see the detailed difference between include, and extend.

Add rails route helpers to a class as class methods

How can i add rails route helpers like "root_path" to a class like my_model.rb as a class method?
So my class is like this:
Class MyModel
def self.foo
return self.root_path
end
end
MyModel.foo
The above doesn't work because Class MyModel doesn't respond to root_path
This is what I know:
I can use include Rails.application.routes.url_helpers, but that only add the module's methods as instance methods
I tried doing extend Rails.application.routes.url_helpers but it didn't work
Please feel free to school me :)
URL routes shouldn't generally need to be accessed from a model. Typically you should only need to access them from your controller when handling a request, or when rendering a view (if you're e.g. formatting a link URL).
So instead of asking your model object for the root path, you would simply call root_path from within your controller or a view.
Edit
If you're just interested in the reason why you're unable to include the module's method as class methods in your class, I would not expect a simple include to work, since that would include the module's methods as as instance methods in your class.
extend would normally work, but in this case it does not due to how the url_helpers method is implemented. From actionpack/lib/action_dispatch/routing/route_set.rb source
def url_helpers
#url_helpers ||= begin
routes = self
helpers = Module.new do
...
included do
routes.install_helpers(self)
singleton_class.send(:redefine_method, :_routes) { routes }
end
The included block containing the routes.install_helpers(self) call indicates that you will need to include the module in order to get the methods install (so extend is out).
The following should work if you call extend in the class context. Try this:
Class MyModel
class << self
include Rails.application.routes.url_helpers
end
end
Class.root_path

Extending a controller in Rails

I have a controller which calls out to another class.
class BlahController < ActionController
def index
OtherClass.get_stuff
end
end
In this class I want to be able to write controller style code.
for instance:
class OtherClass
def self.get_stuff
#foo = bar
end
end
However, I would also like #foo to exist when inside my view, but as it's a separate class those variables aren't making it back through into the controller assigns - so question is, how I can make this so?
(Ignore why I'm having to call out to a separate class, I'm trying to get this code fitting in with a legacy codebase without too much butchery)
class BlahController < ActionController
def index
OtherClass.get_stuff(self)
end
end
class OtherClass
def self.get_stuff(that)
that.instance_variable_set(:#foo, bar)
end
end
Please note that I don't agree with this method. I am just answering the question as you stated it.
I would prefer to accomplish this functionality through mixins and thereby decrease parameter coupling that is present within the code above.
Code structured like this will be difficult to read and maintain. Whenever you can, let the controller directly set all of the variables that the view needs:
class BlahController < ActionController
def index
#foo = OtherClass.get_stuff
end
end
class OtherClass
def self.get_stuff
# return the value that should be assigned to #foo
end
end

Resources