Getting unexpected `Cannot set the value of read-only property` when writing own plugin - gradle

I'm trying to write a DSL like the following:
toolSetups {
someTool {
argsFile = file('run/some_tool/xrun_files.f')
}
someOtherTool {
argsFile = file('run/some_other_tool/xrun_files.f')
}
}
I do this by adding an extension to the project, which is a container for ToolSetup entries:
extensions.add('toolSetups', project.container(ToolSetup))
I followed the examples in the user guide and declared the argsFile property in ToolSetup:
interface ToolSetup {
Property<File> getArgsFile()
}
I searched around and found that this only works if ToolSetup has a name property. I reworked ToolSetup to be:
abstract class ToolSetup {
private final String name
public ToolSetup(String name) {
this.name = name
}
String getName() {
return name
}
abstract Property<File> getArgsFile()
}
I'm getting the following:
Cannot set the value of read-only property 'argsFile' for object of type ToolSetup$Inject.
I also tried explicitly calling argsFile.set(file(...)), but this gives me the same error. I also tried to explicitly create Property instances using ObjectFactory, but still got the same.
I already wrote a plugin that does something similar, where it works, but that plugin is written in Java. I'm guessing there's some trivial mistake here related to Groovy and letting Gradle manage the implementation for me, which I'm not seeing. I'd be very grateful if someone could point it out.
Here's the entire build.gradle file that reproduces this issue:
abstract class ToolSetup {
private final String name
public ToolSetup(String name) {
this.name = name
}
String getName() {
return name
}
abstract Property<File> getArgsFile()
}
extensions.add('toolSetups', project.container(ToolSetup))
toolSetups {
someTool {
argsFile = file('run/some_tool/xrun_files.f')
}
someOtherTool {
argsFile = file('run/some_other_tool/xrun_files.f')
}
}

