How to reflect dynamic method name of an object in Golang - reflection

example
router.Get(path, handler) // works fine
methodStr = "Get"
router.methodStr(path, handler) // error
funcs := map[string]func(){"methodStr": "Get"}
router.funcs["methodStr"](path, handler) // error
reflect.ValueOf(router).MethodByName("Get").Call([]reflect.Value{}) // error
I am getting method names as strings. How to call the router object methods with string names

The first two errors you have aren't valid Go, so I'm not sure what you would expect from them. The last example with reflect doesn't have any arguments for a function that requires 2, which will panic. Adding the 2 arguments works fine:
http://play.golang.org/p/mSziWdW0hn
args := []reflect.Value{
reflect.ValueOf("path"),
reflect.ValueOf("handler"),
}
reflect.ValueOf(router).MethodByName("Get").Call(args)

Related

How can I store different error interface implementations together and then use them for type comparison in Go?

I'm trying to write a table test in go where the test cases will result in different errors. I then want to check if the type of the error matches a an error type defined in the test case, using errors.As(). Each test case is defined by a struct, so there needs to be a type in the struct that can hold any implementation of the interface error, which is then also to verify that the correct type was returned in the test.
I have tried defining the struct as follows
type testCase struct {
testInput string
expectedError error
}
I also have a number of custom errors that implement the error interface, lets say one is called myCustomError
I then declare a variable of that struct like this:
mTest := testCase{
testInput: "some failing string",
expectedError: myCustomError{},
}
if I then do the test like this...
err := someFunc(mTest.testInput)
if errors.As(err, &mTest.expectedError) {
// test have succeeded
}
... the if statement will always return true, regardless of which of my custom error types is returned.
I made a minimal example if this behavior on the Go Playground here: https://play.golang.org/p/uMdbMvfcdQi
In the playground example, I expect the string "matching myError1" to be printed twice, but instead it also matches myError2 when the value is stored as a plain error before it is used to check the type of the variable err.
Is is even possible to do something like this?
Store a pointer to the target value in the test case.
type testCase struct {
testInput string
expectedError interface{}
}
mTest := testCase{
testInput: "some failing string",
expectedError: &myCustomError{},
}
err := someFunc(mTest.testInput)
if errors.As(err, mTest.expectedError) {
// test have succeeded
}
Minimal example: https://play.golang.org/p/igJy9L_ui73

Golang type assertion for list of items

Im calling a API and it returns a dictionary(map) with a list of items as values.
For ex:-
result= {'outputs':[{'state':'md','country':'us'}, {'state':'ny','country':'ny'}]}
The above data is how the data represented in python.
In Python, I directly use result['outputs'][0] to access the list of elements in the list.
In Golang, the same API returns the data but when I try to access the data as result['outputs'][0]
Get this error:-
invalid operation: result["outputs"][0] (type interface {} does not support indexing)
Looks like I need to do a type conversion, what should I use to type convert,
I tried this
result["outputs"][0].(List)
result["outputs"][0].([])
but both throws me an error.
I checked the type of the returned item and this is it - []interface {}
What should be my type conversion?
You wrote the type of the value is []interface{}, so then do a type assertion asserting that type.
Also note that you first have to type assert, and index later, e.g.:
outputs := result["outputs"].([]interface{})
firstOutput := outputs[0]
Also note that the (static) type of firstOutput will again be interface{}. To access its content, you will need another type assertion, most likely a map[string]interface{} or a map[interface{}]interface{}.
If you can, model your data with structs so you don't have to do this "type assertion nonsense".
Also note that there are 3rd party libs that support easy "navigation" inside dynamic objects such as yours. For one, there's github.com/icza/dyno (disclosure: I'm the author).
Using dyno, getting the first output would be like:
firstOutput, err := dyno.Get(result, "outputs", 0)
To get the country of the first output:
country, err := dyno.Get(result, "outputs", 0, "country")
You can also "reuse" previously looked up values, like this:
firstOutput, err := dyno.Get(result, "outputs", 0)
// check error
country, err := dyno.Get(firstOutput, "country")
// check error

Golang: How to append pointer to slice to slice?

