code
docs
tests
The Scala Support Library allows Fragments and Composites to be written as Scala traits.
The Scala Support Library is a Generic mixin class that implements Composites by delegating to Scala traits.
Example mixin declaration:
trait HelloWorldMixin2 { def sayHello(@MaxLength(10) name: String ): String = "Hello " + name }
Example composite declaration:
@Concerns(Array(classOf[ HelloThereConcern ])) trait HelloWorldComposite extends TransientComposite with HelloWorldMixin with HelloWorldMixin2
Example typed concern:
class HelloThereConcern extends ConcernOf[ HelloWorldMixin2 ] with HelloWorldMixin2 { override def sayHello(name: String ) = next.sayHello("there " + name) }
Example generic concern with filter:
@AppliesTo(Array(classOf[ StringFilter ])) class ExclamationGenericConcern extends GenericConcern { def invoke(composite: AnyRef, method: Method, args: Array[ AnyRef ] ) = next.invoke(composite, method, args) + "!" } class StringFilter extends AppliesToFilter { def appliesTo(method: Method, mixin: Class[ _ ], compositeType: Class[ _ ], fragmentClass: Class[ _ ] ) = method .getReturnType .equals(classOf[ String ]) }
And the assembly code.
Note that the ScalaTraitMixin
must be added.
module.transients( HelloWorldComposite.class, HelloWorldComposite2.class ). withMixins( ScalaTraitMixin.class ). withConcerns( ExclamationGenericConcern.class );
That pretty much covers the domain model part. Usage from Java is transparent, since it looks just like interfaces and classes.
The following example separate between command interface (suggestions to change), events (after the fact), and data, so they are in three separate traits below. Only commands are called by client code.
trait TestEntity extends EntityComposite with Commands with Events with Data trait Commands { self: Events => def updateFoo(newValue: String ) { // Call "injected" service val repeated = testService.repeat(newValue) // Check here if input is ok updatedFoo(repeated) } // Service injection - this is really a method call to the ServiceFinder of the composite @Service def testService: TestService } // Raw data of entity goes here trait Data { @UseDefaults def foo: Property[ String ] // Define property def foo_=(v: String ) { foo.set(v) } // Operator overloading for = } trait Events { self: Data => def updatedFoo(newValue: String ) { // Register change by modifying state foo = newValue } }
The self
operator thing solves the @This
injection requirements, although it doesn’t do private injections (i.e. the Entity has to extend Events and Data for it to work).
Everything is statically typed.
And the corresponding assembly code:
module.entities( TestEntity.class ).withMixins( ScalaTraitMixin.class );
The following example is a pretty simple service written as a Scala trait:
trait TestService extends ServiceComposite { def repeat(input: String ): String = input + input }
And the corresponding assembly code:
module.services( TestService.class ).withMixins( ScalaTraitMixin.class );