I was able to get the below to work
buildSrc/src/main/java/com/sample/Resource.java
package com.sample;
import org.gradle.api.provider.Property;
import java.net.URI;
public interface Resource {
// Type must have a read-only 'name' property
String getName();
Property<URI> getUri();
Property<String> getUserName();
}
build.gradle
extensions.add("download", project.objects.domainObjectContainer(com.sample.Resource.class))
download {
register('t1') {
uri = uri("http://abc")
userName = "t1u"
}
register('t2') {
uri = uri("https://abc")
userName = "t2u"
}
}
println "download info "
download.each { println String.format("{name: %s, uri: '%s', userName: %s", it.getName(), it.getUri().getOrNull(), it.getUserName().getOrNull()) }
the output produces
> Configure project :
download info
{name: t1, uri: 'http://abc', userName: t1u
{name: t2, uri: 'https://abc', userName: t2u
BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date

There seems to be a bug in Gradle w.r.t. the injected implementation for the toolSetups container: https://github.com/gradle/gradle/issues/23525
Using project.objects.domainObjectContainer() to create the container, instead of project.container() works.
To define the extension, we have to do:
extensions.add('toolSetups', objects.domainObjectContainer(ToolSetup))
Note, it's not necessary to give Gradle an abstract class for ToolSetup. It can also be supplied an interface, as long as that interface contains a name property. It will be able to inject a constructor by itself. The following is sufficient:
interface ToolSetup {
String getName()
Property<File> getArgsFile()
}
Alternatively, it's also possible to extend the Named interface:
interface ToolSetup extends Named {
Property<File> getArgsFile()
}

Related

How to inject a service with multiples implementations with Net6

Hello i am trying to inject dependency of service with multiples implementations (multiple classes that implement the IDBActionQuery interface). I have this code in my Program.cs
builder.Services.AddTransient<IDBActionQuery<Period>, Select<Period>>();
And i have this interface:
public interface IDBActionQuery<T>
{
Task<List<T>> execute(string query, params (string name, object val)[] pars);
Task<List<T>> buildAndExecute();
}
And this is my Select class:
public class Select<T> : DBMotorBase<T>, IDBActionQuery<T>
{
private readonly IPrintExceptions exceptions;
private readonly IGetPropertiesService propertiesService;
private readonly ISQLParametersService sqlParameterService;
private readonly ISerializeService serializeService;
private readonly IDeserealizeService<T> deserealizeService;
public Select(string _connection,
IPrintExceptions _exceptions, IGetPropertiesService _propertiesService,
ISQLParametersService _sqlParameterService, ISerializeService _serializeService,
IDeserealizeService<T> _deserealizeService) : base(_connection)
{
exceptions = _exceptions;
propertiesService = _propertiesService;
sqlParameterService = _sqlParameterService;
serializeService = _serializeService;
deserealizeService = _deserealizeService;
}
private string build(string schema = "dbo")
{
...
}
public async Task<List<T>> buildAndExecute()
{
return await execute(build(),null);
}
public async Task<List<T>> execute(string query, params (string name, object val)[] pars)
{
...
}
private async Task<List<T>> processCommand(SqlConnection sql,
string query, params (string name, object val)[] pars)
{
...
}
private async Task<List<T>> processReader(SqlCommand cmd)
{
....
}
}
However i am receiving this error:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType:
Why is that???
The constructor of Select<T> should only contain Classes/Interfaces that can be injected. To do that, they must be registered as services like you did with Select<T>.
This is why it's throwing the Some services are not able to be constructed. It can't resolve the services you specified in the constructor. It can't create a fitting instance.
Guessing from your code, the problem is "string _connection". Try injecting "string _connection" via the e.g. IConfiguration or IOptions pattern instead. (Of course the other constructor parameters need to be registered services too!)
Something like this:
appsettings.json:
"ConnectionOptions": {
"Connection": "..."
},
Program.cs:
builder.Services.Configure<ConnectionOptions>
(builder.Configuration.GetSection(nameof(ConnectionOptions)));
Select<T>:
public Select(IOptions<ConnectionOptions> options,
IPrintExceptions exceptions,
IGetPropertiesService propertiesService,
ISQLParametersService sqlParameterService,
ISerializeService serializeService,
IDeserealizeService<T> deserealizeService)
: base(options.Connection)
{
_exceptions = exceptions;
_propertiesService = propertiesService;
_sqlParameterService = sqlParameterService;
_serializeService = serializeService;
_deserealizeService = deserealizeService;
}
If the "string _connection" property is a regular db connection string, I'd use IConfiguration instead.
Check out this question and similar ones for some detailed explanations.

How do I create nested configuration parameters in a custom gradle plugin?

How do I create a nested parameter structure in a custom gradle plugin?
For starters, I am using Gradle 7.2. I want to make an expressive DSL-like structure for my plugin configuration, with a nested element
fileDiff {
file1 = file('${testFile1.getName()}')
file2 = file('${testFile2.getName()}')
messages {
message1 = 'Hi there'
}
}
While learning how to write Gradle Plugins I have been following the gradle plugin implementation docs, and they are great at showing what to do with the extension but not the "plugin" class.
So I have modeled my extension, FileDiffExtension like so
abstract class FileDiffExtension {
abstract RegularFileProperty getFile1()
abstract RegularFileProperty getFile2()
#Nested
abstract Messages getMessages()
void messages(Action<? super Messages> action) {
action.execute(getMessages())
}
}
And the nested Messages class is modeled as such
abstract class Messages {
abstract Property<String> getMessage1()
}
I think I am good up to this point. Then I need to pull my extension into my plugin and this is where I believe I am running into issues. My Plugin class currently looks like
class FileDiffPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.tasks.register('fileDiff', FileDiffTask) {
project.extensions.create('fileDiff', FileDiffExtension)
project.fileDiff.extensions.create("messages", FileDiffExtension.messages)
file1 = project.fileDiff.file1
file2 = project.fileDiff.file2
messages = project.fileDiff.getMessages
}
}
}
I am trying to create a messages extension off of the root level extension fileDiff. Or maybe I am not supposed to set the messages object in the task to the getMessages() abstract method. But I have tried every combination I can think of. The actual task is shown below, but I don't think the problem lies here.
abstract class FileDiffTask extends DefaultTask {
#InputFile
abstract RegularFileProperty getFile1()
#InputFile
abstract RegularFileProperty getFile2()
#Input
abstract Property<Messages> getMessages()
#OutputFile
abstract RegularFileProperty getResultFile()
FileDiffTask() {
resultFile.convention(project.layout.buildDirectory.file('diff-result.txt'))
}
#TaskAction
def diff() {
// Print out the message
println messages.get().message1.toString()
// Now we do some fun file stuff
String diffResult
if (size(file1) == size(file2)) {
diffResult = "Files have the same size at ${file1.get().asFile.getBytes()} bytes}"
} else {
File largestFile = size(file1) > size(file2) ? file1.get().asFile : file2.get().asFile
diffResult = "${largestFile.toString()} is the largest file at ${largestFile.size()} bytes"
}
resultFile.get().asFile.write diffResult
println "File written to $resultFile"
println diffResult
}
private static long size(RegularFileProperty regularFileProperty) {
return regularFileProperty.get().asFile.size()
}
}
To test I am using the gradle test kit, and I'm currently getting the following error.
`
Could not create task ':fileDiff'.
No such property: messages for class: com.robschwartz.plugins.filediff.FileDiffExtension
Possible solutions: messages
`
Hm, according to docs you do not need to do anything with the sub-extension if it's marked as #Nested, it should just work. Have you tried to remove this line entirely?
project.fileDiff.extensions.create("messages", FileDiffExtension.messages)

