After upgrading to Ruby 3, unable to pass multiple arguments to my "initialize" method - initialization

I recently upgraded to Rails 6 with Ruby 3. I have this in a controller
my_object = MyObject.new(my_object_params, #header)
The object “initialize” method is defined like so
def initialize(params, header)
super(params)
user&.header = header
end
But now when attempting to initialize the object, I get the error
ArgumentError:
wrong number of arguments (given 2, expected 0..1)
What’s the proper way in Ruby 3 to pass multiple arguments to an object during initialization?

You need to do this way:
def initialize(params, header)
super(**params) #Note that you need to splat out the params!
user&.header = header
end

Related

Julia scoping issue when creating function from string

I would like to build a Julia application where a user can specify a function using a configuration file (and therefore as a string). The configuration file then needs to be parsed before the function is evaluated in the program.
The problem is that while the function name is known locally, it is not known in the module containing the parser. One solution I have come up with is to pass the local eval function to the parsing function but that does not seem very elegant.
I have tried to come up with a minimal working example here, where instead of parsing a configuration file, the function name is already contained in a string:
module MyFuns
function myfun(a)
return a+2
end
end
module MyUtil
# in a real application, parseconfig would parse the configuration file to extract funstr
function parseconfig(funstr)
return eval(Meta.parse(funstr))
end
function parseconfig(funstr, myeval)
return myeval(Meta.parse(funstr))
end
end
# test 1 -- succeeds
f1 = MyFuns.myfun
println("test1: $(f1(1))")
# test 2 -- succeeds
f2 = MyUtil.parseconfig("MyFuns.myfun", eval)
println("test2: $(f2(1))")
# test 3 -- fails
f3 = MyUtil.parseconfig("MyFuns.myfun")
println("test3: $(f3(1))")
The output is:
test1: 3
test2: 3
ERROR: LoadError: UndefVarError: MyFuns not defined
So, the second approach works but is there a better way to achieve the goal?
Meta.parse() will transform your string to an AST. What MyFuns.myfun refers to depends on the scope provided by the eval() you use.
The issue with your example is that the eval() inside MyUtil will evaluate in the context of that module. If that is the desired behavior, you simply miss using MyFuns inside MyUtil.
But what you really want to do is write a macro. This allows the code to be included when parsing your program, before running it. The macro will have access to a special argument __module__, which is the context where the macro is used. So __module__.eval() will execute an expression in that very scope.
foo = "outside"
module MyMod
foo = "inside"
macro eval(string)
expr = Meta.parse(string)
__module__.eval(expr)
end
end
MyMod.#eval "foo"
# Output is "outside"
See also this explanation on macros:
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#man-macros-1
And for the sake of transforming the answer of #MauricevanLeeuwen into the framework of my question, this code will work:
module MyFuns
function myfun(a)
return a+2
end
end
module MyUtil
macro parseconfig(funstr)
__module__.eval(Meta.parse(funstr))
end
end
f4 = MyUtil.#parseconfig "MyFuns.myfun"
println("test4: $(f4(1))")

Parameter file in ArgParse.jl?

Python's argparse has a simple way to read parameters from a file:
https://docs.python.org/2/library/argparse.html#fromfile-prefix-chars
Instead of passing your arguments one by one:
python script.py --arg1 val1 --arg2 val2 ...
You can say:
python script.py #args.txt
and then the arguments are read from args.txt.
Is there a way to do this in ArgParse.jl?
P.S.: If there is no "default" way of doing this, maybe I can do it by hand, by calling parse_args on a list of arguments read from a file. I know how to do this in a dirty way, but it gets messy if I want to replicate the behavior of argparse in Python, where I can pass multiple files with #, as well as arguments in the command line, and then the value of a parameter is simply the last value passed to this parameter. What's the best way of doing this?
This feature is not currently present in ArgParse.jl, although it would not be difficult to add. I have prepared a pull request.
In the interim, the following code suffices for what you need:
# faithful reproduction of Python 3.5.1 argparse.py
# partial copyright Python Software Foundation
function read_args_from_files(arg_strings, prefixes)
new_arg_strings = AbstractString[]
for arg_string in arg_strings
if isempty(arg_string) || arg_string[1] ∉ prefixes
# for regular arguments, just add them back into the list
push!(new_arg_strings, arg_string)
else
# replace arguments referencing files with the file content
open(arg_string[2:end]) do args_file
arg_strings = AbstractString[]
for arg_line in readlines(args_file)
push!(arg_strings, rstrip(arg_line, '\n'))
end
arg_strings = read_args_from_files(arg_strings, prefixes)
append!(new_arg_strings, arg_strings)
end
end
end
# return the modified argument list
return new_arg_strings
end
# preprocess args, then parse as usual
ARGS = read_args_from_files(ARGS, ['#'])
args = parse_args(ARGS, s)

Why can't I call the methods method on a Perl 6's ClassHOW object?

I can call ^methods on an object and list the method names I can call:
my $object = 'Camelia';
my #object_methods = $object.^methods;
#object_methods.map( { .gist } ).sort.join("\n").say;
^methods returns a list which I store in #object_methods, then later I transform that list of method thingys by calling gist on each one to get the human-sensible form of that method thingy.
But, the ^ in ^methods is an implied .HOW, as show at the end of the object documentation this should work too:
my $object = 'Camelia';
my #object_methods = $object.HOW.methods;
But, I get an error:
Too few positionals passed; expected 2 arguments but got 1
in any methods at gen/moar/m-Metamodel.nqp line 490
in block <unit> at...
And, for what it's worth, this is an awful error message for a language that's trying to be person-friendly about that sort of thing. The file m-Metamodel.nqp isn't part of my perl6 installation. It's not even something I can google because, as the path suggests, it's something that a compilation generates. And, that compilation depends on the version.
A regular method call via . passes the invocant as implicit first argument to the method. A meta-method call via .^ passes two arguments: the meta-object as invocant, and the instance as first positional argument.
For example
$obj.^can('sqrt')
is syntactic sugar for
$obj.HOW.can($obj, 'sqrt')
In your example, this would read
my #object_methods = $object.HOW.methods($object);

Method error with multiple definition of the same submodule

Imagine that I have the following module in a file myMod.jl:
module myMod
type T
i::Int
end
function sum(T1::T, T2::T)
T1.i + T2.i
end
end
Now, I want to define two modules elsewhere that are able to use the type T defined in myMod, so let me write the following code:
# --- Define first module
module A
include("myMod.jl")
type Ta
a::myMod.T
end
end
# --- Define second module
module B
include("myMod.jl")
type Tb
a::myMod.T
end
end
# --- Use the modules
using A, B
v1 = A.Ta(A.myMod.T(1))
v2 = B.Tb(B.myMod.T(2))
So far so good, everything works fine. I can even call the sum method with
A.myMod.sum(v1.a, v1.a) # Returns 2, as expected
However, I can't sum v1.a and v2.a, despite typeof(v1.a) and typeof(v2.a) are both T:
A.myMod.sum(v1.a, v2.a)
Error evaluating .../myTest.jl: sum has no method matching sum(::T,
::T)
I have the feeling that though v2.a is of type T, Julia remembers that is has been created inside module B and thus doesn't recognize it as a T object.
Is there a workaround ? Or a more elegant/julianistic way of making the sum function work with both v1.a and v2.a ?
The include function is basically the same as just pasting the code from myMod.jl into the location where include was called. The way you're using it, it's as though you were defining two different modules, A.myMod and B.myMod, which happen to have the exact same implementation. Although the two modules have identical implementations, they do not share the same identity. Therefore A.myMod.T is not the same type as B.myMod.T.
What you want to do is define the myMod module just once, then use it when defining Ta and Tb. You can do so using using as shown below. I've assumed that myMod is still defined in the file myMod.jl, but you could just define them all in the same file if you wanted to.
include("myMod.jl")
# --- Define first module
module A
using myMod
type Ta
a::myMod.T
end
end
# --- Define second module
module B
using myMod
type Tb
a::myMod.T
end
end
# --- Use the modules
using A, B, myMod
v1 = A.Ta(myMod.T(1))
v2 = B.Tb(myMod.T(2))
myMod.sum(v1.a, v2.a)

PyQt Unbound Method When Passing Argument Between Signal and Slot

I am working on a project in which I need to download and parse an XML file from a network location. I've been trying to use the QtNetwork module to accomplish this, but I'm running into a seemingly simple problem that I've spent many hours trying to solve. My code is as follows:
class NetworkAccessor(QObject):
done = False
def importXml(self):
self.manager = QNetworkAccessManager()
self.manager.finished.connect(self.fileReady(QNetworkReply))
self.manager.get(QNetworkRequest(QUrl("http://192.168.5.243/details.xml")))
def fileReady(self, response):
self.f = QTemporaryFile()
print type(response)
self.f.write(response.readAll())
self.done = True
print "Done"
When I instantiate the NetworkAccessor class and call importXml, I get the following error:
Traceback (most recent call last):
File "C:/SVN-Local/Thermal/PyQtTestTemplate.py", line 40, in updateUi
f = networkAccessor.importXml()
File "C:/SVN-Local/Thermal/PyQtTestTemplate.py", line 14, in importXml
self.connect(self.manager,SIGNAL("finished(QNetworkReply*)"),self.fileReady(QNetworkReply))
File "C:/SVN-Local/Thermal/PyQtTestTemplate.py", line 20, in fileReady
self.f.write(response.readAll())
TypeError: QIODevice.readAll(): first argument of unbound method must have type 'QIODevice'
It seems to indicate that the argument passed to the fileReady method is not instantiated. Furthermore, the print type(response) statement above indicates that response is of type PyQt4.QtNetwork.QNetworkReply.
I've tried various ways of connecting the signal to the slot including the "old fashioned" way: self.connect(self.manager,SIGNAL("finished(QNetworkReply*)"),self.fileReady("QNetworkReply")) as well as using short-circuit parameter passing with no success.
Can somebody show me the proper way to pass an instantiated instance of a QNetworkReply object from a signal to a slot?
Thanks.
Just pass it the slot, the QNetworkReply object will get passed through by the signal:
self.manager.finished.connect(self.fileReady)

Resources