How to get function body as string using build_runner and source_gen? - flutter

My goal is to make my unit tests easy to understand. Currently, they are hard to understand because they have so many nested functions.
I want to use build_runner to generate the code of the unit with all functions unwrapped.
So here is an example of my current test:
test.dart
import 'package:example_usage/src/unwrap.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
class Cat {
String sound() => "Meow";
int walk() => 4;
}
class Dog {
final Cat cat;
Dog(this.cat);
String sayHi() {
return this.cat.sound();
}
int jump() {
return this.cat.walk();
}
}
class MockCat extends Mock implements Cat {}
void main() {
MockCat cat;
Dog dog;
#UnWrap()
void setupCatSoundStub() {
when(cat.sound()).thenReturn("Woof");
}
#UnWrap()
void setupCatWalkstub() {
when(cat.walk()).thenReturn(2);
}
#UnWrap()
void expectCatCalled() {
verify(cat.sound());
}
#UnWrap()
void testDogWoof() {
setupCatSoundStub();
dog = Dog(cat);
final sound = dog.sayHi();
expect(sound, "Woof");
expectCatCalled();
}
void expectCatWalked() {
verify(cat.walk());
}
group('Dog Cat Play', () {
setUp(() {
cat = MockCat();
});
test('Dog woof', () {
testDogWoof();
});
test('Dog woof then jump', () {
testDogWoof();
setupCatWalkstub();
final steps = dog.jump();
expect(steps, 2);
expectCatWalked();
});
});
}
I want to generate a code like this
_$test.dart
void _$main() {
MockCat cat;
Dog dog;
void expectCatWalked() {
verify(cat.walk());
}
group('Dog Cat Play', () {
setUp(() {
cat = MockCat();
});
test('Dog woof', () {
// testDogWoof();
// setupCatSoundStub();
when(cat.sound()).thenReturn("Woof");
dog = Dog(cat);
final sound = dog.sayHi();
expect(sound, "Woof");
// expectCatCalled();
verify(cat.sound());
});
test('Dog woof then jump', () {
// testDogWoof();
// setupCatSoundStub();
when(cat.sound()).thenReturn("Woof");
dog = Dog(cat);
final sound = dog.sayHi();
expect(sound, "Woof");
// expectCatCalled();
verify(cat.sound());
// setupCatWalkstub();
when(cat.walk()).thenReturn(2);
final steps = dog.jump();
expect(steps, 2);
expectCatWalked();
});
});
}
I found some tutorial online but I could find documentations about getting the function body into string ( some like JavaScript's Function.prototype.toString() method ) I am new to code generation so I tried to print all fields but I can't find anything like that.
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
class InfoGenerator extends Generator {
#override
FutureOr<String> generate(LibraryReader library, BuildStep buildStep) {
var buffer = StringBuffer();
// library.allElements.forEach((element) {
// buffer.writeln(
// '// ${element.displayName} - ${element.source.fullName} - ${element.declaration}');
// });
library.allElements.whereType<TopLevelVariableElement>().forEach((element) {
buffer.writeln('/*');
buffer.writeln(element.toString());
buffer.writeln('*/');
buffer.writeln(
'// ${element.name} - ${element.kind.displayName} - ${element.declaration}');
});
return buffer.toString();
}
}
I am also new to annotations so I just made this up
/// What to do here ?
class UnWrap {
const UnWrap();
}
Is what I am trying to do even possible ?

Related

How to invoke a listener from another listener?

I'm using in_app_purchase package and I need to convert/map listener which is listening for List<PurchaseDetails> to another listener as shown below:
class Foo {
Foo() {
InAppPurchase.instance.purchaseStream.listen(_listener);
}
void _listener(List<PurchaseDetails> list) {
// How to pass these ids to `addListener()`
final List<String> ids = list.map((e) => e.productID).toList();
}
void addListener(void Function(List<String>) f) {}
}
This is how I want to use my listener
void main() {
Foo().addListener((List<String> ids) {});
}
Despite what your code comment says, I think what you're really asking for is for the internal _listener to invoke the callback that was previously passed as an argument to addListener (and not for _listener to call addListener directly, which it could just do directly).
Just have addListener save the callback to a member variable and let your internal listener invoke that:
class Foo {
void Function(List<String>)? _listener;
Foo() {
InAppPurchase.instance.purchaseStream.listen(_internalListener);
}
void _internalListener(List<PurchaseDetails> list) {
var listener = _listener;
if (listener == null) {
return;
}
final List<String> ids = list.map((e) => e.productID).toList();
listener(ids);
}
void addListener(void Function(List<String>) f) => _listener = f;
}
If you want callers to be able to call addListener multiple times to register multiple callbacks, you would need to store them in a collection (and provide a mechanism to unregister callbacks):
class Foo {
final _listenerMap = <Object, void Function(List<String>)>{};
Foo() {
InAppPurchase.instance.purchaseStream.listen(_internalListener);
}
void _internalListener(List<PurchaseDetails> list) {
if (_listenerMap.isEmpty) {
return;
}
final List<String> ids = list.map((e) => e.productID).toList();
for (var listener in _listenerMap.values) {
listener(ids);
}
}
Object addListener(void Function(List<String>) f) {
var token = Object();
_listenerMap[token] = f;
return token;
}
void removeListener(Object token) {
_listenerMap.remove(token);
}
}

How to override a Dart method on instantiation? [duplicate]

Is there way to overriding method in Dart like JAVA, for example:
public class A {
public void handleLoad() {
}
}
And when overriding:
A a = new A() {
#Override
public void handleLoad() {
// do some code
}
};
No, Dart does not have anonymous classes. You have to create a class that extends A and instantiate it.
No but it much less useful in Dart because you can just reassign function:
typedef void PrintMsg(msg);
class Printer {
PrintMsg foo = (m) => print(m);
}
main() {
Printer p = new Printer()
..foo('Hello') // Hello
..foo = ((String msg) => print(msg.toUpperCase()))
..foo('Hello'); //HELLO
}
However you will need some extra boilerplate to access instance.
Use type Function:
class A {
final Function h
A(this.h);
void handleLoad(String loadResult) { h(loadResult); }
}
Or
class A {
final Function handleLoad;
A(this.handleLoad);
}
A a = new A((String loadResult){
//do smth.
});

Passing one random value to two super arguments

Basically I want to pass a random argument twice to a parent class.
Consider the following example:
import 'dart:math' as math;
class A {
double a;
double b;
A(this.a, this.b) {
if (a != b) {
print('Error');
} else {
print('Alright');
}
}
}
class B extends A {
B() : super(math.Random().nextDouble(), math.Random().nextDouble());
}
main() {
B();
}
Here is the only solution I can come up with, but this feels so hacky... I hope someone else could provide me with a better one.
import 'dart:math' as math;
class A {
double a;
double b;
A(this.a, this.b) {
if (a != b) {
print('Error');
} else {
print('Alright');
}
}
}
class B extends A {
static List<double> _c = [0];
bool uselessBool;
B() : uselessBool = random(),
super(_c[0], _c[0]);
static bool random() {
_c[0] = math.Random().nextDouble();
return true;
}
}
main() {
B();
}
What I which could exist would be something like this (As you would do in python for example):
import 'dart:math' as math;
class A {
double a;
double b;
A(this.a, this.b) {
if (a != b) {
print('Error');
} else {
print('Alright');
}
}
}
class B extends A {
B() {
var c = math.Random().nextDouble();
super(c, c);
}
}
main() {
B();
}
You can create a hidden constructor by making a named constructor starting with _. By doing that we can create the following example which does what you want:
import 'dart:math' as math;
class A {
double a;
double b;
A(this.a, this.b) {
if (a != b) {
print('Error');
} else {
print('Alright');
}
}
}
class B extends A {
B() : this._(math.Random().nextDouble());
B._(double value) : super(value, value);
}
void main() {
B();
}
If you want more complicated logic you can also take a look at factory constructors which basically works as static methods but must return an instance of the class.

Why can't I set a dynamic property inside a nested function?

I'm trying to create a function that can dynamically set the properties on an object like so:
void main() {
final obj = Item();
obj.update(5);
print(obj.xVal);
}
class ObjectBase {
void _setData(current, newValue) {
current = newValue;
print(current);
}
}
class Item extends ObjectBase {
int _x;
int get xVal => _x;
update(x) {
_setData(_x, x);
}
}
The print statement in _setData works fine, but it doesn't actually appear to change _x, even if it has been passed through. I expected that changing the reference here would update it everywhere.
So why isn't this working and is there a fix?
You can assume that I do have good reason to be calling _setData inside update rather than just implementing the functionality in update.
Update:
A real life example of what i'm trying to achieve
class ViewModel extends ChangeNotifier {
void _setDataFromDependency(current, newValue) {
if (!deepDynamicEquality(current, newValue)) {
current = newValue;
notifyListeners();
}
}
}
class ListScreenViewModel extends ViewModel {
int _localCount = 0;
List<int> _globalList;
ListScreenViewModel();
List<int> get globalList => _globalList;
int get localCount => _localCount;
incrementLocal() {
_localCount++;
notifyListeners();
}
void update(ListStore listStore) {
_setDataFromDependency(_globalList, listStore.globalList);
// if (!deepDynamicEquality(_globalList, listStore.globalList)) {
// _globalList = listStore.globalList;
// notifyListeners();
// }
}
}
An oversimplified workaround is to return the value from _setData . #julemand101 has already answered limitations.
class ObjectBase {
int _setData(current, newValue) {
current = newValue;
print('current: $current');
return current;
}
}
class Item extends ObjectBase {
int _x;
int get xVal => _x;
update(x) {
_x = _setData(_x, x);
}
}

How to register factory of part in Dart

I have a lot of part of library. But all of the same type (extends Part)
part1.dart
part of Parts;
class Part1 extends Part { /* ... */ }
parts.add((varOfSomeClass){ return new Part1(varOfSomeClass + 1); });
part2.dart
part of Parts;
class Part2 extends Part { /* ... */ }
parts.add((varOfSomeClass){ return new Part2(varOfSomeClass - 1); });
parts.dart
library Parts;
part "Part1.dart";
part "Part2.dart";
List<Function> parts = new List<Function>();
class Parts {
getPart(int index) {
if (parts.contains(index)) {
return parts[index](someVarOfThisClass);
}
}
}
OUTPUT: error: unexpected token 'parts'
How to get all included factories without create instance all the Part classes?
For example need to do:
BMW.dart
part of Auto;
class BMW {
String color;
BMW(this.color);
}
list.add((color){
return new BMW(color);
});
Lada.dart
part of Auto;
class Lada {
List<int> color;
}
list.add((color){
var auto = new Lada();
auto.color = hex2rgb(color);
return auto;
});
Auto.dart
library Auto;
class Auto {
getByIndex(int index) {
if (list.contains(index)) {
return list[index](color);
}
return null;
}
}
Looks like your problem is that you have code outside of a class or function definition. If I'm guessing what you want to do correctly, you want BMW.dart to look something like
part of Auto;
class BMW {
String color;
BMW(this.color);
}
and then in your main() method have code like
main() {
List list = [];
list.add((color) => new BMW(color));
}
This will get the code above running. It's probably not the best way to structure your program though. You may want to do some more reading on Dart factory constructors. https://www.dartlang.org/dart-tips/dart-tips-ep-11.html is a good place to start.
I don't know what you need it for but how about using mirrors:
main.dart
library cars;
import 'dart:mirrors';
part 'bmw.dart';
part 'audi.dart';
abstract class Car {
}
void main() {
List<Car> cars = new List<Car>();
Map libraries = currentMirrorSystem().libraries;
LibraryMirror mirror = libraries[libraries.keys.last];
mirror.declarations.forEach((Symbol s, DeclarationMirror mirror) {
if(mirror is ClassMirror) {
if(!mirror.isAbstract && mirror.isAssignableTo(reflectType(Car))) {
// new Symbol(mirror.reflectedType.toString()))
cars.add(mirror.newInstance(#Car, []).reflectee);
}
}
});
print(cars);
}
bmw.dart
part of cars;
class Bmw extends Car {
Bmw.Car() {
}
}
audi.dart
part of cars;
class Audi extends Car {
Audi.Car() {
}
}