I'm pretty new to Elixir and functional programming-languages in general.
In Elixir, I want to call one specific function on Modules, given the Module name as String.
I've got the following (very bad) code working, which pretty much does what I want to:
module_name = elem(elem(Code.eval_file("module.ex", __DIR__), 0), 1)
apply(module_name, :helloWorld, [])
This (at least as I understand it) compiles the (already compiled) module of module.ex in the current directory. I'm extracting the modules name (not as a String, don't know what data-type it actually is) out of the two tuples and run the method helloWorld on it.
There are two problems with this code:
It prints a warning like redefining module Balance. I certainly don't want that to happen in production.
AFAIK this code compiles module.ex. But as module.ex is already compiled and loaded, it don't want that to happen.
I don't need to call methods on these modules by filename, the module-name would be ok too. But it does have to by dynamic, eg. entering "Book" at the command line should, after a check whether the module exists, call the function Book.helloWorld.
Thanks.
Well, thats where asking helps: You'll figure it out yourself the minute you ask. ;)
Just using apply(String.to_existing_atom("Elixir.Module"), :helloWorld, []) now. (maybe the name "Module" isn't allowed, don't know)
Note that you always need to prefix your modules with "Elixir."
defmodule Test do
def test(text) do
IO.puts("#{text}")
end
end
apply(String.to_existing_atom("Elixir.Test"), :test, ["test"])
prints "test"
and returns {:ok}
Here is a simple explanation:
Assuming that you have a module like this:
defmodule MyNamespace.Printer do
def print_hello(name) do
IO.puts("Hello, #{name}!")
end
end
Then you have a string that hold the module name that you can pass around in your application like this:
module_name = "Elixir.MyNamespace.Printer"
Whenever you receive the string module_name, you can instantiate a module and call functions on the module like this:
module = String.to_existing_atom(module_name)
module.print_hello("John")
It will print:
Hello, John!
Another way to dynamically call the function MyNamespace.Printer.print_hello/1 is:
print_hello_func = &module.print_hello/1
print_hello_func.("Jane")
It will print:
Hello, Jane!
Or if you want to have both module_name as atom and function_name as atom, and pass them around to be executed somewhere, you can write something like this:
a_module_name = :"Elixir.MyNamespace.Printer"
a_function_name = :print_hello
Then whenever you have a_module_name and a_function_name as atoms, you can call like this:
apply(a_module_name, a_function_name, ["Jackie"])
It will print
Hello, Jackie!
Of course you can pass module_name and function_name as strings and convert them to atoms later. Or you can pass module name as atom and function as a reference to function.
Also note that the name of a module is an atom so doing String.to_existing_atom isn't usually needed. Consider this code:
defmodule T do
def first([]), do: nil
def first([h|t]), do: h
end
In this case you can simply do the apply in this fashion:
apply(T,:first,[[1,2,3]])
#=> 1
Or this example (List below is the Elixir List module):
apply(List,:first,[[1,2,3]])
#=> 1
I mean to say that if you know the name of the module, it's not necessary to pass it as a string and then convert the string to an existing atom. Simply use the name without quotation marks.
Related
I'm trying to get the funcList visual studio code extension plugin to work with a proprietary language that I use and I'm having a problem getting the regex to work. The plugin documentation can be found here: https://marketplace.visualstudio.com/items?itemName=qrti.funclist and describes creating a settings.json file inside the .vscode folder of your project. My problems arise when trying to modify the regex expressions for the funcList.nativeFilter and funcList.displayFilter configuration values. Here is my current settings.json file:
{
"funcList.nativeFilter": "/(?:^|\\s)Function\\s+\\w+\\(/mg",
"funcList.displayFilter": "/\\s*Function\\s+(.*)/1",
"funcList.sortList": 1,
"funcList.doubleSpacing": false
}
I believe the main problem is the part of \\w+ in the nativeFilter property. This seems to only match on characters/numbers but not any special characters. Here is a snippet of a piece of code that I would like to work with this Function List:
Function Do.Something(paramOne, paramTwo)
'...
End Function
Method Do.Something_Else(paramOne, paramTwo)
'...
End Function
Ideally, the nativeFilter would capture Function Do.Something(paramOne, paramTwo) .. until End Function and then the displayFilter would only capture the first line (ie. Do.Something(paramOne, paramTwo))
Note: according to the docs, nativeFilter does not allow regex groups, but displayFilter allows groups 0-9.
I use this for javascript and the funcList extension. It is similar to what you are trying to do:
// so that "function someName(arg1, arg2) is captured
"funcList.nativeFilter": "/^[a-z]+\\s+\\w+\\s*\\(.*\\)/mgi",
// now display "someName(arg1, arg2)"
"funcList.displayFilter": "/\\S* +(\\w+\\s*\\(.*\\))/1",
You obviously have capital letters, periods and underscores to worry about but hopefully this helps you to some degree. [I see I did not need to actually put the function keyword in to make it work...]
[EDIT] Try this, it seems to work:
"funcList.nativeFilter": "/^Function \\w+.\\w+\\(.*\\)?/mg",
"funcList.displayFilter": "/\\S* +(\\w+.\\w+\\(.*\\))/1",
You just needed the . as it is not included in \w
This seems to be working for my needs:
{
"funcList.nativeFilter": "/^(Function|Method|Macro)\\s+[a-zA-Z0-9.+(){}\\/\\\\[\\],_\\-=:;!##$%^&*|,.<>? ]*\\).*$/mg",
"funcList.displayFilter": "/\\s*((Function|Method|Macro)\\s+[a-zA-Z0-9.+(){}\\/\\\\[\\],_\\-=:;!##$%^&*|,.<>? ]+)/1"
}
With this plugin, you need to capture the entire line in order to get the navigation to work when clicking on a function list.
I'd like to reuse a function that retrieves some info from the database. I am able to add it in the Helper class that Codeception builds and it works, but it feels like I may not be putting this function in the proper place because Codeception "reads" it out in the list of steps.
I want to have something like
$price = $I->getProductPriceFromDatabase($productIdentifier,$arg2,$arg3);
in the steps. This works but I end up with
I get product price from db 13,4,19,9,1
in the output. Is there a better way to set up this helper function so that it is not part of the "output"?
All methods of $I are steps and so they are printed in the steps output.
The only way to hide helper function is to call it from another helper function which looks better in the steps output.
I have a situation like below:
Module Task
def get(a)
fetch(a)
end
def fetch(a)
query(a)
end
def query(a)
puts a
end
end
and only get method is called from outside of module like
Task.get('name')
I want to monkey patch only method query to make some change in response of get method since it calls query intern.
Please suggest a way to do this.
In order to monkey patch in cases like these, we need to include a file in lib folder. In this case you need to make a file inside lib folder of the same name. In that first include the module TASK and then use MODULENAME.module_eval and add the methods in this. In this file u can override the methods in the actual module and add methods to it too.
In order for this to work you will have to require the file u created in lib in config/initializers/app.rb
Incase the module you are over riding is present inside a folder (like in case of a ruby gem) you need to include the whole path. For ex.
Module_1.Module_2.module_eval
where module 2 is inside module 1.
I am trying to create a set of custom tags for some liquid templates using Rails 3. I added a 'liquid_tags.rb' in my lib/ directory with content like this:
class UserControls < Liquid::Tag
def initialize(tag_name)
super
end
def render(context)
tag = "<b>TAG</b>"
end
end
Liquid::Template.register_tag('user_controls', UserControls)
When I try to get the tag in my view via '{% user_controls %}' it tells me the tag isn't found.
Any ideas?
Thanks in advance.
That's right, as marcusmateus says, Rails won't load anything in the lib directory automatically even if you have added it to the autoload_paths unless the class or module name inside the file matches the file name.
To sort this problem just put the custom formatters inside the lib directory, each in their own file (I tried using a module to wrap them all up but no luck)
class MyCustomTag < Liquid::Tag
def initialize(tag_name, params, tokens)
# do something
end
def render(context)
# do something
end
end
Then created an initializer (in config/initializers) responsible for registering the custom tags with Liquid. i.e.
Liquid::Template.register_tag('custom_tag', MyCustomTag)
Liquid::Template.register_tag('custom_tag', MyCustomTag2EtcEtc)
Are you sure that file is getting loaded? If it's not, then register_tag is never getting called. I would throw in a puts statement above register_tag to debug it, make sure that the file is actually being loaded. You may to move the register_tag into an initializer
on config/application.rb try adding this line
config.autoload_paths << File.join(config.root, "lib")
I believe files are only autoloaded if the name of the file matches the name of the class it contains. In the question you state that your file is named 'liquid_tags.rb', but your class is named UserControls... if you rename your filed 'user_controls.rb' it should begin autoloading.
I think it's not loading problem -- I have it also. The tag is being loaded, you can print the current registered tags:
Liquid::Template.tags.inspect
Say I want to make a module for say a set of GUI controls, how would I create a module that would load all of the GUI scripts, and should I put those scripts as modules themselves? I was thinking of having a system like this:
module("bgui", package.seeall)
dofile("modules/bgui/control.lua")
dofile("modules/bgui/container.lua")
dofile("modules/bgui/screenmanager.lua")
dofile("modules/bgui/form.lua")
dofile("modules/bgui/button.lua")
dofile("modules/bgui/textbox.lua")
dofile("modules/bgui/label.lua")
Would all the files run then have the variables they set as part of the bgui module?
Aka if in control.lua I had control = {...} would it be defined as bgui.control or should I make the control.lua a module itself, something like module("bgui.control") would that work as I intend?
Sorry if this isn't very clear had to write it in a rush, thanks :)
You are actually asking two questions here:
First, "is this way of loading lots of files on a module ok?"
The answer is - yes. It is kind of an unspoken standard to call that file mymodule/init.lua. Most people have ?/init.lua included on their path, so you can just write require('modules/bgui') and init.lua will be loaded automatically.
This said, you might want to remove some code duplication by using a temp table and a loop:
# modules/bgui/init.lua
local files = {
'control', 'container', 'screenmanager', 'form', 'button', 'textbox', 'label'
}
for _,file in ipairs(files) do dofile("modules/bgui/" .. file .. ".lua") end
Second, "are objects defined on one file available on bgui?".
The answer is also yes, as long as the file defining the variable is "done" (with dofile or require) before the file using the variable.