ClassLoader

Build Status

#Milestones ###0.0.3 * method, field and metadata optimizations

###0.0.4 * class schema for validation * similar to JSON schema?

###0.0.5 * performance optimizations

#How to use ###Load class There are two ways to load a specific class. So you are able to load a class by its library and name. Let's say we have the following class

library player.data;

class AudioFile { }

Then you can load this class like this

import 'data/audio_file.dart';

main() async {
    var loader = new ClassLoader<AudioFile>(#player.data, #AudioFile);
    await loader.load();
}

Instead of using #player.data it is also possible to use new Symbol('player.data'). Another way to load a class is by using an instance of it which is useful whenever you need to access the metadata of an already instantiated class.

import 'data/audio_file.dart';

main() async {
    var audioFile = new AudioFile();
    var loader = new ClassLoader<AudioFile>.fromInstance(audioFile);
    await loader.load();
}

Private class members are hidden by default. Whenever you need to access private members use the excludePrivateMembers parameter when calling load().

await loader.load(excludePrivateMembers: false);
var pm = loader.fields[#_privateMember];

###Set or get value (field) To access a field like fileName in the following code snippet

class AudioFile { 
    String fileName = "example"; 
}

you can use loader.fields which is a collection that contains all fields of the reflected class.

// set value
loader.fields[#fileName].set('newFile');

// get value
var name = loader.fields[#fileName].get();

###Set or get value (getter & setter) Whenever you need to access a getter or setter use loader.getter and loader.setter.

class AudioFile { 
    String _composer;
        
    String get composer => _composer; 
    set composer(String c) => _composer = c; 
}
// set value
loader.setter[#composer].set('example composer');

// get value
var composer = loader.getter[#composer].get();

A setter can also be accessed by using a setter name composer=. In this case you have to use new Symbol() because the = seems not to be allowed when using the # syntax.

// set value
loader.setter[new Symbol('composer=')].set('example composer');

###Invoke method You can also invoke methods. It is possible to pass positional and named parameter. To access methods you have to use the methods collection provided by the loader instance.

class AudioFile { 
    void mute() {}
    AudioFile convert({format: 'mp3'}) {}
    bool incrementVolumeBy(int i) {}
}
// invoke without parameter
loader.methods[#mute].invoke();

// invoke with positional parameter
var isIncremented = loader.methods[#incrementVolumeBy].invoke([10]);

// invoke with named parameter
loader.methods[#convert].invoke([], {
    #format: 'flac'
});

###Metadata Another useful functionality is the possibility to access the metadata of a class or to query class members by annotations.

@TrackAuthor('Author Name')
class AudioFile {
  String _fileName;
  
  @AudioVolume
  int volume = 100;

  @FileName
  String get fileName => _fileName;

  @FileName
  set fileName(String name) => _fileName = name;

  @MuteMethod
  void mute() {
    // does nothing
  }
}
// Check whether class has specific annotation 
var hasAuthorAnnotation = loader.metadata.contains(#TrackAuthor);

// Get specific annotation
var authorAnnotation = loader.metadata.firstWhere((name, meta) => name == #TrackAuthor && (meta as TrackAuthor).name == 'Author Name', orElse: () => null);

if(authorAnnotation != null) {
    print((authorAnnotation as TrackAuthor).name);
}


// Get first occurrence of method with specific annotation
var muteMethod = loader.methods.firstWhereMetadata((name, meta) => name == #MuteMethod, orElse: () => null);

if(muteMethod != null) {
    muteMethod.invoke();
}


// Get all setter and getter with specific annotation
var fileNameSetter = loader.setter.whereMetadata((name, annotation) => name == #FileName);
var fileNameGetter = loader.getter.whereMetadata((name, annotation) => name == #FileName);

if(fileNameSetter.isNotEmpty()) {
    fileNameSetter.first.set('test_file_name');
}


// Get first occurrence of field with specific annotation 
var volumeField = loader.fields.firstWhereMetadata((name, meta) => name == #AudioVolume, orElse: () => null);

if(volumeField != null) {
    volumeField.set(50);
}

Libraries

apetheory.class_loader
apetheory.class_loader.utilities