Accessing Methods in Ruby Module - ruby-on-rails

I'm writing my first Ruby module and I have this:
/app/module/test_modules/test.rb
test.rb looks similar to:
module TestModules
module Test
def test
puts 'this is a test'
end
end
end
When I call the following from console, I get:
(main)> TestModule::Test.test
//NoMethodError: private method `test' called for TestModules::Test:Module
How do I make test() visible?

You are calling a class method, whereas you defined test as an instance method. You could call it the way you want if you used the module via include or extend. This article does a good job explaining.
module TestModules
module Test
def self.test
puts 'this is a test'
end
end
end

Also,
1)
module TestModules
module Test
def test
puts 'this is a test'
end
module_function :test
end
end
2)
module TestModules
module Test
extend self
def test
puts 'this is a test'
end
end
end

The way that you have defined your method, it is a method on an instance of Test - thus it would work if you did:
blah = TestModule::Test.new
blah.test
note - and do use it this way, you would need to define Test as a class not a module
If you want the function to work on the class itself, then you need to define it like so:
def self.test
....
end
And then you can do TestModules::Test.test

the test method you defined is instance method...try this
module TestModules
module Test
def self.test
puts 'this is a test'
end
end
end
now you can call the method by this TestModules::Test.test

Related

Rails ActiveSupport::TestCase - How do I dynamically define tests together with their helper methods?

