LCOV - code coverage report
Current view: top level - src/model - data_model.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 43 44 97.7 %
Date: 2020-07-30 22:52:57 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          10 :   String get _type => DataHelpers.getType<T>();
      16          20 :   RemoteAdapter<T> get _adapter => _adapters[_type] as RemoteAdapter<T>;
      17          15 :   bool get _isInitialized => _key != null && _adapters != null;
      18             : 
      19             :   // initializers
      20             : 
      21           5 :   T _initialize(final Map<String, RemoteAdapter> adapters,
      22             :       {final String key, final bool save}) {
      23          10 :     if (_isInitialized) return _this;
      24             : 
      25          10 :     _this._adapters = adapters;
      26             : 
      27           5 :     assert(_adapter != null, '''\n
      28           0 : Please ensure `Repository<$T>` has been correctly initialized.''');
      29             : 
      30          50 :     _this._key = _adapter.graph.getKeyForId(_this._adapter.type, _this.id,
      31           5 :         keyIfAbsent: key ?? DataHelpers.generateKey<T>());
      32             : 
      33             :     if (save ?? false) {
      34          30 :       _adapter.localAdapter.save(_this._key, _this);
      35             :     }
      36             : 
      37             :     // initialize relationships
      38             :     for (final metadata
      39          29 :         in _adapter.localAdapter.relationshipsFor(_this).entries) {
      40           8 :       final relationship = metadata.value['instance'] as Relationship;
      41             : 
      42           4 :       relationship?.initialize(
      43             :         adapters: adapters,
      44           4 :         owner: _this,
      45           4 :         name: metadata.key,
      46           8 :         inverseName: metadata.value['inverse'] as String,
      47             :       );
      48             :     }
      49             : 
      50           5 :     return _this;
      51             :   }
      52             : }
      53             : 
      54             : /// Extension that adds syntax-sugar to data classes,
      55             : /// linking them to common [Repository] methods such as
      56             : /// [save] and [delete].
      57             : extension DataModelExtension<T extends DataModel<T>> on DataModel<T> {
      58           1 :   T get _this => this as T;
      59             : 
      60             :   /// Access to this [DataModel]'s [RemoteAdapter] instance.
      61             :   ///
      62             :   /// Intended for use in extensions to augment this model's API
      63             :   /// like [save], [delete], etc.
      64           1 :   @protected
      65             :   @visibleForTesting
      66           1 :   RemoteAdapter<T> get internalAdapter => _adapter;
      67             : 
      68             :   /// Initializes a model copying the identity of supplied [model].
      69             :   ///
      70             :   /// Usage:
      71             :   /// ```
      72             :   /// final post = await repository.findOne('1'); // returns initialized post
      73             :   /// final newPost = Post(title: 'test'); // uninitialized post
      74             :   /// newPost.was(post); // new is now initialized with same key as post
      75             :   /// ```
      76           1 :   T was(T model) {
      77           1 :     assert(model != null && model._isInitialized,
      78             :         'Please initialize model before passing it to `was`');
      79           4 :     return _this._initialize(model._adapters, key: model._key, save: true);
      80             :   }
      81             : 
      82             :   /// Saves this model through a call equivalent to [Repository.save].
      83             :   ///
      84             :   /// Usage: `await post.save()`, `author.save(remote: false, params: {'a': 'x'})`.
      85             :   ///
      86             :   /// **Requires this model to be initialized.**
      87           1 :   Future<T> save(
      88             :       {bool remote,
      89             :       Map<String, dynamic> params,
      90             :       Map<String, String> headers}) async {
      91           1 :     _assertInit('save');
      92           4 :     return await _adapter.save(_this,
      93             :         remote: remote, params: params, headers: headers, init: true);
      94             :   }
      95             : 
      96             :   /// Deletes this model through a call equivalent to [Repository.delete].
      97             :   ///
      98             :   /// Usage: `await post.delete()`
      99             :   ///
     100             :   /// **Requires this model to be initialized.**
     101           1 :   Future<void> delete(
     102             :       {bool remote,
     103             :       Map<String, dynamic> params,
     104             :       Map<String, String> headers}) async {
     105           1 :     _assertInit('delete');
     106           4 :     await _adapter.delete(_this,
     107             :         remote: remote, params: params, headers: headers);
     108             :   }
     109             : 
     110             :   /// Re-fetch this model through a call equivalent to [Repository.findOne].
     111             :   /// with the current object/[id]
     112             :   ///
     113             :   /// **Requires this model to be initialized.**
     114           1 :   Future<T> reload(
     115             :       {bool remote,
     116             :       Map<String, dynamic> params,
     117             :       Map<String, String> headers}) async {
     118           1 :     _assertInit('reload');
     119           4 :     return await _adapter.findOne(_this,
     120             :         remote: remote, params: params, headers: headers, init: true);
     121             :   }
     122             : 
     123             :   /// Watch this model through a call equivalent to [Repository.watchOne].
     124             :   /// with the current object/[id].
     125             :   ///
     126             :   /// **Requires this model to be initialized.**
     127           1 :   DataStateNotifier<T> watch(
     128             :       {bool remote,
     129             :       Map<String, dynamic> params,
     130             :       Map<String, String> headers,
     131             :       AlsoWatch<T> alsoWatch}) {
     132           1 :     _assertInit('watch');
     133           3 :     return _adapter.watchOne(_this,
     134             :         remote: remote, params: params, headers: headers, alsoWatch: alsoWatch);
     135             :   }
     136             : 
     137           1 :   void _assertInit(String method) {
     138           1 :     assert(_isInitialized, '''\n
     139             : This model MUST be initialized in order to call `$method`.
     140             : 
     141             : DON'T DO THIS:
     142             : 
     143           2 :   final ${_type.singularize()} = $T(...);
     144           2 :   ${_type.singularize()}.$method(...);
     145             : 
     146             : DO THIS:
     147             : 
     148           2 :   final ${_type.singularize()} = $T(...).init(context);
     149           2 :   ${_type.singularize()}.$method(...);
     150             : 
     151             : Call `init(context)` on the model first.
     152             : 
     153             : This ONLY happens when a model is manually instantiated
     154             : and had no contact with Flutter Data.
     155             : 
     156             : Initializing models is not necessary in any other case.
     157             : 
     158             : When assigning new models to a relationship, only initialize
     159             : the actual model:
     160             : 
     161             : Family(surname: 'Carlson', dogs: {Dog(name: 'Jerry'), Dog(name: 'Zoe')}.asHasMany).init(context);
     162           1 : ''');
     163             :   }
     164             : }
     165             : 
     166             : /// Returns a model's `_key` private attribute.
     167             : ///
     168             : /// Useful for testing, debugging or usage in [RemoteAdapter] subclasses.
     169           2 : String keyFor<T extends DataModel<T>>(T model) => model?._key;

Generated by: LCOV version 1.14