"Uninitialized constant A::MyModule" error when include MyModule - ruby

I'm getting this error when trying to use include MyModule "Uninitialized constant"
Here is my implementation:
a.rb
class A
include MyModule
puts ARRAY
end
my_module.rb
module MyModule
ARRAY = [1,2,3]
end

Ruby doesn't know about MyModule because it doesn't read my_module.rb
Add at the top of a.rb
require_relative 'my_module'
if these files are in the same folder (or correct path to the file if not)

Related

Classwide method in ruby gem

How can you define c method that is accessible from class One and Two?
module MyGem
class One
def a
c
end
end
class Two
def b
c
end
end
end
There is few ways, for example inheritance vs mixins.
Using mixins, you can define a Module and include in your classes.
module MyGem
class One
include Mixin
def a
c
end
end
class Two
include Mixin
def b
c
end
end
module Mixin
def c
...
end
end
end

Unable to include shoulda tests in a module

I am using Rails with MiniTest and have several classes that are all related through inheritance. I would like to reuse tests by placing them in a module. Something like this:
module MyModule
should 'work' do
assert true
end
end
Then in my tests:
class MyTest < ActiveSupport::TestCase
require MyModule
end
The problem is that I get a NoMethodError: undefined method should
What am I missing?
1) That's not what require is for.
2a) You probably want to extend a module and define a method that you can call instead.
module MyModule
def should_work
should 'work' do
assert true
end
end
end
class MyTest < ActiveSupport::TestCase
extend MyModule
should_work
end
2b) Alternatively, you can just extend the module and have the tests automatically called via Module.extended:
module MyModule
def self.extended(base)
base.should_work
end
def should_work
should 'work' do
assert true
end
end
end
class MyTest < ActiveSupport::TestCase
extend MyModule
end
2c) You can put everything in Module.extended too:
module MyModule
def self.extended(base)
base.should 'work' do
assert true
end
end
end
class MyTest < ActiveSupport::TestCase
extend MyModule
end
You need to require your module file in MyTest class, on the top:
require 'spec_helper'
require_relative '../spec/modules/module_file_name'
Or you can put this relative_path into spec_helper and than just require 'spec_helper' .
Hope this help you

I can not understand "uninitialized constant" in ruby