How to use #ConfigProperties with a Converter class

I tried to implement a custom config type and it worked. However, when I use the custom type with a group of config using the #ConfigProperties it fails to automatically recognize the property by its name and instead treats the property as an object with a nested property.
How can I implement such a behavior correctly? (I am new to Quarkus, so please correct me if I am doing something wrong here)
Here is a code snippet that converts a custom type:
public class Percentage {
private double percentage;
public Percentage() {}
public Percentage(double percentage) {
this.percentage = percentage;
}
public void setPercentage(double percentage) {
this.percentage = percentage;
}
public double getPercentage() {
return this.percentage;
}
}
#Priority(300)
public class PercentageConverter implements Converter<Percentage> {
#Override
public Percentage convert(String value) {
int percentIndex = value.indexOf("%");
return new Percentage(Double.parseDouble(value.substring(0, percentIndex - 1)));
}
}
/// this works ------
public class Hello {
#ConfigProperty(name = "custom.vat")
Percentage vat;
public Hello () {
}
// .....
}
/// however, this fails
#ConfigProperties(prefix = "custom")
public class CustomConfig {
public Percentage vat;
public Percentage profit;
}
javax.enterprise.inject.spi.DeploymentException: No config value of type [double] exists for: custom.vat.percentage
at io.quarkus.arc.runtime.ConfigRecorder.validateConfigProperties(ConfigRecorder.java:39)
Unfortunately, I believe this does not work because Quarkus #ConfigProperties, handles these cases as if they were subgroups and try to map nested properties with the configuration (and not use the Converter).
Feel free to open up an issue in Quarkus GH if you feel this should change: https://github.com/quarkusio/quarkus/issues
Alternately, you can use SR Config #ConfigMapping: https://smallrye.io/docs/smallrye-config/mapping/mapping.html. It covers a few more cases, including direct Conversion and in the future it may replace Quarkus #ConfigProperties.

How can I inject all instances as collection IEnumerable<T> that implement a common interface in Prism?

I want to inject all instances as IEnumerable<T> that implement a common interface. It is for using it in a strategy pattern resolver. How can I do it? I googled that unity container has a way.
I need this to do the same job as link below.
https://ufukhaciogullari.com/blog/strategy-pattern-with-dependency-injection/
public interface ITest
{
string Name { get; set; }
}
public class TestA : ITest
{
public string Name { get; set; } = "A";
}
public class TestB : ITest
{
public string Name { get; set; } = "B";
}
public class TestC : ITest
{
public string Name { get; set; } = "C";
}
public class SomeResolver
{
IEnumerable<ITest> _tests;
public SomeResolver(IEnumerable<ITest> tests)
{
_tests = tests;
}
}
In unity, I can register the following way:
container.RegisterType<IEnumerable<IParserBuilder>, IParserBuilder[]>();
I tried to do the same in Prism, but it fails. How to do it in Prism?
I tried to do the same in Prism, but it fails. How to do it in Prism?
Prism's no dependency injection container, it just tries to hide IUnityContainer from you.
That being said, there's no need to register the IEnumerable<IParserBuilder> in the first place.
container.Resolve<SomeResolver>()
will call the constructor and pass it instances of TestA, TestB and TestC, given you registered them first.
container.Register<ITest, TestA>( "A" );
container.Register<ITest, TestB>( "B" );
container.Register<ITest, TestC>( "C" );
Unfortunately, there's no way to have those A, B and C injected, too, so you have to keep the Name property if you need to identify a specific service (I'd make it read-only, though). I'd love to be able to inject IEnumerable<(string, ITest)> and get the names, too...

Dependency injection in TypeScript