I'm a Golang newbie but I thought I had got the essentials of pointers and references straight, but apparently not:
I have a method that must return a []github.Repository, which is a type from the Github client in go.
The API call returns the results paginated so I must cycle until there's no more results, and add the result of each call to the allRepos variable, and return that. Here's what I have so far:
func (s *inmemService) GetWatchedRepos(ctx context.Context, username string) ([]github.Repository, error) {
s.mtx.RLock()
defer s.mtx.RUnlock()
opt := &github.ListOptions{PerPage: 20}
var allRepos []github.Repository
for {
// repos is of type *[]github.Repository
repos, resp, err := s.ghClient.Activity.ListWatched(ctx, "", opt)
if err != nil {
return []github.Repository{}, err
}
// ERROR: Cannot use repos (type []*github.Repository) as type github.Repository
// but dereferencing it doesn't work, either
allRepos = append(allRepos, repos...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return allRepos, nil
}
My question: how can I append the results of each call and return a result of type []github.Repository?
Also, why doesn't dereferencing work here? I've tried replacing allRepos = append(allRepos, repos...) with allRepos = append(allRepos, *(repos)...) but I get this error message:
Invalid indirect of (repos) (type []*github.Repository)
Well, something is not okay here:
You say in the comment that "repos is of type *[]github.Repository" but the compiler's error message indicates that repos is of type []*Repository". The compiler is never (except when buggy) wrong.
Note that *[]github.Repository and []*Repository are completely different types, especially the second is not a slice of Repositories and you cannot (really, there is no way) dereference these pointers during append(): You have to write a loop and dereference each slice item and append one by one.
What is strange too: github.Repository and Repository seem to be two different types one from package github, the other from the current package. Again, you'll have to get that straight too.
Note that there are no references in Go. Stop thinking about these immediately: This is a concept from other languages which is not helpful (as inexistent) in Go.
In your example the dereferencing is not correct. You should make it like this:
allRepos = append(allRepos, *repos...)
Here a simple example with dereferencing a pointer to a slice of string. https://play.golang.org/p/UDzaG5z8Pf

PHP7. Reflection doesn't work from old versions php

I have a php script which was written on php 5.6.19, works on 5.3 version to, with some installed addons.
I decide to try execute it on php7.
The special of the script that I am initializing a class with parameter by reference via creating a new instance with Reflection::class. And there warning then waited variable by reference but value received.
Definition of the class' constructor method tried to create an instance from:
public function __construct($user, IDatabase &$repository, $errors = null);
Sample of code where this constructor is used:
// define manager type to create (all managers has the same constructor)
$manager = $managersNamespace . ucfirst($this->_manager) . "Manager";
// trying to create the manager
// !!!And here a Warning occurs
$reflect = new \ReflectionClass($manager);
$manager = $reflect->newInstance($user, $database, $errors);
After these I am invoking a method I need, and here the fatal error with stopped the script:
$method = "show" . ucfirst($this->_page) . "Page";
$reflect->getMethod($method)->invoke($manager);
I didn't see any changes in documentation. Anyone had the same issue?
First and foremost, why are you passing an object by reference !?
Objects have pass-by-reference semantics, forcibly trying to pass objects by reference has not made good sense since PHP 4.
Just remove the & ...
Let's ignore that, and pretend there is still a problem, so that you can try to understand what is going on.
To break down the problem, first you need to understand the distinction between a variable and an expression:
mine(1 + 2);
The argument to mine has no name, it's represented by a temporary variable in the engine: it's an expression.
mine(1);
The argument to mine has no name, it's not an expression, but a literal constant, represented by a compiler variable in the engine. It's similar to a temporary variable, a kind of constant expression.
mine($a);
The argument to mine has a name, which you can use to refer to it's value. It's a normal variable.
Only variables can be passed by reference because you cannot refer to expressions or literal constants
Next you need to understand why we pass-by-reference:
function mine(int $thing) {
$thing++;
}
$a = 1;
mine($a);
var_dump($a); // int(1)
In this code, $a is passed to mine() by value, so that the changes that mine() make to $thing are only visible inside the scope of mine. $a is unchanged after the call to mine() returns because $a and $thing are distinct, having been passed-by-value, which means it's value was copied on to the call stack for the invocation of mine().
function mine(int &$thing) {
$thing++;
}
$a = 1;
mine($a);
var_dump($a); // int(2)
In the code above, $a is passed to mine() by reference, this means that $a and $thing are no longer distinct. The changes mine() make to $thing are now visible after the call to mine() returns.
The last piece in the puzzle is Reflection:
function mine(int &$thing) {
$thing++;
}
$a = 1;
$reflector = new ReflectionFunction("mine");
$reflector->invoke($a);
The code above will raise:
Warning: Parameter 1 to mine() expected to be a reference, value given in /usr/src/php-src/refs.php on line 9
This is because ReflectionFunction::invoke and similar reflection functions (ReflectionClass::newInstance) accept their parameters by value and pass them onto the invoked function by value.
But ...
There is still a difference between pass-by-reference semantics, and passing by reference, a dangerous one:
class Foo {
public function qux() {}
}
class Bar {}
function mine(Foo &$foo) {
$foo = new Bar();
}
$foo = new Foo;
mine($foo);
$foo->qux();
Will obviously yield:
PHP Fatal error: Uncaught Error: Call to undefined method Bar::qux() in /usr/src/php-src/refs.php:16
Stack trace:
#0 {main}
thrown in /usr/src/php-src/refs.php on line 16
The declaration of mine() tells lies about the type safety of it's parameter. Type safety is only guaranteed upon entry to the function, the function body is free to break type safety, but it doesn't usually affect the caller when relying on the engines pass by reference semantics for objects.
This is an extremely scary kind of API, that should be avoided.

ocmock's invokeBlockWithArgs using a nil argument

is there any way to invoke a block with nil as a given argument, given that the invokeBlockWithArgs: requires the args to be nil-terminated?
example method definition in a mocked object:
- (void)methodWithCompletion:(void(^)(NSString*, NSError* )) completionBlock;
The given mockObject should call:
completionBlock(#"foo", nil);
however, with invokeBlockWithArgs:
OCMStub([mockObj methodWithCompletion:([OCMArg invokeBlockWithArgs:#"foo", nil, nil])]);
Method fails, with too few arguments; obviously with nil being the termination, it doesn't recognize the second parameter to the block should be nil.
I haven't tested it but theoretically passing [NSNull null] should work.
Adding to the existing answers here, passing [NSNull null] does what you want in this case, which is passing nil as the param there.
I had a case (shown below) where my logic tested the existence of an error object OR my array was empty, and wanted my test to cover both cases and was afraid I'd only be able to test one case
if (error || array.count == 0) {
// fail here
}
Here is my test OCMock code:
NSArray *emptyArray = #[];
OCMStub([requestMock loadListWithCompletion:([OCMArg invokeBlockWithArgs:emptyArray, [NSNull null], nil])]);
...and in the actual invocation of that method, the error param (that I passed [NSNull null] into) was indeed nil, so the logic fell through to the empty array and the error case was still handled.
You can pass [NSNull null]. I just tested, it works.

Resources