/decorator

Python's decorators in dart :fireworks:

Primary LanguageDartMIT LicenseMIT

Decorator

LICENSE Build status Code coverage Pub version Commitizen friendly Commitizen style Maintained

🚀 About

Python's decorators in dart 🎆

Note: This is a work-in-progress

📄 TOC

  1. 🚀 About
  2. 📄 TOC
  3. 📖 Usage
    1. 1. Decorator annotation
    2. 2. Decorator generator
  4. 🚥 Versioning
  5. 📝 Milestones
  6. 🏅 Principle
  7. 👥 Contributing
  8. :octocat: Git
  9. 💄 Code style
  10. ✅ Testing
  11. ✨ Features and 🐛bugs
  12. 📰 Changelog
    1. 1. Decorator annotation
      1. 0.0.1
    2. 2. Decorator generator
      1. 0.0.1
  13. 📜 License

📖 Usage

1. Decorator annotation

import 'package:decorator/decorator.dart';
  
/// There are 2 ways for creating custom decorators
  
/// 1. Either use the provided [DecorateWith] decorator to create wrapper
/// functions and use [DecorateWith] as a proxy decorator
  
/// Greets the host
HostElement<R> greet<R>(HostElement<R> host) {
  print('Hello $host');
  
  return host;
}
  
/// 2. Or implement the [Wrapper] and [FunctionDecorator] interfaces, and create
/// the decorator yourself
  
/// Decorater that null checks all args
class ArgumentsNotNull implements Wrapper, FunctionDecorator {
  const ArgumentsNotNull();
  @override
  bool get runInRelease => true;
  
  @override
  HostElement<R> wraps<R>(HostElement<R> host) {
    if (host.args?.isNotEmpty ?? false) {
      for (var arg in host.args.keys) {
        if (host.args[arg] == null) {
          throw ArgumentError.notNull(arg);
        }
      }
    }
  
    if (host.kwargs?.keys?.isNotEmpty ?? false) {
      for (var kwarg in host.kwargs.keys) {
        if (host.kwargs[kwarg] == null) {
          throw ArgumentError.notNull(kwarg);
        }
      }
    }
  
    return host;
  }
}
  

2. Decorator generator

/// Creating a library is required for part generation to work
library decorator_generator.example;
  
/// Import the decorator package
import 'package:decorator/decorator.dart';
import 'package:logging/logging.dart';
  
/// Import the generated part
/// this is generated by running `pub run build_runner build`
part 'example.d.dart';
  
Future<void> main() async {
  print(joinArgs(['First'], arg2: ['Second']));
  print(joinArgs3(['First'], arg2: ['Second']));
  print(await joinArgs4(['First'], arg2: ['Second']));
  
  /// This will throw automatically before [_joinArgs2]
  /// gets executed
  // print(joinArgs2(['First'], null));
}
  
const Level myLevel = Level('mylevel', 555);
  
/// Logger for `decorator_generator.example`
final Logger logger = Logger('decorator_generator.example');
  
HostElement<R> greet<R>(HostElement<R> host) {
  print('Hello $host');
  
  return host;
}
  
/// This function joins its args
@MyLogger('_joinArgs', myLevel)
String _joinArgs(List<String> arg1, {List<String> arg2}) {
  print('_joingArgs executed');
  
  return ((arg1 ?? [])..addAll(arg2 ?? [])).join();
}
  
/// This function also joins its args but doesnt check them against being null
@ArgumentsNotNull()
String _joinArgs2(List<String> arg1, [List<String> arg2]) {
  print('_joinArgs2 executed');
  
  /// Here arguments are not being null checked
  /// but the [ArgumentsNotNull] decorator will throw even before this code gets
  /// executed
  return (arg1..addAll(arg2)).join();
}
  
/// This one also joins its args but it is decorated with a [HostWrapper], this
/// is useful when the decorater doesn't require any additional args.
@DecorateWith(greet)
String _joinArgs3(List<String> arg1, {List<String> arg2}) {
  print('_joingArgs3 executed');
  
  return ((arg1 ?? [])..addAll(arg2 ?? [])).join();
}
  
