Groovy way to selectively mixin methods from multiple classes - groovy

I'm writing a Groovy script based on commons-io that monitors some source directory and synchronizes its files with some destination directory.
#Grab(group='commons-io', module='commons-io', version='2.4')
import org.apache.commons.io.monitor.*
import org.apache.commons.io.FileUtils
class BaseSynchronizedFileListener extends FileAlterationListenerAdaptor {
def srcDir
def destDir
/* Given a source file, returns the destination file */
File destination(File file) {
new File(destDir, file.getAbsolutePath() - srcDir.getAbsolutePath())
}
}
class CopyOnWriteListener extends BaseSynchronizedFileListener {
#Override
void onFileChange(File file) {
FileUtils.copyFile(file, destination(file))
}
#Override
void onFileCreate(File file) {
FileUtils.copyFile(file, destination(file))
}
}
class DeleteOnDeleteListener extends BaseSynchronizedFileListener {
#Override
void onFileDelete(File file) {
FileUtils.deleteQuietly(destination(file))
}
}
In addition to straight file copies, I want to support Less->CSS compilation, wherein .less files in the source directory are synchronized with .css files in the destination directory.
#Grab(group='org.lesscss', module='lesscss', version='1.3.3')
import org.lesscss.LessCompiler
class CompileLessOnWriteListener extends BaseSynchronizedFileListener {
def compiler = new LessCompiler()
#Override
File destination(File file) {
File dest = super.destination(file)
new File(dest.parentFile, dest.name - '.less' + '.css')
}
void compile(File less) {
compiler.compile(less, destination(less))
}
#Override
void onFileChange(File less) {
compile(less)
}
#Override
void onFileCreate(File less) {
compile(less)
}
}
The problem I'm encountering is when I attempt to create class DeleteCssOnDeleteLessListener to handle the situation when .less files are deleted (which, in turn, deletes the corresponding .css file) -- the code I need to do this exists in two different inheritance trees.
CompileLessOnWriteListener contains the destination() method
DeleteOnDeleteListener contains the onFileDelete() method to delete the CSS file returned by the destination() method
Is there a "Groovy way" to selectively mixin or inherit methods from both of these classes into a new class?
Or do I just need to bite the bullet and create a common super class for CompileLessOnWriteListener and DeleteCssOnDeleteLessListener?

Update
Changed the implementation. Lets see if i got the idea. You need:
Inherit two methods
"Inherit" constructor
It needs to be an instance of an interface
I think a heavy metaprogramming helps here. We can declare two objects to DeleteCssOnDeleteLessListener delegate methods to, and these objects will be accessing properties from it.
For the interface, i think you are better using the as Interface operator.
Dynamically "inherit" the constructors may get tricky. Since it is only two properties, i've declared them. You can delegate the getProperty/setProperty to one of the other two objects, if you prefer DRYing your code:
class DeleteCssOnDeleteLessListener {
def destDir, srcDir
def onLessDelete(file) {
onFileDelete destination( file )
}
}
class CompileLessOnWriteListener {
def destination(file) {
"destination $file from $srcDir"
}
}
class DeleteOnDeleteListener {
def onFileDelete(file) {
"onFileDelete $file and $destDir"
}
}
def delete = new DeleteCssOnDeleteLessListener(destDir: "dest/dir", srcDir: "src/dir")
def compileLess = new CompileLessOnWriteListener()
def deleteOnDelete = new DeleteOnDeleteListener()
delete.metaClass {
destination = compileLess.&destination
onFileDelete = deleteOnDelete.&onFileDelete
}
compileLess.metaClass.getProperty = { property -> delete.getProperty property }
deleteOnDelete.metaClass.getProperty = { property -> delete.getProperty property }
assert delete.onLessDelete("style.less") == "onFileDelete destination style.less from src/dir and dest/dir"

It's not very "Groovy", in my opinion, nor very efficient looking, but at least this approach solves my problem without having to create a common superclass:
class DeleteCssOnDeleteLessListener extends DeleteOnDeleteListener {
#Override
File destination(File f) {
new CompileLessOnWriteListener(destDir: this.destDir, srcDir: this.srcDir).destination(f)
}
}

Related

ArchUnit Rule to prevent access to derived classes?

