I have defined own metamodel class to create a special kind of classes. Now, I would like these classes to automatically register themselves with a special kind of manager. Basically, this would like like this (would only compose be called each time when class' module is being loaded):
use MyManager;
class MyHOW is Metamodel::ClassHOW {
method compose ( Mu \type ) {
self.add_parent( type, MyParentClass );
callsame;
registerMyClass( type );
}
}
Then I have something like:
use v6;
use MyClass;
myclass Foo { ... }
in a module. Then there is a manager object which scans repositories/file system and requires modules with names matching to a certain pattern. Afterwards, it needs to know what myclasses are defined in each module. It could scan the symbol table of the loaded module. But this won't work if the loaded file contains multiple modules or no modules at all – like in the example above.
So far, it looks like the INIT phaser would provide the solution, but I'm struggling to find how to get the body block of a class from within the composer method.
When doing meta-programming, the meta-object's methods are invoked during compilation, as declarations are parsed. Therefore, the compose method is called immediately after the parsing of a myclass foo { } declaration. The result of the module's compilation is then saved, and nothing in the meta-object will be processed again when the module is loaded.
There's no supported way that I'm aware of to inject a load-time callback into the module where a type is being declared. However, it's possible to install the symbols into a separate package - used as a registry - and then find them there.
For example, given I have a lib/MyClass.pm6 that looks like this:
package MyRegistry { }
class MyParentClass { }
class MyHOW is Metamodel::ClassHOW {
method compose ( Mu \type ) {
MyRegistry::{self.name(type)} = type;
self.add_parent( type, MyParentClass );
callsame;
}
}
my package EXPORTHOW {
package DECLARE {
constant myclass = MyHOW;
}
}
And I write some files mods/A.pm6 and mods/B.pm6 like this:
use MyClass;
myclass A { }
And this:
use MyClass;
myclass B { }
Then when I require them in a script like this, and dump the keys in MyRegistry, they'll both be registered there:
use MyClass;
for dir('mods', test => /pm6$/) {
require $_;
}
dd MyRegistry.WHO.values;
Thus giving a predictable way to find them all.
Note that for a technique like this to work, you really need to have them stored into a Stash, since the loader knows how to symbol-merge those, whereas other types touched in different ways during the compilation of different modules will result in load-time conflicts.
You are left with the slight challenge of making sure to install everything under a sufficiently unique key; the type name as I used here is probably not unique enough in general. Probably I'd just generate something sufficiently random that the chance of a collision is hugely unlikely.
Related
class A { has $.name; };
class B is A { submethod BUILD { $!name = 'foo' } };
This code looks natural but throws error.
Attribute $!name not declared in class B
Yes, it is not declared in class B, but we are in the partially constructed object during B::BUILD and documentation says that bless creates the new object, and then walks all subclasses in reverse method resolution order. So $!name attribute should be known for class B in this phase, right?
Is there any way to set parent class attributes during object construction without using new method? I know that new will do the trick here, but BUILD has a lot of syntactic sugar and BUILD / TWEAK feel more DWIMy and straightforward than resolving to low-level blessing in new.
Private attribute syntax ($!foo) is only available for attributes that are lexically visible. That's why they're private :-)
If class A would want other classes be able to change, it would need to provide a mutator method explicitely or implicitely (with is rw).
Or you could let class A trust class B as described at https://docs.raku.org/routine/trusts#(Type_system)_trait_trusts .
Still it feels you would do better using roles:
role A {
has $.name is rw;
}
class B does A {
submethod BUILD { $!name = 'foo' }
}
The other option is to use the is built trait on attributes that you would like the default constructor to initialize.
Consider the following:
class A {
has $.name is built
}
class B is A { }
B.new(name => "Foo").gist.say; # B.new(name => "Foo")
This allows descendend classes to use the named parameter matching the attribute in .new to initialize the value at object creation time. Please note that this will work whether the attribute is public "$." or private "$!".
Hope that helps!
TL;DR All attributes are technically private. This design is a good one. You could just call a method in A from B. There are, of course, other options too.
Why doesn't BUILD see parent class attributes?
Quoting Wikipedia Fragile base class page problem:
One possible solution is to make instance variables private to their defining class and force subclasses to use accessors to modify superclass states.¹
Hence, per Raku Attributes doc:
In Raku, all attributes are private, which means they can be accessed directly only by the class instance itself.
B can call a method in A
This code looks natural:
class A { has $.name }
class B is A { submethod BUILD { $!name = 'foo' } }
Quoting again from Raku doc section linked above:
While there is no such thing as a public (or even protected) attribute, there is a way to have accessor methods generated automatically: replace the ! twigil with the . twigil (the . should remind you of a method call).
Your code generates a $!name attribute (private to A) plus a public .name method. Any code that uses the A class can call its public methods.
Your code hasn't used the autogenerated accessor method. But it could have done so with a couple small changes:
class A { has $.name is rw } # Add `is rw`
class B is A { submethod BUILD { self.name = 'foo' } } # s/$!name/self.name/²
say B.new # B.new(name => "foo")
is rw makes the public .name accessor method a read/write one instead of the default read only one.
Not using is rw
As I now understand from your first comment below, an is rw accessor is disallowed given your requirements. You can achieve any effect that a class supports via its public interface.
Let's first consider a silly example so it's clear you can do anything that any methods can do. Using, say, self.name, in A or B, might actually run one or more methods in A that make a cup of tea and return 'oolong' rather than doing anything with A's $!name:
class A {
has $.name = 'fred'; # Autogenerates a `method name` unless it's defined.
method name { 'oolong' } # Defines a `method name` (so it isn't generated).
}
my \a = A.new;
say a; # A.new(name => "fred")
say a.name; # oolong
Conversely, if an A object changes its $!name, doing so might have no effect whatsoever on the name of the next cup of tea:
class A {
has $.name = 'fred';
method name { 'rooibos' } # ignores `$!name`
method rename { $!name = 'jane' }
}
my \a = A.new;
say a; # A.new(name => "fred")
a.rename;
say a.name; # rooibos
To recap, you can (albeit indirectly) do anything with private state of a class that that class allows via its public API.
For your scenario, perhaps the following would work?:
class A {
has $.name;
multi method name { $!name }
multi method name (\val) { once $!name = val }
}
class B is A {
submethod BUILD { self.name: 42 }
}
my \a = B.new;
say a; # B.new(name => 42)
say a.name; # 42
a.name: 99; # Does nothing
say a.name; # 42
Footnotes
¹ Continuing to quote solutions listed by Wikipedia:
A language could also make it so that subclasses can control which inherited methods are exposed publicly.
Raku allows this.
Another alternative solution could be to have an interface instead of superclass.
Raku also supports this (via roles).
² self.name works where $!name does not. $.name throws a different compiler error with an LTA error message. See Using %.foo in places throws, but changing it to self.foo works.
Sorry that my answer is late in the day, but I feel that your original question is very well pitched and would like to add my variation.
class A {
has $!name;
submethod BUILD( :$!name ) {}
multi method name { $!name }
multi method name(\v) { $!name := v }
method gist(::T:) { "{::T.^name}.new( name => $!name )" }
}
class B is A {
submethod BUILD( :$name ) { self.name: $name // 'foo' }
}
say B.new; #B.new( name => foo )
say A.new(name => 'bar'); #A.new( name => bar )
say B.new(name => 'baz'); #B.new( name => baz )
Raku OO tries to do two mutually incompatible things:
provide a deep OO (similar to C++ / Java)
provide a lightweight OO (similar to Python / Ruby)
This is done by having a core that does #1 and then adding some sugar to it to do #2. The core gives you stuff like encapsulation, multiple inheritance, delegation, trust relationships, role based composition, delegation, MOP, etc. The sugar is all the boilerplate that Raku gives you when you write $. instead of $! so that you can just throw together classes to be lightweight datatypes for loosely structured data.
Many of the answers here bring suggestions from mode #2, but I think that your needs are slightly too specific for that and so my answer tilts towards mode #1.
Some notes to elaborate why I think this is a good solution:
you state that you cannot use is rw - this avoids traits
with proper method accessors, you have control over initialization
BUILD() is not constrained by the public accessor phasing
no need to go to roles here (that's orthogonal)
And some drawbacks:
you have to write your own accessors
you have to write your own .gist method [used by say()]
It is attributed to Larry that "everyone wants the colon(:)". Well, he had the last say, and that the Raku method call syntax self.name: 'foo' echos assignment self.name= 'foo' is, in my view, no accident and meant to ease the mental switch from mode #2 to #1. ;-)
Does Raku succeed to reconcile the irreconcilable? - I think so ... but it does still leave an awkward gear shift.
EDITED to add submethod BUILD to class A
Thanks everyone for great discussion and solution suggestions. Unfortunately there is no simple solution and it became obvious once I understood how Raku constructs object instances.
class A {
has $.name is rw;
};
class B is A {
submethod BUILD {
self.A::name = 123; # accessor method is already here
}
};
B.new.name.say; # will print 123
So if inheritance is used Raku works from parent class to child class fully constructing each class along the way. A is constructed first, $.name param is initialized, public attribute accessor methods are installed. This A instance become available for B construction, but we are not in A build phase anymore. That initialization is finished. My code example shows what is happening with syntactic sugar removed.
The fact that
submethod BUILD {
self.name = 123;
}
is available in class B during BUILD phase does not mean that we (as class B) have this attribute still available for construction. We are only calling write method on already constructed class A. So self.name = 123 really means self.A::name = 123.
TL;DR: Attributes are not collected from parent classes and presented to BUILD in child class to be set at the same time. Parent classes are constructed sequentially and only their method interfaces are available in child BUILD submethod.
Therefore
class A {
has $.name; # no rw
};
class B is A {
submethod BUILD {
$!name = 123;
}
};
will not work because once we reach submethod BUILD in B class attribute $.name is already constructed and it is read only.
Solution for shallow inheritance:
Roles are the way to go.
role A {
has $.name;
};
class B does A {
submethod BUILD {
$!name = 123;
}
};
Roles are copied to class composing them, so class B sees this $.name param as their own and can initialize it. At the same time roles autopun to classes in Raku and standalone my $a = A.new( name => 123 ) can be used as a class.
However roles overdose can lead to orthogonal pattern issues.
Solution for deep inheritance:
There is none. You cannot have secure parent classes with read-only attribute behavior and initialize this attribute in child class builder, because at this moment parent class portion of self will be already constructed and attribute will be already read-only. Best you can do is to wrap attribute of parent class in private method (may be Proxy) and make it write-once this way.
Sad conclusion:
Raku needs improvement in this area. It is not convenient to use it for deep inheritance projects. Maybe new phaser is needed that will mash every attribute from parent classes in role-style and present them to BUILD at the same time. Or some auto-trust mechanism during BUILD. Or anything that will save user from introducing role inheritance and orthogonal role layout (this is doing stuff like class Cro::CompositeConnector does Cro::Connector when class Cro::Connector::Composite is Cro::Connector is really needed) to deep OO code because roles are not golden hammer that is suitable for every data domain.
Is there a way to test private methods in Raku?
I understand that one should ideally define their tests targeting the public methods, but is there a way to do it "the wrong way"? :)
I initially thought about defining a subclass for the Testing that inherited from the class I wanted to test and do the tests there, but it seems that private methods are not inherited.
Then I saw the 'trusts' routine, but I wouldn't want to reference a Testing class on any of the classes of the code.
Is there something like changing the 'private' property of a method via introspection?
What would be the best way to call/test a private method?
This can be done using introspection.
Consider this is the class you want to test:
class SomeClass {
has Int $!attribute;
method set-value(Int $value) returns Nil {
$!attribute = $value;
return;
}
method get-value returns Int {
return $!attribute;
}
# Private method
method !increase-value-by(Int $extra) returns Nil {
$!attribute += $extra;
return;
}
}
You may create a test like this:
use Test;
use SomeClass;
plan 3;
my SomeClass $some-class = SomeClass.new;
my Method:D $increase-value = $some-class.^find_private_method: 'increase-value-by';
$some-class.set-value: 1;
$increase-value($some-class, 4);
is $some-class.get-value, 5, '1+4 = 5';
$increase-value($some-class, 5);
is $some-class.get-value, 10, '5+5 = 10';
my SomeClass $a-new-class = SomeClass.new;
$a-new-class.set-value: 0;
$increase-value($a-new-class, -1);
is $a-new-class.get-value, -1, '0+(-1) = -1; The method can be used on a new class';
done-testing;
You first create an instance of the class and the use ^find_private_method to get its private Method. Then you can call that Method by passing an instance of a class as the first parameter.
There's a more complete explanation on this answer:
How do you access private methods or attributes from outside the type they belong to?
A fresh cup of tea and #Julio's and #JJ's answers inspired the following:
class SomeClass { method !private ($foo) { say $foo } }
use MONKEY-TYPING; augment class SomeClass { trusts GLOBAL }
my SomeClass $some-class = SomeClass.new;
$some-class!SomeClass::private(42); # 42
My solution tweaks the class using monkey typing. Monkey typing is a generally dodgy thing to do (hence the LOUD pragma). But it seems tailor made for a case just like this. Augment the class with a trusts GLOBAL and Bob's your Uncle.
Raku requires the SomeClass:: qualification for this to work. (Perhaps when RakuAST macros arrive there'll be a tidy way to get around that.) My inclination is to think that having to write a class qualification is OK, and the above solution is much better than the following, but YMMV...
Perhaps, instead:
use MONKEY-TYPING;
augment class SomeClass {
multi method FALLBACK ($name where .starts-with('!!!'), |args) {
.(self, |args) with $?CLASS.^find_private_method: $name.substr: 3
}
}
and then:
$some-class.'!!!private'(42); # 42
I've used:
A multi for the FALLBACK, and have required that the method name string starts with !!!;
A regular method call (. not !);
Calling the method by a string version of its name.
The multi and !!! is in case the class being tested already has one or more FALLBACK methods declared.
A convention of prepending !!! seems more or less guaranteed to ensure that the testing code will never interfere with how the class is supposed to work. (In particular, if there were some call to a private method that didn't exist, and there was existing FALLBACK handling, it would handle that case without this monkey FALLBACK getting involved.)
It should also alert anyone reading the test code that something odd is going on, in the incredibly unlikely case that something weird did start happening, either because I'm missing something that I just can't see, or because some FALLBACK code within a class just so happened to use the same convention.
Besides using introspection, you can try and use a external helper role to access all private methods and call them directly. For instance:
role Privateer {
method test-private-method ( $method-name, |c ) {
self!"$method-name"(|c);
}
}
class Privateed does Privateer {
method !private() { return "⌣" }
}
my $obj = Privateed.new;
say $obj.test-private-method( "private" );
The key here is to call a method by name, which you can do with public and private methods, although for private methods you need to use their special syntax self!.
I have some services which are used by many classes in my application. As an example this could be a LoggerService which logs messages to an internal store and prints them to the console.
This service could look something like this:
export class LoggerService {
let _logs = [];
addLog(msg) {
this._logs.push(msg);
console.log(this._logs.length + ': ' + msg);
}
}
I guess the usual way in Aurelia would be to use this class with dependency injection which works just fine since it uses a singleton per default. An example usage could then look something like this:
import {autoinject, Aurelia} from 'aurelia-framework';
import {LoggerService} from 'LoggerService';
#autoinject
export class SomeViewModel {
let _loggerService;
constructor(loggerService) {
this._loggerService = loggerService
}
somethingChanged() {
this._loggerService.addLog('Something changed...');
}
}
Basically this approach works just fine but it feels a little "clumsy" at a larger scale and I hope that it could be simplified:
Using dependency injection requires a lot of boilerplate code in each "using class" which unnecessarily "bloats" them (import statement, a dedicated class member which needs to be assigned in the constructor etc.).
I'm used to having such classes inside a dedicated namespace which lives in the window object. This allows me convenient debugging since I can call code like MyNamespace.LoggerService.addLog('blahblah'); directly from the command line. How do I accomplish this best with Aurelia?
Is there an more straight forward way which would also give me a better debug experience from the command line?
Honest answer? If you follow ES6 modular code conventions, not really. You will always be importing modules if and when you require them. There are things that you can do, which I'll go over in this answer.
You don't actually need to use Aurelia's dependency injection if you don't want to. However, that means that you'll have to handle the lifecycle of the object yourself. Because your object is a class, you'll have to call "new" on it when you import it. However, you can write your logging module differently, and instead of exporting a class, you can export a function, like so:
let _logs = [];
export var LoggerService = {
addLog: function (msg) {
_logs.push(msg);
console.log(_logs.length + ': ' + msg);
}
}
That way, all you have to do is import LoggerService and call LoggerService.addLog inside of your Aurelia view without having to inject it. This is a more functional approach than the class instantiation approach, which can work very well.
import {LoggerService} from 'LoggerService';
export class SomeViewModel {
constructor() {
}
somethingChanged() {
LoggerService.addLog('Something changed...');
}
}
Another thing you can do (I wouldn't recommend this) is simply make this a window variable. This would mean that you wouldn't have to import modules. This violates module principles, though, and things get messy really quickly. Modules exist for a reason, and although import statements do add lines of code, the benefits are worth the extra space.
For your use case, I would use the function-based approach above, and I would check a "development" flag somewhere so that you can expose properties on the window object so that you can access them through the console. (You wouldn't want this available in a production environment, as people could easily mess up your application.) Something like
if (developmentFlag) { window.loggerService = LoggerService; }
This may be the best approach until we're able to run the import statement in the console REPL.
Geb uses a static field called content to define the contents of a page or module. The value of the content field is a closure.
class GebishOrgHomePage extends Page {
static content = {
manualsMenu {
module MenuModule, $("#header-content ul li", 0)
}
links { $('.link-list li a') }
}
}
Intellij already has support for this content dsl, however it does not support the module and moduleList methods. This causes limited auto-complete support when working with modules.
To fix this I'd like to write a GroovyDSL script that adds the missing method definitions to the content closure and its nested closures. However, I've no idea how to add methods to a closure that is not passed to a method, since enclosingCall requires a concrete method name.
And the other thing is that those methods must have a generic return type like this:
<T extends Module> T module(Class<T> m) {
// return an instance of T
}
If you use the latest snapshot then module() calls will be understood by your IDE. This is down to moving module() to Navigator exactly for what you are after - autocompletion and strong typing.
Have a look at the current version of section 6.4 of the Book of Geb. The moduleList() will be gone in a future release and that section explains what to use instead. The module() method taking a map argument to initialise module properties will also go, you now initialise the module yourself and pass the instance to module() and there is an example of doing this in 6.4. Thanks to all that you will get autocompletion around module defintions and usage in IntelliJ.
I am working on providing a type definition file for fabric.js. The general structure is shown in the following sample:
declare module fabric {
export interface Canvas {
selectionBorderColor: string;
selectionColor: string;
...
}
var Canvas: {
new (): Canvas;
}
}
This pattern allows me to use fabric.Canvas in a 'interface-like' way, so that variables are associated with the fabric.Canvas interface. At the same time it allows me to call "static members" (such as the constructor of fabric.Canvas).
But this leads to a problem when using a field of interface 'fabric.Canvas' within a class. The following sample shows such an case:
This problem only occurs when placing the interface within a module, otherwise everything works fine.
Any solutions for this problem?
There is some type confusion because you have an interface and a field with the same name - I know this is common in the lib.d.ts file, but I don't think it is a good practice when writing new TypeScript code. It seems to be something of a necessity for defining existing code.
If you rename var Canvas to var MyCanvas (or anything else) your code works.
I tend to prefix my interfaces with an I, for example ICanvas - but this isn't a TypeScript convention (yet).
declare module fabric {
export class Canvas {
selectionBorderColor: string;
selectionColor: string;
}
}
class MyClass {
canvas: fabric.Canvas;
}