Run a helper method on a model class method - ruby-on-rails

I created a helper method that I want to run on a model class method and getting a method not found error.
lib/model_helper
module ModelHelper
def method_i_want_to_use
puts "I want to use this method"
end
end
model/foo
class Foo < ActiveRecord::Base
include ModelHelper
def self.bar
method_i_want_to_use
end
end
This setup gives me a no method error.

You have to extend the module instead of include.
extend ModelHelper
include makes the methods available as instance methods of Foo. That means, you can call the method method_i_want_to_use on instances of Foo, not on Foo itself. If you want to call on Foo itself, then use extend.
module ModelHelper
def method_i_want_to_use
puts "I want to use this method"
end
end
class Foo
extend ModelHelper
def self.bar
method_i_want_to_use
end
end
Foo.bar
# >> I want to use this method

Related

Can't access to attr_accessor in Concern Ruby on Rails

I want to register the class method in concern and access to attr_accessor, but it doesn't work. This is my sample code. Please help me how can I do this. Thank you so much!
app/controllers/concerns/foobar_concern.rb
module FoobarConcern
extend ActiveSupport::Concern
included do
class << self
attr_accessor :foo
end
end
class_methods do
def test_method(bar)
self.foo = bar
end
end
end
app/controllers/foobar_controller.rb
class FoobarController < ApplicationController
include FoobarConcern
test_method 'Just test'
def index
self.foo => NoMethodError: undefined method "foo"
foo => NameError: undefined local variable or method "foo"
end
end
Just delegate required methods to the class like this
module FoobarConcern
extend ActiveSupport::Concern
included do
delegate :foo, :foo=, to: :class
class << self
attr_accessor :foo
end
end
end
The issue is that you're defining a method at the class level (FoobarController.foo) but calling it on an instance of the class (FoobarController.new.foo).
One option is to call the foo method on the class instead:
def index
self.class.foo
end
You can also define an accessor method for instances of the class like:
module FoobarConcern
extend ActiveSupport::Concern
included do
class << self
attr_accessor :foo
end
end
class_methods do
def test_method(bar)
self.foo = bar
end
end
# -- NEW ---
# This `foo` method is defined for instances of the class and calls the class method.
def foo
self.class.foo
end
end

Can a module class method be called from a class nested in that module?

Is it possible to call a module's class method from a nested class method? For instance, if I had:
module A
def self.foo
'hello'
end
end
module A
class B
def self.bar
foo # Fails since A is not in B's ancestor chain
end
end
end
I know that I can call foo directly on A by using
def self.bar
A.foo
end
But ideally I would like a way to have A be part of B's ancestor chain if possible. Any tips would be appreciated.
I'm not aware of a straightforward way to do exactly what you're asking. But you can move A's self methods to instance methods in another module and have B extend that module:
module A
module ClassMethods
def foo
'hello'
end
end
extend ClassMethods
end
module A
class B
extend A::ClassMethods
def self.bar
foo
end
end
end
With ActiveSupport (you already have it if you use Rails) you can do something like this:
def self.bar
self.to_s.deconstantize.constantize.foo
end
More about inflectors:
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html

Why isn't a module being read by my View-backed Model?