A colleague did some refactoring and moved a method to a superclass to be able to reuse it from an other child-class, too. The IDE handled that quickly and without any complains.
That method, however, internally referred to a constant which was not moved with the method, i.e. the parent class thus now referred to a constant in one of its child-classes which of course is a no-no!
How can I phrase an ArchUnit rule that prevents such references from parent to members/methods/local classes and enums/etc. in child-classes (or in general: anything in classes further down the hierarchy)?
For direct dependencies, you can use the following custom ArchCondition:
#ArchTest
ArchRule rule = noClasses().should(new ArchCondition<JavaClass>("depend on their children") {
#Override
public void check(JavaClass parentClass, ConditionEvents events) {
parentClass.getDirectDependenciesFromSelf().stream()
.filter(dependency -> dependency.getTargetClass() != parentClass
&& dependency.getTargetClass().isAssignableTo(parentClass.getName()))
.forEach(dependency -> events.add(satisfied(dependency, dependency.getDescription())));
}
});
It can easily be adapted to also catch transitive dependencies such as in this example:
class Parent {
Friend friend;
}
class Friend {
Child child;
}
class Child extends Parent {
}
You can basically replace getDirectDependenciesFromSelf with getTransitiveDependenciesFromSelf:
#ArchTest
ArchRule rule = noClasses().should(new ArchCondition<JavaClass>("depend on their children") {
#Override
public void check(JavaClass parentClass, ConditionEvents events) {
parentClass.getTransitiveDependenciesFromSelf().stream()
.filter(dependency -> dependency.getTargetClass() != parentClass
&& dependency.getTargetClass().isAssignableTo(parentClass.getName()))
.forEach(dependency -> events.add(satisfied(dependency, parentClass.getName()
+ " (transitively) depends on its child: " + dependency.getDescription())));
}
});
FYI: I'm using the following static imports:
import static com.tngtech.archunit.lang.SimpleConditionEvent.satisfied;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

Is there a way of getting the names of all subclasses

I want to get the names of all subclasses of a base class. From that I want to #:build an enum where every entry is the name of a class. Is this possible?
Here is the code. The order of macro executions is undefined, as specified in documentation.
[AutoBuildingMacro.hx]
import haxe.macro.Context;
import haxe.macro.Expr;
final mapofnames = new Map<String,Array<String>>();
class AutoBuildingMacro {
public static macro function fromBaseClass(base):Array<Field> {
var parent = haxe.macro.ExprTools.toString(base);
var names = mapofnames[parent];
if ( names == null ) {
names = new Array<String>();
mapofnames[parent] = names;
}
names.push(Context.getLocalClass().toString());
trace(mapofnames);
return null;
}
}
[Main.hx]
import AutoBuildingMacro;
class Main {
static public function main() {}
}
#:autoBuild(AutoBuildingMacro.fromBaseClass(Parent))
class Parent {
}
class Child1 extends Parent {
}
class Child2 extends Parent {
}
class Child3 extends Child2 {
}
Output:
AutoBuildingMacro.hx:15: {Parent => [Child2]}
AutoBuildingMacro.hx:15: {Parent => [Child2,Child3]}
AutoBuildingMacro.hx:15: {Parent => [Child2,Child3,Child1]}
You are looking for auto-build macro feature https://haxe.org/manual/macro-auto-build.html
And then enum building https://haxe.org/manual/macro-enum-building.html
I wrote a library called compiletime that has this feature:
// Returns a list of all the classes that inherit MySuperClass, no matter what package
CompileTime.getAllClasses(MySuperClass);
The approach I took worked around the build order problem by using the Context.onGenerate hook to build an array of matching classes at the end of compilation (rather than as each class is compiled) and add the array to “metadata”, which can still be modified in the “onGenerate” step. I then use this metadata in a separate runtime class to get the array of classes at runtime.

Black listing synchronized keyword for groovy scripts