I'm looking into the possibilities to do TDD with TypeScript.
If I write my tests in TypeScript, is it possible to make the import statements return mocks for my class under test?
Or is the only feasible approach to write the tests in pure javascript and deal with injecting AMDs myself?
I have developed an IoC container called InversifyJS with advanced dependency injection features like contextual bindings.
You need to follow 3 basic steps to use it:
1. Add annotations
The annotation API is based on Angular 2.0:
import { injectable, inject } from "inversify";
#injectable()
class Katana implements IKatana {
public hit() {
return "cut!";
}
}
#injectable()
class Shuriken implements IShuriken {
public throw() {
return "hit!";
}
}
#injectable()
class Ninja implements INinja {
private _katana: IKatana;
private _shuriken: IShuriken;
public constructor(
#inject("IKatana") katana: IKatana,
#inject("IShuriken") shuriken: IShuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };
}
2. Declare bindings
The binding API is based on Ninject:
import { Kernel } from "inversify";
import { Ninja } from "./entities/ninja";
import { Katana } from "./entities/katana";
import { Shuriken} from "./entities/shuriken";
var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);
export default kernel;
3. Resolve dependencies
The resolution API is based on Ninject:
import kernel = from "./inversify.config";
var ninja = kernel.get<INinja>("INinja");
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
The latest release (2.0.0) supports many use cases:
Kernel modules
Kernel middleware
Use classes, string literals or Symbols as dependency identifiers
Injection of constant values
Injection of class constructors
Injection of factories
Auto factory
Injection of providers (async factory)
Activation handlers (used to inject proxies)
Multi injections
Tagged bindings
Custom tag decorators
Named bindings
Contextual bindings
Friendly exceptions (e.g. Circular dependencies)
You can learn more about it at https://github.com/inversify/InversifyJS
I use infuse.js for Dependency Injection in TypeScript.
Reference the d.ts
/// <reference path="definition/infusejs/infusejs.d.ts"/>
Initialize your injector at startup
this.injector = new infuse.Injector();
Map Dependencies
this.injector.mapClass( 'TodoController', TodoController );
this.injector.mapClass( 'TodoView', TodoView );
this.injector.mapClass( 'TodoModel', TodoModel, true ); // 'true' Map as singleton
Inject Dependencies
export class TodoController
{
static inject = ['TodoView', 'TodoModel'];
constructor( todoView:TodoView, todoModel:TodoModel )
{
}
}
It's string based as opposed to being type based (as reflection isn't yet possible in TypeScript). Despite that, it works very well in my applications.
Try this Dependency Injector (Typejector)
GitHub Typejector
With new TypeScript 1.5 it is possible using annotation way
For example
#injection
class SingletonClass {
public cat: string = "Kitty";
public dog: string = "Hot";
public say() {
alert(`${this.cat}-Cat and ${this.dog}-Dog`);
}
}
#injection
class SimpleClass {
public say(something: string) {
alert(`You said ${something}?`);
}
}
#resolve
class NeedInjectionsClass {
#inject(SingletonClass)
public helper: SingletonClass;
#inject(SimpleClass)
public simpleHelper: SimpleClass;
constructor() {
this.helper.say();
this.simpleHelper.say("wow");
}
}
class ChildClass extends NeedInjectionsClass {
}
var needInjection = new ChildClass();
For question case:
some property should realise pseudo Interface (or abstract class) like in next example.
class InterfaceClass {
public cat: string;
public dog: string;
public say() {
}
}
#injection(true, InterfaceClass)
class SingletonClass extends InterfaceClass {
public cat: string = "Kitty";
public dog: string = "Hot";
public say() {
alert(`${this.cat}-Cat and ${this.dog}-Dog`);
}
}
#injection(true, InterfaceClass)
class MockInterfaceClass extends InterfaceClass {
public cat: string = "Kitty";
public dog: string = "Hot";
public say() {
alert(`Mock-${this.cat}-Cat and Mock-${this.dog}-Dog`);
}
}
#injection
class SimpleClass {
public say(something: string) {
alert(`You said ${something}?`);
}
}
#resolve
class NeedInjectionsClass {
#inject(InterfaceClass)
public helper: InterfaceClass;
#inject(SimpleClass)
public simpleHelper: SimpleClass;
constructor() {
this.helper.say();
this.simpleHelper.say("wow");
}
}
class ChildClass extends NeedInjectionsClass {
}
var needInjection = new ChildClass();
Note: Mock injection should define after source code, because it mast redefine class-creator for interface
For people who use Angular2 I have developed Fluency Injection https://www.npmjs.com/package/fluency-injection. The documentation is quite complete and it mimics the behaviour of Angular2's DI.
Feedback is much appreciated and I hope it helps you :)
You can use the solution:
Lightweight dependency injection container for JavaScript/TypeScript
import {autoInjectable, container} from "tsyringe";
class MyService {
move(){
console.log('myService move 123', );
}
}
class MyServiceMock {
move(){
console.log('mock myService move 777', );
}
}
#autoInjectable()
export class ClassA {
constructor(public service?: MyService) {
}
move(){
this.service?.move();
}
}
container.register(MyService, {
useClass: MyServiceMock
});
new ClassA().move();
output:
mock myService move 777
I've been developing a DI solution called Pigly. An example given the original question regarding injecting and testing (admittedly not automatic-mock generation - although you could try ts-auto-mock as I've done here):
Given:
interface IDb {
set(key: string, value: string);
}
interface IApi {
setName(name: string);
}
class Api implements IApi {
constructor(private db: IDb) {}
setName(name: string){
this.db.set("name", name);
}
}
We can bind the types with,
import { Kernel, toSelf, to, toConst } from 'pigly';
import * as sinon from 'sinon';
let spy = sinon.spy();
let kernel = new Kernel();
kernel.bind(toSelf(Api));
kernel.bind<IApi>(to<Api>());
kernel.bind<IDb>(toConst({ set: spy }));
then resolve and test with:
let api = kernel.get<IApi>();
api.setName("John");
console.log(spy.calledWith("name", "John"));
execution/compilation of this example requires a typescript transformer - to compile the interface-symbols and constructor provider into plain javascript. There are a few ways to do this. The ts-node + ttypescript approach is to have a tsconfig.json:
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"moduleResolution": "node",
"plugins": [{
"transform": "#pigly/transformer"
}]
}
}
and execute with
ts-node --compiler ttypescript example-mock.ts
Pigly has the distinction of not requiring any changes to your (or third-party) classes, at the expense of either the use of a typescript transformer, or more verbose binding if (you don't want to use the transformer). Its still experimental, but I think it shows promise.
TypeScript works well with AMD loaders like requirejs. If confgured properly, TypeScript will output fully AMD compliant javascript.
In a testing situation, you could configure requirejs to inject testable modules.
You can give this a shot: https://www.npmjs.com/package/easy-injectionjs. It is a generic use dependency injection package.
#EasySingleton creates a single instance of the dependency through the entire application. It is ideal for a service of some sort.
#EasyPrototype creates as many instances of the dependency as needed. It is ideal for changeable dependencies.
#EasyFactory is primarily used for inheritance:
You can do anything using this package:
Simple usage (Coming from the readme):
import { Easy, EasyFactory, EasyPrototype, EasySingleton } from 'easy-injectionjs';
#EasyFactory()
abstract class Person {
abstract getName();
abstract setName(v: string);
}
// #EasyObservable()
#EasySingleton()
class Somebody extends Person{
// #Easy()
constructor (private name: string) {
super()
this.name = 'Sal';
}
public getName() {
return this.name;
}
public setName(v: string) {
this.name = v;
}
}
#EasyPrototype()
class Nobody extends Person{
#Easy()
somebody: Person;
constructor () {
super()
}
public getName() {
return this.somebody.getName();
}
public setName(v: string) {
this.somebody.setName(v);
}
}
#EasyPrototype()
class Data {
#Easy()
somebody: Person;
name: string;
change(v: string) {
this.somebody.setName(v);
}
getName(): string {
return this.somebody.getName();
}
}
let n = new Nobody();
console.log(n.getName()) // Prints Sal
n.setName('awesome');
console.log(n.getName()) // Prints awesome
let d = new Data()
console.log(d.getName()) // Prints awesome
d.change('Gelba')
console.log(n.getName()) // Prints Gelba
d.change('kaa')
console.log(n.getName()) // Prints Kaa
Even if you want to inject node modules you can do this:
import * as IExpress from 'express';
import { Easy, EasySingleton } from 'easy-injectionjs';
#EasySingleton()
class Express extends IExpress {}
#EasySingleton()
export class App {
#Easy()
private _express: Express;
}
let app = new App();
console.log(app)
Of course, the usage of the express server isn't for console logging. It is just for testing :D.
Hope that helps :D
Dime is a very simple dependency injection library. It's very early into development, though, so it probably has some bugs. There is more information on the wiki page.
Example usage:
import { ItemsService } from './items-service'; // ItemsService is an interface
import { Inject } from '#coined/dime';
class ItemsWidget {
#Inject()
private itemsService: ItemsService;
render() {
this.itemsService.getItems().subscribe(items => {
// ...
});
}
}
// Setup
const appPackage = new Package("App", {
token: "itemsService",
provideClass: AmazonItemsService // AmazonItemsService implements ItemsService
});
Dime.mountPackages(appPackage);
// Display the widget
const widget = new ItemsWidget();
widget.render();
I work on AutoFixtureTS that is inspired by AutoFixture. AutoFixtureTS makes it easier for TypeScript developers to do Test-Driven Development by automating non-relevant Test Fixture Setup, allowing the Test Developer to focus on the essentials of each test case.
http://ronniehegelund.github.io/AutoFixtureTS/
Its still just prototype code, but check it out :-)
/ronnie

Resources