I am using Ruby v1.9.2 and the Ruby on Rails v3.2.2 gem. I had the following module
module MyModule
extend ActiveSupport::Concern
included do
def self.my_method(arg1, arg2)
...
end
end
end
and I wanted to alias the class method my_method. So, I stated the following (not working) code:
module MyModule
extend ActiveSupport::Concern
included do
def self.my_method(arg1, arg2)
...
end
# Note: the following code doesn't work (it raises "NameError: undefined
# local variable or method `new_name' for #<Class:0x00000101412b00>").
def self.alias_class_method(new_name, old_name)
class << self
alias_method new_name, old_name
end
end
alias_class_method :my_new_method, :my_method
end
end
In other words, I thought to extend the Module class someway in order to add an alias_class_method method available throughout MyModule. However, I would like to make it to work and to be available in all my Ruby on Rails application.
Where I should put the file related to the Ruby core extension of the Module class? Maybe in the Ruby on Rails lib directory?
How should I properly "extend" the Module class in the core extension file?
Is it the right way to proceed? That is, for example, should I "extend" another class (Object, BasicObject, Kernel, ...) rather than Module? or, should I avoid implementing the mentioned core extension at all?
But, more important, is there a Ruby feature that makes what I am trying to accomplish so that I don't have to extend its classes?
You could use define_singleton_method to wrap your old method under a new name, like so:
module MyModule
def alias_class_method(new_name, old_name)
define_singleton_method(new_name) { old_name }
end
end
class MyClass
def my_method
puts "my method"
end
end
MyClass.extend(MyModule)
MyClass.alias_class_method(:my_new_method, :my_method)
MyClass.my_new_method # => "my method"
Answering your comment, you wouldn't have to extend every single class by hand. The define_singleton_method is implemented in the Object class. So you could simply extend the Object class, so every class should have the method available...
Object.extend(MyModule)
Put this in an initializer in your Rails app and you should be good to go...
I found an answer on this website: http://engineering.lonelyplanet.com/2012/12/09/monitoring-our-applications-ruby-methods/
The solution is to use class_eval with a block. That enables using variables from the enclosing scope.
module Alias
def trigger
#trigger = true
end
def method_added(name)
if #trigger
#trigger = false
with_x = "#{name}_with_x"
without_x = "#{name}_without_x"
define_method(with_x) do
"#{send(without_x)} with x"
end
alias_method without_x, name
alias_method name, with_x
end
end
def singleton_method_added(name)
if #trigger
#trigger = false
with_x = "#{name}_with_x"
without_x = "#{name}_without_x"
define_singleton_method(with_x) do
"singleton #{send(without_x)} with x"
end
singleton_class.class_eval do
alias_method without_x, name
alias_method name, with_x
end
end
end
end
class TestAlias
extend Alias
trigger
def self.foo
'foo'
end
trigger
def bar
'bar'
end
end
TestAlias.foo # => 'singleton foo with x'
TestAlias.new.bar # => 'bar with x'
If you don't have singleton_class then you should probably upgrade your version of Ruby. If that's not possible you can do this:
class Object
def singleton_class
class << self
self
end
end
end
The accepted answer was confusing and did not work.
class Module
def alias_class_method(new_name, old_name)
define_singleton_method(new_name, singleton_method(old_name))
end
end
module MyModule
def self.my_method
'my method'
end
end
MyModule.alias_class_method(:my_new_method, :my_method)
MyModule.my_new_method # => "my_method"
Related
currently I have a module like this:
module MyModule
def A
end
.....
end
and I have a model that I want to use that method A as a class method. However, the thing is I only need that A method. If I extend it, I am gonna extend the other unnecessary class methods into my model. Therefore, is there a way for me to do sth like MyModule.A without rewriting the module like this:
module MyModule
def A
...
end
def self.A
...
end
.....
end
It is kind of repeating myself if I do it that way. I still feel there is a better way to do it in Rails.
Use Module#module_function to make a single function to be a module function:
module M
def m1; puts "m1"; end
def m2; puts "m2"; end
module_function :m2
end
or:
module M
def m1; puts "m1"; end
module_function # from now on all functions are defined as module_functions
def m2; puts "m2"; end
end
M.m1 #⇒ NoMethodError: undefined method `m1' for M:Module
M.m2 #⇒ "m2"
Yes, you can define it as a module_function, then you should be able to access it using module name.
Ex:
module Mod
def my_method
100
end
def self.my_method_1
200
end
module_function :my_method
end
Mod.my_method
# => 100
Mod.my_method_1
# => 200
Note: No need to add the self defined methods in module_function, they are accessible directly. But it's needed for methods defined without self
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
I want to define a class and let many helpers use.
I can include MvaasPortal moude in fine,
Then I can new the object , but can not use any methods of the object,
It's so strange.
If I can not use the methods in the object, why I can new the object.
Ruby is so strange.
#portal = Portal.new
There is no methods in #portal object
mvaas_portal.rb
module MvaasPortal
module InstanceMethods
class Portal
def initialize(server_url)
~~~~
end
def query_server(body_to_send={},session_id=nil)
~~~
end
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
end
end
If you're using rails, you can use ActiveSupport::Concern : http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
If don't, take a look at the first example on the link.
Moreover, your namespace is a little bit weird and misses some context. Here is an example with a dummy method :
require 'active_support/concern'
module MvaasPortal
include ActiveSupport::Concern
def an_instance_method
puts "Here!"
end
end
class Portal
include MvaasPortal
end
Portal.new.an_instance_method
=> "Here!"
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.
In my rails projects, I often use this sort of behavior in my classes and models:
class Whatever
class WhateverError < StandardError; end
def initialize(params={})
raise WhateverError.new("Bad params: #{params}") if condition
# actual class code to follow
end
end
The trouble is, this is both hugely repetitive and fairly verbose. I'd love it if I could just do this whenever I need to raise a class-specific error:
class ErrorRaiser
include ClassErrors
def initialize(params={})
error("Bad params: #{params}") if condition
error if other_condition # has default message
# actual class code to follow
end
def self.class_method
error if third_condition # class method, behaves identically
end
end
I'm having major trouble creating such a module. My sad early attempts have tended to look something like the below, but I'm pretty confused about what's available within the scope of the module, how to dynamically create classes (within methods?) or whether I have straightforward access to the "calling" class at all.
My basic requirements are that error be both a class method and an instance method, that it be "namespaced" to the class calling it, and that it have a default message. Any thoughts/help? Is this even possible?
module ClassErrorable
# This and the "extend" bit (theoretically) allow error to be a class method as well
module ClassMethods
def self.error(string=nil)
ClassErrorable.new(string).error
end
end
def self.included(base)
set_error_class(base)
base.extend ClassMethods
end
def self.set_error_class(base)
# I'm shaky on the scoping. Do I refer to this with # in a class method
# but ## in an instance method? Should I define it here with # then?
##error_class = "##{base.class}Error".constantize
end
def self.actual_error
# This obviously doesn't work, and in fact,
# it raises a syntax error. How can I make my
# constant a class inheriting from StandardError?
##actual_error = ##error_class < StandardError; end
end
def initialize(string)
#string = string || "There's been an error!"
end
def error(string=nil)
raise ##actual_error.new(string)
end
end
How about something like this (written in pure Ruby; it could be refactored to use some Rails-specific features like .constantize):
module ClassErrorable
module ClassMethods
def error(message = nil)
klass = Object::const_get(exception_class_name)
raise klass.new(message || "There's been an error!")
end
def exception_class_name
name + 'Error'
end
end
def self.included(base)
base.extend ClassMethods
Object::const_set(base.exception_class_name, Class.new(Exception))
end
def error(message = nil)
self.class.error(message)
end
end