There is an application where users can provide custom groovy scripts. They can write their own functions in those scripts. I want to restrict people from using the 'synchronized' keyword as well as some other keywords in these scripts. For example it should not be possible to write a function like below.
public synchronized void test() {
}
I am creating a CompilerConfiguration and using the SecureASTCustomizer. However adding org.codehaus.groovy.syntax.Types.KEYWORD_SYNCHRONIZED to the list of black listed tokens doesn't seem to do the job. (if I add org.codehaus.groovy.syntax.Types.PLUS it's preventing the usage of '+' within scripts.. but doesn't seem to do the job for synchronized)
Any ideas on how to achieve this ...
You can do something like this:
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.syntax.SyntaxException
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.classgen.GeneratorContext
class SynchronizedRemover extends org.codehaus.groovy.control.customizers.CompilationCustomizer {
SynchronizedRemover() {
super(org.codehaus.groovy.control.CompilePhase.CONVERSION)
}
void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
classNode.methods.each { mn ->
if (mn.modifiers & 0x0020) { // 0x0020 is for synchronized
source.addError(new SyntaxException("Synchronized is not allowed", mn.lineNumber, mn.columnNumber))
}
}
}
}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(new SynchronizedRemover())
def shell = new GroovyShell(config)
shell.evaluate '''
class Foo { public synchronized void foo() { println 'bar' } }
'''
The idea is to create a compilation customizer that checks generated classes and for each method, add an error if the synchronized modifier is present. For synchronized block inside methods, you can probably use the SecureASTCustomizer with a custom statement checker.

Use Groovy Category implicitly in all instance methods of class

I have simple Groovy category class which adds method to String instances:
final class SampleCategory {
static String withBraces(String self) {
"($self)"
}
}
I want to use this category in my unit tests (for example). It looks like this:
class MyTest {
#Test
void shouldDoThis() {
use (SampleCategory) {
assert 'this'.withBraces() == '(this)'
}
}
#Test
void shouldDoThat() {
use (SampleCategory) {
assert 'that'.withBraces() == '(that)'
}
}
}
What I'd like to achieve, however, is ability to specify that category SampleCategory is used in scope of each and every instance method of MyTest so I don't have to specify use(SampleCategory) { ... } in every method.
Is it possible?
You can use mixin to apply the category directly to String's metaClass. Assign null to the metaClass to reset it to groovy defaults. For example:
#Before void setUp() {
String.mixin(SampleCategory)
}
#After void tearDown() {
String.metaClass = null
}
#Test
void shouldDoThat() {
assert 'that'.withBraces() == '(that)'
}
Now you have the option to use extension modules instead of categories:
http://mrhaki.blogspot.se/2013/01/groovy-goodness-adding-extra-methods.html
On the plus side Intellij will recognize the extensions. I've just noticed that it doesn't even need to be a separate module as suggested by the link, just add META-INF/services/org.codehaus.groovy.runtime.ExtensionModule to the project:
# File: src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName = module
moduleVersion = 1.0
extensionClasses = SampleExtension
The extension class is pretty much defined like a normal category:
class SampleExtension {
static String withBraces(String self) {
"($self)"
}
}
Can be used like:
def "Sample extension"() {
expect: 'this'.withBraces() == '(this)'
}
If you are using Spock there is a #Use annotation that can be used on the specifications. The drawback with that is that Intellij will not recognize it.

Groovy expando metaclass

I've developed a Class that has some methods that augment Integer, it mainly lets me do this:
def total = 100.dollars + 50.euros
Now I have to extend Integer.metaClass doing something like this:
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
I tried putting that at the bottom of the file, before the Money class declaration, but the compiler says that a class Named Money already exists, I know why it happens (because groovy creates a class with the name of the file with an empty static void main to run this code).
I also tried using a static block inside the class like this:
static {
Integer.metaClass.getDollars = {->
Money.Dollar(delegate)
}
}
This neither works.
A third solution would be to change the file name (like MoneyClass.groovy) and keep the class name (class Money) but that seems a bit weird.
Is there anything else I can do? Thanks.
Just put it in any method of any class maybe a bean TypeEnhancer.groovy:
public class TypeEnhancer {
public void start() {
Integer.metaClass.getDollars() = {-> Money.Dollar(delegate) }
}
public void stop() {
Integer.metaClass = null
}
}
Just create and initalize by calling start(): new TypeEnhancer().start();.
To disable the enhancement, call new TypeEnhancer().stop();. The bean can also used as Spring bean.

Resources