-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#5 Adding DSL to define properties #9
#5 Adding DSL to define properties #9
Conversation
- user is able to specify -- default value -- if field is required -- validator - validation messages displayed in the props dialog
In validator dsl please change receiver instead of using 'it' |
How about introducing new type of field, checkbox / boolean? |
Maybe we should inverse logic because most of fields will be 'required', so that maybe you need to make it required by default an introduce instead 'optional' method |
src/main/kotlin/com/neva/gradle/fork/gui/PropertyDialogField.kt
Outdated
Show resolved
Hide resolved
@@ -39,21 +41,33 @@ open class ForkExtension(val project: Project) { | |||
} | |||
|
|||
fun inPlaceConfig(name: String, configurer: Config.() -> Unit) { | |||
config(InPlaceConfig(project, name), configurer) | |||
if (configExists(name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike extracting such trivial methods
src/main/kotlin/com/neva/gradle/fork/config/properties/PropertyDefinition.kt
Outdated
Show resolved
Hide resolved
PR updated, I will update README in next PR.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very nice one!!! ;)
@@ -136,7 +142,7 @@ abstract class Config(val project: Project, val name: String) { | |||
} | |||
|
|||
private fun promptValidate() { | |||
val invalidProps = prompts.values.filter { !it.valid }.map { it.name } | |||
val invalidProps = properties.filter { prop -> prop.validate().hasErrors() }.map { it.name } | |||
if (invalidProps.isNotEmpty()) { | |||
throw ForkException("Fork cannot be performed, because of missing properties: $invalidProps." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing or invalid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
val invalidProps = properties.filter(Property::isInvalid).map { it.name }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, please correct message in exception
if (name.isBlank()) throw PropertyException("Name of property definition cannot be blank!") | ||
} | ||
|
||
val PASSWORD = PropertyType.PASSWORD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the purpose of these vals?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those values are used in DSL to simplify property type specification:
type = CHECKBOX
instead of
type = com.neva.gradle.fork.config.properties.PropertyType.CHECKBOX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, but even Gradle is not doing this that way (see Test
task and setTestLogging
accepting enum), importing enum will be not so bad and I am opting for this approach instead
src/main/kotlin/com/neva/gradle/fork/config/properties/PropertyDefinition.kt
Outdated
Show resolved
Hide resolved
errors.add(message) | ||
} | ||
|
||
fun hasErrors() = errors.isNotEmpty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasErrors -> passing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean:
val hasErrors = errors::isNotEmpty
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, name of method , nvm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about introducing built-int / predefined validators for properties ending with Path
or Url
? ;) which of course could be overidden / relaxed
} | ||
|
||
private fun setPropertyValue() { | ||
when (propField) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use when
as an expression here?
private fun setPropertyValue() = when (propField) {
....
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only if I would add an empty else block. Since when is not returning any value I would leave it as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok now i get it, we could also use sealed
class and wrap all propField
types in it but i think this is a bit different case. THX
src/main/kotlin/com/neva/gradle/fork/gui/PropertyDialogField.kt
Outdated
Show resolved
Hide resolved
src/main/kotlin/com/neva/gradle/fork/gui/PropertyDialogField.kt
Outdated
Show resolved
Hide resolved
I added two new types: PATH & URL and default validation for them. By default properties whose names end with "path" or "url" get those types. This can be overridden. |
I already pushed updates to reflect new features in README. |
README.md
Outdated
@@ -129,6 +130,51 @@ Properties can be provided by (order makes precedence): | |||
|
|||
4. Mixed approach. | |||
|
|||
### Validating properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defining section should be before validating ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I changed naming
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approved but code style may be still improved
README.md
Outdated
fork { | ||
properties(mapOf( | ||
"enableSmbClient" to { | ||
type = CHECKBOX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer PropertyType.CHECKBOX
with import or introduce a method checkbox()
in a same manner as optional()
that mutes internal state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
optional() | ||
defaultValue = "-server -Xmx1024m -XX:MaxPermSize=256M -Djava.awt.headless=true" | ||
validator = { | ||
if (!value.startsWith("-")) error("This is not a JVM option!") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (value.split(" ").any{ !it.startsWith("-"))
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
import groovy.lang.Closure | ||
import org.gradle.api.Project | ||
import org.gradle.api.tasks.Input | ||
import org.gradle.util.ConfigureUtil | ||
|
||
open class ForkExtension(val project: Project) { | ||
|
||
private val propertiesDefinitions = PropertyDefinitions() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only last part plural / propertyDefinitions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
@@ -23,23 +27,27 @@ open class ForkExtension(val project: Project) { | |||
} | |||
|
|||
fun config(configurer: Config.() -> Unit) { | |||
config(SourceTargetConfig(project, Config.NAME_DEFAULT), configurer) | |||
config(SourceTargetConfig(project, propertiesDefinitions, Config.NAME_DEFAULT), configurer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about changing to
SourceTargetConfig(this, Config.NAME_DEFAULT)
the ForkExtension
is already holding project, property definitions could be then public or internal. don't be afraid od passing these heavylooking objects. this will simplify code much
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
@@ -14,13 +17,16 @@ import java.io.FileInputStream | |||
import java.io.FileOutputStream | |||
import java.util.* | |||
|
|||
abstract class Config(val project: Project, val name: String) { | |||
abstract class Config(val project: Project, private val propertyDefinitions: PropertyDefinitions, val name: String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config(ext: ForkExtension, val name: String)
class PropertyDefinitions { | ||
private val definitions = mutableMapOf<String, PropertyDefinition>() | ||
|
||
fun configure(propertiesConfiguration: Map<String, PropertyDefinition.() -> Unit>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are in a scope of Property
(class name is scoping Property
keyword) so that why no just configuration
as an arg name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
definitions += propertiesConfiguration.mapValues { PropertyDefinition(it.key).apply(it.value) } | ||
} | ||
|
||
fun getProperty(prompt: PropertyPrompt): Property { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
however, you are right, in that case getProperty
is ok.. :)
required = false | ||
} | ||
|
||
private fun calculateDefaultType() = when { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calculate
is rather math related. I prefer here determine
/ examine
etc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
import java.awt.Color | ||
import javax.swing.* | ||
|
||
class PropertyDialogField(private val property: Property, private val propField: JComponent, private val validationMessageLabel: JLabel, private val dialog: JDialog) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please reformat that to
https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting
each field in constructor on separate line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
get() = property.value | ||
|
||
fun validateAndDisplayErrors(): Boolean { | ||
setPropertyValue() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set
indicates setter; please avoid setter like methods which are not setters actually
maybe better assign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
I pushed changes addressing CR remarks. This version doesn't support GroovyDSL. I have another branch that supports Groovy. However, validators don't work in Groovy and the overall solution is ugly hacking. |
I finally added support for Groovy DSL, now DSL is exactly the same in Groovy and Kotlin:
|
``` | ||
|
||
#### Property definition | ||
Property definition can consists of: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blank line after header
#### Property definition | ||
Property definition can consists of: | ||
* type specification: `type = TYPE_NAME` | ||
* there are five types available: `TEXT` x(default one), `CHECKBOX` (representing boolean), `PASSWORD`, `PATH` & `URL`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are
/ not needed
action.execute(this) | ||
} | ||
|
||
fun property(name: String, action: Action<in PropertyDefinition>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why in
is here?
} | ||
|
||
fun property(name: String, action: Action<in PropertyDefinition>) { | ||
val definition = project.objects.newInstance(PropertyDefinition::class.java, name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you used new API introduced in 4.9 :) hope you know the purpose, teach me :)
@@ -14,13 +16,18 @@ import java.io.FileInputStream | |||
import java.io.FileOutputStream | |||
import java.util.* | |||
|
|||
abstract class Config(val project: Project, val name: String) { | |||
abstract class Config(private val forkExtension: ForkExtension, val name: String) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just extension
or ext
. you will not use any other extensions so that fully qualified forkExtension
is over-expressive for me (whole plugin is fork
scoped :))
package com.neva.gradle.fork.config.properties | ||
|
||
class PropertyDefinitions { | ||
private val definitions = mutableMapOf<String, PropertyDefinition>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mine and Cognifide's is matching each other, I have no problem with that :>
Intellij default + blank line around fields :D
open class PropertyDefinition @Inject constructor(val name: String) { | ||
|
||
init { | ||
if (name.isBlank()) throw PropertyException("Name of property definition cannot be blank!") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow Kotlin code style / if need { }
} | ||
|
||
fun validator(validateAction: Action<in Validator>) { | ||
validator = validateAction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not this.validator = validator
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike repeating type in var/field name
this.defaultValue = defaultValue | ||
} | ||
|
||
fun url(defaultValue: String = "") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider implementing select
option, it should not be hard?`
fun select(options: List<String>, defaultValue: String)` // when values <=> keys
fun select(options: Map<String, String>, defaultValue: String)
private val propField: JComponent, | ||
private val validationMessageLabel: JLabel, | ||
private val dialog: JDialog | ||
) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx
I propose the following DSL: