Let's say we want to create a simple dart file, that just declares a class containing consts. E.g.
class StringNames {
static const String helloWorld = 'Hello World';
}
But rather than write this manually, we want to generate this file programmatically. We want a program that will create this file for us.
We could write such a program in dart itself, e.g.
import 'dart:io';
void main() {
final f1 = 'file1.dart';
File(f1).writeAsString('class stringNames {\nstatic const String helloWorld = \'HelloWorld\';\n}');
}
Naturally this feels wrong. Is there a better way to achieve this?
You could use multi-line string as described here in the Dart language tour:
https://dart.dev/guides/language/language-tour#strings
So your example becomes like this, which is much more readable:
import 'dart:io';
void main() {
final f1 = 'file1.dart';
File(f1).writeAsString('''
class StringNames {
static const String helloWorld = 'Hello World';
}
''');
}
You can also use this and insert values from variables inside the string like this:
import 'dart:io';
void main() {
final f1 = 'file1.dart';
final someValue = 42;
File(f1).writeAsString('''
class StringNames {
static const String helloWorld = 'Hello World';
static const int someValue = $someValue;
}
''');
}
Related
I have a function like this in C language:
char* getString() {
return "SOME_STRING";
}
now I want to invoke it by FFI in dart, and this is my code:
import 'dart:io';
import 'dart:ffi';
void main(List<String> arguments) {
print('${getString()}');
}
final DynamicLibrary nativeAppTokenLib = Platform.isAndroid
? DynamicLibrary.open('lib_native_get_string.so')
: DynamicLibrary.process();
final String Function() getString = nativeAppTokenLib
.lookup<NativeFunction<*** Function()>>('getString')
.asFunction();
I wonder what should I put instead of *** as the native type?
Try:
import 'dart:ffi';
import 'dart:io';
import "package:ffi/ffi.dart";
...
final Pointer<Utf8> Function() _getString = nativeAppTokenLib
.lookup<NativeFunction<Pointer<Utf8> Function()>>('getString')
.asFunction();
String getString() => _getString().toDartString();
This uses package:ffi's Utf8 type to represent characters. The toDartString extension method on Pointer<Utf8> is the intended way to convert those to a string.
I am writing a small "language" to create javascript code. Essentially it is hiding/showing some html form elements. But i need to add custom javascript code to some, e.g. what to do on a click-event.
Action:
'on' eventName=ID 'do' code=CODE
;
terminal BEGIN: "!$";
terminal END: "$!";
terminal CODE:
BEGIN -> END
;
I can now create an Eclipse-Plugin and code in my language, but the value of the field code contains the BEGIN and END characters.
on eventName do !$
var x = thisIsJavaScript();
console.log(x);
$!
My value is:
!$
var x = thisIsJavaScript();
console.log(x);
$!
I want only the part in between without !$ and $!.
Any hint is appreciated.
Thank you very much!
you should write a valueconverter for your terminal rule
import org.eclipse.xtext.common.services.DefaultTerminalConverters;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.nodemodel.INode;
import com.google.inject.Inject;
public class MyDslConverters extends DefaultTerminalConverters {
#Inject
private CODEValueConverter codeValueConverter;
#ValueConverter(rule = "CODE")
public IValueConverter<String> CODE() {
return codeValueConverter;
}
public static class CODEValueConverter implements IValueConverter<String> {
#Override
public String toValue(String string, INode node) throws ValueConverterException {
return string.substring(2, string.length()-2);
}
#Override
public String toString(String value) throws ValueConverterException {
return "!$" + value + "$!";
}
}
}
I have a list of models that I need to create a mini reflective system.
I analyzed the Serializable package and understood how to create one generated file per file, however, I couldn't find how can I create one file for a bulk of files.
So, how to dynamically generate one file, using source_gen, for a list of files?
Example:
Files
user.dart
category.dart
Generated:
info.dart (containg information from user.dart and category.dart)
Found out how to do it with the help of people in Gitter.
You must have one file, even if empty, to call the generator. In my example, it is lib/batch.dart.
source_gen: ^0.5.8
Here is the working code:
The tool/build.dart
import 'package:build_runner/build_runner.dart';
import 'package:raoni_global/phase.dart';
main() async {
PhaseGroup pg = new PhaseGroup()
..addPhase(batchModelablePhase(const ['lib/batch.dart']));
await build(pg,
deleteFilesByDefault: true);
}
The phase:
batchModelablePhase([Iterable<String> globs =
const ['bin/**.dart', 'web/**.dart', 'lib/**.dart']]) {
return new Phase()
..addAction(
new GeneratorBuilder(const
[const BatchGenerator()], isStandalone: true
),
new InputSet(new PackageGraph.forThisPackage().root.name, globs));
}
The generator:
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:glob/glob.dart';
import 'package:build_runner/build_runner.dart';
class BatchGenerator extends Generator {
final String path;
const BatchGenerator({this.path: 'lib/models/*.dart'});
#override
Future<String> generate(Element element, BuildStep buildStep) async {
// this makes sure we parse one time only
if (element is! LibraryElement)
return null;
String libraryName = 'raoni_global', filePath = 'lib/src/model.dart';
String className = 'Modelable';
// find the files at the path designed
var l = buildStep.findAssets(new Glob(path));
// get the type of annotation that we will use to search classes
var resolver = await buildStep.resolver;
var assetWithAnnotationClass = new AssetId(libraryName, filePath);
var annotationLibrary = resolver.getLibrary(assetWithAnnotationClass);
var exposed = annotationLibrary.getType(className).type;
// the caller library' name
String libName = new PackageGraph.forThisPackage().root.name;
await Future.forEach(l.toList(), (AssetId aid) async {
LibraryElement lib;
try {
lib = resolver.getLibrary(aid);
} catch (e) {}
if (lib != null && Utils.isNotEmpty(lib.name)) {
// all objects within the file
lib.units.forEach((CompilationUnitElement unit) {
// only the types, not methods
unit.types.forEach((ClassElement el) {
// only the ones annotated
if (el.metadata.any((ElementAnnotation ea) =>
ea.computeConstantValue().type == exposed)) {
// use it
}
});
});
}
});
return '''
$libName
''';
}
}
It seems what you want is what this issue is about How to generate one output from many inputs (aggregate builder)?
[Günter]'s answer helped me somewhat.
Buried in that thread is another thread which links to a good example of an aggregating builder:
1https://github.com/matanlurey/build/blob/147083da9b6a6c70c46eb910a3e046239a2a0a6e/docs/writing_an_aggregate_builder.md
The gist is this:
import 'package:build/build.dart';
import 'package:glob/glob.dart';
class AggregatingBuilder implements Builder {
/// Glob of all input files
static final inputFiles = new Glob('lib/**');
#override
Map<String, List<String>> get buildExtensions {
/// '$lib$' is a synthetic input that is used to
/// force the builder to build only once.
return const {'\$lib$': const ['all_files.txt']};
}
#override
Future<void> build(BuildStep buildStep) async {
/// Do some operation on the files
final files = <String>[];
await for (final input in buildStep.findAssets(inputFiles)) {
files.add(input.path);
}
String fileContent = files.join('\n');
/// Write to the file
final outputFile = AssetId(buildStep.inputId.package,'lib/all_files.txt');
return buildStep.writeAsString(outputFile, fileContent);
}
}
I have this JavaScript class:
'use strict;'
/* global conf */
var properties = {
'PROPERTIES': {
'CHANNEL': 'sport',
'VIEW_ELEMENTS': {
'LOADER_CLASS': '.loader',
'SPLASH_CLASS': '.splash'
}
}
};
In JavaScript I can use these properties: properties.PROPERTIES.CHANNEL
Is it possible to convert this to DART? Is there a best practise to do that?
There are different way.
You could just create a map
my_config.dart
const Map properties = const {
'CHANNEL': 'sport',
'VIEW_ELEMENTS': const {
'LOADER_CLASS': '.loader',
'SPLASH_CLASS': '.splash'
}
}
then use it like
main.dart
import 'my_config.dart';
main() {
print(properties['VIEW_ELEMENTS']['SPLASH_CLASS']);
}
or you can use classes to get proper autocompletion and type checking
my_config.dart
const properties = const Properties('sport', const ViewElements('.loader', '.splash'));
class Properties {
final String channel;
final ViewElements viewElements;
const Properties(this.channel, this.viewElements;
}
class ViewElements {
final String loaderClass;
final String splashClass;
const ViewElements(this.loaderClass, this.splashClass);
}
main.dart
import 'my_config.dart';
main() {
print(properties.viewElements.splashClass);
}
Following up on the above answer using classes, it may be convenient to implement static variables, the downside is that it still must be compiled/rebuilt.
class CONFIG {
static final String BUILD = "Release";
static final String DEPLOYMENT = "None";
}
This can be used from a separate class after importing via:
var xyz = CONFIG.BUILD;
I got this Dart Script below and I want to access the methods from the class hello_world by JavaScript after I compiled the Dart Script with dart2js.
Does anybody know how this works?!
I already know how to access the functions like foo(...), thats not the problem, but it does not work the same way with classes and methods.
And the tutorials on dartlang.org only explain how to access functions, not methods and classes.
I dont get it...
import 'dart:js' as js;
class hello_world {
String hello = 'Hello World!';
String getHello() {
print("getHello!!!!!");
return hello;
}
void ausgabe() {
print("Hallo Welt");
//return 0;
}
}
String foo(int n) {
print("hallo");
void foo2() {
print("hallo2");
}
//works
js.context['foo2'] = foo2;
return 'Hallo';
}
void main() {
int zahl1 = 3;
int zahl2 = 1234;
String w = 'test';
hello_world test = new hello_world();
//works
js.context['foo'] = foo;
}
Assuming you want to create a Js function bind on a Dart method you can do almost the same thing :
void main() {
hello_world test = new hello_world();
// define a 'getHelloOnTest' Js function
js.context['getHelloOnTest'] = test.getHello;
}
Now on Js side you can use :
getHelloOnTest();