I have two files in folder pc under my home directory.
First file:
class A
class << self
protected
def foo
puts "In foo"
end
end
end
Second file:
class B < A
def bar
self.class.class_eval { foo }
end
end
B.new.bar
My problem is when I run the second file I get the following error:
second.rb:1:in `<main>': uninitialized constant A (NameError)
Why is that?
B.new.bar
# => In foo
just works fine in my console. I guess you probably forgot to require the file containing A from the file B.
In file B use
require 'a'
(assuming the file containing A is called a.rb).
I read the various comments, and just to avoid confusion, here's the full content of the two files.
class_a.rb
class A
class << self
protected
def foo
puts "In foo"
end
end
end
class_b.rb
require_relative 'class_a'
class B < A
def bar
self.class.class_eval { foo }
end
end
puts B.new.bar
And here's how to execute them from the console
$ ruby class_b.rb
In foo
Of course, you should execute the file class_b.rb, not class_a.rb or you will not see any result.
Try require_relative 'class_a' or require class_a.
Note that the file's extension is not included.
This should work, assuming it's all in the same file. If not, you'll need to require the first file into the next:
# class_b.rb
require 'class_a.rb'
class B < A
def bar
self.class.class_eval { foo }
end
end
B.new.bar
#=> "In foo"
UPDATE:
In order to require the file, you may need to cite the path of the file relative to your current directory. For instance, if class_a.rb is located in ~/home and you're running irb (or class_b.rb is in ~/home), then you'd include class_a.rb by citing its relative path as follows:
require './class_a'

__FILE__ variable in imported files

I have two files in two different directories:
module MyModule
def my_method path
p File.join (File.dirname __FILE__), path
end
end
and
require_relative '../modules/mymodule' # definition of MyModule
class MyClass
extend MyModule
my_method 'my_file.yml'
end
I am getting output like my_home_dir/modules/my_file.yml but I want it to be my_home_dir/files/my_file.yml where files is the name of the directory where MyClass is defined.
I know I can use full path when I call my_method but is there a way for imported files to still have __FILE__ set to the name of the importing file?
Basically in my_method I need to have the full path of the file and I want to pass just a path relative to my calling file's path.
__FILE__ always is the name of the file containing the __FILE__ variable, so saying my_method will always return where my_method is defined, not where MyClass calls it.
You can probably get at the information you want using caller:
module MyModule
def my_method path
p caller
end
end
include MyModule # definition of MyModule
class MyClass
extend MyModule
my_method 'my_file.yml'
end
my_class = MyClass.new
Which outputs:
["test.rb:10:in `<class:MyClass>'", "test.rb:8:in `<main>'"]
Edit:
the caller array has only file names without paths...
Well, I'd hoped you'd know how to work around that but....
This is in test.rb:
require './test2'
class MyClass
extend MyModule
my_method __FILE__, 'my_file.yml'
end
my_class = MyClass.new
This is in test2.rb:
module MyModule
def my_method path, file
dir = File.dirname(path)
p caller.map{ |c| File.join(dir, c) }
end
end
Running test.rb outputs:
["./test.rb:4:in `<class:MyClass>'", "./test.rb:2:in `<main>'"]
There's a simpler method available if you can pass in a block; use the block's binding:
# In example.rb:
module Example
def execute_if_main(&block)
if $PROGRAM_NAME == eval("__FILE__", block.binding)
yield
end
end
end
Then in the test file:
# in test.rb:
require_relative 'example.rb'
include Example
execute_if_main do
puts "hi, I'm being loaded"
end
This will execute the block only if test.rb is being loaded directly by the Ruby interpreter.
If test.rb is loaded instead via some other file via require, the block at the end won't
be executed (which is the idea)
Ruby 2.7 adds Module#const_source_location
const_source_location(:MyMoudule)

How do I include a Module into another Module (Refactor AASM code and custom states into Module)

I'm trying to refactor a superfat model which has quite a few lines of ActsAsStateMachine code related to the states and transitions, and I was hoping to refactor this to a module call CallStates.
#in lib/CallStates.rb
module CallStates
module ClassMethods
aasm_column :status
aasm_state :state1
aasm_state :state2
aasm_state :state3
end
def self.included(base)
base.send(:include, AASM)
base.extend(ClassMethods)
end
end
And then in the model
include CallStates
My question concerns how to include Module behavior into a Module such that a single Module can be included into the model. I've tried class_eval do as well to no avail. Thanks for any insightful thoughts you have on the matter.
You can't include one module in another exactly, but you can tell a module to include other modules in the class into which it's included:
module Bmodule
def greet
puts 'hello world'
end
end
module Amodule
def self.included klass
klass.class_eval do
include Bmodule
end
end
end
class MyClass
include Amodule
end
MyClass.new.greet # => hello world
It's best to only do this if Bmodule is actually data that is necessary for Amodule to function, otherwise it can lead to confusion because it's not explicitly included in MyClass.
You include a module into another module by ... including a module into another module, of course!
module Bmodule
def greet
puts 'hello world'
end
end
module Amodule
include Bmodule
end
class MyClass
include Amodule
end
MyClass.new.greet # => hello world
Rails
For Rails, you'll want to do something like this:
module_b.rb
module ModuleB
extend ActiveSupport::Concern
included do
include ModuleA
end
end
my_model.rb
class MyModel < ActiveRecord::Base
include ModuleB
end
ModuleA is being included in ModuleB, which is then included in MyModel class.
I like this syntax the best:
module Bmodule
def greet
puts 'hello world'
end
end
module Amodule
def self.included(receiver)
receiver.send :include, Bmodule
end
end
class MyClass
include Amodule
end
MyClass.new.greet # => hello world

Resources