Is it possible to get module name given a Ruby file - ruby

I wrote a Ruby file my.rb, which has a module definition:
module MyModule
...
end
Another Ruby script requires my.rb on the fly, and I want to dynamically get the module name defined in my.rb. Is it possible?

You can use TracePoint to do that:
# :class is the start of a class or module definition
TracePoint.new(:class) do |trace|
puts "defined a class or module named: #{trace.self}"
end.enable do
require './my'
end
when running this script it will output:
defined a class or module named: MyModule
and only classes that get defined within the enable block get output

Related

Rails and MiniTest: How to write tests for something in app/lib/mymodule (Class not found)?

Rails 5.2 here.
I want to test a class defined in app/lib/legacy/export.rb:
# app/lib/legacy/export.rb
module Legacy
class Export
def initialize ; end
end
end
However, a test in test/services/legacy_export_test.rb
# test/services/legacy_export_test.rb
require 'test_helper'
class LegacyExportTest < ActiveSupport::TestCase
test 'can be initialized' do
Legacy::Export.new
end
end
will spit out NameError: uninitialized constant Legacy::Export.
It works well if I put the class definition in app/lib/export.rb (and remove the module definition).
I can also reference this class in Controllers and in the rails console (rails c).
Trying to reference the class starting with the top-level-"namespace" (::Legacy::Export) does not help either. I find answers to questions how to reference lib folders (and subdirectories) in the test/ folder, but this is not what I need.
require 'lib/legacy/export will tell me cannot load such file, as will require 'legacy/export'.
I assumed that the (Auto-)Load-stuff of Rails and MiniTest are the same, but obviously there is some additional configuration to be done.
What has to be done? Where would I find this information?
The problem is that your class namespace / class path doesn't match how Rails autoloading works out of the box.
When you use a class that wasn't previously declared, Rails by default will look on specific paths (defined on config.autoload_paths)
app/controllers
app/controllers/concerns
app/models
app/models/concerns
...
When you use User for the first time, as it's not defined (yet) it will loop over those paths and try to require app/controllers/user.rb, app/controllers/concerns/user.rb, app/models/user.rb, until it founds the User class
if your class is namespaced as Legacy::Export, then it will look for app/models/legacy/export.rb, app/models/concerns/legacy/export.rb, app/controllers/legacy/export.rb, etc.
That's why it can't find your class: Your file is located on app/lib, that's not within the paths Rails use to look for.
There are different solutions:
Option #1
Require the file explicitly. (The Ruby way)
require_relative '../../app/lib/legacy/export'
Option #2
Add app/lib to autoload_path (in config/application.rb)
(The Rails Way)
module YourApp
class Application < Rails::Application
# ...
config.autoload_paths << Rails.root.join("app/lib")
end
end
Option #3
Adapt namespace to match what autoloading expects (instead of changing the configuration)
Example: move you file to something like app/models/legacy/export.rb

Unable to override RSpec Sysntax module method

I wanted to override the expect in under Syntax module. So, i have placed the below code into the .config/initializers/syntax.rb file
module RSpec
module Expectations
module Syntax
def enable_expect(syntax_host=::RSpec::Matchers)
return if expect_enabled?(syntax_host)
syntax_host.module_exec do
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
::RSpec::Expectations::ExpectationTarget.for(value, block)
end
end
end
end
end
end
And required this inside the env.rb file.
require_relative '../../.config/initializers/syntax'
This is not overriding the existing method. I'm using RSpec gem 3.2.0
What went wrong with the configuration?
I suggest you to put this override codes in spec/support directory and require it in rails or spec helper instead of putting in initializers.

Extending Modules from a gem instead of monkey patching

Assuming that "clean" Ruby monkey patching isn't an option, and all gem extension methods need to be completely contained within their own namespace, and given a structure like
module ARubyGem
class GemClassOne
def method1
# instance method to be available when calling extended module
end
def self.method2
# singleton method to also be available in extended module
end
end
end
Then which of these is the better way to extend a gem if the original gem code has some singleton methods that begin with "self"?
module ARubyGemExtension
class GemClassOneExtension < ARubyGem::GemClassOne
def method_to_override
# new code here
end
end
end
vs.
module ARubyGemExtension
include ARubyGem
class GemClassOneExtension
def method_to_override
# new code here
end
end
end
Seems like you would need to go with:
module ARubyGemExtension
class GemClassOneExtension < ARubyGem::GemClassOne
def method_to_override
# new code here
end
end
end
(Remember to namespace GemClassOne.)
I don't know anything about the gem you're using, but unless it's specifically set up for providing methods through include, I think you may not get what you're after.

How I include a module (this module has a module inside that) inside another module in Ruby

I have a module as following,
main.rb:
module Main
include Dad::Mam
end
and
in dad.rb:
module Dad
module Mam
puts "Mam is saying you are very lazy..."
end
end
How can I name this file? dad.rb is right?
but when running
$ ruby main.rb
I am getting an Error like,
main.rb:2:in <module:Main>': uninitialized constant Main::Dad
(NameError) from main.rb:1:in'
I need to show the sentance inside the puts under Mam module while running ruby main.rb,
I am confused about using ruby's modules, please anyone help me and guide me..
In this case, since you're just writing a simple script, use #require_relative
require_relative 'dad'
module Main
include Dad::Mam
end
For an actual app or library, you would want to manage the load path (a global variable holding an array that tells ruby where to look for files) and then use a normal require

Ruby: Class object to use in different file

I want to use the object created in main script globally.
for ex I have different module in different file and class in different ruby file
FileNameModA.rb
module A
# ........
# some code here
# ........
end
FileNameModB.rb
module B
# ........
# some code here
# ........
objReport.addReport() # getting an error "ruby uninitialized local variable or object: Main"
end
FileNameClass_Report.rb
class Report
def addreport()
end
end
MainScript.rb
require "FileNameModA"
require "FileNameModB"
require "FileNameClass_Report"
include ModuleA
include ModuleB
objReport = Report.New
objReport.addReport() # Works fine here
Could you please let me know how to create and use object in different file/module of Ruby?
when you require "FileNameModB" it executes the definition of module B and that definition calls objReport.addReport which does not exist in that context
i'm not sure what are you trying to achieve by calling addReport during module definition but you could swap some lines and make it work like this:
require "FileNameClass_Report"
$objReport = Report.New
require "FileNameModA"
require "FileNameModB" # update your file to call $objReport.addReport
include ModuleA
include ModuleB
whole thing looks like a mess though, try to rethink your classes/modules to avoid including them into global object

Resources