I have this class:
class SpecialAwesome
module Controller
def builder
SpecialAwesome.with_member(current_member)
which fails saying:
NoMethodError: undefined method `with_member'
But then I see the method here at:
class SpecialAwesome
module Options
def with_member member
self.class.new options.merge(:member => member)
end
How come the other file doesn't recognize this method?
The with_member method is defined as an instance method on SpecialAwesome::Options, not as a class method of SpecialAwesome. Probably, this is an issue.
with_member is not SpecialAwesome class method, but it is defined in Options module. To make it work as you expect, you can for example use extend method:
class SpecialAwesome
module Options
# ...
end
extend Options
# ...
end
which will add methods defined in Options module as SpecialAwesome class methods.
Related
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.
I defined a helper method: MembersHelper
module MembersHelper
def current_segment
Segment.where(current: true).first
end
end
then included it in a class call Base in app/service/enum_data/base.rb file
module EnumData
class Base
include MembersHelper
end
end
And used it from Base's subclass: GetAll in app/service/enum_data/get_all.rb file
module EnumData
class GetAll < Base
def self.call
reference_data = current_segment.entities.all
end
end
end
But I got an error
undefined local variable or method 'current_segment' for EnumData::GetByCategory:Class
I fixed it by moving current_segment method to Base class, but I want to know why it doesn't work when I include that helper method? Did I miss something?
You are using include, which makes current_segment an instance method in the including classes while what you need, is a class instance method (singleton method). In order to achieve it you should use extend:
module EnumData
class Base
extend MembersHelper
end
end
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.
My model, Widget.rb, has include ApplicationHelper and my instance methods have no trouble using any method defined in application_helper.rb
However, when I try to use one of the helper methods in any of my class methods such as
def self.send_broadcast(guid)
track_guids(guid) # defined in application_helper.rb
end
I get No Method error.
Is there some secret handshake to permit use of a ApplicationHelper method inside a class method?
ApplicationHelper is just a module:
module ApplicationHelper
def track_guids(something)
end
end
class Widget
extend ApplicationHelper
def self.send_broadcast(guid)
track_guids(guid)
end
end
Now you should have access to the module methods from a class method. I'm not sure if you can both extend and include the same module though... not really sure what that'd do.
Edit to add:
I'm not sure what will happen if you try both extending and including the same module into the class. With extend you get the module included at the class-level, with include it is included at the instance-level. It might give you the methods at both class and instance if you do both... or it might die horribly. Give it a try?
I don't think you can access instance methods unless self is an instance. You could make an instance of Widget and call a class method from that, or you could try to call the methods from the module directly.
I have a model, Show and a module Utilities
class Show < ActiveRecord::Base
include Utilities
...
def self.something
fix_url("www.google.com")
end
end
My Utilities file is in lib/utilities.rb
module Utilities
def fix_url(u)
!!( u !~ /\A(?:http:\/\/|https:\/\/)/i ) ? "http://#{u}" : u
end
end
But Rails is throwing a NoMethodError for "fix_url" when I call it in my show class. Do I have to do something different when including a module in my model?
Thanks!
try injecting that mixin via the extend instead of include. Basically, because you are calling the mixin method from a class method, but including a mixin only makes its instance methods available. You can use the extend style to get class methods.
Search around for Ruby include and extend to learn the differences. A common pattern is to do it like here:
http://www.dcmanges.com/blog/27
Where you use the included hook to mixin both instance and class level methods.
#Tony - this works for me
class User < ActiveRecord::Base
extend Utilities
def self.test
go()
end
end
module Utilities
def go
puts "hello"
end
end
From console:
>> User.test
hello
=> nil
At no point do I have to explicitly call a method with self.
It worked for me. Have you tried restarting your server/console session?
Edit: If you want to just call Utilities.fix_url you can do that - no include/extend necessary.