LCOV - code coverage report
Current view: top level - model - data_model.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 38 38 100.0 %
Date: 2021-12-09 18:46:36 Functions: 0 0 -

          Line data    Source code
       1             : part of flutter_data;
       2             : 
       3             : /// A mixin to "tag" and ensure the implementation of an [id] getter
       4             : /// in data classes managed through Flutter Data.
       5             : ///
       6             : /// It contains private state and methods to track the model's identity.
       7             : abstract class DataModel<T extends DataModel<T>> {
       8             :   Object? get id;
       9             : 
      10             :   // "late" finals
      11             :   String? _key;
      12             :   Map<String, RemoteAdapter>? _adapters;
      13             : 
      14             :   // computed
      15          14 :   String get _internalType => DataHelpers.getType<T>();
      16           7 :   RemoteAdapter<T> get remoteAdapter =>
      17          21 :       _adapters?[_internalType]! as RemoteAdapter<T>;
      18             : 
      19          21 :   bool get isInitialized => _key != null && _adapters != null;
      20             : 
      21             :   // initializers
      22             : 
      23           7 :   T _initialize(final Map<String, RemoteAdapter> adapters,
      24             :       {String? key, bool save = false}) {
      25           7 :     if (isInitialized) {
      26             :       if (save) {
      27             :         // ensure model is persisted, no need to notify
      28          28 :         remoteAdapter.localAdapter.save(_key!, this as T, notify: false);
      29             :       }
      30             :       return this as T;
      31             :     }
      32             : 
      33           7 :     _adapters = adapters;
      34             : 
      35          49 :     _key = remoteAdapter.graph.getKeyForId(remoteAdapter.internalType, id,
      36           7 :         keyIfAbsent: key ?? DataHelpers.generateKey<T>());
      37             : 
      38             :     if (save) {
      39          28 :       remoteAdapter.localAdapter.save(_key!, this as T);
      40             :     }
      41             : 
      42             :     // initialize relationships
      43             :     for (final metadata
      44          34 :         in remoteAdapter.localAdapter.relationshipsFor(this as T).entries) {
      45          12 :       final relationship = metadata.value['instance'] as Relationship?;
      46             : 
      47           6 :       relationship?.initialize(
      48             :         adapters: adapters,
      49             :         owner: this,
      50          12 :         name: metadata.value['name'] as String,
      51          12 :         inverseName: metadata.value['inverse'] as String?,
      52             :       );
      53             :     }
      54             : 
      55             :     return this as T;
      56             :   }
      57             : }
      58             : 
      59             : /// Extension that adds syntax-sugar to data classes,
      60             : /// linking them to common [Repository] methods such as
      61             : /// [save] and [delete].
      62             : extension DataModelExtension<T extends DataModel<T>> on DataModel<T> {
      63             :   /// Initializes a model copying the identity of supplied [model].
      64             :   ///
      65             :   /// Usage:
      66             :   /// ```
      67             :   /// final post = await repository.findOne('1'); // returns initialized post
      68             :   /// final newPost = Post(title: 'test'); // uninitialized post
      69             :   /// newPost.was(post); // new is now initialized with same key as post
      70             :   /// ```
      71           1 :   T was(T model) {
      72             :     assert(model.isInitialized,
      73             :         'Please initialize model before passing it to `was`');
      74           3 :     return _initialize(model._adapters!, key: model._key, save: true);
      75             :   }
      76             : 
      77             :   /// Saves this model through a call equivalent to [Repository.save].
      78             :   ///
      79             :   /// Usage: `await post.save()`, `author.save(remote: false, params: {'a': 'x'})`.
      80             :   ///
      81             :   /// **Requires this model to be initialized.**
      82           2 :   Future<T> save({
      83             :     bool? remote,
      84             :     Map<String, dynamic>? params,
      85             :     Map<String, String>? headers,
      86             :     OnData<T>? onSuccess,
      87             :     OnDataError<T>? onError,
      88             :   }) async {
      89           1 :     _assertInit('save');
      90           3 :     return await remoteAdapter.save(
      91             :       this as T,
      92             :       remote: remote,
      93             :       params: params,
      94             :       headers: headers,
      95             :       onSuccess: onSuccess,
      96             :       onError: onError,
      97             :     );
      98             :   }
      99             : 
     100             :   /// Deletes this model through a call equivalent to [Repository.delete].
     101             :   ///
     102             :   /// Usage: `await post.delete()`
     103             :   ///
     104             :   /// **Requires this model to be initialized.**
     105           2 :   Future<void> delete({
     106             :     bool? remote,
     107             :     Map<String, dynamic>? params,
     108             :     Map<String, String>? headers,
     109             :     OnData<void>? onSuccess,
     110             :     OnDataError<void>? onError,
     111             :   }) async {
     112           1 :     _assertInit('delete');
     113           3 :     await remoteAdapter.delete(
     114             :       this,
     115             :       remote: remote,
     116             :       params: params,
     117             :       headers: headers,
     118             :       onSuccess: onSuccess,
     119             :       onError: onError,
     120             :     );
     121             :   }
     122             : 
     123             :   /// Re-fetch this model through a call equivalent to [Repository.findOne].
     124             :   /// with the current object/[id]
     125             :   ///
     126             :   /// **Requires this model to be initialized.**
     127           2 :   Future<T?> reload({
     128             :     bool? remote,
     129             :     Map<String, dynamic>? params,
     130             :     Map<String, String>? headers,
     131             :   }) async {
     132           1 :     _assertInit('reload');
     133           3 :     return await remoteAdapter.findOne(
     134             :       this,
     135             :       remote: remote,
     136             :       params: params,
     137             :       headers: headers,
     138             :     );
     139             :   }
     140             : 
     141           1 :   void _assertInit(String method) {
     142           1 :     if (isInitialized) {
     143             :       return;
     144             :     }
     145           1 :     throw AssertionError('''\n
     146             : This model MUST be initialized in order to call `$method`.
     147             : 
     148             : DON'T DO THIS:
     149             : 
     150           2 :   final ${_internalType.singularize()} = $T(...);
     151           2 :   ${_internalType.singularize()}.$method(...);
     152             : 
     153             : DO THIS:
     154             : 
     155           2 :   final ${_internalType.singularize()} = $T(...).init(ref.read);
     156           2 :   ${_internalType.singularize()}.$method(...);
     157             : 
     158             : Call `init(ref.read)` on the model first.
     159             : 
     160             : This ONLY happens when a model is manually instantiated
     161             : and had no contact with Flutter Data.
     162             : 
     163             : Initializing models is not necessary in any other case.
     164             : 
     165             : When assigning new models to a relationship, only initialize
     166             : the actual model:
     167             : 
     168             : Family(surname: 'Carlson', dogs: {Dog(name: 'Jerry'), Dog(name: 'Zoe')}.asHasMany)
     169             :   .init(ref.read);
     170           1 : ''');
     171             :   }
     172             : }
     173             : 
     174             : /// Returns a model's `_key` private attribute.
     175             : ///
     176             : /// Useful for testing, debugging or usage in [RemoteAdapter] subclasses.
     177           2 : String? keyFor<T extends DataModel<T>>(T model) => model._key;
     178             : 
     179           1 : @visibleForTesting
     180             : @protected
     181             : RemoteAdapter? adapterFor<T extends DataModel<T>>(T model) =>
     182           1 :     model.remoteAdapter;

Generated by: LCOV version 1.15