My controller calls the method bar:
class CompsController < ApplicationController
include ApplicationHelper
def quick_create
#var = Matview.bar #projects
end
end
bar is defined in a model that represents a materialized view in my database (it is not in my schema):
class MatView < ActiveRecord::Base
include ApplicationHelper
table_name = 'mat_views'
def self.bar(arg)
foo arg
end
end
'bar' calls method foo, which is defined in my ApplicationHelper:
module ApplicationHelper
def foo(arg1)
#do stuff
end
end
I've included the ApplicationHelper in both my controller and model, and yet I get this error:
NoMethodError in CompsController#quick_create
undefined method `foo' for Matview(Table doesn't exist):Class
why?
Matview.bar #projects
Is calling a class level method on the MatView class.
But your foo and bar are both instance method definitions. To make them class methods, you need def self.bar(arg) or def self.foo(arg1)
And to get class methods into your ActiveRecord model, you need to extend, not include the module:
class MatView < ActiveRecord::Base
extend ApplicationHelper
end
Or, if that does not sound like what you meant to do, then maybe you meant to do:
Matview.new.bar #projects
in which case the instance methods like you wrote them should work.

Rails: callbacks from module

I try to do this:
app/models/my_model.rb:
class MyModel < ActiveRecord::Base
include MyModule
...
end
lib/my_module.rb:
module MyModule
before_destroy :my_func #!
def my_func
...
end
end
but I get an error:
undefined method `before_destroy' for MyModule:Module
How can I correct it.
Also I'm new to ruby. What type has these "attributes": before_destroy, validates, has_many?
Are they variables or methods or what?
Thanks
before_destroy, validates, etc. are not attributes or anything like that. These are method calls.
In ruby, the body of a class is all executable code, meaning that each line of the class body is executed by the interpeter just like a method body would.
before_destroy :my_func is a usual ruby method call. The method that gets called is before_destroy, and it receives a symbol :my_func as an argument. This method is looked up in the class (or module) in the scope of which it is called.
So moving on to your question, I think now you should understand that when the interpreter loads your module
module MyModule
before_destroy :my_func #!
def my_func
...
end
end
it starts executing its body and searches for the method before_destroy in this module and cannot find one. What you want to do is call this method not on the module, but rather on the class where the module is included. For that we have a common idiom using the Module#included method:
module MyModule
module InstanceMethods
def my_func
...
end
end
def self.included(base)
base.send :include, InstanceMethods
base.before_destroy :my_func
end
end
In lib/my_module.rb, do this:
class MyInheritedClass < ActiveRecord::Base
before_destroy :my_func
def my_func
...
end
end
In app/models/my_model.rb, do this:
class MyModel < MyInheritedClass
...
end
There is no before_destroy filter in the module you are trying to create above. What my code does is creating a class that will inherit from ActiveRecord::Base and that will be your template class which all your other classes can inherit from. The template class also contains all properties of ActiveRecord::Base.
u can correct this by removing before_destroy from MyModule and place it in MyModel instead
before_destroy and other callbacks are only available to classes which extends ActiveRecord::Base, more info here
hope this helps =)

What does #self.included(base) do in Ruby on Rails' Restful Authentication?

I thought we would do
helper_method :current_user, :logged_in?, :authorized?
to make these controller methods available for use as helper methods in views. But in Restful Authentication's lib/authenticated_system.rb, I see:
# Inclusion hook to make #current_user and #logged_in?
# available as ActionView helper methods.
def self.included(base)
base.send :helper_method, :current_user, :logged_in?, :authorized? if base.respond_to? :helper_method
end
Why is it done this way instead of that single line? Also, I don't see included being called anywhere.
The self.included function is called when the module is included. It allows methods to be executed in the context of the base (where the module is included).
More info: a ruby mixin tutorial.
Out of the same reason which Peter has mentioned I would like to add an example so that it's easy for the newbie developers to understand self.included(base) and self.extended(base) :
module Module1
def fun1
puts 'fun1 from Module1'
end
def self.included(_base)
def fun2
puts 'fun2 from Module1'
end
end
def self.extended(_base)
def fun3
puts 'fun3 from Module1'
end
end
end
module Module2
def foo
puts 'foo from Module2'
end
def self.extended(_base)
def bar
puts 'bar from Module2'
end
end
end
class Test
include Module1
extend Module2
def abc
puts 'abc form Test'
end
end
class Test2
extend Module1
end
Test.new.abc #=> abc form Test
Test.new.fun1 #=> fun1 from Module1
Test.new.fun2 #=> fun2 from Module1
Test.foo #=> foo from Module2
Test.bar #=> bar from Module2
Test.new.fun3 #=> **NoMethodError** (undefined method `fun3' ..)
*Test2*.fun3 #=> fun3 from Module1
extend : methods will be accessible as class methods
include : methods will be available as instance methods
"base" in self.extended(base) / self.included(base) :
The base parameter in the static extended method will be either an
instance object or class object of the class that extended the module
depending whether you extend a object or class, respectively.
When a class includes a module the module’s self.included method will
be invoked. The base parameter will be a class object for the class
that includes the module.
When the AuthenticatedSystem method is included using the include method, the self.included method is triggered with whatever it was included into being the argument of base.
The code you've shown calls helper_method and defines some helpful helpers, but only if the base has a helper_method method.
It's done that way so including the module can set up the helper methods as well as adding additional methods to the class.
As it is the first result when searching Google for "self.included(base)" I will try to give a small example on how it works. I don't know how much it differs from the restful-authentication-approach.
It is basically used to make methods from one module available in another module.
module One
def hello
puts 'hello from module One'
end
end
module Two
def self.included(base)
base.class_eval do
include One
end
end
end
class ExampleClass
include Two
end
ExampleClass.new.hello # => hello from module One
Want to digger into self.included and self.extended ?
Please look at here: https://ruby-doc.org/core-2.2.1/Module.html#method-i-included

Resources