/// Another one just for fun
@MyLogger.detached('loggerName')
Future<String> _joinArgs4(List<String> arg1, {List<String> arg2}) async {
  print('_joingArgs4 executed');
  
  return ((arg1 ?? [])..addAll(arg2 ?? [])).join();
}
  
/// Decorater that null checks all args
class ArgumentsNotNull implements Wrapper, FunctionDecorator {
  const ArgumentsNotNull();
  @override
  bool get runInRelease => true;
  
  @override
  HostElement<R> wraps<R>(HostElement<R> host) {
    if (host.args?.isNotEmpty ?? false) {
      for (var arg in host.args.keys) {
        if (host.args[arg] == null) {
          throw ArgumentError.notNull(arg);
        }
      }
    }
  
    if (host.kwargs?.keys?.isNotEmpty ?? false) {
      for (var kwarg in host.kwargs.keys) {
        if (host.kwargs[kwarg] == null) {
          throw ArgumentError.notNull(kwarg);
        }
      }
    }
  
    return host;
  }
}
  
/// Decorater that logs the given host with any args
class MyLogger implements Wrapper, FunctionDecorator {
  /// Name of the logger to use
  final String loggerName;
  final Level logLevel;
  
  /// Wether this is a detached logger
  final bool isDetached;
  
  /// Uses the given logger [loggerName] for logging at [logLevel]
  const MyLogger(this.loggerName, [this.logLevel = Level.FINEST])
      : isDetached = false;
  
  /// Uses a detached logger for logging
  const MyLogger.detached(this.loggerName, [this.logLevel = Level.FINEST])
      : isDetached = true;
  
  @override
  bool get runInRelease => false;
  
  @override
  HostElement<R> wraps<R>(HostElement<R> host) {
    final message = '$host was called with '
        '\nargs:${host.args} and \nkwargs:${host.kwargs}';
  
    if (isDetached) {
      Logger.detached(loggerName).log(logLevel, message);
    } else {
      Logger(loggerName).log(logLevel, message);
    }
  
    return host;
  }
}
  

🚥 Versioning

This project follows Semantic Versioning 2.0.0

📝 Milestones

  • Prepare v1.0.0
    • allow decorating class methods
    • allow decorating class fields
    • allow decorating top-level methods
    • allow decorating top-level fields
    • allow decorating classes
    • allow decorating libraries

🏅 Principle

This project follows The Twelve-Factor App principle

👥 Contributing

  • 🍴 Fork this repo

  • ⬇️ Clone your forked version
    git clone https://github.com/<you>/decorator.git

  • ➕ Add this repo as a remote
    git remote add upstream https://github.com/devkabiir/decorator.git

  • ⏬ Make sure you have recent changes
    git fetch upstream

  • ✨ Make a new branch with your proposed changes/fixes/additions
    git checkout upstream/master -b name_of_your_branch

  • 📑 Make sure you follow guidelines for Git

  • ⏫ Push your changes
    git push origin name_of_your_branch

  • 🔃 Make a pull request

:octocat: Git

  • ✔️ Sign all commits. Learn about signing-commits
  • Use commitizen with cz-emoji adapter
  • Check existing commits to get an idea
  • Run the pre_commit script from project root pub run pre_commit
  • If you're adding an and in your commit message, it should probably be separate commits
  • Link relevant issues/commits with a # sign in the commit message
  • Limit message length per line to 72 characters (excluding space required for linking issues/commits)
  • Add commit description if message isn't enough for explaining changes

💄 Code style

  • Maintain consistencies using included .editorconfig
  • Everything else as per standard dart guidelines

✅ Testing

  • Add tests for each new addition/feature
  • Do not remove/change tests when refactoring
    • unless fixing already broken test.

✨ Features and 🐛bugs

Please file feature requests and bugs at the issue-tracker.

📰 Changelog

Changes for latest release at github-releases

1. Decorator annotation

0.0.1

  • Initial version

2. Decorator generator

0.0.1

  • Initial version

📜 License

MIT License
  
Copyright (c) 2019-Present Dinesh Ahuja <dev@kabiir.me>
  
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
  
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
  
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.