How to create several class and instance methods in Rails model - ruby-on-rails

I am doing this
def self.a
...
end
def a
class.a
end
But for several method I would have to replicate instance methods.
I was thinking in a module
module A
def a; end
end
And then use it in my model like this:
extend A
include A
But I am not sure where to put it according Rails folder structure, or even if to put module inside my model.
Any advice?

Opt 1 - Extend Self
If you want to have all of your instance methods as class methods as well, you can simply use extend self
class A
def foo
...
end
def bar
...
end
extend self
end
This would allow you to call foo as either A.foo or A.new.foo.
Opt 2 - Included Module
If you only want some of your instance methods to be available as class methods, then you should create a module as you proposed. You could put that module in the lib/ folder and either require it or add lib to your autoload paths.
You can also include the module directly in the class like so:
class A
def not_shared
...
end
module SharedMethods
def foo
...
end
def bar
...
end
end
extend SharedMethods
include SharedMethods
end
Opt 3 - Delegate
If you're using Rails (or just ActiveSupport), you can also make use of the delegate method it adds to class/module.
class A
def not_shared
...
end
def foo
...
end
def bar
...
end
delegate :foo, :bar, to: 'self.class'
end
See here for details:
http://rdoc.info/docs/rails/3.0.0/Module:delegate

You you want to create a module, like shared_methods.rb, you'd put the file in the /lib directory.
You would include the module like this:
class NewClass
include SharedMethods
...
end

Related

extend self in a module

It seems that this two pieces of codes do the same job, but I would like to understand why I'd rather use one or another
First example:
module MyModule
extend self
def first_method
end
def second_method
end
end
Second example:
module MyModule
def self.first_method
end
def self.second_method
end
end
Your first example defines two instance methods and makes them also available as class (or module) methods via extend:
module MyModule
def first_method; end
def second_method; end
end
MyModule.instance_methods #=> [:second_method, :first_method]
MyModule.methods - Module.methods #=> []
MyModule.extend MyModule
MyModule.instance_methods #=> [:second_method, :first_method]
MyModule.methods - Module.methods #=> [:second_method, :first_method]
Whereas your second example just defines two class (or module) methods and no instance methods:
module MyModule
def self.first_method; end
def self.second_method; end
end
MyModule.instance_methods #=> []
MyModule.methods - Module.methods #=> [:second_method, :first_method]
The first variant can be useful when you want to provide some utility functions that can be called as:
MyModule.first_method
or be included in other modules / classes:
class Foo
include MyModule
def another_method
first_method # <- no explicit receiver needed
end
end
Ruby also provides the helper method module_function to define methods that way:
module MyModule
def first_method
end
module_function :first_method
end
It adds the method as a class methods and makes the instance method private. It's how the methods in Kernel work.

Call method of included module from class

I have a use case where I have class A which includes module B.
class A
include B
def do_one_thing
# override module's method. do something different instead
end
def do_another_thing
# Call `do_one_thing` from here,
# but call the module's method, not the one I overrode above.
end
end
module B
included do
def do_one_thing
# ...
end
end
# some other methods
end
As shown above, I'm calling do_one_thing from do_another_thing. My problem is that I need to call the module's method (i.e. the super method). Is this possible in Rails?
To property use the included method, you'll need your B module to extend ActiveSupport::Concern but that won't give you the behaviour you want.
If I were you I'd abandon that pattern and use simple native Ruby module patterns:
module B
def do_one_thing
puts 'in module'
# ...
end
# some other methods
end
class A
include B
def do_one_thing
super
puts 'in class'
# override module's method. do something different instead
end
def do_another_thing
do_one_thing
# Call `do_one_thing` from here,
# but call the module's method, not the one I overrode above.
end
end
A.new.do_one_thing
The above code will correctly use the module inheritance you are looking for.
Read more about Ruby module inheritance here
You can 'save' included method before override
module B
extend ActiveSupport::Concern
included do
def do_one_thing
puts 'do_one_thing'
end
end
end
class A
include B
alias_method :old_do_one_thing, :do_one_thing
def do_one_thing
puts "I'd rather do this"
end
def do_another_thing
old_do_one_thing
end
end
a= A.new
a.do_one_thing
a.do_another_thing

