[byte-buddy]Why only last interceptor works - byte-buddy

There is a method testFooin my code which looks like this:
newClassBuilder = newClassBuilder.method(ElementMatchers.nameStartsWith("test"))
.intercept(MethodDelegation.to(new InstMethodsInter(new MethodInterceptor1())));
newClassBuilder = newClassBuilder.method(ElementMatchers.named("testFoo"))
.intercept(MethodDelegation.to(new InstMethodsInter(new MethodInterceptor2())));
newClassBuilder = newClassBuilder.method(ElementMatchers.nameEndsWith("Foo"))
.intercept(MethodDelegation.to(new InstMethodsInter(new MethodInterceptor3())));
only MethodInterceptor3 works.
I have lots of interceptors.
What do I need to do?

You have to add a most-specific matcher yourself and specify the interceptors yourself there:
newClassBuilder.method(ElementMatchers.nameStartsWith("test").and(ElementMatchers.named("testFoo"))
.intercept(MethodDelegation.to(new InstMethodsInter(new MethodInterceptor1()).andThen(MethodDelegation.to(InstMethodsInter(new MethodInterceptor2()))));
This is necessary as the later matcher override any previous matcher that also matches a method. The most specific matcher needs to be applied last and interceptors are not stacked.
However, it seems like you should refactor your interception logic here to make this unnecessary.

Related

How to add comment and spent time in one call to Gitlab API?

I have two separate calls
addSpentTime(projectId, issueIid, duration, date) {
const endpoint = this.apiUrl(
`/projects/${projectId}/issues/${issueIid}/notes?body=/spend ${duration} ${date}`
);
return this.ajaxRequest("POST", endpoint);
}
addComment(projectId, issueIid, comment) {
const endpoint = this.apiUrl(
`/projects/${projectId}/issues/${issueIid}/notes?body=${comment}`
);
return this.ajaxRequest("POST", endpoint);
}
The first one is for sending how much time has been spent on an issue and the latter is for adding a comment to the issue.
I'd like to do both of these actions in one call like this
addSpentTimeAndComment(projectId, issueIid, duration, date, comment) {
const endpoint = this.apiUrl(
`/projects/${projectId}/issues/${issueIid}/notes?body=${comment}/spend ${duration} ${date}`
);
return this.ajaxRequest("POST", endpoint);
}
but the suffix /spend ${duration} ${date} makes it into the comment as well. It seems that the API takes literally everything that comes after body= as a part of the comment. I tried to add \n, \\n, spaces, backslashes to signify the end of the comment so that the /spend part gets evaluated as well and I also tried changing the order so that the /spend is before the comment body but nothing seemed to work. The GitLab API docs don't mention any standard way of ending the comment or maybe they do but I can't find it anywhere. Furthermore, I don't even know what it's called - delimiter, escaping symbol...?
Is there a way of signifying the ending of the comment, giving a way of appending other commands?
As part of the comment (project note) call, append the time as a Quick Action using the /spend <time> call.
By posting that to the API with the comment, GitLab will add the time at the same time it adds the comment.

Karate Netty - CallSingle but not so single

What I had till today:
I have get_jwt.feature and I call it as a part of karate-config.js. Since I have used one account test#test.com I needed only one jwt and I can reuse it across scenarios. callSingle worked as a charm in this case.
Today:
Suddenly I have need for jwt's from two accounts which I dont want to generate for each scenario, callSingle falls short of this task as it does exactly what its supposed to be doing. Now I have hacky idea, I can simply make two files, get_jwt.feature and get_jwt_user2.feature, and single call them each.
So my question: Is there a better way of doing this?
You can use "2 levels" of calls. So point the callSingle() to a JS function that calls get_jwt.feature 2 times, maybe with different arguments and then return a JSON. Pseudo-code below. First is get_jwts.js:
function fn(users) {
var jwt1 = karate.call('get_jwt.feature', users.user1);
var jwt2 = karate.call('get_jwt.feature', users.user2);
return { jwt1: jwt1, jwt2: jwt2 };
};
Then in karate-config.js
config.jwts = karate.callSingle('classpath:get_jwts.js', users);
And now you should be able to do this:
* print jwts.jwt1
* print jwts.jwt2
You can also do a feature-->feature call chain. Do let me know if this works !
EDIT: see Babu's answer in the comments, looks like you can pass an array to callSingle() ! so that may be quite convenient :)

Cucumber Ordered Tagged Hooks

I am trying to use an ordered, tagged hook using Java cucumber. For example:
#Before("#quicklink", order = 20)
The compiler doesn't seem to like it. Is it not possible to have an ordered, tagged hook ? Seems like a reasonable combination of functionality. If so, what is the syntax ?
thnx
I have tried the same but in a different way
#Before(value = "#quicklink", order = 20)
But, this may create odd issues if you have another hook method with the same order number for other tests. Like both the methods will run for this scenario.
So I suggest using the tagged expressions if you are using same order like as follows:
For other methods use
#Before(value = "~#quicklink", order = 20)
this will make sure this scenario will never run on other methods
and for this scenario alone,
#Before(value = "#quicklink", order = 20)
this will make sure the above method will never run for those.
If you are using 2x version of tagged expressions in your project, you can replace the '~' with a 'not'
This might come handy if you want to replace a method in hooks class in just a specific scenario.
#Before(value = "#quicklink", order = 20)
You should be able to specify the order for hooks like this:
Annotated method style (if you are using cucumber-java):
#Before(order = 10)
public void doSomething(){
// Do something before each scenario
}
Lambda style (if you are using cucumber-java8):
Before(10, () -> {
// Do something before each scenario
});

How can I dry this Rails Controller Action further

Our application uses a number of environments so we can experiment with settings without breaking things. In a typical controller action, I have something like this:
def some_action
...
if #foo.development_mode == 'Production'
#settings = SomeHelper::Production.lan(bar)
elsif #foo.development_mode == 'Beta'
#settings = SomeHelper::Beta.lan(nas)
elsif #foo.development_mode == 'Experimental'
#settings = SomeHelper::Experimental.lan(nas)
end
...
end
Since we have dozens of these, I figured I could try and dry things up with something like this:
#settings = "SomeHelper::#{#foo.development_mode}.lan(bar)"
Which obviously doesn't work - I just get:
"NasHelper::Production.lan(bar)"
How can I reduce this down or do I have to stick with what I've got??
If your concern is that you're ending up with a String rather than the object, you can use String.constantize (Rails only, with standard Ruby you'd have to implement this; it uses Object.const_get(String))
Another option would be .const_get (e.g. Object.const_get(x) where x is your string), you it doesn't, on its own, nest correctly, so you would have to split at "::", etc.
Also, there's the option of using eval to evaluate the String.
But note: eval should be used with great care (it's powerful), or not at all.
Edit:
This means that instead of:
#settings = "SomeHelper::#{#foo.development_mode}.lan(bar)"
You could run:
#settings = "SomeHelper::#{#foo.development_mode}".constantize.lan(bar)
Useful Sources:
http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
http://www.ruby-forum.com/topic/183112
http://blog.grayproductions.net/articles/eval_isnt_quite_pure_evil
In the first case, #settings receives the result of the method SomeHelper::Production.lan(bar); in the second, #settings just gets a string. You could use the send method of Object linked here to fire the method, or eval, but this wouldn't be my first choice.
It looks like you might be reinventing a wheel -- Rails already has the concept of "environments" pretty well wired into everything -- they are defined in app/config/environments. You set the environment when you launch the server, and can test like Rails.env.production?. To create new environments, just copy the existing environment file of the one closest to the new one, e.g. copy production.rb to beta.rb and edit as necessary, then test Rails.env.beta?, for example.
But this still leaves you testing which one all over the place. You can add to the config hash (e.g. config.some_helper.lan = 'bar'), which value you can assign to #settings directly. You have to make sure there's either a default or it's defined in all environments, but I think this is probably the right approach ... not knowing exactly what you aim to accomplish.

Testing dynamic attributes with cucumber, undefined method

We have a Style model with dynamic attributes, which can be saved by filling one field with the attribute key and the next field with the value.
A typical params hash looks like this:
{"utf8"=>"✓", "style"=>{"collection_id"=>"48", "program_id"=>"989", "number"=>"454632", "name"=>"t67f", "category_id"=>"19", "field_KEY"=>"VALUE"}, "commit"=>"save", "id"=>"4521"}
This works as intended when clicking it through, and the "field_KEY" => "VALUE" pair creates a new dynamic attribute with a getter(field_KEY) and setter(field_KEY=) method.
The Problem is: If the process is simulated with cucumber, something calls the getters for all keys in the hash before the attributes are set, including field_KEY.
Normal attributes will return nil for a new record, but since the getter for field_KEY has not yet been created, this results in an
`UndefinedMethodError: undefined method 'field_KEY'`.
Now my question: would you rather track down the caller of the field_KEY getter and mess around with cucumber, or should I try to simulate a fake method, something like:
def check_method(method_name)
if method_name =~ /^field_/
nil
else
... # let the Error be raised
end
Better ideas or solutions are more than welcome
Thanks
The Problem was:
The call to field_KEY came from pickle, because I included the step
And the style's "field_KEY" should be "VALUE"
which looks like this:
Then(/^#{capture_model}'s (\w+) (should(?: not)?) be #{capture_value}$/) do |name, attribute, expectation, expected|
actual_value = model(name).send(attribute)
expectation = expectation.gsub(' ', '_')
case expected
when 'nil', 'true', 'false'
actual_value.send(expectation, send("be_#{expected}"))
when /^[+-]?[0-9_]+(\.\d+)?$/
actual_value.send(expectation, eql(expected.to_f))
else
actual_value.to_s.send(expectation, eql(eval(expected)))
end
end
I still don't know why the dynamic_attribute getter had not been created up to this point.
What I ended up doing:
In my opinion (also, it solved the problem ;)), cucumber tests should be black-box tests, thats why I chose to change the steps and now I use
And the "key1" field should contain "KEY"
which checks if the field has been filled with the correct value after the page reloads.