extend a module with submodule methods and constants - ruby

I want to be able to extend a module with the methods and constants of a submodule.
If I use extend I receive undefined constant.
The only way I made it work is using both extend and include. I tried also using self.method on submodule.
module Car
module Container
HOLA = 'Helloo!'
def testing
HOLA
end
end
include Container
extend Container
end
So this both should work:
Car.testing # Hello!
Car::HOLA # Hello!
I guess this is a code smell..., but what other ways to make it work you know?

As per the description shared, it seems like you want to access the nested module constants and methods in some_other_class.
This is the module definition as mentioned in the post.
module Car
module Container
HOLA = 'Helloo!'
def testing
HOLA
end
end
end
Now Suppose , that you want to use this method in some class say Vehicle
require 'car' #this is the module file since it is residing in some other file
class Vehicle
extend Car
def test
Car.testing
end
end
Now calling Vehicle.new.test will print
"Helloo!"
Hope it helps!!

Related

Extending Modules in Cucumber

I know that in Cucumber that I can create a module and then include that module into the World object and all the methods that I have created within the newly created module are available globally
module MyModule
def my_method
end
end
World(MyModule)
Now anywhere in my cucumber tests I can call my_method and it will work
The issue I see here and an issue I have come across is duplication of method names, as the application gets bigger or other testers/developers work on the application.
So if I was to wrap everything up in its own module and create module methods like so
module MyModule
def self.my_method
page.find('#element')
end
end
World(MyModule)
MyModule.my_method
# This will return undefined variable or method 'page' for MyModule module
So being new to using modules I have read that you can extend other modules so that you can use those methods within another module
So to access the page method I would need to access Capybara::DSL
module MyModule
extend Capybara::DSL
def self.my_method
page.find('#element')
end
end
World(MyModule)
MyModule.my_method now works, but my question is rather than extend individual namespaces for every module that I need access to is there a way to extend/include everything or is this a bad practice?
Another example of where things fail are when I try to access instances of a class
module SiteCss
def login_page
Login.new
end
end
class Login < SitePrism::Page
element :username, "#username"
end
module MyModule
extend Capybara::DSL
def self.my_method
page.find('#element')
login_page.username.set('username')
end
end
World(MyModule)
So with this example if I was it try and call login_page.username I would get
undefined method `login_page`
I'm really looking for the correct way to be doing this.
In conclusion I am trying to understand how to use custom modules and classes in cucumber without having to load everything into the World object.
Yes, it's not pretty to extend a module multiple times, if that's really what you want to do, I can suggest a way you can improve it a bit:
base class which will inherit your page object framework and extend(include is probably the correct option here):
module Pages
class BasePage < SitePrism::Page
extend Capybara::DSL
end
end
Now your login class:
module Pages
class Login < BasePage
element :username, "#username"
def yourmethod
page.find('#element')
username.set('username')
end
end
end
Now the bits you are probably interested in, expose yourmethod to cucumber world:
module SiteCss
def page_object
#page_object ||= Pages::Login.new
end
end
World(SiteCss)
Now you should have access to yourmethod in a cleaner way...
Usage:
#page_object.yourmethod
Although the most important suggestion I could give you is run from SitePrism... Create your own page object framework... don't fall into SitePrism trap...

Include module in a class within the module

I have two classes. I want to namespace them. I also have a bit of functionality that they share. I do something like this:
module Talker
def say_bye
puts 'bye'
end
class Bob
include Talker
def say_yo
puts 'yo'
end
end
class Tom
include Talker
def say_hello
puts 'hello'
end
end
end
These are all valid method calls.
Talker::Bob.new.say_yo
Talker::Bob.new.say_bye
Talker::Tom.new.say_hello
Talker::Tom.new.say_bye
I was told: "This include is going to include Bob again. You should close the Talker module before starting a class that includes Talker." Can somebody explain to me if I'm doing something that results in unexpected ruby behavior or is considered taboo? Is it a bad practice to include a module like this from inside a class within that module? What might be a criticism for this pattern? Should I use inheritance here?
I figured it out. The include ends up being somewhat recursive. The code above will do things like this:
Talker::Bob::Bob
Talker::Bob::Tom
Talker::Bob::Bob::Bob::Tom::Bob::Tom
And that is not something to have. So he was right when he said it would re-include the other classes in the module into each class that includes the module.

Self value in Module within Module in Ruby

Why given a module like this:
module TestModule
module Configuration
# Return the configuration values set in this module
def options
puts "OPTIONS IS IN"
puts self.inspect
end
end
end
I get that the options method is in TestModule and not in Configuration?
EDIT: I add the gem that I am taking a look at, and the one that has given me this confusion:
Check this file: configuration.rb in line 37 the method options is defined.
In the class Client however, when options is called (line 11) is used doing Awesome instead of Configuration. Why is that? I don't see any class named Awesome where those modules are being mixed.
The gem in question does (in awesome.rb)
module Awesome
extend Configuration
end
So all of the methods on Awesome::Configuration (such as options) become singleton methods on the Awesome module
Technically, it's not part of either. It would have to be mixed in to a class to make it part of a class. To make it callable directly on the module, you need to prefix it with self. in the definition to make it a module method instead of an instance method:
def self.options
self #=> TestModule::Configuration
end
Here's a good tutorial on mixins which allows you to use the instance methods: http://rubylearning.com/satishtalim/modules_mixins.html

Test modules with Test::Unit

I encountered a problem when trying to test a module with Test::Unit. What I used to do is this:
my_module.rb:
class MyModule
def my_func
5 # return some value
end
end
test_my_module.rb:
require 'test/unit'
require 'my_module'
class TestMyModule < Unit::Test::TestCase
include MyModule
def test_my_func
assert_equal(5, my_func) # test the output value given the input params
end
end
Now the problem is, if my_module declares an initialize method, it gets included in the test class and this causes a bunch of problems since Test::Unit seems to override/generate an initialize method. So I'm wondering what is the best way to test a module?
I'm also wondering wether my module should become a class at this point since the initialize method is made for initializing the state of something. Opinions?
Thanks in advance !
Including an initialize method in a module feels very wrong to me, so I'd rethink that at the very least.
To answer your question about testing this as a module more directly, though, I would create a new, empty class, include your module in it, create an instance of that class, and then test against that instance:
class TestClass
include MyModule
end
class TestMyModule < Unit::Test::TestCase
def setup
#instance = TestClass.new
end
def test_my_func
assert_equal(5, #instance.my_func) # test the output value given the input params
end
end
Yeah, your initialize should definitely suggest that you're going towards a class. A module in ruby often feels like an interface in other languages, as long as you implement some basic things when you include the module you'll get a lot for free.
Enumerable is a great example, as long as you define [] and each when you include Enumerable you suddenly get pop, push, etc.
So my gut feeling about testing modules is that you should probably be testing classes that include the module rather than testing the module itself unless the module is designed to not be included in anything, it's simply a code storage mechanism.

Extending cucumber with modules and accessing Before/After hooks

I am trying to write a generic module to extend the World class. I need to access the Before and After hooks from within the module. I am doing this by using the extended method but Before/After does not seem to be available at this point.
module MyWorld
def MyWorld.extended(obj)
obj.Before do
# this doesn't work
end
end
end
Is there another way to access these hooks?
Found out how to do it:
module MyWorld
def MyWorld.extended(obj)
Main.Before do
# some stuff
end
Main.After do
# some other stuff
end
end
end

Resources