Ruby on Rails module include module

I want to include a module in a rails helper(is also a module).
The helper is:
module SportHelper
.....
end
And the module is:
module Formula
def say()
....
end
end
Now, I want to use the method say in SportHelper. What should I do?
If I write like this:
module SportHelper
def speak1()
require 'formula'
extend Formula
say()
end
def speak2()
require 'formula'
extend Formula
say()
end
end
This will work, but I don't want to do so, I just want to add the methods on the helper module,not every methods.
You need just to include this module in your helper:
require 'formula'
module SportHelper
include Formula
def speak1
say
end
def speak2
say
end
end
Maybe you don't need this line require 'formula', if it's already in the load path. For check this you can inspect $LOAD_PATH variable. For more information see this answer.
Basic difference between extend and include is that include is for adding methods to an instance of a class and extend is for adding class methods.
module Foo
def foo
puts 'heyyyyoooo!'
end
end
class Bar
include Foo
end
Bar.new.foo # heyyyyoooo!
Bar.foo # NoMethodError: undefined method ‘foo’ for Bar:Class
class Baz
extend Foo
end
Baz.foo # heyyyyoooo!
Baz.new.foo # NoMethodError: undefined method ‘foo’ for #<Baz:0x1e708>
And if you use extend inside the object method, it will adding methods to an instance of a class, but they would be available only inside this one method.
I think directly include should work
module SportHelper
include SportHelper
.........
end
end
I tested like below:
module A
def test
puts "aaaa"
end
end
module B
include A
def test1
test
end
end
class C
include B
end
c = C.new()
c.test1 #=> aaaa
It should work.

Rails: dynamically define class method based on parent class name within module/concern

I want to dynamically generate a class method in a Mixin, based on the class name that include this Mixin.
Here is my current code:
module MyModule
extend ActiveSupport::Concern
# def some_methods
# ...
# end
module ClassMethods
# Here is where I'm stuck...
define_method "#{self.name.downcase}_status" do
# do something...
end
end
end
class MyClass < ActiveRecord::Base
include MyModule
end
# What I'm trying to achieve:
MyClass.myclass_status
But this give me the following method name:
MyClass.mymodule::classmethods_status
Getting the base class name inside the method definition works (self, self.name...) but I can't make it works for the method name...
So far, I've tried
define_method "#{self}"
define_method "#{self.name"
define_method "#{self.class}"
define_method "#{self.class.name}"
define_method "#{self.model_name}"
define_method "#{self.parent.name}"
But none of this seems to do the trick :/
Is there any way I can retrieve the base class name (not sure what to call the class that include my module). I've been struggling with this problem for hours now and I can't seem to figure out a clean solution :(
Thanks!
I found a clean solution: using define_singleton_method (available in ruby v1.9.3)
module MyModule
extend ActiveSupport::Concern
included do
define_singleton_method "#{self.name}_status" do
# do stuff
end
end
# def some_methods
# ...
# end
module ClassMethods
# Not needed anymore!
end
end
You can't do it like that - at this point it is not yet known which class (or classes) are including the module.
If you define a self.included method it will be called each time the module is included and the thing doing the including will be passed as an argument. Alternatively since you are using AS::Concern you can do
included do
#code here is executed in the context of the including class
end
You can do something like this:
module MyModule
def self.included(base)
(class << base; self; end).send(:define_method, "#{base.name.downcase}_status") do
puts "Hey!"
end
base.extend(ClassMethods)
end
module ClassMethods
def other_method
puts "Hi!"
end
end
end
class MyClass
include MyModule
end
MyClass.myclass_status
MyClass.other_method
Works for extend:
module MyModule
def self.extended who
define_method "#{who.name.downcase}_status" do
p "Inside"
end
end
end
class MyClass
extend MyModule
end
MyClass.myclass_status

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