Ruby: include dynamic module name

I have a situation in my Rails application where I need to enable arbitrary modules depending on the current state of execution. The module provides custom application code, which is necessary only under certain conditions. Basically, I pull the company name out of the current context and use this as the file name for the module and its definition:

p = self.user.company.subdomain + ".rb" if File.exists?(Rails.root + "lib/" + p) include self.class.const_get(self.user.company.subdomain.capitalize.to_sym) self.custom_add_url end 

My test module is as follows:

 module Companyx def custom_add_url puts "Calling custom_add_url" end end 

Now it works fine in the console. I can pull the user out and turn on the module as follows:

 [1] pry(main)> c = Card.find_by_personal_url("username") [2] pry(main)> include c.class.const_get(c.user.company.subdomain.capitalize)=> Object [3] pry(main)> c.custom_add_url 

Call custom_add_url

If I try to run the include string from my model, I get

 NoMethodError: undefined method `include' for #<Card:0x007f91f9094fb0> 

Can anyone suggest why the include statement will work on the console, but not in my model code?

+6
source share
2 answers

Include is the method for the class.

If you want to call it inside the model, you need to execute the code in the context of this singleton class.

 p = self.user.company.subdomain + ".rb" if File.exists?(Rails.root + "lib/" + p) myself = self class_eval do include self.const_get(myself.user.company.subdomain.capitalize.to_sym) end self.custom_add_url 

EDIT:

class <itself does not accept a block; class_eval, therefore, it saves the state of local variables. I changed my mind to use it.

+5
source

I do like that. I found this answer useful: How to convert a string to a constant in Ruby?

Turns out I was looking for a method of constantization. This is the line I use in my code:

 include "ModuleName::#{var.attr}".constantize 

Edit:

So, I ran into various problems, actually using this line. Partly because I tried to call it inside a method in a class. But since I call only one method in the class (which calls / runs everything else), the final working version that I have is now

 "ModuleName::#{var.attr}".constantize.new.methodname 

Obviously, the name method is an instance method, so you can get rid of the new one if your class method.

+8
source

Source: https://habr.com/ru/post/914338/


All Articles