I am building some abstraction into my Rails (5.2) tests as I want to run the same tests multiple times with different parameter sets.
I can successfully create a helper to generate test classes on the fly. This looks like the following, within my test_helper.rb:
class << self
def test_configs(configs: DEFAULT_CONFIGS, &block)
configs.each do |c|
Class.new(ActiveSupport::TestCase) { yield(c) }
end
end
end
I can use this helper in any given test file as follows:
require 'test_helper'
class SampleTest < ActiveSupport::TestCase
test_configs do |c|
test "#{c[:name]} - something is true" do
puts "#{c[:name]}" => # Correctly outputs c[:name]
end
end
end
"#{c[:name]}" is correctly interpolated for each iteration according to what "config" is passed from the helper. So far so good.
I am having though a problem creating some helper methods that also make use of the variable c, either within the test_configs method itself or within single test files.
None of the following works in giving a consistent match between the c variable that is passed to the test titles and what happens within the tests themselves:
# Approach 1
class SampleTest < ActiveSupport::TestCase
test_configs do |c|
def config_name
"#{c[:name]}" # => undefined local variable or method `c'
end
test "#{c[:name]} - something is true" do
puts "#{config_name}"
end
end
end
# Approach 2
class SampleTest < ActiveSupport::TestCase
test_configs do |c|
define_method("config_name") {
"#{c[:name]}"
}
test "#{c[:name]} - something is true" do
puts "#{config_name}" # => Uses only the last definition
end
end
end
# Approach 3
class << self
def test_configs(configs: DEFAULT_CONFIGS, &block)
configs.each do |c|
define_method "config_name" do # (same with def config_name)
"#{c[:name]}"
end
Class.new(ActiveSupport::TestCase) { yield(c) }
end
end
end
# => undefined local variable or method `config_name'
How do I get to correctly "inject" methods which make use of the passed variable?
The right approach was similar to the 3rd one in my question, but yield must be replaced by instance_eval in order for the code within the block to inherit the context of the new class I am creating:
# test_helper.rb
class ActiveSupport::TestCase
class << self
def test_configs(configs: DEFAULT_CONFIGS, &block)
configs.each do |c|
Class.new(ActiveSupport::TestCase) {
define_singleton_method "config_title" do
"#{c[:name]}".capitalize
end
define_method "config_name" do
"#{c[:name]}"
end
instance_eval(&block)
end
end
end
end
end
# sample_test.rb
require 'test_helper'
class SampleTest < ActiveSupport::TestCase
test_configs do
test "#{config_title} - something is true" do # => Correctly invokes the class method defined above
puts "#{config_name}" # => Correctly invokes the instance method defined above
end
end
end
As you can see, there is also a difference in that class methods must be defined in order to be used within test titles, while instance methods must be defined for being used within tests.
In case c must be passed to the block (e.g. for defining additional methods within the block itself), instance_exec should be the way to go: docs.

How to enable monkey patch for specific method?

I am using a gem, for some reason, one of its method needs to be patched before it can be used by some of my code.
The problem is here, how can I enable this patch just for some of my code, say. for some method inside a class, I need to enable this patch; some I want to disable this patch.
How to do this?
class FromGem
def BlahBlah
#patch here
end
end
class A
def Test1
#need patch
end
def Test2
# don't need patch
end
end
This is what Refinements are for.
Say, we have the following third-party code:
class FromGem
def say_hello
'Hello'
end
end
FromGem.new.say_hello
# => 'Hello'
And we want to extend it to say "Hello World" instead, we'd do something like this:
module ExtendFromGem
def say_hello
super + ' World'
end
end
class FromGem
prepend ExtendFromGem
end
FromGem.new.say_hello
# => 'Hello World'
That's just the standard way of extending behavior, of course, this is still global. If we want to restrict the scope of our monkey-patch, we will need to use Refinements:
module ExtendedFromGem
module ExtendFromGem
def say_hello
super + ' World'
end
end
refine FromGem do
prepend ExtendFromGem
end
end
FromGem.new.say_hello
# => 'Hello'
# We haven't activated our Refinement yet!
using ExtendedFromGem
FromGem.new.say_hello
# => 'Hello World'
# There it is!
Now, what we want to write is this:
class A
def test1
using ExtendedFromGem
FromGem.new.say_hello
end
def test2
FromGem.new.say_hello
end
end
A.new.test1
# => 'Hello World'
A.new.test2
# => 'Hello'
Unfortunately, that doesn't work: Refinements only work in script scope, in which case Refinements are only active after the call to using, or they work in module scope, in which case they are active for the whole module body, even before the call to using, so what we can do, is this (IMO, this is cleaner):
class A
using ExtendedFromGem
def test1
FromGem.new.say_hello
end
end
class A
def test2
FromGem.new.say_hello
end
end
A.new.test1
# => 'Hello World'
A.new.test2
# => 'Hello'
or this:
class A
def test2
FromGem.new.say_hello
end
end
using ExtendedFromGem
class A
def test1
FromGem.new.say_hello
end
end
A.new.test1
# => 'Hello World'
A.new.test2
# => 'Hello'
Et voilĂ : test1 sees the refinement, test2 doesn't.
If you want to change the behavior just in one place, then you do not need anything to do, just write few lines of code that meets your requirement using or without using the gem's method
If you are gonna need that modified behavior in multiple places then creating a method that implements the changed behavior. In this case; you are gonna use Adapter Design pattern;
Steps:
You create AdaptorClass that Uses the Original class's behavior to provide you the new behavior you desire.
Now, you use the Adaptor's behavior rather than original class's to do your job where required.
Try not to modify the original class; follow Open-Closed Principle
class A
def Test1
# need patch
# you use GemAdapter rather than FromGem
end
def Test2
# don't need patch
# use FromGem class
end
def Test3
# need patch
# you use GemAdapter rather than FromGem
end
end
class GemAdapter
def new_behavior
gem_instance = FromGem.new
# implement your new behavior you desire
end
end
This could also be a solution
class << is used to modify a particular instance; a part of meta-programming
class A
def print
p 'Original method'
end
end
class B
def initialize
#new_instance_of_a = A.new
end
def my_method
modifiable_a = A.new
# modifying the instance of class A i.e. modifiable_a
class << modifiable_a
def print
p 'Monkey patch'
end
end
modifiable_a .print
end
def normal_method
#new_instance_of_a.print
end
end
However, modifying a local_instance does not make more sense. If this modified instance could be used in some more places then using this method is worthy

Module level method and a Class within same module in Ruby

I have the similar module in my Rails application
module Demo
def self.create
puts 'inside module method'
AnotherTestClass.new('testing')
end
class ModuleClass
def initialize(test)
#test = test
puts "inside class #{#test}"
end
end
end
module Demo
class AnotherTestClass < ModuleClass
end
end
So when I call Demo.create in my Controller, I am getting the following error
undefined method `create' for Demo:Module
Note: When I execute this code in irb, it works.

How to alias a class method within a module?

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"

Overwriting Rails Test Test::Unit::TestCase

I am trying to overwrite/modify the teardown function of a Test::Unit::TestCase test.
During the teardown of the test (after it has finished), I want to do some extra stuff.
I tried this, but it doesn't work (keeps executing the original teardown):
module Test
module Unit
class TestCase
def teardown_modified
# do modifications
teardown_original
end
alias teardown_original teardown
alias teardown teardown_modified
end
end
end
Do you want it in one TestCase or in all?
If you need a change for all TestCases:
gem 'test-unit'
require 'test/unit'
module Test
module Unit
module Fixture
alias :run_teardown_old :run_teardown
def run_teardown
# do modifications
puts "In modified teardown"
run_teardown_old
end #def run_teardown
end #module Fixture
end #module Unit
end #module Test
class MyTest < Test::Unit::TestCase
def teardown
puts "In teardown"
end
def test_4()
assert_equal(2,1+1)
end
end
You might find that using alias_method_chain produces better results:
class Test::Unit::TestCase
def teardown_with_hacks
teardown_without_hacks
end
alias_method_chain :teardown, :hacks
end
This sets up a lot of the stuff for you automatically.

Resources