├── .fvmrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_report.yaml ├── README.md ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── melos.yaml ├── packages ├── cached │ ├── .gitignore │ ├── .metadata │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── build.yaml │ ├── cached_logo.png │ ├── cached_sygnet.png │ ├── example │ │ ├── README.md │ │ ├── cached_example │ │ │ ├── .gitignore │ │ │ ├── CHANGELOG.md │ │ │ ├── README.md │ │ │ ├── analysis_options.yaml │ │ │ ├── bin │ │ │ │ ├── example.dart │ │ │ │ └── gen.dart │ │ │ ├── pubspec.lock │ │ │ └── pubspec.yaml │ │ └── persistent_storage_example │ │ │ ├── .gitignore │ │ │ ├── CHANGELOG.md │ │ │ ├── README.md │ │ │ ├── analysis_options.yaml │ │ │ ├── bin │ │ │ ├── example.dart │ │ │ ├── gen.dart │ │ │ ├── todo.dart │ │ │ └── todo_storage.dart │ │ │ ├── pubspec.lock │ │ │ └── pubspec.yaml │ ├── lib │ │ ├── cached.dart │ │ └── src │ │ │ ├── cached_generator.dart │ │ │ ├── config.dart │ │ │ ├── extensions.dart │ │ │ ├── models │ │ │ ├── cache_peek_method.dart │ │ │ ├── cached_function.dart │ │ │ ├── cached_function_local_config.dart │ │ │ ├── cached_getter.dart │ │ │ ├── cached_method.dart │ │ │ ├── check_if_should_cache_method.dart │ │ │ ├── class_with_cache.dart │ │ │ ├── clear_all_cached_method.dart │ │ │ ├── clear_cached_method.dart │ │ │ ├── constructor.dart │ │ │ ├── deletes_cache_method.dart │ │ │ ├── param.dart │ │ │ └── streamed_cache_method.dart │ │ │ ├── templates │ │ │ ├── all_params_template.dart │ │ │ ├── cache_peek_method_template.dart │ │ │ ├── cached_getter_template.dart │ │ │ ├── cached_method_template.dart │ │ │ ├── cached_method_with_params_template.dart │ │ │ ├── class_template.dart │ │ │ ├── clear_all_cached_method_template.dart │ │ │ ├── clear_cached_method_template.dart │ │ │ ├── deletes_cache_method_template.dart │ │ │ ├── file_template.dart │ │ │ ├── interface_template.dart │ │ │ ├── param_template.dart │ │ │ └── streamed_method_template.dart │ │ │ └── utils │ │ │ ├── asserts.dart │ │ │ ├── common_generator.dart │ │ │ ├── persistent_storage_holder_texts.dart │ │ │ ├── type_cast_appender.dart │ │ │ └── utils.dart │ ├── pubspec.yaml │ └── test │ │ ├── cache_peek_method_generation_test.dart │ │ ├── cached_getter_generation_test.dart │ │ ├── cached_method_generation_test.dart │ │ ├── cast_appender_test.dart │ │ ├── class_generation_test.dart │ │ ├── clear_all_cached_method_generation_test.dart │ │ ├── clear_cached_method_generation_test.dart │ │ ├── constructor_generation_test.dart │ │ ├── deletes_cache_method_generation_test.dart │ │ ├── inputs │ │ ├── cache_peek_method_generation_test_input.dart │ │ ├── cached_getter_generation_test_input.dart │ │ ├── cached_method_generation_test_input.dart │ │ ├── class_generation_test_input.dart │ │ ├── clear_all_cached_method_generation_test_input.dart │ │ ├── clear_cached_method_generation_test_input.dart │ │ ├── constructor_generation_test_input.dart │ │ ├── deletes_cache_method_generation_test_input.dart │ │ ├── persistent_storage_test_input.dart │ │ ├── should_cache_generation_test_input.dart │ │ └── streamed_cache_method_generation_test_input.dart │ │ ├── integration │ │ ├── async_integration_test.dart │ │ ├── asynchronous │ │ │ └── cached_test_asynchronous.dart │ │ ├── conditional │ │ │ └── cached_test_conditional.dart │ │ ├── conditional_integration_test.dart │ │ ├── direct_persistent_storage │ │ │ └── cached_test_direct_persistent_storage.dart │ │ ├── lazy_persistent_storage_integration_test.dart │ │ ├── persistent_storage │ │ │ └── cached_test_persistent_storage.dart │ │ ├── persistent_storage_integration_test.dart │ │ ├── simple │ │ │ └── cached_test_simple.dart │ │ ├── simple_integration_test.dart │ │ ├── static │ │ │ └── cached_test_static.dart │ │ └── static_integration_test.dart │ │ ├── persistent_storage_generation_test.dart │ │ ├── should_cache_generation_test.dart │ │ ├── streamed_cache_method_generation_test.dart │ │ └── utils │ │ ├── test_storage.dart │ │ ├── test_storage_to_write.dart │ │ ├── test_storage_with_data.dart │ │ └── test_utils.dart └── cached_annotation │ ├── .gitignore │ ├── .metadata │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── lib │ ├── cached_annotation.dart │ └── src │ │ ├── cache_key.dart │ │ ├── cache_peek.dart │ │ ├── cached.dart │ │ ├── clear_all_cached.dart │ │ ├── clear_cached.dart │ │ ├── deletes_cache.dart │ │ ├── direct_persistent_cached.dart │ │ ├── ignore.dart │ │ ├── ignore_cache.dart │ │ ├── lazy_persistent_cached.dart │ │ ├── persistent_cached.dart │ │ ├── persistent_storage │ │ ├── cached_storage.dart │ │ ├── not_implemented_storage.dart │ │ └── persistent_storage_holder.dart │ │ ├── streamed_cache.dart │ │ └── with_cache.dart │ └── pubspec.yaml ├── pubspec.lock ├── pubspec.yaml └── scripts └── coverage.sh /.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.29.2" 3 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [] 5 | projects: [] 6 | assignees: [] 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | ## Thanks for taking the time to fill out this bug report! 12 | - type: textarea 13 | id: what-happened 14 | attributes: 15 | label: Desctibe the bug 16 | description: "A clear and concise description of what the bug is." 17 | placeholder: "I was trying to..." 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: repro 22 | attributes: 23 | label: Reproduction steps 24 | description: "How do you trigger this bug? Please walk us through it step by step." 25 | placeholder: | 26 | 1. 27 | 2. 28 | ... 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: expected 33 | attributes: 34 | label: Expected behaviour 35 | description: "What effect do you expect after fix?" 36 | placeholder: "I expect to see..." 37 | validations: 38 | required: true 39 | - type: input 40 | id: dart_version 41 | attributes: 42 | label: Dart version 43 | description: What version of dart are you using? 44 | placeholder: "2.19.0, 3.0.0, 3.1.0, etc." 45 | validations: 46 | required: true 47 | - type: dropdown 48 | id: version 49 | attributes: 50 | label: Package version 51 | description: What version of our package are you using? 52 | options: 53 | - "1.0.0" 54 | - "1.0.1" 55 | - "1.0.2" 56 | - "1.0.3" 57 | - "1.0.4" 58 | - "1.1.0" 59 | - "1.2.0" 60 | - "1.2.1" 61 | - "1.2.2" 62 | - "1.3.0" 63 | - "1.3.1" 64 | - "1.4.0" 65 | - "1.5.0" 66 | - "1.5.1" 67 | - "1.6.0" 68 | - "1.6.1" 69 | - "1.6.2" 70 | - "1.6.3" 71 | - "1.6.4" 72 | - "1.7.0" 73 | validations: 74 | required: true 75 | - type: dropdown 76 | id: platform 77 | attributes: 78 | label: What platform are you seeing the problem on? 79 | multiple: true 80 | options: 81 | - iOS 82 | - Android 83 | - Web 84 | - Windows 85 | - macOS 86 | - Linux 87 | - All 88 | validations: 89 | required: true 90 | - type: textarea 91 | id: logs 92 | attributes: 93 | label: Relevant log output 94 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 95 | placeholder: "Paste logs here or tell us about not having any logs." 96 | render: shell 97 | validations: 98 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_report.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request Report 2 | description: File a feature request report 3 | title: "[Feature]: " 4 | labels: [] 5 | projects: [] 6 | assignees: [] 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | ## Thanks for taking the time to fill out this feature request report! 12 | - type: textarea 13 | id: problem_description 14 | attributes: 15 | label: Problem 16 | description: "A clear and concise description of what the problem is." 17 | placeholder: "I was trying to..." 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: desired_solution 22 | attributes: 23 | label: Desired Solution 24 | description: "What do you want to happen?" 25 | placeholder: "I want to be able to..." 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: alternatives_considered 30 | attributes: 31 | label: Alternatives Considered 32 | description: "Have you tried any alternative approaches?" 33 | placeholder: "I tried..." 34 | - type: dropdown 35 | id: platform 36 | attributes: 37 | label: On which platorm do you expect this solution? 38 | multiple: true 39 | options: 40 | - iOS 41 | - Android 42 | - Web 43 | - Windows 44 | - macOS 45 | - Linux 46 | - All 47 | validations: 48 | required: true -------------------------------------------------------------------------------- /.github/README.md: -------------------------------------------------------------------------------- 1 | ../packages/cached/README.md -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pub" 4 | directory: "/packages/cached" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "pub" 8 | directory: "/packages/cached_annotation" 9 | schedule: 10 | interval: "weekly" 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "weekly" 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - 'rc/*' 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Install dart 17 | uses: dart-lang/setup-dart@v1.4 18 | with: 19 | sdk: stable 20 | 21 | - name: Install melos 22 | run: dart pub global activate melos 23 | 24 | - name: Install coverage 25 | run: dart pub global activate coverage 26 | 27 | - name: Melos Bootstrap 28 | run: melos bs 29 | 30 | - name: Install dependencies 31 | run: melos run get 32 | 33 | - name: Run build_runner 34 | run: melos run generate 35 | 36 | - name: Analyze project source 37 | run: melos run analyze 38 | 39 | - name: Run tests 40 | run: melos run test:coverage 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .atom/ 7 | .buildlog/ 8 | .history 9 | .svn/ 10 | .vscode 11 | .idea 12 | .DS_Store 13 | *.iml 14 | 15 | # Flutter/Dart/Pub related 16 | .dart_tool/ 17 | coverage/ 18 | coverage.lcov 19 | 20 | # Melos-generated file 21 | pubspec_overrides.yaml 22 | 23 | # FVM Version Cache 24 | .fvm/ -------------------------------------------------------------------------------- /melos.yaml: -------------------------------------------------------------------------------- 1 | name: cached 2 | 3 | command: 4 | bootstrap: 5 | usePubspecOverrides: true 6 | 7 | packages: 8 | - packages/** 9 | 10 | scripts: 11 | get: 12 | run: melos exec dart pub get 13 | 14 | generate: 15 | run: melos run generate:packages && melos run generate:examples 16 | 17 | generate:packages: 18 | run: melos exec --depends-on="build_runner" --no-private dart run build_runner build --delete-conflicting-outputs 19 | 20 | generate:examples: 21 | run: melos exec --depends-on="build_runner" --depends-on="cached" dart run build_runner build --delete-conflicting-outputs 22 | 23 | test: 24 | run: melos exec --dir-exists="test" dart test 25 | 26 | test:coverage: 27 | run: melos exec -c 1 --fail-fast -- "\$MELOS_ROOT_PATH/scripts/coverage.sh" 28 | description: Run Dart tests for a specific package in this project. 29 | packageFilters: 30 | dirExists: test 31 | 32 | analyze: 33 | run: melos exec dart analyze 34 | 35 | get:fvm: 36 | run: melos exec fvm dart pub get 37 | 38 | test:fvm: 39 | run: melos exec --dir-exists="test" dart test 40 | 41 | analyze:fvm: 42 | run: melos exec fvm dart analyze 43 | 44 | generate:fvm: 45 | run: melos run generate:packages:fvm && melos run generate:examples:fvm 46 | 47 | generate:packages:fvm: 48 | run: melos exec --depends-on="build_runner" --no-private fvm dart run build_runner build --delete-conflicting-outputs 49 | 50 | generate:examples:fvm: 51 | run: melos exec --depends-on="build_runner" --depends-on="cached" fvm dart run build_runner build --delete-conflicting-outputs 52 | -------------------------------------------------------------------------------- /packages/cached/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | *.cached.dart 31 | *.g.dart 32 | -------------------------------------------------------------------------------- /packages/cached/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 097d3313d8e2c7f901932d63e537c1acefb87800 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /packages/cached/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.7.1 2 | * Update dependencies 3 | 4 | ## 1.7.0 5 | * Fix not working peristent storage with the getters 6 | * Add `@DirectPersistentCached()` annotation - avoid store cached data in generated classes by cached, data will be stored only in external storage 7 | * Add `@LazyPersistentCached()` annotation - initialize cache from external storage only after the first method call 8 | * Mark `@Cached(persistentStorage: )` parameter as @Deprecated 9 | * Update documentation 10 | 11 | ## 1.6.4 12 | * Fix the `PersistentStorageHolder` which wasn't checking if stored value type matches the method return type (see [#105](https://github.com/Iteo/cached/issues/105)) 13 | 14 | ## 1.6.3 15 | * Update SDK version `>=2.17.0 <4.0.0` 16 | 17 | ## 1.6.2 18 | * Update analyzer 19 | 20 | ## 1.6.1 21 | * Fix `@Cached(limit: )` parameter - LRU cache algorithm was not working correctly. 22 | 23 | ## 1.6.0 24 | * Add `@Cached(persistentStorage: )` parameter, which allows you to turn on cache persisting with usage of external storage. 25 | 26 | ## 1.5.1 27 | * Fix documentation links 28 | * Fix intendation in documentation 29 | 30 | ## 1.5.0 31 | * Add `@Cached(where: )` parameter - function triggered before caching the value for conditional caching. 32 | * Documentation update with examples for `where:` parameter 33 | 34 | ## 1.4.0 35 | * Add `@DeletesCache()` annotation 36 | * Make parameters anonymous in `@CachePeek()` and `@CacheKey()` annotations 37 | * Add support for getters for `@Cached()` annotation 38 | 39 | ## 1.3.1 40 | * Allow `@ClearCached()` and `@ClearAllCached()` methods to return `Future` 41 | 42 | ## 1.3.0 43 | * Add `@PeakCache()` annotation 44 | * Fix generating `@StreamedCache()` with ignored parameters 45 | 46 | ## 1.2.2 47 | * Fix analysis 48 | 49 | ## 1.2.1 50 | * Fix decumentation redirects 51 | 52 | ## 1.2.0 53 | * Add `@StreamedCache()` annotation 54 | 55 | ## 1.1.0 56 | * Add `@IgnoreCache()` annotation 57 | * Add `@CacheKey()` annotation 58 | 59 | ## 1.0.4 60 | * Change analyzer version to ">=3.0.0 <5.0.0" 61 | 62 | ## 1.0.3 63 | * Downgrade meta package to 1.7.0 64 | 65 | ## 1.0.2 66 | * Fix README.md 67 | 68 | ## 1.0.1 69 | * Fix analyzer warnings 70 | 71 | ## 1.0.0 72 | * Initial version of cached library 73 | -------------------------------------------------------------------------------- /packages/cached/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Iteo.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/cached/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:linteo/analysis_options.yaml 2 | 3 | analyzer: 4 | errors: 5 | invalid_dependency: ignore 6 | exclude: 7 | - ./**/*.cached.dart 8 | - test/inputs/* 9 | 10 | linter: 11 | rules: 12 | avoid_annotating_with_dynamic: false 13 | comment_references: false 14 | prefer_constructors_over_static_methods: false 15 | avoid_print: false 16 | avoid_classes_with_only_static_members: false 17 | avoid_setters_without_getters: false 18 | sort_pub_dependencies: false -------------------------------------------------------------------------------- /packages/cached/build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | cached: 5 | generate_for: 6 | - test/integration/** 7 | 8 | builders: 9 | cached: 10 | import: "package:cached/cached.dart" 11 | builder_factories: ["cachedBuilder"] 12 | build_extensions: { ".dart": [".cached.dart"] } 13 | auto_apply: dependents 14 | build_to: source 15 | -------------------------------------------------------------------------------- /packages/cached/cached_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iteo/cached/6b43300bc0df5159317284cf3615d4b882af2cbb/packages/cached/cached_logo.png -------------------------------------------------------------------------------- /packages/cached/cached_sygnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Iteo/cached/6b43300bc0df5159317284cf3615d4b882af2cbb/packages/cached/cached_sygnet.png -------------------------------------------------------------------------------- /packages/cached/example/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ### Cached 4 | 5 | Example with the Cached library implementation. 6 | 7 | [Jump to Source](https://github.com/Iteo/cached/tree/master/packages/cached/example/cached_example) 8 | 9 | ### Cached with persistence storage 10 | 11 | Example with a custom implementation of persistent storage for caching data in Cached. 12 | 13 | [Jump to Source](https://github.com/Iteo/cached/tree/master/packages/cached/example/persistent_storage_example) -------------------------------------------------------------------------------- /packages/cached/example/cached_example/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub. 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build output. 6 | build/ 7 | *.cached.dart 8 | -------------------------------------------------------------------------------- /packages/cached/example/cached_example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /packages/cached/example/cached_example/README.md: -------------------------------------------------------------------------------- 1 | A simple command-line application. 2 | -------------------------------------------------------------------------------- /packages/cached/example/cached_example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:linteo/analysis_options.yaml 2 | 3 | analyzer: 4 | errors: 5 | invalid_dependency: ignore 6 | return_of_invalid_type: ignore 7 | argument_type_not_assignable: ignore 8 | inference_failure_on_function_invocation: ignore 9 | inference_failure_on_untyped_parameter: ignore 10 | strict_raw_type: ignore 11 | exclude: [ 'bin/**cached.dart' ] 12 | 13 | linter: 14 | rules: 15 | avoid_positional_boolean_parameters: false 16 | sort_pub_dependencies: false 17 | unnecessary_lambdas: false 18 | avoid_dynamic_calls: false 19 | avoid_print: false 20 | comment_references: false 21 | -------------------------------------------------------------------------------- /packages/cached/example/cached_example/bin/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'gen.dart'; 4 | 5 | /// Open gen.dart file to check how use [Cached] package, 6 | /// if you need more information about the package, 7 | /// take a look at the readme! 8 | 9 | /// Run main method to check how great [Cached] package is it! 10 | void main(List arguments) async { 11 | print('======= CACHED BY ITEO =======\n'); 12 | 13 | final gen = Gen(); 14 | final sw = Stopwatch(); 15 | 16 | const JsonEncoder encoder = JsonEncoder.withIndent(' '); 17 | 18 | final streamSubscription = gen.getDataCacheStream().listen((event) { 19 | print( 20 | 'Cache updated with new value:\n${encoder.convert(jsonDecode(event.body))}', 21 | ); 22 | }); 23 | 24 | final response = await gen.getDataWithCached(); 25 | 26 | print('Response json:'); 27 | 28 | final String prettyprint = encoder.convert(jsonDecode(response.body)); 29 | print('$prettyprint\n'); 30 | 31 | sw.start(); 32 | await gen.getDataWithCached(); 33 | sw.stop(); 34 | 35 | print('With cached: ${sw.elapsedMilliseconds} ms'); 36 | 37 | sw.start(); 38 | await gen.getDataWithoutCached(); 39 | sw.stop(); 40 | 41 | print('Without cached: ${sw.elapsedMilliseconds} ms'); 42 | 43 | print('\n=============================='); 44 | 45 | await streamSubscription.cancel(); 46 | } 47 | -------------------------------------------------------------------------------- /packages/cached/example/cached_example/bin/gen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:cached_annotation/cached_annotation.dart'; 4 | import 'package:http/http.dart'; 5 | 6 | /// Do not forget add part keyword for file, 7 | /// because then class not be generated. 8 | part 'gen.cached.dart'; 9 | 10 | /// You can change url to your own and run main method to check 11 | /// how many time you save with [Cached] package. 12 | /// 13 | /// IMPORTANT! 14 | /// response body must be a json string 15 | const _url = 'https://jsonplaceholder.typicode.com/todos/94'; 16 | 17 | /// Annotating a class with @WithCache will flag it as a needing 18 | /// to be processed by Cached code generator. 19 | /// It can take one additional boolean parameter useStaticCache. 20 | /// If this parameter is set to true, generator will generate 21 | /// cached class with static cache. It means each instance of this class 22 | /// will have access to the same cache. Default value is set to false 23 | @WithCache(useStaticCache: true) 24 | abstract mixin class Gen implements _$Gen { 25 | /// Factory constructor 26 | factory Gen() = _Gen; 27 | 28 | /// Method decorator that flag it as needing to be processed 29 | /// by Cached code generator. 30 | /// 31 | /// There are 4 possible additional parameters: 32 | /// 33 | /// * ttl - time to live. In seconds. Set how long cache will be alive. 34 | /// Default value is set to null, means infinitive ttl. 35 | /// 36 | /// * syncWrite - Affects only async methods (those one that returns Future) 37 | /// If set to true first method call will be cached, and if 38 | /// following (the same) call will occur, all of them will get 39 | /// result from the first call. Default value is set to false; 40 | /// 41 | /// * limit - limit how many results for different method call arguments 42 | /// combination will be cached. Default value null, means no limit. 43 | /// 44 | /// * where - function triggered before caching the value. 45 | /// If returns `true`: value will be cached, 46 | /// if returns `false`: cache will not happen. 47 | /// Useful to signal that a certain result must not be cached 48 | /// (e.g. condition whether or not to cache known once acquiring data) 49 | /// 50 | /// * persistentStorage - defines optional usage of external persistent 51 | /// storage (e.g. shared preferences) 52 | /// 53 | /// If set to `true` in order to work, you have to set 54 | /// `PersistentStorageHolder.storage` in your main.dart 55 | /// file 56 | /// 57 | /// Important: 58 | /// If you want to utilize persistent storage, all 59 | /// methods which use Cached library's annotations has 60 | /// to be async 61 | /// 62 | /// Additional annotation @IgnoreCache 63 | /// 64 | /// That annotation must be above a field in a method and must be bool, 65 | /// if true the cache will be ignored. Also you can use parameter `useCacheOnError` 66 | /// in the annotation and if set true then return the last cached value 67 | /// when an error occurs. 68 | /// 69 | /// Additional annotation @ignore 70 | /// 71 | /// Arguments with @ignore annotations will be ignored while generating cache key. 72 | @Cached( 73 | syncWrite: true, 74 | ttl: 30, 75 | limit: 10, 76 | where: _shouldCache, 77 | ) 78 | Future getDataWithCached({ 79 | @IgnoreCache(useCacheOnError: true) bool ignoreCache = false, 80 | }) { 81 | return get(Uri.parse(_url)); 82 | } 83 | 84 | /// @Cached annotation also works with getters 85 | @Cached( 86 | syncWrite: true, 87 | ttl: 30, 88 | limit: 10, 89 | where: _shouldCache, 90 | ) 91 | Future get getDataWithCachedGetter async => get(Uri.parse(_url)); 92 | 93 | /// Method for measure example, you can go to example.dart file 94 | /// and run main method, so you can check how great [Checked] package is it. 95 | Future getDataWithoutCached() { 96 | return get(Uri.parse(_url)); 97 | } 98 | 99 | /// Method for getting stream of cache updates 100 | @StreamedCache(methodName: 'getDataWithCached') 101 | Stream getDataCacheStream(); 102 | 103 | /// Method for getting data of cache method 104 | @CachePeek('getDataWithCached') 105 | Response? peekDataCache(); 106 | 107 | /// Method annotated with this annotation can be used to clear result 108 | /// of method annotated with Cached annotation. 109 | /// 110 | /// IMPORTANT! 111 | /// the ClearCached argument or method name has to correspond 112 | /// to cached method name. 113 | @ClearCached('getDataWithCached') 114 | void clearDataCache(); 115 | 116 | /// Method annotated with @DeletesCache annotation will clear 117 | /// cached results if it returns with value; however if this method 118 | /// would throw exception, cached data would not be cleared 119 | /// 120 | /// IMPORTANT! 121 | /// Method names passed in annotation must correspond to 122 | /// valid cached method names 123 | @DeletesCache(['getDataWithCached']) 124 | Future deletesCache() async { 125 | return 1; 126 | } 127 | 128 | /// Method with this annotation will clear cached values for all methods. 129 | @clearAllCached 130 | void clearAllCache(); 131 | } 132 | 133 | Future _shouldCache(Response response) async { 134 | final json = jsonDecode(response.body) as Map; 135 | print('Up to you: check conditionally and decide if should cache: $json'); 136 | print('For now: always cache'); 137 | return true; 138 | } 139 | -------------------------------------------------------------------------------- /packages/cached/example/cached_example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A simple command-line application. 3 | version: 1.0.0 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=3.6.0 <4.0.0" 8 | 9 | dependencies: 10 | cached_annotation: 11 | path: "../../../cached_annotation" 12 | http: ^0.13.4 13 | 14 | dependency_overrides: 15 | cached_annotation: 16 | path: "../../../cached_annotation" 17 | 18 | dev_dependencies: 19 | build_runner: ^2.4.14 20 | cached: 21 | path: "../../" 22 | linteo: ^1.0.1 23 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | 46 | *.cached.dart -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/README.md: -------------------------------------------------------------------------------- 1 | A simple command-line application. 2 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:linteo/analysis_options.yaml 2 | 3 | analyzer: 4 | errors: 5 | invalid_dependency: ignore 6 | return_of_invalid_type: ignore 7 | argument_type_not_assignable: ignore 8 | inference_failure_on_function_invocation: ignore 9 | inference_failure_on_untyped_parameter: ignore 10 | strict_raw_type: ignore 11 | exclude: [ 'bin/**cached.dart' ] 12 | 13 | linter: 14 | rules: 15 | avoid_positional_boolean_parameters: false 16 | sort_pub_dependencies: false 17 | unnecessary_lambdas: false 18 | avoid_dynamic_calls: false 19 | avoid_print: false 20 | comment_references: false 21 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/bin/example.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import 'gen.dart'; 4 | import 'todo_storage.dart'; 5 | 6 | /// Open [cached_example] project in ../example directory to check 7 | /// how use [Cached] package. 8 | /// 9 | /// 10 | /// Run this method. It'll create a database folder with two files: 11 | /// - todos.hive 12 | /// - todos.lock 13 | /// 14 | /// From now on, every program run will use data stored in these files. 15 | /// 16 | /// Examples of fetching times: 17 | /// - 1st run: 306 ms 18 | /// - 2nd run: 1 ms 19 | /// - Delete ./database directory manually or with [gen.clearTodos] method 20 | /// - 3rd run: 298 ms 21 | Future main(List arguments) async { 22 | /// The assignment of this variable is mandatory for [Cached] to store 23 | /// data in permanent memory. 24 | PersistentStorageHolder.storage = await TodoStorage.create(); 25 | 26 | final gen = Gen(); 27 | 28 | /// Uncomment to deleteTodos 29 | // await gen.clearTodos(); 30 | 31 | await _fetchTodos(gen); 32 | } 33 | 34 | Future _fetchTodos(Gen gen) async { 35 | final stopwatch = Stopwatch(); 36 | 37 | stopwatch.start(); 38 | final todos = await gen.getTodos(); 39 | stopwatch.stop(); 40 | 41 | final count = todos.length; 42 | print('Todos count: $count items'); 43 | 44 | final fetchTime = stopwatch.elapsedMilliseconds; 45 | print('Fetch time: $fetchTime ms \n'); 46 | 47 | print('First 5 todos:'); 48 | print(todos.sublist(0, 5)); 49 | } 50 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/bin/gen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:cached_annotation/cached_annotation.dart'; 4 | import 'package:http/http.dart' as http; 5 | 6 | import 'todo.dart'; 7 | 8 | part 'gen.cached.dart'; 9 | 10 | /// Sample API URL 11 | /// 12 | /// Returns a list: 13 | /// [ 14 | /// { 15 | /// "userId": 1, 16 | /// "id": 1, 17 | /// "title": "delectus aut autem", 18 | /// "completed": false 19 | /// }, 20 | /// { 21 | /// "userId": 1, 22 | /// "id": 2, 23 | /// "title": "quis ut nam facilis et officia qui", 24 | /// "completed": false 25 | /// }, 26 | /// 198 more... 27 | /// ] 28 | const _url = 'https://jsonplaceholder.typicode.com/todos'; 29 | 30 | @withCache 31 | abstract mixin class Gen implements _$Gen { 32 | factory Gen() = _Gen; 33 | 34 | /// For the sake of readability, we will pass only a [persistentStorage] 35 | /// here, but you can use any params combination 36 | @PersistentCached() 37 | Future> getTodos() async { 38 | final uri = Uri.parse(_url); 39 | final response = await http.get(uri); 40 | final decodedBody = jsonDecode(response.body); 41 | 42 | return decodedBody.map((e) => Todo.fromJson(e)).toList(); 43 | } 44 | 45 | /// We will pass only a [directPersistentStorage] 46 | /// here becouse you can not use any params combination 47 | @DirectPersistentCached() 48 | Future> getDirectTodos() async { 49 | final uri = Uri.parse(_url); 50 | final response = await http.get(uri); 51 | final decodedBody = jsonDecode(response.body); 52 | 53 | return decodedBody.map((e) => Todo.fromJson(e)).toList(); 54 | } 55 | 56 | /// To delete persisted data, you can also use [ClearAllCached], 57 | /// [DeletesCache] annotations. 58 | @ClearCached('getTodos') 59 | Future clearTodos() async { 60 | print('Deleting todos from database...'); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/bin/todo.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive_ce/hive.dart'; 2 | 3 | /// Required for code generation 4 | part 'todo.g.dart'; 5 | 6 | /// Simple model class with [fromJson] and [toJson] methods, prepared for 7 | /// Hive [TypeAdapter] code generation 8 | @HiveType(typeId: 0) 9 | class Todo { 10 | Todo({ 11 | required this.id, 12 | required this.userId, 13 | required this.title, 14 | required this.completed, 15 | }); 16 | 17 | Todo.fromJson(Map json) 18 | : id = json['id'] as int, 19 | userId = json['userId'] as int, 20 | title = json['title'] as String, 21 | completed = json['completed'] as bool; 22 | 23 | @HiveField(0) 24 | final int userId; 25 | @HiveField(1) 26 | final int id; 27 | @HiveField(2) 28 | final String title; 29 | @HiveField(3) 30 | final bool completed; 31 | 32 | Map toJson() => { 33 | 'id': id, 34 | 'userId': userId, 35 | 'title': title, 36 | 'completed': completed, 37 | }; 38 | 39 | @override 40 | String toString() { 41 | return 'Todo(id: $id, userId: $userId, title: $title, completed: $completed)\n'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/bin/todo_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:hive_ce/hive.dart'; 3 | 4 | import 'todo.dart'; 5 | 6 | /// Example Hive database configuration 7 | const _path = './database/'; 8 | const _storageKey = '_todos'; 9 | 10 | /// You have to extend [CachedStorage] to use Cached library's persistent 11 | /// storage functionality. You have to provide your own CRUD logic by 12 | /// overriding all base methods. 13 | /// 14 | /// Cached library is persisting data in a {key: value} manner. It'll give you 15 | /// a key and it is your responsibility to serialize it so that you can 16 | /// deserialize it later with the same key. 17 | /// 18 | /// Important: 19 | /// Please be aware of typing and error handling. This has to be done on your 20 | /// side. 21 | class TodoStorage extends CachedStorage { 22 | TodoStorage._(); 23 | 24 | /// You can connect to your storage asynchronously, but it isn't mandatory 25 | static Future create() async { 26 | Hive.init(_path); 27 | Hive.registerAdapter(TodoAdapter()); 28 | await Hive.openBox(_storageKey); 29 | 30 | return TodoStorage._(); 31 | } 32 | 33 | /// In [write] method, you only have to provide a way to serialize 34 | /// your fresh data. In our case, Hive is doing the job for us with 35 | /// its adapter. 36 | /// 37 | /// Use a given [key] parameter as your key to store results. 38 | /// 39 | /// DO NOT modify [key] parameter value! 40 | @override 41 | Future write(String key, Map data) async { 42 | final box = Hive.box(_storageKey); 43 | return box.put(key, data); 44 | } 45 | 46 | /// In [read] method, have to provide a way to read your previously serialized 47 | /// data. Again, in this example, Hive is doing the job for us. 48 | /// 49 | /// Use a [key] parameter as your key to read/filter results. 50 | /// 51 | /// DO NOT modify [key] parameter value! 52 | @override 53 | Future> read(String key) async { 54 | final box = Hive.box(_storageKey); 55 | final result = box.get(key) ?? {}; 56 | 57 | /// This is necessary for generic classes to provide compatibility 58 | /// with Hive, because it can only return 59 | /// `Map`, not `Map` 60 | return result.cast(); 61 | } 62 | 63 | /// In [delete] method, you have to provide a deletion logic only 64 | /// for a given key. 65 | /// 66 | /// DO NOT modify [key] parameter value! 67 | @override 68 | Future delete(String key) async { 69 | final box = Hive.box(_storageKey); 70 | return box.delete(key); 71 | } 72 | 73 | /// In [deleteAll] method, you have to provide a deletion logic for 74 | /// all persisted data. 75 | @override 76 | Future deleteAll() async { 77 | final box = Hive.box(_storageKey); 78 | return box.deleteFromDisk(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/cached/example/persistent_storage_example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: persistent_storage_example 2 | description: A sample command-line application. 3 | version: 1.0.0 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=3.6.0 <4.0.0" 8 | 9 | dependencies: 10 | cached_annotation: 11 | path: "../../../cached_annotation" 12 | hive_ce: ^2.10.1 13 | http: ^0.13.5 14 | 15 | dependency_overrides: 16 | cached_annotation: 17 | path: "../../../cached_annotation" 18 | 19 | dev_dependencies: 20 | build_runner: ^2.4.6 21 | cached: 22 | path: "../../" 23 | linteo: ^1.0.1 24 | hive_ce_generator: 1.8.2 25 | -------------------------------------------------------------------------------- /packages/cached/lib/cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:build/build.dart'; 2 | import 'package:cached/src/cached_generator.dart'; 3 | import 'package:cached/src/config.dart'; 4 | import 'package:source_gen/source_gen.dart'; 5 | 6 | ///it's function used by build runner 7 | Builder cachedBuilder(BuilderOptions options) { 8 | return PartBuilder( 9 | [ 10 | CachedGenerator( 11 | config: Config.fromJson(options.config), 12 | ), 13 | ], 14 | '.cached.dart', 15 | header: ''' 16 | // coverage:ignore-file 17 | // GENERATED CODE - DO NOT MODIFY BY HAND 18 | // ignore_for_file: type=lint 19 | ''', 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/cached/lib/src/cached_generator.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:build/build.dart'; 3 | import 'package:cached/src/config.dart'; 4 | import 'package:cached/src/models/class_with_cache.dart'; 5 | import 'package:cached/src/templates/file_template.dart'; 6 | import 'package:cached_annotation/cached_annotation.dart'; 7 | import 'package:source_gen/source_gen.dart'; 8 | 9 | class CachedGenerator extends GeneratorForAnnotation { 10 | const CachedGenerator({ 11 | required this.config, 12 | }); 13 | 14 | final Config config; 15 | 16 | @override 17 | String generateForAnnotatedElement( 18 | Element element, 19 | ConstantReader annotation, 20 | BuildStep buildStep, 21 | ) { 22 | if (element is! ClassElement) { 23 | throw InvalidGenerationSourceError( 24 | '[ERROR] Annotation WithCache cannot target ${element.runtimeType}', 25 | element: element, 26 | ); 27 | } 28 | 29 | final cachedClass = ClassWithCache.fromElement(element, config); 30 | final template = FileTemplate(cachedClass); 31 | 32 | return template.generate(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/cached/lib/src/config.dart: -------------------------------------------------------------------------------- 1 | class Config { 2 | const Config({ 3 | this.limit, 4 | this.ttl, 5 | this.syncWrite, 6 | this.useStaticCache, 7 | this.onCacheOnError, 8 | }); 9 | 10 | factory Config.fromJson(Map json) { 11 | return Config( 12 | limit: json['limit'] as int?, 13 | ttl: json['ttl'] as int?, 14 | syncWrite: json['syncWrite'] as bool?, 15 | useStaticCache: json['useStaticCache'] as bool?, 16 | onCacheOnError: json['onCacheOnError'] as bool?, 17 | ); 18 | } 19 | 20 | final int? limit; 21 | final int? ttl; 22 | final bool? syncWrite; 23 | final bool? useStaticCache; 24 | final bool? onCacheOnError; 25 | } 26 | -------------------------------------------------------------------------------- /packages/cached/lib/src/extensions.dart: -------------------------------------------------------------------------------- 1 | extension StringExtension on String { 2 | String startsWithLowerCase() { 3 | final lowerCase = this[0].toLowerCase(); 4 | final textPart = substring(1); 5 | 6 | return '$lowerCase$textPart'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/cache_peek_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:cached/src/config.dart'; 4 | import 'package:cached/src/models/param.dart'; 5 | import 'package:cached_annotation/cached_annotation.dart'; 6 | import 'package:collection/collection.dart'; 7 | 8 | import 'package:source_gen/source_gen.dart'; 9 | import 'package:source_helper/source_helper.dart'; 10 | 11 | class CachePeekMethod { 12 | const CachePeekMethod({ 13 | required this.name, 14 | required this.targetMethodName, 15 | required this.returnType, 16 | required this.params, 17 | }); 18 | 19 | factory CachePeekMethod.fromElement( 20 | MethodElement element, 21 | List classMethods, 22 | Config config, 23 | ) { 24 | final annotation = getAnnotation(element); 25 | 26 | var methodName = ''; 27 | 28 | if (annotation != null) { 29 | final reader = ConstantReader(annotation); 30 | methodName = reader.read('methodName').stringValue; 31 | } 32 | 33 | final targetMethod = classMethods 34 | .where( 35 | (m) => m.name == methodName, 36 | ) 37 | .firstOrNull; 38 | 39 | if (targetMethod == null) { 40 | throw InvalidGenerationSourceError( 41 | '[ERROR] Method "$methodName" do not exists', 42 | element: element, 43 | ); 44 | } 45 | 46 | final peekCacheMethodType = element.returnType; 47 | final peekCacheMethodTypeStr = peekCacheMethodType.getDisplayString( 48 | withNullability: false, 49 | ); 50 | 51 | const futureTypeChecker = TypeChecker.fromRuntime(Future); 52 | final targetMethodReturnType = targetMethod.returnType.isDartAsyncFuture 53 | ? targetMethod.returnType.typeArgumentsOf(futureTypeChecker)?.single 54 | : targetMethod.returnType; 55 | 56 | final targetMethodTypeStr = targetMethodReturnType?.getDisplayString( 57 | withNullability: false, 58 | ); 59 | 60 | if (peekCacheMethodTypeStr != targetMethodTypeStr) { 61 | throw InvalidGenerationSourceError( 62 | '[ERROR] Peek cache method return type needs to be a $targetMethodTypeStr?', 63 | element: element, 64 | ); 65 | } 66 | 67 | const cachedAnnotationTypeChecker = TypeChecker.fromRuntime(Cached); 68 | 69 | if (!cachedAnnotationTypeChecker.hasAnnotationOf(targetMethod)) { 70 | throw InvalidGenerationSourceError( 71 | '[ERROR] Method "$methodName" do not have @cached annotation', 72 | element: element, 73 | ); 74 | } 75 | 76 | final cachedAnnotation = 77 | cachedAnnotationTypeChecker.firstAnnotationOf(targetMethod); 78 | final hasDirectPersistenStorage = 79 | cachedAnnotation?.getField('directPersistentStorage')?.toBoolValue() ?? 80 | false; 81 | if (hasDirectPersistenStorage) { 82 | throw InvalidGenerationSourceError( 83 | "[ERROR] Method '$methodName' has 'directPersistentStorage' set to true." 84 | "@CachePeek is unavailable for methods with 'directPersistentStorage'.", 85 | element: element, 86 | ); 87 | } 88 | 89 | const ignoreTypeChecker = TypeChecker.any([ 90 | TypeChecker.fromRuntime(Ignore), 91 | TypeChecker.fromRuntime(IgnoreCache), 92 | ]); 93 | 94 | final targetMethodParameters = targetMethod.parameters 95 | .where((p) => !ignoreTypeChecker.hasAnnotationOf(p)) 96 | .toList(); 97 | 98 | if (!ListEquality( 99 | EqualityBy( 100 | (p) => Param.fromElement(p, config), 101 | ), 102 | ).equals( 103 | targetMethodParameters, 104 | element.parameters, 105 | )) { 106 | throw InvalidGenerationSourceError( 107 | '[ERROR] Method "${targetMethod.name}" should have same parameters as ' 108 | '"${element.name}", excluding ones marked with @ignore and @ignoreCache', 109 | element: element, 110 | ); 111 | } 112 | 113 | return CachePeekMethod( 114 | name: element.name, 115 | returnType: peekCacheMethodTypeStr, 116 | params: targetMethodParameters.map((p) => Param.fromElement(p, config)), 117 | targetMethodName: methodName, 118 | ); 119 | } 120 | 121 | final String name; 122 | final String targetMethodName; 123 | final Iterable params; 124 | final String returnType; 125 | 126 | static DartObject? getAnnotation(MethodElement element) { 127 | const methodAnnotationChecker = TypeChecker.fromRuntime(CachePeek); 128 | return methodAnnotationChecker.firstAnnotationOf(element); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/cached_function.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:cached/src/models/check_if_should_cache_method.dart'; 4 | import 'package:cached/src/utils/asserts.dart'; 5 | import 'package:cached_annotation/cached_annotation.dart'; 6 | import 'package:source_gen/source_gen.dart'; 7 | 8 | abstract class CachedFunction { 9 | CachedFunction({ 10 | required this.name, 11 | required this.syncWrite, 12 | required this.isAsync, 13 | required this.isGenerator, 14 | required this.returnType, 15 | required this.limit, 16 | required this.ttl, 17 | required this.checkIfShouldCacheMethod, 18 | required this.persistentStorage, 19 | required this.directPersistentStorage, 20 | required this.lazyPersistentStorage, 21 | }); 22 | 23 | final String name; 24 | final bool syncWrite; 25 | final String returnType; 26 | final bool isGenerator; 27 | final bool isAsync; 28 | final int? limit; 29 | final int? ttl; 30 | final CheckIfShouldCacheMethod? checkIfShouldCacheMethod; 31 | final bool? persistentStorage; 32 | final bool? directPersistentStorage; 33 | final bool? lazyPersistentStorage; 34 | 35 | static void assertIsValid(ExecutableElement element) { 36 | assertMethodNotVoid(element); 37 | assertMethodIsNotAbstract(element); 38 | } 39 | 40 | static DartObject? getAnnotation( 41 | ExecutableElement element, 42 | ) { 43 | final methodAnnotationChecker = TypeChecker.fromRuntime(T); 44 | return methodAnnotationChecker.firstAnnotationOf(element); 45 | } 46 | 47 | static bool hasCachedAnnotation(ExecutableElement element) { 48 | return getAnnotation(element) != null; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/cached_function_local_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:cached/src/models/cached_function.dart'; 4 | import 'package:cached/src/models/check_if_should_cache_method.dart'; 5 | import 'package:cached/src/utils/asserts.dart'; 6 | import 'package:cached_annotation/cached_annotation.dart'; 7 | import 'package:source_gen/source_gen.dart'; 8 | 9 | class CachedFunctionLocalConfig { 10 | factory CachedFunctionLocalConfig.fromElement(ExecutableElement element) { 11 | final cachedAnnotation = CachedFunction.getAnnotation(element); 12 | if (cachedAnnotation != null) { 13 | return _build(cachedAnnotation, element); 14 | } 15 | 16 | const message = '[ERROR] Expected @Cached annotation with parameters'; 17 | throw InvalidGenerationSourceError(message); 18 | } 19 | 20 | CachedFunctionLocalConfig._({ 21 | required this.persistentStorage, 22 | required this.directPersistentStorage, 23 | required this.syncWrite, 24 | required this.limit, 25 | required this.ttl, 26 | required this.checkIfShouldCacheMethod, 27 | required this.lazyPersistentStorage, 28 | }); 29 | 30 | bool persistentStorage; 31 | bool directPersistentStorage; 32 | bool lazyPersistentStorage; 33 | 34 | bool? syncWrite; 35 | int? limit; 36 | int? ttl; 37 | CheckIfShouldCacheMethod? checkIfShouldCacheMethod; 38 | 39 | static CachedFunctionLocalConfig _build( 40 | DartObject cachedAnnotation, 41 | ExecutableElement element, 42 | ) { 43 | final reader = ConstantReader(cachedAnnotation); 44 | 45 | bool persistentStorage = false; 46 | bool directPersistentStorage = false; 47 | bool lazyPersistentStorage = false; 48 | 49 | bool? syncWrite; 50 | int? limit; 51 | int? ttl; 52 | CheckIfShouldCacheMethod? checkIfShouldCacheMethod; 53 | 54 | final syncWriteField = reader.peek('syncWrite'); 55 | if (syncWriteField != null) { 56 | syncWrite = syncWriteField.boolValue; 57 | } 58 | 59 | if (reader 60 | .instanceOf(const TypeChecker.fromRuntime(LazyPersistentCached))) { 61 | lazyPersistentStorage = true; 62 | } else if (reader 63 | .instanceOf(const TypeChecker.fromRuntime(DirectPersistentCached))) { 64 | directPersistentStorage = true; 65 | } else if (reader 66 | .instanceOf(const TypeChecker.fromRuntime(PersistentCached))) { 67 | persistentStorage = true; 68 | } 69 | 70 | final limitField = reader.peek('limit'); 71 | if (limitField != null) { 72 | limit = limitField.intValue; 73 | } 74 | 75 | final ttlField = reader.peek('ttl'); 76 | if (ttlField != null) { 77 | ttl = ttlField.intValue; 78 | } 79 | 80 | final shouldCacheFunctionField = reader.peek('where'); 81 | if (shouldCacheFunctionField != null) { 82 | checkIfShouldCacheMethod = _checkIfShouldCacheMethod( 83 | element, 84 | shouldCacheFunctionField, 85 | ); 86 | } 87 | 88 | final shouldUseStorage = reader.peek('persistentStorage'); 89 | if (shouldUseStorage != null && shouldUseStorage.isBool) { 90 | persistentStorage = shouldUseStorage.boolValue; 91 | } 92 | 93 | if (persistentStorage || directPersistentStorage || lazyPersistentStorage) { 94 | assertPersistentStorageShouldBeAsync(element); 95 | } 96 | 97 | return CachedFunctionLocalConfig._( 98 | ttl: ttl, 99 | limit: limit, 100 | syncWrite: syncWrite, 101 | checkIfShouldCacheMethod: checkIfShouldCacheMethod, 102 | persistentStorage: persistentStorage, 103 | directPersistentStorage: directPersistentStorage, 104 | lazyPersistentStorage: lazyPersistentStorage, 105 | ); 106 | } 107 | 108 | static CheckIfShouldCacheMethod _checkIfShouldCacheMethod( 109 | ExecutableElement element, 110 | ConstantReader field, 111 | ) { 112 | final objectValue = field.objectValue; 113 | final shouldCacheFunction = objectValue.toFunctionValue(); 114 | 115 | if (shouldCacheFunction == null) { 116 | final message = '[ERROR] Expected $objectValue to be a function.'; 117 | throw InvalidGenerationSourceError(message); 118 | } 119 | 120 | return CheckIfShouldCacheMethod.fromElements( 121 | annotatedMethod: element, 122 | shouldCacheFunction: shouldCacheFunction, 123 | ); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/cached_getter.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:cached/src/models/cached_function.dart'; 4 | import 'package:cached/src/models/cached_function_local_config.dart'; 5 | import 'package:cached_annotation/cached_annotation.dart'; 6 | 7 | const _defaultSyncWriteValue = false; 8 | 9 | class CachedGetter extends CachedFunction { 10 | CachedGetter({ 11 | required super.name, 12 | required super.syncWrite, 13 | required super.returnType, 14 | required super.isGenerator, 15 | required super.isAsync, 16 | required super.limit, 17 | required super.ttl, 18 | required super.checkIfShouldCacheMethod, 19 | required super.persistentStorage, 20 | required super.directPersistentStorage, 21 | required super.lazyPersistentStorage, 22 | }); 23 | 24 | factory CachedGetter.fromElement( 25 | PropertyAccessorElement element, 26 | Config config, 27 | ) { 28 | CachedFunction.assertIsValid(element); 29 | 30 | var isDirect = false; 31 | var isPersistent = false; 32 | var isLazy = false; 33 | 34 | if (CachedFunction.hasCachedAnnotation(element)) { 35 | isDirect = true; 36 | } else if (CachedFunction.hasCachedAnnotation( 37 | element, 38 | )) { 39 | isLazy = true; 40 | isPersistent = true; 41 | } else if (CachedFunction.hasCachedAnnotation(element)) { 42 | isPersistent = true; 43 | } 44 | 45 | final localConfig = CachedFunctionLocalConfig.fromElement(element); 46 | final unsafeSyncWrite = localConfig.syncWrite ?? config.syncWrite; 47 | final syncWrite = unsafeSyncWrite ?? _defaultSyncWriteValue; 48 | final limit = isDirect ? null : localConfig.limit ?? config.limit; 49 | final ttl = isDirect ? null : localConfig.ttl ?? config.ttl; 50 | final persistentStorage = isPersistent || localConfig.persistentStorage; 51 | final lazyPersistentStorage = !isDirect && isLazy; 52 | final returnType = element.returnType.getDisplayString( 53 | withNullability: true, 54 | ); 55 | 56 | return CachedGetter( 57 | name: element.name, 58 | syncWrite: syncWrite, 59 | limit: limit, 60 | ttl: ttl, 61 | checkIfShouldCacheMethod: localConfig.checkIfShouldCacheMethod, 62 | isAsync: element.isAsynchronous, 63 | isGenerator: element.isGenerator, 64 | persistentStorage: persistentStorage, 65 | directPersistentStorage: isDirect, 66 | returnType: returnType, 67 | lazyPersistentStorage: lazyPersistentStorage, 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/cached_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:cached/src/models/cached_function.dart'; 4 | import 'package:cached/src/models/cached_function_local_config.dart'; 5 | import 'package:cached/src/models/param.dart'; 6 | import 'package:cached/src/utils/asserts.dart'; 7 | import 'package:cached_annotation/cached_annotation.dart'; 8 | 9 | const _defaultSyncWriteValue = false; 10 | 11 | class CachedMethod extends CachedFunction { 12 | CachedMethod({ 13 | required this.params, 14 | required super.name, 15 | required super.syncWrite, 16 | required super.returnType, 17 | required super.isGenerator, 18 | required super.isAsync, 19 | required super.limit, 20 | required super.ttl, 21 | required super.checkIfShouldCacheMethod, 22 | required super.persistentStorage, 23 | required super.directPersistentStorage, 24 | required super.lazyPersistentStorage, 25 | }); 26 | 27 | factory CachedMethod.fromElement( 28 | MethodElement element, 29 | Config config, 30 | ) { 31 | CachedFunction.assertIsValid(element); 32 | 33 | var isDirect = false; 34 | var isPersistent = false; 35 | var isLazy = false; 36 | 37 | if (CachedFunction.hasCachedAnnotation(element)) { 38 | isDirect = true; 39 | } else if (CachedFunction.hasCachedAnnotation( 40 | element, 41 | )) { 42 | isPersistent = true; 43 | isLazy = true; 44 | } else if (CachedFunction.hasCachedAnnotation(element)) { 45 | isPersistent = true; 46 | } 47 | 48 | final localConfig = CachedFunctionLocalConfig.fromElement(element); 49 | final unsafeSyncWrite = localConfig.syncWrite ?? config.syncWrite; 50 | final syncWrite = unsafeSyncWrite ?? _defaultSyncWriteValue; 51 | final limit = isDirect ? null : localConfig.limit ?? config.limit; 52 | final ttl = isDirect ? null : localConfig.ttl ?? config.ttl; 53 | final persistentStorage = isPersistent || localConfig.persistentStorage; 54 | final lazyPersistentStorage = !isDirect && isLazy; 55 | final returnType = element.returnType.getDisplayString( 56 | withNullability: true, 57 | ); 58 | final params = element.parameters.map( 59 | (e) => Param.fromElement(e, config), 60 | ); 61 | 62 | final method = CachedMethod( 63 | name: element.name, 64 | syncWrite: syncWrite, 65 | limit: limit, 66 | ttl: ttl, 67 | checkIfShouldCacheMethod: localConfig.checkIfShouldCacheMethod, 68 | isAsync: element.isAsynchronous, 69 | isGenerator: element.isGenerator, 70 | persistentStorage: persistentStorage, 71 | directPersistentStorage: isDirect, 72 | params: params, 73 | lazyPersistentStorage: lazyPersistentStorage, 74 | returnType: returnType, 75 | ); 76 | 77 | assertOneIgnoreCacheParam(method); 78 | 79 | return method; 80 | } 81 | 82 | final Iterable params; 83 | } 84 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/check_if_should_cache_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:cached/src/utils/asserts.dart'; 3 | import 'package:cached/src/utils/utils.dart'; 4 | 5 | class CheckIfShouldCacheMethod { 6 | const CheckIfShouldCacheMethod({ 7 | required this.name, 8 | required this.returnType, 9 | required this.isAsync, 10 | }); 11 | 12 | factory CheckIfShouldCacheMethod.fromElements({ 13 | required ExecutableElement annotatedMethod, 14 | required ExecutableElement shouldCacheFunction, 15 | }) { 16 | assertMethodReturnsBool(shouldCacheFunction); 17 | assertHasSingleParameterWithGivenType( 18 | shouldCacheFunction, 19 | annotatedMethod.returnType, 20 | ); 21 | assertNotSyncAsyncMismatch( 22 | annotatedMethod, 23 | shouldCacheFunction, 24 | ); 25 | 26 | final name = shouldCacheFunction.name; 27 | final returnType = shouldCacheFunction.returnType; 28 | final returnTypeStr = returnType.getDisplayString(withNullability: false); 29 | final isAsync = 30 | shouldCacheFunction.isAsynchronous || isFuture(returnTypeStr); 31 | 32 | return CheckIfShouldCacheMethod( 33 | name: name, 34 | returnType: returnTypeStr, 35 | isAsync: isAsync, 36 | ); 37 | } 38 | 39 | final String name; 40 | final String returnType; 41 | final bool isAsync; 42 | } 43 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/class_with_cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:cached/src/models/cache_peek_method.dart'; 4 | import 'package:cached/src/models/cached_function.dart'; 5 | import 'package:cached/src/models/cached_getter.dart'; 6 | import 'package:cached/src/models/cached_method.dart'; 7 | import 'package:cached/src/models/clear_all_cached_method.dart'; 8 | import 'package:cached/src/models/clear_cached_method.dart'; 9 | import 'package:cached/src/models/constructor.dart'; 10 | import 'package:cached/src/models/deletes_cache_method.dart'; 11 | import 'package:cached/src/models/streamed_cache_method.dart'; 12 | import 'package:cached/src/utils/asserts.dart'; 13 | import 'package:cached/src/utils/utils.dart'; 14 | import 'package:cached_annotation/cached_annotation.dart'; 15 | import 'package:collection/collection.dart'; 16 | import 'package:source_gen/source_gen.dart'; 17 | 18 | const _defaultUseStaticCache = false; 19 | 20 | class ClassWithCache { 21 | const ClassWithCache({ 22 | required this.name, 23 | required this.useStaticCache, 24 | required this.methods, 25 | required this.constructor, 26 | required this.clearMethods, 27 | required this.streamedCacheMethods, 28 | required this.cachePeekMethods, 29 | required this.deletesCacheMethods, 30 | required this.getters, 31 | this.clearAllMethod, 32 | }); 33 | 34 | factory ClassWithCache.fromElement(ClassElement element, Config config) { 35 | assertAbstract(element); 36 | assertOneConstFactoryConstructor(element); 37 | 38 | const classAnnotationChecker = TypeChecker.fromRuntime(WithCache); 39 | final annotation = classAnnotationChecker.firstAnnotationOf(element); 40 | 41 | bool? useStaticCache; 42 | 43 | if (annotation != null) { 44 | final reader = ConstantReader(annotation); 45 | final useStaticCacheField = reader.read('useStaticCache'); 46 | if (useStaticCacheField.isBool) { 47 | useStaticCache = useStaticCacheField.boolValue; 48 | } 49 | } 50 | 51 | final constructor = element.constructors 52 | .map((element) => Constructor.fromElement(element, config)) 53 | .first; 54 | 55 | final methods = element.methods 56 | .where(CachedFunction.hasCachedAnnotation) 57 | .map((e) => CachedMethod.fromElement(e, config)); 58 | 59 | final methodsWithTtls = methods 60 | .where((method) => method.ttl != null) 61 | .map((method) => method.name); 62 | 63 | final getters = element.accessors 64 | .where((element) => element.isGetter) 65 | .where(CachedFunction.hasCachedAnnotation) 66 | .map((e) => CachedGetter.fromElement(e, config)); 67 | 68 | final gettersWithTtls = getters 69 | .where((getter) => getter.ttl != null) 70 | .map((getter) => getter.name); 71 | 72 | final clearMethods = element.methods 73 | .where((element) => ClearCachedMethod.getAnnotation(element) != null) 74 | .inspect(assertCorrectClearMethodType) 75 | .map( 76 | (e) => ClearCachedMethod.fromElement( 77 | e, 78 | config, 79 | {...methodsWithTtls, ...gettersWithTtls}, 80 | ), 81 | ); 82 | 83 | assertValidateClearCachedMethods(clearMethods, methods, getters); 84 | 85 | final clearAllMethod = element.methods 86 | .where((element) => ClearAllCachedMethod.getAnnotation(element) != null) 87 | .inspect(assertCorrectClearMethodType) 88 | .map( 89 | (e) => ClearAllCachedMethod.fromElement( 90 | e, 91 | config, 92 | {...methodsWithTtls, ...gettersWithTtls}, 93 | ), 94 | ); 95 | 96 | assertOneClearAllCachedAnnotation(clearAllMethod); 97 | 98 | final streamedCacheMethods = element.methods 99 | .where((element) => StreamedCacheMethod.getAnnotation(element) != null) 100 | .inspect(assertCorrectStreamMethodType) 101 | .map( 102 | (e) => StreamedCacheMethod.fromElement( 103 | e, 104 | [...element.methods, ...element.accessors], 105 | config, 106 | ), 107 | ) 108 | .toList(); 109 | 110 | assertOneCacheStreamPerCachedMethod( 111 | [...element.methods, ...element.accessors], 112 | streamedCacheMethods, 113 | ); 114 | 115 | final cachePeekMethods = element.methods 116 | .where((element) => CachePeekMethod.getAnnotation(element) != null) 117 | .inspect(assertCorrectCachePeekMethodType) 118 | .map( 119 | (e) => CachePeekMethod.fromElement( 120 | e, 121 | [...element.methods, ...element.accessors], 122 | config, 123 | ), 124 | ) 125 | .toList(); 126 | 127 | assertOneCachePeekPerCachedMethod( 128 | [...element.methods, ...element.accessors], 129 | cachePeekMethods, 130 | ); 131 | 132 | final deletesCacheMethods = element.methods 133 | .where((element) => DeletesCacheMethod.getAnnotation(element) != null) 134 | .inspect(assertCorrectDeletesCacheMethodType) 135 | .map( 136 | (e) => DeletesCacheMethod.fromElement( 137 | e, 138 | config, 139 | {...methodsWithTtls, ...gettersWithTtls}, 140 | ), 141 | ) 142 | .toList(); 143 | 144 | assertValidateDeletesCacheMethods( 145 | deletesCacheMethods, 146 | [...methods, ...getters], 147 | ); 148 | 149 | return ClassWithCache( 150 | name: element.name, 151 | useStaticCache: 152 | useStaticCache ?? config.useStaticCache ?? _defaultUseStaticCache, 153 | methods: methods, 154 | clearMethods: clearMethods, 155 | streamedCacheMethods: streamedCacheMethods, 156 | constructor: constructor, 157 | clearAllMethod: clearAllMethod.firstOrNull, 158 | cachePeekMethods: cachePeekMethods, 159 | deletesCacheMethods: deletesCacheMethods, 160 | getters: getters, 161 | ); 162 | } 163 | 164 | final bool useStaticCache; 165 | final String name; 166 | final Constructor constructor; 167 | final Iterable methods; 168 | final Iterable clearMethods; 169 | final Iterable streamedCacheMethods; 170 | final Iterable cachePeekMethods; 171 | final Iterable deletesCacheMethods; 172 | final ClearAllCachedMethod? clearAllMethod; 173 | final Iterable getters; 174 | } 175 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/clear_all_cached_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:cached/src/config.dart'; 4 | import 'package:cached/src/models/param.dart'; 5 | import 'package:cached/src/utils/asserts.dart'; 6 | import 'package:cached_annotation/cached_annotation.dart'; 7 | 8 | import 'package:source_gen/source_gen.dart'; 9 | 10 | class ClearAllCachedMethod { 11 | const ClearAllCachedMethod({ 12 | required this.name, 13 | required this.returnType, 14 | required this.isAsync, 15 | required this.params, 16 | required this.isAbstract, 17 | required this.ttlsToClear, 18 | }); 19 | 20 | factory ClearAllCachedMethod.fromElement( 21 | MethodElement element, 22 | Config config, 23 | Set ttlsToClear, 24 | ) { 25 | if (PersistentStorageHolder.isStorageSet) { 26 | assertPersistentStorageShouldBeAsync(element); 27 | } 28 | 29 | return ClearAllCachedMethod( 30 | name: element.name, 31 | returnType: element.returnType.getDisplayString(withNullability: true), 32 | isAsync: element.isAsynchronous, 33 | isAbstract: element.isAbstract, 34 | params: element.parameters.map((e) => Param.fromElement(e, config)), 35 | ttlsToClear: ttlsToClear, 36 | ); 37 | } 38 | 39 | final String name; 40 | final String returnType; 41 | final bool isAbstract; 42 | final bool isAsync; 43 | final Iterable params; 44 | final Set ttlsToClear; 45 | 46 | static DartObject? getAnnotation(MethodElement element) { 47 | const methodAnnotationChecker = TypeChecker.fromRuntime(ClearAllCached); 48 | return methodAnnotationChecker.firstAnnotationOf(element); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/clear_cached_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:cached/src/config.dart'; 4 | import 'package:cached/src/extensions.dart'; 5 | import 'package:cached/src/models/param.dart'; 6 | import 'package:cached/src/utils/asserts.dart'; 7 | import 'package:cached_annotation/cached_annotation.dart'; 8 | 9 | import 'package:source_gen/source_gen.dart'; 10 | 11 | const String _clearPrefix = 'clear'; 12 | 13 | class ClearCachedMethod { 14 | const ClearCachedMethod({ 15 | required this.name, 16 | required this.methodName, 17 | required this.returnType, 18 | required this.isAsync, 19 | required this.params, 20 | required this.isGenerator, 21 | required this.isAbstract, 22 | required this.shouldClearTtl, 23 | }); 24 | 25 | factory ClearCachedMethod.fromElement( 26 | MethodElement element, 27 | Config config, 28 | Set ttlsToClear, 29 | ) { 30 | if (PersistentStorageHolder.isStorageSet) { 31 | assertPersistentStorageShouldBeAsync(element); 32 | } 33 | 34 | final annotation = getAnnotation(element); 35 | String? methodName; 36 | 37 | if (annotation != null) { 38 | methodName = _getMethodName(annotation); 39 | } 40 | 41 | final name = element.name; 42 | if (methodName == null || methodName.isEmpty) { 43 | methodName = _validateMethodName(name, element, methodName); 44 | } 45 | 46 | final returnType = element.returnType; 47 | final displayType = returnType.getDisplayString(withNullability: true); 48 | final parameters = element.parameters; 49 | final mappedParams = parameters.map((e) => Param.fromElement(e, config)); 50 | final shouldClearTtl = ttlsToClear.contains(methodName); 51 | 52 | return ClearCachedMethod( 53 | name: name, 54 | methodName: methodName, 55 | returnType: displayType, 56 | isAsync: element.isAsynchronous, 57 | isGenerator: element.isGenerator, 58 | isAbstract: element.isAbstract, 59 | params: mappedParams, 60 | shouldClearTtl: shouldClearTtl, 61 | ); 62 | } 63 | 64 | final String name; 65 | final String methodName; 66 | final String returnType; 67 | final bool isGenerator; 68 | final bool isAbstract; 69 | final bool isAsync; 70 | final Iterable params; 71 | final bool shouldClearTtl; 72 | 73 | static DartObject? getAnnotation(MethodElement element) { 74 | const methodAnnotationChecker = TypeChecker.fromRuntime(ClearCached); 75 | return methodAnnotationChecker.firstAnnotationOf(element); 76 | } 77 | 78 | static String? _getMethodName(DartObject annotation) { 79 | final reader = ConstantReader(annotation); 80 | final methodNameField = reader.read('methodName'); 81 | 82 | if (methodNameField.isString) { 83 | return methodNameField.stringValue; 84 | } 85 | 86 | return null; 87 | } 88 | 89 | static String _validateMethodName( 90 | String name, 91 | MethodElement element, 92 | String? methodName, 93 | ) { 94 | final prefixNotContained = !name.contains(_clearPrefix); 95 | 96 | if (prefixNotContained) { 97 | const message = 98 | "[ERROR] Name of method for which cache should be cleared is not provider. Provide it trough annotation parameter (`@ClearCached('methodName')`) or through clear function name e.g. `void ${_clearPrefix}MethodName();`"; 99 | throw InvalidGenerationSourceError(message, element: element); 100 | } 101 | 102 | final fixedName = name.replaceAll(_clearPrefix, ''); 103 | return fixedName.startsWithLowerCase(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/constructor.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:cached/src/models/param.dart'; 4 | 5 | class Constructor { 6 | const Constructor({ 7 | required this.params, 8 | }); 9 | 10 | factory Constructor.fromElement(ConstructorElement element, Config config) { 11 | return Constructor( 12 | params: element.parameters.map((e) => Param.fromElement(e, config)), 13 | ); 14 | } 15 | 16 | final Iterable params; 17 | } 18 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/deletes_cache_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:cached/src/config.dart'; 4 | import 'package:cached/src/models/param.dart'; 5 | import 'package:cached_annotation/cached_annotation.dart'; 6 | 7 | import 'package:source_gen/source_gen.dart'; 8 | 9 | class DeletesCacheMethod { 10 | const DeletesCacheMethod({ 11 | required this.name, 12 | required this.methodNames, 13 | required this.returnType, 14 | required this.isAsync, 15 | required this.params, 16 | required this.isGenerator, 17 | required this.ttlsToClear, 18 | }); 19 | 20 | factory DeletesCacheMethod.fromElement( 21 | MethodElement element, 22 | Config config, 23 | Set ttlsToClear, 24 | ) { 25 | final annotation = getAnnotation(element); 26 | 27 | List? methodNames; 28 | 29 | if (annotation != null) { 30 | final reader = ConstantReader(annotation); 31 | final methodNameField = reader.read('methodNames'); 32 | 33 | if (methodNameField.isList && 34 | !methodNameField.listValue.any( 35 | (value) => value.toStringValue() == null, 36 | )) { 37 | methodNames = methodNameField.listValue 38 | .map( 39 | (e) => e.toStringValue()!, 40 | ) 41 | .toList(); 42 | } 43 | } 44 | 45 | return DeletesCacheMethod( 46 | name: element.name, 47 | methodNames: methodNames ?? [], 48 | returnType: element.returnType.getDisplayString(withNullability: true), 49 | isAsync: element.isAsynchronous, 50 | isGenerator: element.isGenerator, 51 | params: element.parameters.map((e) => Param.fromElement(e, config)), 52 | ttlsToClear: methodNames != null 53 | ? ttlsToClear 54 | .where((element) => methodNames!.contains(element)) 55 | .toList() 56 | : [], 57 | ); 58 | } 59 | 60 | final String name; 61 | final List methodNames; 62 | final String returnType; 63 | final bool isGenerator; 64 | final bool isAsync; 65 | final Iterable params; 66 | final List ttlsToClear; 67 | 68 | static DartObject? getAnnotation(MethodElement element) { 69 | const methodAnnotationChecker = TypeChecker.fromRuntime(DeletesCache); 70 | return methodAnnotationChecker.firstAnnotationOf(element); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/param.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/element.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:cached_annotation/cached_annotation.dart'; 4 | import 'package:source_gen/source_gen.dart'; 5 | 6 | const _defaultOnCacheOnError = false; 7 | 8 | class IgnoreCacheAnnotation { 9 | const IgnoreCacheAnnotation({ 10 | required this.useCacheOnError, 11 | }); 12 | 13 | final bool useCacheOnError; 14 | } 15 | 16 | class CacheKeyAnnotation { 17 | const CacheKeyAnnotation( 18 | this.cacheFunctionCall, 19 | ); 20 | 21 | final String cacheFunctionCall; 22 | 23 | @override 24 | bool operator ==(Object other) => 25 | identical(this, other) || 26 | other is CacheKeyAnnotation && 27 | runtimeType == other.runtimeType && 28 | cacheFunctionCall == other.cacheFunctionCall; 29 | 30 | @override 31 | int get hashCode => cacheFunctionCall.hashCode; 32 | } 33 | 34 | class Param { 35 | const Param({ 36 | required this.name, 37 | required this.type, 38 | required this.isNamed, 39 | required this.isOptional, 40 | required this.ignoreCacheKey, 41 | this.ignoreCacheAnnotation, 42 | this.cacheKeyAnnotation, 43 | this.defaultValue, 44 | }); 45 | 46 | factory Param.fromElement(ParameterElement element, Config config) { 47 | // Ignore cache annotation data 48 | const paramAnnotationChecker = TypeChecker.fromRuntime(IgnoreCache); 49 | const cacheKeyAnnotationChecker = TypeChecker.fromRuntime(CacheKey); 50 | 51 | final annotation = paramAnnotationChecker.firstAnnotationOf(element); 52 | final cacheKeyAnnotation = 53 | cacheKeyAnnotationChecker.firstAnnotationOf(element); 54 | 55 | IgnoreCacheAnnotation? annotationData; 56 | CacheKeyAnnotation? cacheKeyAnnotationData; 57 | 58 | if (annotation != null && cacheKeyAnnotation != null) { 59 | throw InvalidGenerationSourceError( 60 | '[ERROR] Ignore cache cannot be used with cache key annotation', 61 | element: element, 62 | ); 63 | } 64 | 65 | if (annotation != null) { 66 | if (element.type.getDisplayString(withNullability: true) != 'bool') { 67 | throw InvalidGenerationSourceError( 68 | '[ERROR] Ignore cache param need to be not nullable bool', 69 | element: element, 70 | ); 71 | } 72 | annotationData = IgnoreCacheAnnotation( 73 | useCacheOnError: config.onCacheOnError ?? _defaultOnCacheOnError, 74 | ); 75 | 76 | final reader = ConstantReader(annotation); 77 | final useCacheOnErrorField = reader.read('useCacheOnError'); 78 | if (useCacheOnErrorField.isBool) { 79 | annotationData = IgnoreCacheAnnotation( 80 | useCacheOnError: useCacheOnErrorField.boolValue, 81 | ); 82 | } 83 | } 84 | 85 | if (cacheKeyAnnotation != null) { 86 | final reader = ConstantReader(cacheKeyAnnotation); 87 | final cacheKeyFuncReader = reader.read('cacheKeyGenerator'); 88 | final cacheKeyFunc = cacheKeyFuncReader.objectValue.toFunctionValue(); 89 | final elementType = element.type; 90 | 91 | if (cacheKeyFunc != null) { 92 | if (cacheKeyFunc.librarySource.fullName 93 | .startsWith('/cached_annotation/') && 94 | cacheKeyFunc.name == 'iterableCacheKeyGenerator' && 95 | !(elementType.isDartCoreList || 96 | elementType.isDartCoreIterable || 97 | elementType.isDartCoreSet)) { 98 | throw InvalidGenerationSourceError( 99 | '[ERROR] Iterable cache key generator requires iterable parameter', 100 | element: element, 101 | ); 102 | } 103 | 104 | cacheKeyAnnotationData = CacheKeyAnnotation( 105 | cacheKeyFunc.name, 106 | ); 107 | } 108 | } 109 | 110 | const ignoreParamAnnotationChecker = TypeChecker.fromRuntime(Ignore); 111 | final ignoreAnnotation = 112 | ignoreParamAnnotationChecker.firstAnnotationOf(element); 113 | 114 | return Param( 115 | name: element.name, 116 | type: element.type.getDisplayString(withNullability: true), 117 | ignoreCacheAnnotation: annotationData, 118 | defaultValue: element.defaultValueCode, 119 | isNamed: element.isNamed, 120 | isOptional: element.isOptional, 121 | ignoreCacheKey: ignoreAnnotation != null, 122 | cacheKeyAnnotation: cacheKeyAnnotationData, 123 | ); 124 | } 125 | 126 | final String name; 127 | final String type; 128 | final bool isNamed; 129 | final bool isOptional; 130 | final String? defaultValue; 131 | final IgnoreCacheAnnotation? ignoreCacheAnnotation; 132 | final bool ignoreCacheKey; 133 | final CacheKeyAnnotation? cacheKeyAnnotation; 134 | 135 | bool get isPositional => !isNamed; 136 | 137 | bool get isRequired => !isOptional; 138 | 139 | bool get isRequiredPositional => isRequired && isPositional; 140 | 141 | bool get isOptionalPositional => isOptional && isPositional; 142 | 143 | bool get isRequiredNamed => isRequired && isNamed; 144 | 145 | bool get isOptionalNamed => isOptional && isNamed; 146 | 147 | String get typeWithName => '$type $name'; 148 | 149 | @override 150 | bool operator ==(Object other) => 151 | identical(this, other) || 152 | other is Param && 153 | runtimeType == other.runtimeType && 154 | name == other.name && 155 | type == other.type && 156 | isNamed == other.isNamed && 157 | isOptional == other.isOptional && 158 | defaultValue == other.defaultValue && 159 | ignoreCacheAnnotation == other.ignoreCacheAnnotation && 160 | ignoreCacheKey == other.ignoreCacheKey && 161 | cacheKeyAnnotation == other.cacheKeyAnnotation; 162 | 163 | @override 164 | int get hashCode => Object.hash( 165 | name, 166 | type, 167 | isNamed, 168 | isOptional, 169 | defaultValue, 170 | ignoreCacheAnnotation, 171 | ignoreCacheKey, 172 | cacheKeyAnnotation, 173 | ); 174 | } 175 | -------------------------------------------------------------------------------- /packages/cached/lib/src/models/streamed_cache_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/constant/value.dart'; 2 | import 'package:analyzer/dart/element/element.dart'; 3 | import 'package:analyzer/dart/element/nullability_suffix.dart'; 4 | import 'package:cached/src/config.dart'; 5 | import 'package:cached/src/models/param.dart'; 6 | import 'package:cached_annotation/cached_annotation.dart'; 7 | import 'package:collection/collection.dart'; 8 | import 'package:source_gen/source_gen.dart'; 9 | import 'package:source_helper/source_helper.dart'; 10 | 11 | class StreamedCacheMethod { 12 | const StreamedCacheMethod({ 13 | required this.name, 14 | required this.targetMethodName, 15 | required this.coreReturnType, 16 | required this.params, 17 | required this.emitLastValue, 18 | required this.coreReturnTypeNullable, 19 | }); 20 | 21 | factory StreamedCacheMethod.fromElement( 22 | MethodElement element, 23 | List classMethods, 24 | Config config, 25 | ) { 26 | final annotation = getAnnotation(element); 27 | 28 | var methodName = ''; 29 | var emitLastValue = false; 30 | 31 | if (annotation != null) { 32 | final reader = ConstantReader(annotation); 33 | emitLastValue = reader.read('emitLastValue').boolValue; 34 | methodName = reader.read('methodName').stringValue; 35 | } 36 | 37 | final targetMethod = 38 | classMethods.where((m) => m.name == methodName).firstOrNull; 39 | 40 | if (targetMethod == null) { 41 | throw InvalidGenerationSourceError( 42 | '[ERROR] Method "$methodName" do not exists', 43 | element: element, 44 | ); 45 | } 46 | const streamTypeChecker = TypeChecker.fromRuntime(Stream); 47 | final coreCacheStreamMethodType = 48 | element.returnType.typeArgumentsOf(streamTypeChecker)?.single; 49 | final coreCacheSteamMethodTypeStr = 50 | coreCacheStreamMethodType?.getDisplayString(withNullability: true); 51 | 52 | const futureTypeChecker = TypeChecker.fromRuntime(Future); 53 | final targetMethodSyncReturnType = targetMethod.returnType.isDartAsyncFuture 54 | ? targetMethod.returnType.typeArgumentsOf(futureTypeChecker)?.single 55 | : targetMethod.returnType; 56 | 57 | final targetMethodSyncTypeStr = 58 | targetMethodSyncReturnType?.getDisplayString(withNullability: true); 59 | 60 | if (coreCacheSteamMethodTypeStr != targetMethodSyncTypeStr) { 61 | throw InvalidGenerationSourceError( 62 | '[ERROR] Streamed cache method return type needs to be a Stream<$targetMethodSyncTypeStr>', 63 | element: element, 64 | ); 65 | } 66 | 67 | const cachedAnnotationTypeChecker = TypeChecker.fromRuntime(Cached); 68 | 69 | if (!cachedAnnotationTypeChecker.hasAnnotationOf(targetMethod)) { 70 | throw InvalidGenerationSourceError( 71 | '[ERROR] Method "$methodName" do not have @cached annotation', 72 | element: element, 73 | ); 74 | } 75 | 76 | const ignoreTypeChecker = TypeChecker.any([ 77 | TypeChecker.fromRuntime(Ignore), 78 | TypeChecker.fromRuntime(IgnoreCache), 79 | ]); 80 | 81 | final targetMethodParameters = targetMethod.parameters 82 | .where((p) => !ignoreTypeChecker.hasAnnotationOf(p)) 83 | .toList(); 84 | 85 | if (!ListEquality( 86 | EqualityBy( 87 | (p) => Param.fromElement(p, config), 88 | ), 89 | ).equals(targetMethodParameters, element.parameters)) { 90 | throw InvalidGenerationSourceError( 91 | '[ERROR] Method "${targetMethod.name}" should have same parameters as "${element.name}", excluding ones marked with @ignore and @ignoreCache', 92 | element: element, 93 | ); 94 | } 95 | 96 | return StreamedCacheMethod( 97 | name: element.name, 98 | coreReturnType: coreCacheSteamMethodTypeStr ?? 'dynamic', 99 | emitLastValue: emitLastValue, 100 | params: targetMethodParameters.map((p) => Param.fromElement(p, config)), 101 | targetMethodName: methodName, 102 | coreReturnTypeNullable: coreCacheStreamMethodType?.nullabilitySuffix == 103 | NullabilitySuffix.question, 104 | ); 105 | } 106 | 107 | final String name; 108 | final String targetMethodName; 109 | final Iterable params; 110 | final String coreReturnType; 111 | final bool emitLastValue; 112 | final bool coreReturnTypeNullable; 113 | 114 | static DartObject? getAnnotation(ExecutableElement element) { 115 | const methodAnnotationChecker = TypeChecker.fromRuntime(StreamedCache); 116 | return methodAnnotationChecker.firstAnnotationOf(element); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/all_params_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/param.dart'; 2 | import 'package:cached/src/templates/param_template.dart'; 3 | import 'package:source_gen/source_gen.dart'; 4 | 5 | class AllParamsTemplate { 6 | AllParamsTemplate(this.params); 7 | 8 | final Iterable params; 9 | 10 | String generateParams() { 11 | return _generateParams((param) => param.generateParam()); 12 | } 13 | 14 | String generateThisParams() { 15 | return _generateParams((param) => param.generateThisParam()); 16 | } 17 | 18 | String generateFields({bool addOverrideAnnotation = false}) { 19 | return params 20 | .map(ParamTemplate.new) 21 | .map((e) => e.generateField()) 22 | .map((e) => '${addOverrideAnnotation ? "@override\n" : ''}$e') 23 | .join('\n'); 24 | } 25 | 26 | String generateParamsUsage() { 27 | final positional = _positional(); 28 | final named = _named(); 29 | 30 | return [ 31 | ...positional, 32 | ...named, 33 | ].join(','); 34 | } 35 | 36 | Iterable _positional() { 37 | return params 38 | .where((e) => e.isPositional) 39 | .map(ParamTemplate.new) 40 | .map((e) => e.generateParameterUsage()); 41 | } 42 | 43 | Iterable _named() { 44 | return params 45 | .where((e) => e.isNamed) 46 | .map(ParamTemplate.new) 47 | .map((e) => e.generateParameterUsage()); 48 | } 49 | 50 | String _generateParams( 51 | String Function(ParamTemplate) selector, 52 | ) { 53 | final paramTemplates = params.map( 54 | ParamTemplate.new, 55 | ); 56 | final positionals = paramTemplates.where( 57 | (e) => e.param.isRequiredPositional, 58 | ); 59 | final optionals = paramTemplates.where( 60 | (e) => e.param.isOptionalPositional, 61 | ); 62 | final named = paramTemplates.where( 63 | (e) => e.param.isNamed, 64 | ); 65 | 66 | final hasOptionals = optionals.isNotEmpty; 67 | final hasNamed = named.isNotEmpty; 68 | if (hasOptionals && hasNamed) { 69 | throw InvalidGenerationSourceError( 70 | "[ERROR] Method or constructor has optional positional params an named params which shouldn't be possible.", 71 | ); 72 | } 73 | 74 | final positionalParams = positionals.map(selector).join(','); 75 | final optionalParams = _optionalParams(optionals, selector, hasOptionals); 76 | final namedParams = _namedParams(named, selector, hasNamed); 77 | 78 | return [positionalParams, optionalParams, namedParams] 79 | .whereType() 80 | .where((element) => element.isNotEmpty) 81 | .join(','); 82 | } 83 | 84 | String? _optionalParams( 85 | Iterable optionals, 86 | String Function(ParamTemplate) paramGeneratorSelector, 87 | bool hasOptionals, 88 | ) { 89 | final optionalParamsList = optionals.map(paramGeneratorSelector); 90 | final optionalParamsJoined = optionalParamsList.join(','); 91 | return hasOptionals ? '[$optionalParamsJoined]' : null; 92 | } 93 | 94 | String? _namedParams( 95 | Iterable named, 96 | String Function(ParamTemplate) paramGeneratorSelector, 97 | bool hasNamed, 98 | ) { 99 | final namedParamsList = named.map(paramGeneratorSelector); 100 | final namedParamsJoined = namedParamsList.join(','); 101 | return hasNamed ? '{$namedParamsJoined}' : null; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/cache_peek_method_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/cache_peek_method.dart'; 2 | import 'package:cached/src/templates/all_params_template.dart'; 3 | import 'package:cached/src/utils/utils.dart'; 4 | 5 | class CachePeekMethodTemplate { 6 | CachePeekMethodTemplate( 7 | this.method, { 8 | required this.className, 9 | }) : paramsTemplate = AllParamsTemplate(method.params); 10 | 11 | final CachePeekMethod method; 12 | final String className; 13 | final AllParamsTemplate paramsTemplate; 14 | 15 | String generateMethod() { 16 | final params = paramsTemplate.generateParams(); 17 | final paramKey = getParamKey(method.params); 18 | final cacheMapName = getCacheMapName(method.targetMethodName); 19 | 20 | return ''' 21 | @override 22 | ${method.returnType}? ${method.name}($params) { 23 | final paramsKey = "$paramKey"; 24 | 25 | return $cacheMapName[paramsKey]; 26 | } 27 | '''; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/cached_getter_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/cached_getter.dart'; 2 | import 'package:cached/src/templates/cached_method_template.dart'; 3 | 4 | class CachedGetterTemplate extends CachedMethodTemplate { 5 | CachedGetterTemplate( 6 | CachedGetter super.function, { 7 | required super.useStaticCache, 8 | required super.isCacheStreamed, 9 | }); 10 | 11 | @override 12 | String get paramsKey => ''; 13 | 14 | @override 15 | String generateDefinition() { 16 | return 'get ${function.name}'; 17 | } 18 | 19 | @override 20 | String generateUsage() { 21 | return function.name; 22 | } 23 | 24 | @override 25 | String generateOnCatch() { 26 | return 'rethrow;'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/cached_method_with_params_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/cached_method.dart'; 2 | import 'package:cached/src/models/param.dart'; 3 | import 'package:cached/src/templates/all_params_template.dart'; 4 | import 'package:cached/src/templates/cached_method_template.dart'; 5 | import 'package:cached/src/utils/utils.dart'; 6 | import 'package:collection/collection.dart'; 7 | 8 | class CachedMethodWithParamsTemplate extends CachedMethodTemplate { 9 | CachedMethodWithParamsTemplate( 10 | this.method, { 11 | required bool useStaticCache, 12 | required bool isCacheStreamed, 13 | }) : paramsTemplate = AllParamsTemplate(method.params), 14 | super( 15 | method, 16 | useStaticCache: useStaticCache, 17 | isCacheStreamed: isCacheStreamed, 18 | ); 19 | 20 | final AllParamsTemplate paramsTemplate; 21 | final CachedMethod method; 22 | 23 | Param? get ignoreCacheParam => method.params.firstWhereOrNull( 24 | (element) => element.ignoreCacheAnnotation != null, 25 | ); 26 | 27 | @override 28 | String get paramsKey => getParamKey(method.params); 29 | 30 | @override 31 | String generateDefinition() { 32 | final params = paramsTemplate.generateParams(); 33 | return '${function.name}($params)'; 34 | } 35 | 36 | @override 37 | String generateUsage() { 38 | final paramsUsage = paramsTemplate.generateParamsUsage(); 39 | return '${function.name}($paramsUsage)'; 40 | } 41 | 42 | @override 43 | String generateAdditionalCacheCondition() { 44 | final ignoreCacheParam = this.ignoreCacheParam; 45 | if (ignoreCacheParam != null) { 46 | return '|| ${ignoreCacheParam.name}'; 47 | } 48 | 49 | return ''; 50 | } 51 | 52 | @override 53 | String generateOnCatch() { 54 | final ignoreCacheAnnotation = ignoreCacheParam?.ignoreCacheAnnotation; 55 | final useCacheOnError = ignoreCacheAnnotation?.useCacheOnError; 56 | final safeUseCacheOnError = useCacheOnError ?? false; 57 | final text = safeUseCacheOnError 58 | ? 'if (cachedValue != null) { return cachedValue;\n }' 59 | : ''; 60 | 61 | return '${text}rethrow;'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/clear_all_cached_method_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/cached_getter.dart'; 2 | import 'package:cached/src/models/cached_method.dart'; 3 | import 'package:cached/src/models/clear_all_cached_method.dart'; 4 | import 'package:cached/src/models/streamed_cache_method.dart'; 5 | import 'package:cached/src/templates/all_params_template.dart'; 6 | import 'package:cached/src/utils/common_generator.dart'; 7 | import 'package:cached/src/utils/persistent_storage_holder_texts.dart'; 8 | import 'package:cached/src/utils/utils.dart'; 9 | 10 | class ClearAllCachedMethodTemplate { 11 | ClearAllCachedMethodTemplate({ 12 | required this.cachedMethods, 13 | required this.cachedGetters, 14 | required Iterable streamedCacheMethods, 15 | this.method, 16 | this.isPersisted = false, 17 | this.isDirectPersisted = false, 18 | }) : paramsTemplate = AllParamsTemplate(method?.params ?? {}), 19 | streamedCacheMethodPerName = { 20 | for (final m in streamedCacheMethods) m.targetMethodName: m, 21 | }; 22 | 23 | final ClearAllCachedMethod? method; 24 | final Iterable cachedMethods; 25 | final Iterable cachedGetters; 26 | final Map streamedCacheMethodPerName; 27 | final AllParamsTemplate paramsTemplate; 28 | final bool isPersisted; 29 | final bool isDirectPersisted; 30 | 31 | String get asyncModifier => isFuture(method!.returnType) ? 'async' : ''; 32 | 33 | String get awaitIfNeeded => isFuture(method!.returnType) ? 'await' : ''; 34 | 35 | String generateMethod() { 36 | if (method == null) { 37 | return ''; 38 | } 39 | 40 | if (method!.isAbstract) { 41 | return _generateAbstractMethod(); 42 | } 43 | 44 | final isFutureBoolType = isFutureBool(method!.returnType); 45 | final isBoolType = isBool(method!.returnType); 46 | if (isFutureBoolType || isBoolType) { 47 | return _generateBoolMethod(); 48 | } 49 | 50 | final storageAwait = _generatePersistentStorageAwait(); 51 | final params = paramsTemplate.generateParams(); 52 | final paramsUsage = paramsTemplate.generateParamsUsage(); 53 | 54 | return ''' 55 | @override 56 | ${method!.returnType} ${method!.name}($params) $asyncModifier { 57 | $storageAwait 58 | 59 | $awaitIfNeeded super.${method!.name}($paramsUsage); 60 | 61 | ${_generateCacheClearMethods()} 62 | ${_generateClearPersistentStorage()} 63 | } 64 | '''; 65 | } 66 | 67 | String _generateCacheClearMethods() { 68 | return [ 69 | ...cachedMethods.map(_generateClearMapsFromMethod), 70 | ...cachedGetters.map(_generateClearMapsFromGetter), 71 | ].join('\n'); 72 | } 73 | 74 | String _generateClearPersistentStorage() { 75 | if (isPersisted || isDirectPersisted) { 76 | final isAsync = method?.isAsync ?? false; 77 | final body = isAsync ? 'await $deleteAllText' : deleteAllText; 78 | 79 | return ''' 80 | if ($isStorageSetText) { 81 | $body; 82 | } 83 | '''; 84 | } 85 | 86 | return ''; 87 | } 88 | 89 | String _generateBoolMethod() { 90 | final storageAwait = _generatePersistentStorageAwait(); 91 | final params = paramsTemplate.generateParams(); 92 | final syncType = syncReturnType(method!.returnType); 93 | final paramsUsage = paramsTemplate.generateParamsUsage(); 94 | 95 | return ''' 96 | @override 97 | ${method!.returnType} ${method!.name}($params) $asyncModifier { 98 | $storageAwait 99 | 100 | final $syncType toReturn; 101 | 102 | final result = super.${method!.name}($paramsUsage); 103 | toReturn = $awaitIfNeeded result; 104 | 105 | if (toReturn) { 106 | ${_generateCacheClearMethods()} 107 | } 108 | 109 | return toReturn; 110 | } 111 | '''; 112 | } 113 | 114 | String _generateAbstractMethod() { 115 | return ''' 116 | @override 117 | ${method!.returnType} ${method!.name}() $asyncModifier { 118 | ${_generateCacheClearMethods()} 119 | } 120 | '''; 121 | } 122 | 123 | String _generatePersistentStorageAwait() { 124 | return CommonGenerator.generatePersistentStorageAwait( 125 | isPersisted: isPersisted, 126 | isAsync: method!.isAsync, 127 | name: method!.name, 128 | ); 129 | } 130 | 131 | String _generateClearMapsFromMethod(CachedMethod clearedMethod) { 132 | if (clearedMethod.directPersistentStorage ?? false) { 133 | return ''; 134 | } 135 | 136 | final baseName = clearedMethod.name; 137 | return _generateClearMaps( 138 | baseName, 139 | streamedCacheMethodPerName[baseName], 140 | ); 141 | } 142 | 143 | String _generateClearMapsFromGetter(CachedGetter clearedMethod) { 144 | final baseName = clearedMethod.name; 145 | return _generateClearMaps( 146 | baseName, 147 | streamedCacheMethodPerName[baseName], 148 | ); 149 | } 150 | 151 | String _generateClearMaps( 152 | String baseName, 153 | StreamedCacheMethod? streamedCacheMethod, 154 | ) { 155 | final cacheMapName = getCacheMapName(baseName); 156 | final containsTtl = method?.ttlsToClear.contains(baseName) ?? false; 157 | final ttlMapName = getTtlMapName(baseName); 158 | final clearTtl = containsTtl ? '$ttlMapName.clear();' : ''; 159 | final clearedStreamCache = clearStreamedCache(streamedCacheMethod); 160 | 161 | return ''' 162 | $cacheMapName.clear(); 163 | $clearTtl 164 | $clearedStreamCache 165 | '''; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/clear_cached_method_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/clear_cached_method.dart'; 2 | import 'package:cached/src/models/streamed_cache_method.dart'; 3 | import 'package:cached/src/templates/all_params_template.dart'; 4 | import 'package:cached/src/utils/common_generator.dart'; 5 | import 'package:cached/src/utils/persistent_storage_holder_texts.dart'; 6 | import 'package:cached/src/utils/utils.dart'; 7 | 8 | class ClearCachedMethodTemplate { 9 | ClearCachedMethodTemplate( 10 | this.method, { 11 | this.streamedCacheMethod, 12 | this.isPersisted = false, 13 | this.isDirectPersisted = false, 14 | }) : paramsTemplate = AllParamsTemplate(method.params); 15 | 16 | final ClearCachedMethod method; 17 | final AllParamsTemplate paramsTemplate; 18 | final StreamedCacheMethod? streamedCacheMethod; 19 | final bool isPersisted; 20 | final bool isDirectPersisted; 21 | 22 | String get asyncModifier => isFuture(method.returnType) ? 'async' : ''; 23 | 24 | String get awaitIfNeeded => isFuture(method.returnType) ? 'await' : ''; 25 | 26 | String generateMethod() { 27 | if (method.isAbstract) { 28 | return _generateAbstractMethod(); 29 | } 30 | 31 | final isFutureBoolType = isFutureBool(method.returnType); 32 | final isBoolType = isBool(method.returnType); 33 | if (isFutureBoolType || isBoolType) { 34 | return _generateBoolMethod(); 35 | } 36 | 37 | final storageAwait = _generatePersistentStorageAwait(); 38 | final params = paramsTemplate.generateParams(); 39 | final paramsUsage = paramsTemplate.generateParamsUsage(); 40 | 41 | return ''' 42 | @override 43 | ${method.returnType} ${method.name}($params) $asyncModifier { 44 | $storageAwait 45 | 46 | $awaitIfNeeded super.${method.name}($paramsUsage); 47 | 48 | ${_generateClearMaps()} 49 | 50 | ${_generateClearPersistentStorage()} 51 | } 52 | '''; 53 | } 54 | 55 | String _generateBoolMethod() { 56 | final storageAwait = _generatePersistentStorageAwait(); 57 | final params = paramsTemplate.generateParams(); 58 | final paramsUsage = paramsTemplate.generateParamsUsage(); 59 | final syncType = syncReturnType(method.returnType); 60 | 61 | return ''' 62 | @override 63 | ${method.returnType} ${method.name}($params) $asyncModifier { 64 | $storageAwait 65 | 66 | final $syncType toReturn; 67 | 68 | final result = super.${method.name}($paramsUsage); 69 | toReturn = $awaitIfNeeded result; 70 | 71 | if(toReturn) { 72 | ${_generateClearMaps()} 73 | } 74 | 75 | return toReturn; 76 | } 77 | '''; 78 | } 79 | 80 | String _generateAbstractMethod() { 81 | return ''' 82 | @override 83 | ${method.returnType} ${method.name}() $asyncModifier { 84 | ${_generateClearMaps()} 85 | } 86 | '''; 87 | } 88 | 89 | String _generatePersistentStorageAwait() { 90 | return CommonGenerator.generatePersistentStorageAwait( 91 | isPersisted: isPersisted, 92 | isAsync: method.isAsync, 93 | name: method.name, 94 | ); 95 | } 96 | 97 | String _generateClearMaps() { 98 | if (isDirectPersisted) { 99 | return ''; 100 | } 101 | 102 | final cacheMapName = getCacheMapName(method.methodName); 103 | final ttlMapName = getTtlMapName(method.methodName); 104 | final shouldClearTtl = method.shouldClearTtl ? '$ttlMapName.clear();' : ''; 105 | final clearedStreamCache = clearStreamedCache(streamedCacheMethod); 106 | 107 | return ''' 108 | $cacheMapName.clear(); 109 | $shouldClearTtl 110 | $clearedStreamCache 111 | '''; 112 | } 113 | 114 | String _generateClearPersistentStorage() { 115 | if (isPersisted || isDirectPersisted) { 116 | final isAsync = method.isAsync; 117 | final mapName = getCacheMapName(method.methodName); 118 | final body = 119 | isAsync ? "await $deleteText('$mapName')" : "$deleteText('$mapName')"; 120 | 121 | return ''' 122 | if ($isStorageSetText) { 123 | $body; 124 | } 125 | '''; 126 | } 127 | 128 | return ''; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/deletes_cache_method_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/deletes_cache_method.dart'; 2 | import 'package:cached/src/models/streamed_cache_method.dart'; 3 | import 'package:cached/src/templates/all_params_template.dart'; 4 | import 'package:cached/src/utils/common_generator.dart'; 5 | import 'package:cached/src/utils/persistent_storage_holder_texts.dart'; 6 | import 'package:cached/src/utils/utils.dart'; 7 | 8 | class DeletesCacheMethodTemplate { 9 | DeletesCacheMethodTemplate( 10 | this.method, 11 | this.streamedCacheMethods, { 12 | this.directPersistedMethods = const [], 13 | this.isPersisted = false, 14 | this.isDirectPersisted = false, 15 | }) : paramsTemplate = AllParamsTemplate(method.params); 16 | 17 | final DeletesCacheMethod method; 18 | final AllParamsTemplate paramsTemplate; 19 | final List directPersistedMethods; 20 | final List? streamedCacheMethods; 21 | final bool isPersisted; 22 | final bool isDirectPersisted; 23 | 24 | String generateMethod() { 25 | final asyncModifier = isFuture(method.returnType) ? 'async' : ''; 26 | final awaitIfNeeded = isFuture(method.returnType) ? 'await' : ''; 27 | 28 | final params = paramsTemplate.generateParams(); 29 | final paramsUsage = paramsTemplate.generateParamsUsage(); 30 | 31 | return ''' 32 | @override 33 | ${method.returnType} ${method.name}($params) $asyncModifier { 34 | ${_generatePersistentStorageAwait()} 35 | 36 | final result = $awaitIfNeeded super.${method.name}($paramsUsage); 37 | 38 | ${_generateClearMaps()} 39 | 40 | ${_mapPersistentStorages()} 41 | 42 | return result; 43 | } 44 | '''; 45 | } 46 | 47 | String _generatePersistentStorageAwait() { 48 | return CommonGenerator.generatePersistentStorageAwait( 49 | isPersisted: isPersisted, 50 | isAsync: method.isAsync, 51 | name: method.name, 52 | ); 53 | } 54 | 55 | String _generateClearMaps() { 56 | final persistedMethodsToClear = method.methodNames 57 | .where((method) => !directPersistedMethods.contains(method)) 58 | .toList(); 59 | 60 | return [ 61 | ...persistedMethodsToClear.map(_methodToClear), 62 | ...method.ttlsToClear.map(_methodTtlsToClear), 63 | ...streamedCacheMethods?.map(clearStreamedCache) ?? [], 64 | ].join('\n'); 65 | } 66 | 67 | String _methodToClear(String methodToClear) { 68 | final cacheMapName = getCacheMapName(methodToClear); 69 | return '$cacheMapName.clear();'; 70 | } 71 | 72 | String _methodTtlsToClear(String ttlToClearMethodName) { 73 | final ttlMapName = getTtlMapName(ttlToClearMethodName); 74 | return '$ttlMapName.clear();'; 75 | } 76 | 77 | String _mapPersistentStorages() { 78 | if (isPersisted || isDirectPersisted) { 79 | final mappedMethods = method.methodNames.map(_generateClearStorage); 80 | return ''' 81 | if ($isStorageSetText) { 82 | ${mappedMethods.join('\n')} 83 | } 84 | '''; 85 | } 86 | 87 | return ''; 88 | } 89 | 90 | String _generateClearStorage(String methodName) { 91 | final isAsync = method.isAsync; 92 | final mapName = getCacheMapName(methodName); 93 | final body = 94 | isAsync ? "await $deleteText('$mapName')" : "$deleteText('$mapName')"; 95 | 96 | return '$body;'; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/file_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/class_with_cache.dart'; 2 | import 'package:cached/src/templates/class_template.dart'; 3 | import 'package:cached/src/templates/interface_template.dart'; 4 | 5 | class FileTemplate { 6 | FileTemplate(ClassWithCache classWithCache) 7 | : mixinTemplate = InterfaceTemplate(classWithCache), 8 | classTemplate = ClassTemplate(classWithCache); 9 | 10 | final InterfaceTemplate mixinTemplate; 11 | final ClassTemplate classTemplate; 12 | 13 | String generate() { 14 | return ''' 15 | ${mixinTemplate.generate()} 16 | 17 | ${classTemplate.generate()} 18 | '''; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/interface_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/class_with_cache.dart'; 2 | 3 | class InterfaceTemplate { 4 | InterfaceTemplate(this.classWithCache); 5 | 6 | final ClassWithCache classWithCache; 7 | 8 | String generate() { 9 | final constructor = classWithCache.constructor; 10 | final params = constructor.params; 11 | final name = classWithCache.name; 12 | final getters = params.map( 13 | (e) => '${e.type} get ${e.name};', 14 | ); 15 | 16 | return ''' 17 | abstract class _\$$name { 18 | ${getters.join('\n')} 19 | } 20 | '''; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/param_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/param.dart'; 2 | 3 | class ParamTemplate { 4 | const ParamTemplate(this.param); 5 | 6 | final Param param; 7 | 8 | String get _requiredKeyword => param.isRequiredNamed ? 'required' : ''; 9 | 10 | String get _defaultValue => 11 | param.defaultValue != null ? '= ${param.defaultValue}' : ''; 12 | 13 | String generateParam() { 14 | return '$_requiredKeyword ${param.type} ${param.name} $_defaultValue'; 15 | } 16 | 17 | String generateThisParam() { 18 | return '$_requiredKeyword this.${param.name} $_defaultValue'; 19 | } 20 | 21 | String generateField() { 22 | return 'final ${param.type} ${param.name};'; 23 | } 24 | 25 | String generateParameterUsage() { 26 | if (param.isNamed) { 27 | return '${param.name}: ${param.name}'; 28 | } else { 29 | return param.name; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/cached/lib/src/templates/streamed_method_template.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/streamed_cache_method.dart'; 2 | import 'package:cached/src/templates/all_params_template.dart'; 3 | import 'package:cached/src/utils/utils.dart'; 4 | 5 | class StreamedCacheMethodTemplate { 6 | StreamedCacheMethodTemplate( 7 | this.method, { 8 | required this.useStaticCache, 9 | required this.className, 10 | }) : paramsTemplate = AllParamsTemplate(method.params); 11 | 12 | final StreamedCacheMethod method; 13 | final bool useStaticCache; 14 | final String className; 15 | final AllParamsTemplate paramsTemplate; 16 | 17 | String generateStreamMap() { 18 | return 'static final ${getCacheStreamControllerName(method.targetMethodName)} = ${_streamMapInitializer()};'; 19 | } 20 | 21 | String generateMethod() { 22 | final params = paramsTemplate.generateParams(); 23 | final paramKey = getParamKey(method.params); 24 | final cacheStreamControllerName = getCacheStreamControllerName( 25 | method.targetMethodName, 26 | ); 27 | 28 | return ''' 29 | @override 30 | Stream<${method.coreReturnType}> ${method.name}($params) async* { 31 | final paramsKey = "$paramKey"; 32 | final streamController = $cacheStreamControllerName; 33 | final stream = streamController.stream 34 | ${_streamFilter()} 35 | .map((event) => event.value); 36 | 37 | ${_lastValueEmit()} 38 | 39 | yield* stream; 40 | } 41 | '''; 42 | } 43 | 44 | String _lastValueEmit() { 45 | if (!method.emitLastValue) { 46 | return ''; 47 | } 48 | 49 | final cacheMapName = getCacheMapName(method.targetMethodName); 50 | return ''' 51 | if($cacheMapName.containsKey(paramsKey)) { 52 | final lastValue = $cacheMapName[paramsKey]; 53 | ${_yieldLastValue()} 54 | } 55 | '''; 56 | } 57 | 58 | String _yieldLastValue() { 59 | if (method.coreReturnTypeNullable) { 60 | return 'yield lastValue;'; 61 | } 62 | 63 | return ''' 64 | if(lastValue != null) { 65 | yield lastValue; 66 | } 67 | '''; 68 | } 69 | 70 | String _streamMapInitializer() => 71 | '''StreamController,${method.coreReturnType}>>.broadcast()'''; 72 | 73 | String _streamFilter() { 74 | const eventFilter = '.where((event) => event.key.instance == this)'; 75 | final text = useStaticCache ? '' : eventFilter; 76 | 77 | return ''' 78 | $text 79 | .where((event) => event.key.paramsKey == null || event.key.paramsKey == paramsKey) 80 | '''; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/cached/lib/src/utils/common_generator.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/utils/persistent_storage_holder_texts.dart'; 2 | import 'package:source_gen/source_gen.dart'; 3 | 4 | class CommonGenerator { 5 | static const awaitCompleterFutureText = 'await _completerFuture;'; 6 | static const completerCompleteText = '_completer.complete();'; 7 | 8 | static String generatePersistentStorageAwait({ 9 | required bool isPersisted, 10 | required bool isAsync, 11 | required String name, 12 | }) { 13 | if (!isPersisted) { 14 | return ''; 15 | } 16 | 17 | if (!isAsync) { 18 | final message = '[ERROR] All of Cached Persistent Storage methods ' 19 | 'have to be async. Source: $name'; 20 | throw InvalidGenerationSourceError(message); 21 | } 22 | 23 | return ''' 24 | if ($isStorageSetText) { 25 | $awaitCompleterFutureText 26 | } 27 | '''; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/cached/lib/src/utils/persistent_storage_holder_texts.dart: -------------------------------------------------------------------------------- 1 | const readCodeText = 'await PersistentStorageHolder.read'; 2 | const writeCodeText = 'await PersistentStorageHolder.write'; 3 | const deleteText = 'PersistentStorageHolder.delete'; 4 | const deleteAllText = 'PersistentStorageHolder.deleteAll()'; 5 | const isStorageSetText = 'PersistentStorageHolder.isStorageSet'; 6 | -------------------------------------------------------------------------------- /packages/cached/lib/src/utils/type_cast_appender.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | class TypeCastAppender { 4 | String wrapWithTryCatchAndAddGenericCast({ 5 | required String returnType, 6 | required String codeToWrap, 7 | }) { 8 | final result = appendCastIfNeeded(returnType); 9 | if (result.isNotEmpty) { 10 | return """ 11 | try { 12 | $codeToWrap$result; 13 | } on NoSuchMethodError { 14 | throw Exception(''' 15 | You have to provide your generic classes with a `.cast()` 16 | method, if you want to store them inside a persistent storage. 17 | E.g.: 18 | 19 | class MyClass { 20 | // ... 21 | 22 | MyClass cast() { 23 | return MyClass(); 24 | } 25 | } 26 | 27 | '''); 28 | } 29 | """; 30 | } 31 | 32 | return '$codeToWrap;'; 33 | } 34 | 35 | @visibleForTesting 36 | String appendCastIfNeeded(String returnType) { 37 | final startsWithFuture = returnType.startsWith('Future<'); 38 | if (startsWithFuture) { 39 | return _generateFutureType(returnType); 40 | } 41 | 42 | return ''; 43 | } 44 | 45 | String _generateFutureType(String futureType) { 46 | final type = _getGenericType(futureType); 47 | final isGeneric = type.contains('<') && type.contains('>'); 48 | 49 | if (isGeneric) { 50 | return _generateCast(type); 51 | } 52 | 53 | return ''; 54 | } 55 | 56 | String _getGenericType(String type) { 57 | final start = type.indexOf('<'); 58 | final stop = type.lastIndexOf('>'); 59 | final result = type.substring(start + 1, stop); 60 | 61 | return result; 62 | } 63 | 64 | String _generateCast(String type) { 65 | final nestedType = _getGenericType(type); 66 | final castMethod = '.cast<$nestedType>()'; 67 | 68 | return castMethod; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/cached/lib/src/utils/utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/models/param.dart'; 2 | import 'package:cached/src/models/streamed_cache_method.dart'; 3 | 4 | final futureRegexp = RegExp(r'^Future<(.+)>$'); 5 | final futureBoolRegexp = RegExp(r'^Future$'); 6 | final futureVoidRegexp = RegExp(r'^Future$'); 7 | final voidRegexp = RegExp(r'^void$'); 8 | final boolRegexp = RegExp(r'^bool$'); 9 | 10 | String getCacheStreamControllerName(String targetMethodName) => 11 | '_${targetMethodName}CacheStreamController'; 12 | 13 | String getParamKey(Iterable params) => params 14 | .where( 15 | (element) => 16 | element.ignoreCacheAnnotation == null && !element.ignoreCacheKey, 17 | ) 18 | .map( 19 | (e) => _generateParamKeyPartCall( 20 | name: e.name, 21 | cacheKeyAnnotation: e.cacheKeyAnnotation, 22 | ), 23 | ) 24 | .join(); 25 | 26 | String getCacheMapName(String methodName) => '_${methodName}Cached'; 27 | 28 | String getTtlMapName(String methodName) => '_${methodName}Ttl'; 29 | 30 | bool isFuture(String returnType) => futureRegexp.hasMatch(returnType); 31 | 32 | bool isVoid(String returnType) => voidRegexp.hasMatch(returnType); 33 | 34 | bool isAsyncVoid(String returnType) => futureVoidRegexp.hasMatch(returnType); 35 | 36 | bool isFutureBool(String returnType) => futureBoolRegexp.hasMatch(returnType); 37 | 38 | bool isBool(String returnType) => boolRegexp.hasMatch(returnType); 39 | 40 | String syncReturnType(String returnType) { 41 | if (isFuture(returnType)) { 42 | return futureRegexp.firstMatch(returnType)?.group(1) ?? ''; 43 | } 44 | 45 | return returnType; 46 | } 47 | 48 | String clearStreamedCache(StreamedCacheMethod? method) { 49 | if (method != null && method.coreReturnTypeNullable) { 50 | final controllerName = 51 | getCacheStreamControllerName(method.targetMethodName); 52 | 53 | return ''' 54 | $controllerName.sink.add(MapEntry(StreamEventIdentifier( 55 | instance: this, 56 | ), 57 | null, 58 | )); 59 | '''; 60 | } 61 | return ''; 62 | } 63 | 64 | String _generateParamKeyPartCall({ 65 | required String name, 66 | required CacheKeyAnnotation? cacheKeyAnnotation, 67 | }) => 68 | cacheKeyAnnotation != null 69 | ? '\${${cacheKeyAnnotation.cacheFunctionCall}($name)}' 70 | : '\${$name.hashCode}'; 71 | 72 | extension Inspect on Iterable { 73 | Iterable inspect(void Function(T) fn) sync* { 74 | for (final value in this) { 75 | fn(value); 76 | yield value; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /packages/cached/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cached 2 | description: Dart package with build-in code generation. Simplifies and speedup creation of cache mechanism for dart classes. 3 | version: 1.7.1 4 | repository: https://github.com/Iteo/cached 5 | issue_tracker: https://github.com/Iteo/cached/issues 6 | homepage: https://github.com/Iteo/cached 7 | documentation: https://github.com/Iteo/cached/blob/master/packages/cached/README.md 8 | topics: 9 | - cache 10 | - lru 11 | platforms: 12 | android: 13 | ios: 14 | linux: 15 | macos: 16 | web: 17 | windows: 18 | 19 | environment: 20 | sdk: ">=3.6.0 <4.0.0" 21 | 22 | dependencies: 23 | analyzer: ^7.3.0 24 | build: ^2.4.1 25 | cached_annotation: ^1.7.1 26 | collection: ^1.16.0 27 | meta: ^1.7.0 28 | source_gen: ^2.0.0 29 | source_helper: ^1.3.2 30 | 31 | dev_dependencies: 32 | async: ^2.9.0 33 | build_runner: ^2.4.13 34 | path: ^1.8.2 35 | source_gen_test: ^1.1.1 36 | test: ^1.21.1 37 | linteo: ^1.0.1 38 | 39 | screenshots: 40 | - description: The cached package logo. 41 | path: cached_sygnet.png 42 | -------------------------------------------------------------------------------- /packages/cached/test/cache_peek_method_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'CachePeekMethodReturnType', 10 | 'MethodShouldExists', 11 | 'MethodShouldHaveCachedAnnotation', 12 | 'MethodShouldHaveSameParams', 13 | 'MethodShouldHaveSameParamsNullable', 14 | 'MethodShouldHaveSameParamsNoParams', 15 | 'MethodShouldHaveSameParamsWithoutIgnore', 16 | 'MethodShouldHaveSameParamsWithoutIgnoreCache', 17 | 'SimpleMethod', 18 | 'FutureMethod', 19 | 'Parameters', 20 | 'DuplicateTarget', 21 | 'ShouldBeAbstract', 22 | 'CachePeekWithCacheKey', 23 | 'StaticCache', 24 | }; 25 | 26 | final reader = await initializeLibraryReaderForDirectory( 27 | path.join('test', 'inputs'), 28 | 'cache_peek_method_generation_test_input.dart', 29 | ); 30 | 31 | testAnnotatedElements( 32 | reader, 33 | const CachedGenerator(config: Config()), 34 | expectedAnnotatedTests: expectedAnnotatedTests, 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /packages/cached/test/cached_getter_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'VoidGetter', 10 | 'FutureVoidGetter', 11 | 'AbstractGetter', 12 | 'Getter', 13 | 'AsyncGet', 14 | 'AsyncGeneratorGetter', 15 | 'SyncGeneratorGetter', 16 | 'CachedWithLimit', 17 | 'CachedWithTtl', 18 | 'AsyncSyncWrite', 19 | 'SyncSyncWrite', 20 | 'PersistentCachedGetter', 21 | 'DirectPersistentCachedGetter', 22 | 'LazyPersistentCachedGetter', 23 | }; 24 | 25 | final reader = await initializeLibraryReaderForDirectory( 26 | path.join('test', 'inputs'), 27 | 'cached_getter_generation_test_input.dart', 28 | ); 29 | 30 | testAnnotatedElements( 31 | reader, 32 | const CachedGenerator(config: Config()), 33 | expectedAnnotatedTests: expectedAnnotatedTests, 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /packages/cached/test/cached_method_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'VoidMethod', 10 | 'FutureVoidMethod', 11 | 'AbstractMethod', 12 | 'MethodWithNoArguments', 13 | 'AsyncMethodWithNoArguments', 14 | 'AsyncGeneratorMethodWithNoArguments', 15 | 'SyncGeneratorMethodWithNoArguments', 16 | 'MethodWithPositionalArgs', 17 | 'MethodWithOptionalArgs', 18 | 'MethodWithNamedArgs', 19 | 'MethodWithPositionalAndOptionalArgs', 20 | 'MethodWithPositionalAndNamedArgs', 21 | 'CachedWithLimit', 22 | 'CachedWithTtl', 23 | 'AsyncSyncWrite', 24 | 'SyncSyncWrite', 25 | 'StringIgnoreCache', 26 | 'IgnoreCacheParam', 27 | 'IgnoreCacheParamCacheOnError', 28 | 'IgnoreParam', 29 | 'CacheKeyParam', 30 | 'IgnoreCacheWithCacheKeyParam', 31 | 'IterableCacheKeyOnNonIterable', 32 | 'IterableCacheKeyOnIterable', 33 | }; 34 | 35 | final reader = await initializeLibraryReaderForDirectory( 36 | path.join('test', 'inputs'), 37 | 'cached_method_generation_test_input.dart', 38 | ); 39 | 40 | testAnnotatedElements( 41 | reader, 42 | const CachedGenerator(config: Config()), 43 | expectedAnnotatedTests: expectedAnnotatedTests, 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /packages/cached/test/cast_appender_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/utils/type_cast_appender.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | typedef TestFunction = void Function(String, String); 5 | 6 | void main() { 7 | final testData = { 8 | 'int': '', 9 | 'List': '', 10 | 'Future': '', 11 | 'Future>': '.cast()', 12 | 'List': '', 13 | 'Map': '', 14 | 'Future>': '.cast()', 15 | 'MyClass>': '', 16 | 'Future>>': '.cast>()', 17 | 'Future>': '.cast()', 18 | 'Future>>>': 19 | '.cast>>()', 20 | }; 21 | 22 | TestFunction getAppenderFunction({ 23 | required bool usingStorage, 24 | }) { 25 | return (String input, String expected) { 26 | final appender = TypeCastAppender(); 27 | 28 | test('Should generate correct cast for $input', () { 29 | final result = appender.appendCastIfNeeded(input); 30 | expect(result, expected); 31 | }); 32 | }; 33 | } 34 | 35 | group('shouldUsePersistentStorage == true', () { 36 | final testAppending = getAppenderFunction(usingStorage: true); 37 | testData.forEach(testAppending); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /packages/cached/test/class_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'NonAbastractClass', 10 | 'NotFactoryConstructor', 11 | 'MultipleConstructors', 12 | 'ValidClass', 13 | }; 14 | 15 | final reader = await initializeLibraryReaderForDirectory( 16 | path.join('test', 'inputs'), 17 | 'class_generation_test_input.dart', 18 | ); 19 | 20 | testAnnotatedElements( 21 | reader, 22 | const CachedGenerator(config: Config()), 23 | expectedAnnotatedTests: expectedAnnotatedTests, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/cached/test/clear_all_cached_method_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'MultipleClearAllMethods', 10 | 'InvalidReturnType', 11 | 'InvalidReturnTypeNonAbstract', 12 | 'AbstractWithParams', 13 | 'ValidAbstractWithTtl', 14 | 'ValidAbstract', 15 | 'ValidAbstractFuture', 16 | 'ValidReturnFutureBool', 17 | 'ValidReturnFutureVoid', 18 | 'ClearAllCachedDirectPersistentStorage', 19 | 'ClearAllCachedDirectPersistentStorageAndPersistentStorage', 20 | }; 21 | 22 | final reader = await initializeLibraryReaderForDirectory( 23 | path.join('test', 'inputs'), 24 | 'clear_all_cached_method_generation_test_input.dart', 25 | ); 26 | 27 | testAnnotatedElements( 28 | reader, 29 | const CachedGenerator(config: Config()), 30 | expectedAnnotatedTests: expectedAnnotatedTests, 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/cached/test/clear_cached_method_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | 9 | const expectedAnnotatedTests = { 10 | 'NoTargetMethod', 11 | 'MultipleClearMethods', 12 | 'InvalidName', 13 | 'InvalidReturnType', 14 | 'InvalidReturnTypeNonAbstract', 15 | 'AbstractWithParams', 16 | 'ValidAbstractWithTtl', 17 | 'ValidAbstract', 18 | 'ValidAbstractFuture', 19 | 'ValidAbstractWithTwoCachedMethod', 20 | 'ValidReturnFutureBool', 21 | 'ValidReturnFutureVoid', 22 | 'ClearCachedDirectPersistentStorage', 23 | }; 24 | 25 | final reader = await initializeLibraryReaderForDirectory( 26 | path.join('test', 'inputs'), 27 | 'clear_cached_method_generation_test_input.dart', 28 | ); 29 | 30 | testAnnotatedElements( 31 | reader, 32 | const CachedGenerator(config: Config()), 33 | expectedAnnotatedTests: expectedAnnotatedTests, 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /packages/cached/test/constructor_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'PositionalArguments', 10 | 'OptionalArguments', 11 | 'NamedArguments', 12 | 'PositionalAndOptinalArguments', 13 | 'PositionalAndNamesArguments', 14 | }; 15 | 16 | final reader = await initializeLibraryReaderForDirectory( 17 | path.join('test', 'inputs'), 18 | 'constructor_generation_test_input.dart', 19 | ); 20 | 21 | testAnnotatedElements( 22 | reader, 23 | const CachedGenerator(config: Config()), 24 | expectedAnnotatedTests: expectedAnnotatedTests, 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/cached/test/deletes_cache_method_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'ShouldHaveCachedMethod', 10 | 'NoMethodsSpecified', 11 | 'InvalidTarget', 12 | 'CantBeAbstract', 13 | 'ValidNoTtl', 14 | 'ValidTtl', 15 | 'ValidStreamed', 16 | 'ValidTwoMethods', 17 | 'ValidSync', 18 | 'DeleteCachedDirectPersistentStorage', 19 | 'DeleteAllCachedPersistentStorage', 20 | }; 21 | 22 | final reader = await initializeLibraryReaderForDirectory( 23 | path.join('test', 'inputs'), 24 | 'deletes_cache_method_generation_test_input.dart', 25 | ); 26 | 27 | testAnnotatedElements( 28 | reader, 29 | const CachedGenerator(config: Config()), 30 | expectedAnnotatedTests: expectedAnnotatedTests, 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/cached/test/inputs/class_generation_test_input.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:source_gen_test/source_gen_test.dart'; 3 | 4 | @ShouldThrow('[ERROR] Class NonAbastractClass need to be abstract') 5 | @withCache 6 | class NonAbastractClass {} 7 | 8 | @ShouldThrow( 9 | '[ERROR] Class NotFactoryConstructor need to have one factory constructor') 10 | @withCache 11 | abstract class NotFactoryConstructor { 12 | NotFactoryConstructor(); 13 | } 14 | 15 | @ShouldThrow( 16 | '[ERROR] To many constructors in MultipleConstructors class. Class can have ' 17 | 'only one constructor', 18 | ) 19 | @withCache 20 | abstract class MultipleConstructors { 21 | factory MultipleConstructors() = _MultipleConstructors; 22 | 23 | factory MultipleConstructors.other() = _MultipleConstructorsOther; 24 | } 25 | 26 | @ShouldGenerate( 27 | r''' 28 | abstract class _$ValidClass {} 29 | 30 | class _ValidClass with ValidClass implements _$ValidClass { 31 | _ValidClass(); 32 | } 33 | ''', 34 | ) 35 | @withCache 36 | abstract class ValidClass { 37 | factory ValidClass() = _ValidClass; 38 | } 39 | -------------------------------------------------------------------------------- /packages/cached/test/inputs/constructor_generation_test_input.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:source_gen_test/source_gen_test.dart'; 3 | 4 | @ShouldGenerate( 5 | r''' 6 | abstract class _$PositionalArguments { 7 | int get a; 8 | String? get b; 9 | Stream? get c; 10 | } 11 | 12 | class _PositionalArguments 13 | with PositionalArguments 14 | implements _$PositionalArguments { 15 | _PositionalArguments(this.a, this.b, this.c); 16 | 17 | @override 18 | final int a; 19 | @override 20 | final String? b; 21 | @override 22 | final Stream? c; 23 | } 24 | ''', 25 | ) 26 | @withCache 27 | abstract class PositionalArguments implements _$PositionalArguments { 28 | factory PositionalArguments( 29 | int a, 30 | String? b, 31 | Stream? c, 32 | ) = _PositionalArguments; 33 | } 34 | 35 | @ShouldGenerate( 36 | r''' 37 | abstract class _$OptionalArguments { 38 | String? get b; 39 | Stream? get c; 40 | } 41 | 42 | class _OptionalArguments with OptionalArguments implements _$OptionalArguments { 43 | _OptionalArguments([this.b, this.c]); 44 | 45 | @override 46 | final String? b; 47 | @override 48 | final Stream? c; 49 | } 50 | ''', 51 | ) 52 | @withCache 53 | abstract class OptionalArguments implements _$OptionalArguments { 54 | factory OptionalArguments([ 55 | String? b, 56 | Stream? c, 57 | ]) = _OptionalArguments; 58 | } 59 | 60 | @ShouldGenerate( 61 | r''' 62 | abstract class _$NamedArguments { 63 | int get a; 64 | String? get b; 65 | Stream? get c; 66 | } 67 | 68 | class _NamedArguments with NamedArguments implements _$NamedArguments { 69 | _NamedArguments({required this.a, this.b, required this.c}); 70 | 71 | @override 72 | final int a; 73 | @override 74 | final String? b; 75 | @override 76 | final Stream? c; 77 | } 78 | ''', 79 | ) 80 | @withCache 81 | abstract class NamedArguments implements _$NamedArguments { 82 | factory NamedArguments({ 83 | required int a, 84 | String? b, 85 | required Stream? c, 86 | }) = _NamedArguments; 87 | } 88 | 89 | @ShouldGenerate( 90 | r''' 91 | abstract class _$PositionalAndOptinalArguments { 92 | int get a; 93 | String? get b; 94 | Stream? get c; 95 | } 96 | 97 | class _PositionalAndOptinalArguments 98 | with PositionalAndOptinalArguments 99 | implements _$PositionalAndOptinalArguments { 100 | _PositionalAndOptinalArguments(this.a, this.b, [this.c]); 101 | 102 | @override 103 | final int a; 104 | @override 105 | final String? b; 106 | @override 107 | final Stream? c; 108 | } 109 | ''', 110 | ) 111 | @withCache 112 | abstract class PositionalAndOptinalArguments 113 | implements _$PositionalAndOptinalArguments { 114 | factory PositionalAndOptinalArguments( 115 | int a, 116 | String? b, [ 117 | Stream? c, 118 | ]) = _PositionalAndOptinalArguments; 119 | } 120 | 121 | @ShouldGenerate( 122 | r''' 123 | abstract class _$PositionalAndNamesArguments { 124 | int get a; 125 | String? get b; 126 | Stream? get c; 127 | double get d; 128 | } 129 | 130 | class _PositionalAndNamesArguments 131 | with PositionalAndNamesArguments 132 | implements _$PositionalAndNamesArguments { 133 | _PositionalAndNamesArguments(this.a, this.b, {this.c, required this.d}); 134 | 135 | @override 136 | final int a; 137 | @override 138 | final String? b; 139 | @override 140 | final Stream? c; 141 | @override 142 | final double d; 143 | } 144 | ''', 145 | ) 146 | @withCache 147 | abstract class PositionalAndNamesArguments 148 | implements _$PositionalAndNamesArguments { 149 | factory PositionalAndNamesArguments( 150 | int a, 151 | String? b, { 152 | Stream? c, 153 | required double d, 154 | }) = _PositionalAndNamesArguments; 155 | } 156 | -------------------------------------------------------------------------------- /packages/cached/test/inputs/should_cache_generation_test_input.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:source_gen_test/annotations.dart'; 3 | 4 | @ShouldThrow( 5 | '[ERROR] Parameter: candidate (of type String) should match type int.') 6 | @withCache 7 | abstract class ShouldCacheIncompatibleTypes { 8 | factory ShouldCacheIncompatibleTypes() = _ShouldCacheIncompatibleTypes; 9 | 10 | @Cached(where: _shouldCacheStringCandidate) 11 | int cachedMethod() { 12 | return 1; 13 | } 14 | } 15 | 16 | bool _shouldCacheStringCandidate(String candidate) { 17 | return true; 18 | } 19 | 20 | @ShouldThrow( 21 | '[ERROR] Asynchronous and synchronous mismatch. Check return types of: ' 22 | 'cachedMethod and _shouldCacheAsyncAnnotated.') 23 | @withCache 24 | abstract class ShouldCacheAsyncAnnotated { 25 | factory ShouldCacheAsyncAnnotated() = _ShouldCacheAsyncAnnotated; 26 | 27 | @Cached(where: _shouldCacheAsyncAnnotated) 28 | Future cachedMethod() { 29 | return 1; 30 | } 31 | } 32 | 33 | bool _shouldCacheAsyncAnnotated(int candidate) { 34 | return true; 35 | } 36 | 37 | @ShouldThrow( 38 | '[ERROR] Asynchronous and synchronous mismatch. Check return types of: ' 39 | 'cachedMethod and _shouldCacheAsyncCondition.') 40 | @withCache 41 | abstract class ShouldCacheAsyncCondition { 42 | factory ShouldCacheAsyncCondition() = _ShouldCacheAsyncCondition; 43 | 44 | @Cached(where: _shouldCacheAsyncCondition) 45 | int cachedMethod() { 46 | return 1; 47 | } 48 | } 49 | 50 | Future _shouldCacheAsyncCondition(int candidate) { 51 | return true; 52 | } 53 | 54 | @ShouldThrow( 55 | '[ERROR] `_shouldCacheReturnsVoid` must be a bool or Future method') 56 | @withCache 57 | abstract class ShouldCacheReturnsVoid { 58 | factory ShouldCacheReturnsVoid() = _ShouldCacheReturnsVoid; 59 | 60 | @Cached(where: _shouldCacheReturnsVoid) 61 | Future cachedMethod() { 62 | return 1; 63 | } 64 | } 65 | 66 | void _shouldCacheReturnsVoid(int candidate) {} 67 | 68 | @ShouldGenerate(r''' 69 | abstract class _$AlwaysCache {} 70 | 71 | class _AlwaysCache with AlwaysCache implements _$AlwaysCache { 72 | _AlwaysCache(); 73 | 74 | final _cachedMethodCached = {}; 75 | 76 | @override 77 | int cachedMethod() { 78 | final cachedValue = _cachedMethodCached[""]; 79 | if (cachedValue == null) { 80 | final int toReturn; 81 | try { 82 | final result = super.cachedMethod(); 83 | 84 | toReturn = result; 85 | } catch (_) { 86 | rethrow; 87 | } finally {} 88 | 89 | final shouldCache = _alwaysCache(toReturn); 90 | if (!shouldCache) { 91 | return toReturn; 92 | } 93 | 94 | _cachedMethodCached[""] = toReturn; 95 | 96 | return toReturn; 97 | } else { 98 | return cachedValue; 99 | } 100 | } 101 | } 102 | ''') 103 | @withCache 104 | abstract class AlwaysCache { 105 | factory AlwaysCache() = _AlwaysCache; 106 | 107 | @Cached(where: _alwaysCache) 108 | int cachedMethod() { 109 | return 1; 110 | } 111 | } 112 | 113 | bool _alwaysCache(int candidate) { 114 | return true; 115 | } 116 | 117 | @ShouldGenerate(r''' 118 | abstract class _$NeverCache {} 119 | 120 | class _NeverCache with NeverCache implements _$NeverCache { 121 | _NeverCache(); 122 | 123 | final _cachedMethodCached = {}; 124 | 125 | @override 126 | int cachedMethod() { 127 | final cachedValue = _cachedMethodCached[""]; 128 | if (cachedValue == null) { 129 | final int toReturn; 130 | try { 131 | final result = super.cachedMethod(); 132 | 133 | toReturn = result; 134 | } catch (_) { 135 | rethrow; 136 | } finally {} 137 | 138 | final shouldCache = _neverCache(toReturn); 139 | if (!shouldCache) { 140 | return toReturn; 141 | } 142 | 143 | _cachedMethodCached[""] = toReturn; 144 | 145 | return toReturn; 146 | } else { 147 | return cachedValue; 148 | } 149 | } 150 | } 151 | ''') 152 | @withCache 153 | abstract class NeverCache { 154 | factory NeverCache() = _NeverCache; 155 | 156 | @Cached(where: _neverCache) 157 | int cachedMethod() { 158 | return 1; 159 | } 160 | } 161 | 162 | bool _neverCache(int candidate) { 163 | return false; 164 | } 165 | 166 | @ShouldGenerate(r''' 167 | abstract class _$Parameters {} 168 | 169 | class _Parameters with Parameters implements _$Parameters { 170 | _Parameters(); 171 | 172 | final _cachedMethodCached = {}; 173 | 174 | @override 175 | int cachedMethod(int x, String y) { 176 | final cachedValue = _cachedMethodCached["${y.hashCode}"]; 177 | if (cachedValue == null) { 178 | final int toReturn; 179 | try { 180 | final result = super.cachedMethod(x, y); 181 | 182 | toReturn = result; 183 | } catch (_) { 184 | rethrow; 185 | } finally {} 186 | 187 | final shouldCache = _annotatedMethodHasParameters(toReturn); 188 | if (!shouldCache) { 189 | return toReturn; 190 | } 191 | 192 | _cachedMethodCached["${y.hashCode}"] = toReturn; 193 | 194 | return toReturn; 195 | } else { 196 | return cachedValue; 197 | } 198 | } 199 | } 200 | ''') 201 | @withCache 202 | abstract class Parameters { 203 | factory Parameters() = _Parameters; 204 | 205 | @Cached(where: _annotatedMethodHasParameters) 206 | int cachedMethod(@ignore int x, String y) { 207 | return 1; 208 | } 209 | } 210 | 211 | bool _annotatedMethodHasParameters(int candidate) { 212 | return true; 213 | } 214 | 215 | @ShouldGenerate(r''' 216 | abstract class _$AnnotatedMethodHasKey {} 217 | 218 | class _AnnotatedMethodHasKey 219 | with AnnotatedMethodHasKey 220 | implements _$AnnotatedMethodHasKey { 221 | _AnnotatedMethodHasKey(); 222 | 223 | final _cachedMethodCached = {}; 224 | 225 | @override 226 | Future cachedMethod(List x) async { 227 | final cachedValue = _cachedMethodCached["${iterableCacheKeyGenerator(x)}"]; 228 | if (cachedValue == null) { 229 | final int toReturn; 230 | try { 231 | final result = super.cachedMethod(x); 232 | 233 | toReturn = await result; 234 | } catch (_) { 235 | rethrow; 236 | } finally {} 237 | 238 | final shouldCache = await _annotatedMethodHasKey(toReturn); 239 | if (!shouldCache) { 240 | return toReturn; 241 | } 242 | 243 | _cachedMethodCached["${iterableCacheKeyGenerator(x)}"] = toReturn; 244 | 245 | return toReturn; 246 | } else { 247 | return cachedValue; 248 | } 249 | } 250 | } 251 | ''') 252 | @withCache 253 | abstract class AnnotatedMethodHasKey { 254 | factory AnnotatedMethodHasKey() = _AnnotatedMethodHasKey; 255 | 256 | @Cached(where: _annotatedMethodHasKey) 257 | Future cachedMethod(@iterableCacheKey List x) { 258 | return x[0]; 259 | } 260 | } 261 | 262 | Future _annotatedMethodHasKey(int candidate) { 263 | return true; 264 | } 265 | 266 | @ShouldGenerate(r''' 267 | abstract class _$StaticCache {} 268 | 269 | class _StaticCache with StaticCache implements _$StaticCache { 270 | _StaticCache(); 271 | 272 | static final _cachedMethodCached = {}; 273 | 274 | @override 275 | Future cachedMethod(int x) async { 276 | final cachedValue = _cachedMethodCached["${x.hashCode}"]; 277 | if (cachedValue == null) { 278 | final int? toReturn; 279 | try { 280 | final result = super.cachedMethod(x); 281 | 282 | toReturn = await result; 283 | } catch (_) { 284 | rethrow; 285 | } finally {} 286 | 287 | final shouldCache = await _useStaticCache(toReturn); 288 | if (!shouldCache) { 289 | return toReturn; 290 | } 291 | 292 | _cachedMethodCached["${x.hashCode}"] = toReturn; 293 | 294 | return toReturn; 295 | } else { 296 | return cachedValue; 297 | } 298 | } 299 | } 300 | ''') 301 | @WithCache(useStaticCache: true) 302 | abstract class StaticCache { 303 | factory StaticCache() = _StaticCache; 304 | 305 | @Cached(where: _useStaticCache) 306 | Future cachedMethod(int x) { 307 | return y; 308 | } 309 | } 310 | 311 | Future _useStaticCache(int? candidate) { 312 | return true; 313 | } 314 | -------------------------------------------------------------------------------- /packages/cached/test/integration/asynchronous/cached_test_asynchronous.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import '../../utils/test_utils.dart'; 4 | 5 | part 'cached_test_asynchronous.cached.dart'; 6 | 7 | const int ttlDurationSeconds = 1; 8 | 9 | @withCache 10 | abstract mixin class AsynchronousCached implements _$AsynchronousCached { 11 | factory AsynchronousCached( 12 | TestDataProvider dataProvider, 13 | ) = _AsynchronousCached; 14 | 15 | int _counter = 0; 16 | 17 | @Cached(syncWrite: true) 18 | Future syncCachedValue({@ignoreCache bool refresh = false}) { 19 | return dataProvider.fetchRandomValue(); 20 | } 21 | 22 | @StreamedCache(methodName: 'syncCachedValue', emitLastValue: false) 23 | Stream syncCachedValueStream(); 24 | 25 | @Cached(syncWrite: true) 26 | Future syncCachedValueWithoutIgnore({bool smth = false}) { 27 | return dataProvider.fetchRandomValue(); 28 | } 29 | 30 | @Cached(syncWrite: true) 31 | Future syncCachedValueWithIgnore({@ignore bool smth = false}) { 32 | return dataProvider.fetchRandomValue(); 33 | } 34 | 35 | @Cached(ttl: ttlDurationSeconds, syncWrite: true) 36 | Future syncCachedValueWithTTl() async { 37 | _counter++; 38 | await Future.delayed(const Duration(milliseconds: 100)); 39 | return dataProvider.fetchRandomValue(); 40 | } 41 | 42 | @Cached(syncWrite: false) 43 | Future asyncCachedValue({@ignoreCache bool refresh = false}) { 44 | return dataProvider.fetchRandomValue(); 45 | } 46 | 47 | @Cached(ttl: ttlDurationSeconds, syncWrite: false) 48 | Future asyncCachedValueWithTTl() async { 49 | _counter++; 50 | await Future.delayed(const Duration(milliseconds: 100)); 51 | return dataProvider.fetchRandomValue(); 52 | } 53 | 54 | @StreamedCache(methodName: 'asyncCachedValue', emitLastValue: false) 55 | Stream asyncCacheStream(); 56 | 57 | @CachePeek('asyncCachedValue') 58 | int? asyncCachePeek(); 59 | 60 | @ClearCached('asyncCachedValue') 61 | Future clearAsyncCachedValue(); 62 | 63 | @ClearCached('asyncCachedValueWithTTl') 64 | void clearAsyncTTLCachedValue(); 65 | 66 | @ClearCached('syncCachedValue') 67 | void clearCachedValue(); 68 | 69 | @ClearCached('syncCachedValueWithTTl') 70 | void clearCachedValueWithTTl(); 71 | 72 | @clearAllCached 73 | void clearAll(); 74 | 75 | void resetCounter() => _counter = 0; 76 | 77 | int counter() => _counter; 78 | 79 | @Cached(syncWrite: true) 80 | Future get syncCachedValueGetter { 81 | return dataProvider.fetchRandomValue(); 82 | } 83 | 84 | @StreamedCache(methodName: 'syncCachedValueGetter', emitLastValue: false) 85 | Stream syncCachedValueGetterStream(); 86 | 87 | @StreamedCache(methodName: 'asyncCachedValueGetter', emitLastValue: false) 88 | Stream asyncCacheGetterStream(); 89 | 90 | @Cached(ttl: ttlDurationSeconds, syncWrite: true) 91 | Future get syncCachedValueGetterWithTTl async { 92 | _counter++; 93 | await Future.delayed(const Duration(milliseconds: 100)); 94 | return dataProvider.fetchRandomValue(); 95 | } 96 | 97 | @Cached(syncWrite: false) 98 | Future get asyncCachedValueGetter { 99 | return dataProvider.fetchRandomValue(); 100 | } 101 | 102 | @Cached(ttl: ttlDurationSeconds, syncWrite: false) 103 | Future get asyncCachedValueGetterWithTTl async { 104 | _counter++; 105 | await Future.delayed(const Duration(milliseconds: 100)); 106 | return dataProvider.fetchRandomValue(); 107 | } 108 | 109 | @CachePeek('asyncCachedValueGetter') 110 | int? asyncCacheGetterPeek(); 111 | 112 | @ClearCached('asyncCachedValueGetter') 113 | Future clearAsyncCachedValueGetter(); 114 | 115 | @ClearCached('asyncCachedValueGetterWithTTl') 116 | void clearAsyncTTLCachedValueGetter(); 117 | 118 | @ClearCached('syncCachedValueGetter') 119 | void clearCachedValueGetter(); 120 | 121 | @ClearCached('syncCachedValueGetterWithTTl') 122 | void clearCachedValueGetterWithTTl(); 123 | 124 | @DeletesCache(['asyncCachedValue']) 125 | Future deleteAsyncCachedValue() async { 126 | await Future.delayed(const Duration(milliseconds: 100)); 127 | } 128 | 129 | @DeletesCache(['asyncCachedValue']) 130 | Future deleteAsyncCachedValueFail() async { 131 | await Future.delayed(const Duration(milliseconds: 100)); 132 | throw Exception(); 133 | } 134 | 135 | @DeletesCache(['asyncCachedValueWithTTl']) 136 | Future deleteAsyncTTLCachedValue() async { 137 | await Future.delayed(const Duration(milliseconds: 100)); 138 | } 139 | 140 | @DeletesCache(['asyncCachedValueWithTTl']) 141 | Future deleteAsyncTTLCachedValueFail() async { 142 | await Future.delayed(const Duration(milliseconds: 100)); 143 | throw Exception(); 144 | } 145 | 146 | @DeletesCache(['syncCachedValue']) 147 | Future deleteCachedValue() async { 148 | await Future.delayed(const Duration(milliseconds: 100)); 149 | } 150 | 151 | @DeletesCache(['syncCachedValue']) 152 | Future deleteCachedValueFail() async { 153 | await Future.delayed(const Duration(milliseconds: 100)); 154 | throw Exception(); 155 | } 156 | 157 | @DeletesCache(['syncCachedValueWithTTl']) 158 | Future deleteTTLCachedValue() async { 159 | await Future.delayed(const Duration(milliseconds: 100)); 160 | } 161 | 162 | @DeletesCache(['syncCachedValueWithTTl']) 163 | Future deleteTTLCachedValueFail() async { 164 | await Future.delayed(const Duration(milliseconds: 100)); 165 | throw Exception(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /packages/cached/test/integration/conditional/cached_test_conditional.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import '../../utils/test_utils.dart'; 4 | 5 | part 'cached_test_conditional.cached.dart'; 6 | 7 | @withCache 8 | abstract mixin class ConditionalCached implements _$ConditionalCached { 9 | factory ConditionalCached( 10 | TestDataProvider dataProvider, 11 | ) = _ConditionalCached; 12 | 13 | @Cached(where: _alwaysCache) 14 | int alwaysCachedValue() { 15 | return dataProvider.getRandomValue(); 16 | } 17 | 18 | @Cached(where: _neverCache) 19 | int neverCachedValue() { 20 | return dataProvider.getRandomValue(); 21 | } 22 | 23 | @Cached(where: _alwaysCacheFutureCondition) 24 | Future alwaysCachedValueWithFutureCondition() { 25 | return dataProvider.fetchRandomValue(); 26 | } 27 | 28 | @Cached(where: _neverCacheFutureCondition) 29 | Future neverCachedValueWithFutureCondition() async { 30 | return dataProvider.getRandomValue(); 31 | } 32 | 33 | @Cached(where: _alwaysCache) 34 | int cachedTimestamp({ 35 | @ignoreCache bool refresh = false, 36 | }) { 37 | return dataProvider.getCurrentTimestamp(); 38 | } 39 | 40 | @Cached(where: _alwaysCache) 41 | int alwaysCachedTimestampWithIgnore({@ignoreCache bool smth = true}) { 42 | return dataProvider.getCurrentTimestamp(); 43 | } 44 | 45 | @Cached(where: _neverCache) 46 | int neverCachedTimestampWithIgnore({@ignoreCache bool smth = true}) { 47 | return dataProvider.getCurrentTimestamp(); 48 | } 49 | 50 | @Cached(where: _nullable) 51 | int? nullableCachedValue() { 52 | return null; 53 | } 54 | 55 | @Cached(where: _futureNullable) 56 | Future anotherNullableCachedValue() { 57 | return Future.value(); 58 | } 59 | 60 | @Cached(where: _alwaysCache) 61 | int get alwaysCachedValueGetter { 62 | return dataProvider.getRandomValue(); 63 | } 64 | 65 | @Cached(where: _neverCache) 66 | int get neverCachedValueGetter { 67 | return dataProvider.getRandomValue(); 68 | } 69 | 70 | @Cached(where: _nullable) 71 | int? get nullableCachedValueGetter { 72 | return null; 73 | } 74 | 75 | @Cached(where: _futureNullable) 76 | Future get anotherNullableCachedValueGetter { 77 | return Future.value(); 78 | } 79 | } 80 | 81 | bool _alwaysCache(int candidate) { 82 | return true; 83 | } 84 | 85 | bool _neverCache(int candidate) { 86 | return false; 87 | } 88 | 89 | Future _alwaysCacheFutureCondition(int candidate) async { 90 | await Future.delayed(const Duration(milliseconds: 100)); 91 | return Future.value(true); 92 | } 93 | 94 | Future _neverCacheFutureCondition(int candidate) async { 95 | await Future.delayed(const Duration(milliseconds: 100)); 96 | return Future.value(false); 97 | } 98 | 99 | bool _nullable(int? candidate) { 100 | return candidate == null; 101 | } 102 | 103 | Future _futureNullable(int? candidate) async { 104 | return candidate == null; 105 | } 106 | -------------------------------------------------------------------------------- /packages/cached/test/integration/conditional_integration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | import '../utils/test_utils.dart'; 4 | import 'conditional/cached_test_conditional.dart'; 5 | 6 | void main() { 7 | group( 8 | 'Conditional cache', 9 | () { 10 | late TestDataProvider dataProvider; 11 | 12 | setUp(() { 13 | dataProvider = TestDataProvider(); 14 | }); 15 | 16 | test( 17 | 'cached value should be the same on the second method call (condition: always cached)', 18 | () { 19 | final cachedClass = ConditionalCached(dataProvider); 20 | final cachedValue = cachedClass.alwaysCachedValue(); 21 | final secondCachedValue = cachedClass.alwaysCachedValue(); 22 | 23 | expect(cachedValue, equals(secondCachedValue)); 24 | }); 25 | 26 | test( 27 | 'cached value should be different on the second method call (condition: never cached)', 28 | () { 29 | final cachedClass = ConditionalCached(dataProvider); 30 | final cachedValue = cachedClass.neverCachedValue(); 31 | final secondCachedValue = cachedClass.neverCachedValue(); 32 | 33 | expect(cachedValue, isNot(equals(secondCachedValue))); 34 | }); 35 | 36 | test( 37 | 'cached value should be the same on the second method call (condition: always cached) (async version)', 38 | () async { 39 | final cachedClass = ConditionalCached(dataProvider); 40 | final cachedValue = 41 | await cachedClass.alwaysCachedValueWithFutureCondition(); 42 | final secondCachedValue = 43 | await cachedClass.alwaysCachedValueWithFutureCondition(); 44 | 45 | expect(cachedValue, equals(secondCachedValue)); 46 | }); 47 | 48 | test( 49 | 'cached value should be different on the second method call (condition: never cached) (async version)', 50 | () { 51 | final cachedClass = ConditionalCached(dataProvider); 52 | final cachedValue = cachedClass.neverCachedValueWithFutureCondition(); 53 | final secondCachedValue = 54 | cachedClass.neverCachedValueWithFutureCondition(); 55 | 56 | expect(cachedValue, isNot(equals(secondCachedValue))); 57 | }); 58 | 59 | test( 60 | 'setting ignoreCache to true should always return new value (condition: always cached)', 61 | () async { 62 | final cachedClass = ConditionalCached(dataProvider); 63 | final cachedValue = cachedClass.alwaysCachedTimestampWithIgnore(); 64 | await Future.delayed(const Duration(milliseconds: 10)); 65 | final secondCachedValue = cachedClass.cachedTimestamp(refresh: true); 66 | 67 | expect(cachedValue, isNot(equals(secondCachedValue))); 68 | }); 69 | 70 | test( 71 | 'setting ignoreCache to true should always return new value (condition: never cached)', 72 | () async { 73 | final cachedClass = ConditionalCached(dataProvider); 74 | final cachedValue = cachedClass.neverCachedTimestampWithIgnore(); 75 | await Future.delayed(const Duration(milliseconds: 10)); 76 | final secondCachedValue = cachedClass.cachedTimestamp(refresh: true); 77 | 78 | expect(cachedValue, isNot(equals(secondCachedValue))); 79 | }); 80 | }, 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /packages/cached/test/integration/direct_persistent_storage/cached_test_direct_persistent_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import '../../utils/test_utils.dart'; 4 | 5 | part 'cached_test_direct_persistent_storage.cached.dart'; 6 | 7 | @withCache 8 | abstract mixin class DirectPersistentCachedStorage 9 | implements _$DirectPersistentCachedStorage { 10 | factory DirectPersistentCachedStorage(TestDataProvider dataProvider) = 11 | _DirectPersistentCachedStorage; 12 | 13 | @directPersistentCached 14 | Future directPersistentCachedValue() async => 15 | dataProvider.fetchRandomValue(); 16 | 17 | @directPersistentCached 18 | Future anotherDirectPersistentCachedValue() async => 19 | dataProvider.fetchRandomValue(); 20 | 21 | @directPersistentCached 22 | Future> directPersistentCachedList() async => 23 | List.filled(5, dataProvider.getRandomValue()); 24 | } 25 | -------------------------------------------------------------------------------- /packages/cached/test/integration/lazy_persistent_storage_integration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | import '../utils/test_storage.dart'; 5 | import '../utils/test_storage_to_write.dart'; 6 | import '../utils/test_storage_with_data.dart'; 7 | import '../utils/test_utils.dart'; 8 | import 'direct_persistent_storage/cached_test_direct_persistent_storage.dart'; 9 | 10 | void main() { 11 | group( 12 | 'DirectPersistentStorage', 13 | () { 14 | late TestDataProvider dataProvider; 15 | 16 | setUp(() { 17 | dataProvider = TestDataProvider(); 18 | }); 19 | 20 | tearDownAll(() { 21 | PersistentStorageHolder.storage = TestStorage(); 22 | }); 23 | 24 | test('return same data from storage which save data', () async { 25 | PersistentStorageHolder.storage = TestStorageWithWrite( 26 | { 27 | '_directPersistentCachedValueCached': {}, 28 | '_anotherDirectPersistentCachedValueCached': {}, 29 | '_directPersistentCachedListCached': {}, 30 | }, 31 | ); 32 | final cachedClass = DirectPersistentCachedStorage(dataProvider); 33 | 34 | final firstResult = await cachedClass.directPersistentCachedValue(); 35 | final secondResult = await cachedClass.directPersistentCachedValue(); 36 | 37 | expect(firstResult, equals(secondResult)); 38 | }); 39 | 40 | test('return random data from a storage which does not save data', 41 | () async { 42 | PersistentStorageHolder.storage = TestStorageWithData( 43 | { 44 | '_directPersistentCachedValueCached': {}, 45 | '_anotherDirectPersistentCachedValueCached': {}, 46 | '_directPersistentCachedListCached': {}, 47 | }, 48 | ); 49 | final cachedClass = DirectPersistentCachedStorage(dataProvider); 50 | 51 | final firstResult = await cachedClass.directPersistentCachedValue(); 52 | final secondResult = await cachedClass.directPersistentCachedValue(); 53 | 54 | expect(firstResult, isNot(equals(secondResult))); 55 | }); 56 | }, 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /packages/cached/test/integration/persistent_storage/cached_test_persistent_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import '../../utils/test_utils.dart'; 4 | 5 | part 'cached_test_persistent_storage.cached.dart'; 6 | 7 | @withCache 8 | abstract mixin class PersistentCachedStorage 9 | implements _$PersistentCachedStorage { 10 | factory PersistentCachedStorage(TestDataProvider dataProvider) = 11 | _PersistentCachedStorage; 12 | 13 | @persistentCached 14 | Future persistentCachedValue() => dataProvider.fetchRandomValue(); 15 | 16 | @persistentCached 17 | Future anotherPersistentCachedValue() => dataProvider.fetchRandomValue(); 18 | 19 | @persistentCached 20 | Future> persistentCachedList() async => 21 | List.filled(5, dataProvider.getRandomValue()); 22 | 23 | @CachePeek('persistentCachedValue') 24 | int? peekCachedValue(); 25 | } 26 | -------------------------------------------------------------------------------- /packages/cached/test/integration/persistent_storage_integration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | import '../utils/test_storage.dart'; 5 | import '../utils/test_storage_with_data.dart'; 6 | import '../utils/test_utils.dart'; 7 | import 'persistent_storage/cached_test_persistent_storage.dart'; 8 | 9 | void main() { 10 | group( 11 | 'PersistentStorage', 12 | () { 13 | late TestDataProvider dataProvider; 14 | 15 | setUp(() { 16 | dataProvider = TestDataProvider(); 17 | }); 18 | 19 | tearDownAll(() { 20 | PersistentStorageHolder.storage = TestStorage(); 21 | }); 22 | 23 | test( 24 | "returns other data then the existing one in storage, when storage's cached value (on init) is null", 25 | () async { 26 | PersistentStorageHolder.storage = TestStorageWithData( 27 | { 28 | '_persistentCachedValueCached': { 29 | '': null, 30 | }, 31 | '_anotherPersistentCachedValueCached': { 32 | '': 1, 33 | }, 34 | '_persistentCachedListCached': { 35 | '': [0, 1, 2], 36 | }, 37 | }, 38 | ); 39 | final cachedClass = PersistentCachedStorage(dataProvider); 40 | 41 | final result = await cachedClass.persistentCachedValue(); 42 | 43 | expect(result.runtimeType, equals(int)); 44 | expect(result, isNot(null)); 45 | }); 46 | 47 | test( 48 | 'returns the data from itself, when its cached value (on init) is correct', 49 | () async { 50 | const testValue = 1; 51 | PersistentStorageHolder.storage = TestStorageWithData( 52 | { 53 | '_persistentCachedValueCached': { 54 | '': testValue, 55 | }, 56 | '_anotherPersistentCachedValueCached': { 57 | '': 0, 58 | }, 59 | '_persistentCachedListCached': { 60 | '': [], 61 | }, 62 | }, 63 | ); 64 | final cachedClass = PersistentCachedStorage(dataProvider); 65 | 66 | await cachedClass.anotherPersistentCachedValue(); 67 | final result = cachedClass.peekCachedValue(); 68 | 69 | expect(result, equals(testValue)); 70 | }); 71 | 72 | test( 73 | 'returns the data from itself, when its cached list value (on init) is correct', 74 | () async { 75 | const testValue = [1, 2, 3, 4, 5]; 76 | PersistentStorageHolder.storage = TestStorageWithData( 77 | { 78 | '_persistentCachedValueCached': { 79 | '': 0, 80 | }, 81 | '_anotherPersistentCachedValueCached': { 82 | '': 0, 83 | }, 84 | '_persistentCachedListCached': { 85 | '': testValue, 86 | }, 87 | }, 88 | ); 89 | final cachedClass = PersistentCachedStorage(dataProvider); 90 | 91 | await cachedClass.anotherPersistentCachedValue(); 92 | final result = await cachedClass.persistentCachedList(); 93 | 94 | expect(result, equals(testValue)); 95 | }); 96 | 97 | test( 98 | "returns other data then the existing one in storage, when storage's cached list value type (on init) is not the same as declared method return type", 99 | () async { 100 | const testValue = [1, 2, '3', 4, 5]; 101 | PersistentStorageHolder.storage = TestStorageWithData( 102 | { 103 | '_persistentCachedValueCached': { 104 | '': 0, 105 | }, 106 | '_anotherPersistentCachedValueCached': { 107 | '': 0, 108 | }, 109 | '_persistentCachedListCached': { 110 | '': testValue, 111 | }, 112 | }, 113 | ); 114 | final cachedClass = PersistentCachedStorage(dataProvider); 115 | 116 | await cachedClass.anotherPersistentCachedValue(); 117 | final result = await cachedClass.persistentCachedList(); 118 | 119 | expect(result.runtimeType, equals(List)); 120 | expect(result.runtimeType, isNot(equals(testValue.runtimeType))); 121 | }); 122 | 123 | test( 124 | "returns other data then the existing one in storage, when storage's cached value type (on init) is not the same as declared method return type", 125 | () async { 126 | const invalidType = 'invalidType'; 127 | PersistentStorageHolder.storage = TestStorageWithData( 128 | { 129 | '_persistentCachedValueCached': { 130 | '': invalidType, 131 | }, 132 | '_anotherPersistentCachedValueCached': { 133 | '': 1, 134 | }, 135 | '_persistentCachedListCached': { 136 | '': [0, 1, 2], 137 | }, 138 | }, 139 | ); 140 | final cachedClass = PersistentCachedStorage(dataProvider); 141 | 142 | final result = await cachedClass.persistentCachedValue(); 143 | 144 | expect(result.runtimeType, equals(int)); 145 | expect(result.runtimeType, isNot(equals(invalidType.runtimeType))); 146 | }); 147 | }, 148 | ); 149 | } 150 | -------------------------------------------------------------------------------- /packages/cached/test/integration/simple/cached_test_simple.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import '../../utils/test_utils.dart'; 4 | 5 | part 'cached_test_simple.cached.dart'; 6 | 7 | @withCache 8 | abstract mixin class SimpleCached implements _$SimpleCached { 9 | factory SimpleCached( 10 | TestDataProvider dataProvider, 11 | ) = _SimpleCached; 12 | 13 | @cached 14 | int cachedValue() { 15 | return dataProvider.getRandomValue(); 16 | } 17 | 18 | @cached 19 | int anotherCachedValue() { 20 | return dataProvider.getRandomValue(); 21 | } 22 | 23 | @cached 24 | int cachedValueWithCustomKey( 25 | @CacheKey(_cachedKeyGenerator) String value, 26 | ) { 27 | return dataProvider.getRandomValue(); 28 | } 29 | 30 | @cached 31 | int cachedWithIterableCacheKey(@iterableCacheKey List values) { 32 | return dataProvider.getRandomValue(); 33 | } 34 | 35 | @cached 36 | int cachedWithNullableList(@iterableCacheKey List? values) { 37 | return dataProvider.getRandomValue(); 38 | } 39 | 40 | @cached 41 | int cachedTimestamp({ 42 | @ignoreCache bool refresh = false, 43 | }) { 44 | return dataProvider.getCurrentTimestamp(); 45 | } 46 | 47 | @cached 48 | int anotherCachedTimestamp({ 49 | @ignoreCache bool refresh = false, 50 | }) { 51 | return dataProvider.getCurrentTimestamp(); 52 | } 53 | 54 | @cached 55 | int cachedTimestampWithoutIgnore({bool smth = false}) { 56 | return dataProvider.getCurrentTimestamp(); 57 | } 58 | 59 | @cached 60 | int cachedTimestampWithIgnore({@ignore bool smth = false}) { 61 | return dataProvider.getCurrentTimestamp(); 62 | } 63 | 64 | @cached 65 | int? nullableCachedValue() { 66 | return null; 67 | } 68 | 69 | @StreamedCache(methodName: 'cachedValue') 70 | Stream streamOfCachedValue(); 71 | 72 | @StreamedCache(methodName: 'cachedTimestamp', emitLastValue: true) 73 | Stream streamOfCachedTimestampLastValue(); 74 | 75 | @StreamedCache(methodName: 'nullableCachedValue', emitLastValue: true) 76 | Stream nullableCacheValueStream(); 77 | 78 | @CachePeek('cachedValue') 79 | int? cachePeekValue(); 80 | 81 | @CachePeek('cachedTimestamp') 82 | int? timestampCachePeekValue(); 83 | 84 | @CachePeek('cachedValueWithCustomKey') 85 | int? peekCachedValueWithCustomKey( 86 | @CacheKey(_cachedKeyGenerator) String value, 87 | ); 88 | 89 | @clearCached 90 | void clearCachedValue(); 91 | 92 | @ClearCached('cachedTimestamp') 93 | Future clearTimestamp(); 94 | 95 | @clearAllCached 96 | void clearAll(); 97 | 98 | @cached 99 | int get cachedValueGetter { 100 | return dataProvider.getRandomValue(); 101 | } 102 | 103 | @cached 104 | int? get nullableCachedValueGetter { 105 | return null; 106 | } 107 | 108 | @StreamedCache( 109 | methodName: 'nullableCachedValueGetter', 110 | emitLastValue: true, 111 | ) 112 | Stream nullableCacheGetterValueStream(); 113 | 114 | @StreamedCache(methodName: 'cachedValueGetter') 115 | Stream streamOfCachedGetterValue(); 116 | 117 | @CachePeek('nullableCachedValueGetter') 118 | int? nullableCacheGetterPeekValue(); 119 | 120 | @CachePeek('cachedValueGetter') 121 | int? cacheGetterPeekValue(); 122 | 123 | @clearCached 124 | void clearCachedValueGetter(); 125 | 126 | @DeletesCache(['cachedValue']) 127 | void deleteCachedValue() {} 128 | 129 | @DeletesCache(['cachedValue']) 130 | void deleteCachedValueFail() { 131 | throw Exception(); 132 | } 133 | 134 | @Cached( 135 | syncWrite: true, 136 | limit: 5, 137 | ) 138 | int getCachedValueWithLimitFive(int parameter) { 139 | return dataProvider.getRandomValue(); 140 | } 141 | 142 | @CachePeek('getCachedValueWithLimitFive') 143 | int? peekCachedValueWithLimitFive(int parameter); 144 | 145 | @Cached( 146 | syncWrite: true, 147 | limit: 1, 148 | ) 149 | int getCachedValueWithLimitOne(int parameter) { 150 | return dataProvider.getRandomValue(); 151 | } 152 | 153 | @CachePeek('getCachedValueWithLimitOne') 154 | int? peekCachedValueWithLimitOne(int parameter); 155 | } 156 | 157 | String _cachedKeyGenerator(dynamic value) => value.toString(); 158 | -------------------------------------------------------------------------------- /packages/cached/test/integration/static/cached_test_static.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | import '../../utils/test_utils.dart'; 4 | 5 | part 'cached_test_static.cached.dart'; 6 | 7 | @WithCache(useStaticCache: true) 8 | abstract mixin class StaticCached implements _$StaticCached { 9 | factory StaticCached(TestDataProvider dataProvider) = _StaticCached; 10 | 11 | @cached 12 | int cachedValue() { 13 | return dataProvider.getRandomValue(); 14 | } 15 | 16 | @StreamedCache(methodName: 'cachedValue') 17 | Stream cachedValueCacheStream(); 18 | 19 | @CachePeek('cachedValue') 20 | int? cachedValueCachePeek(); 21 | 22 | @clearAllCached 23 | void clearCache(); 24 | 25 | @cached 26 | int get cachedValueGetter { 27 | return dataProvider.getRandomValue(); 28 | } 29 | 30 | @StreamedCache(methodName: 'cachedValueGetter') 31 | Stream cachedValueGetterCacheStream(); 32 | 33 | @CachePeek('cachedValueGetter') 34 | int? cachedValueGetterCachePeek(); 35 | 36 | @DeletesCache(['cachedValue']) 37 | void deleteCachedValue() {} 38 | 39 | @DeletesCache(['cachedValue']) 40 | void deleteCachedValueFail() { 41 | throw Exception(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/cached/test/integration/static_integration_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:async/async.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | import '../utils/test_utils.dart'; 5 | import 'static/cached_test_static.dart'; 6 | 7 | void main() { 8 | group('Static cache', () { 9 | late TestDataProvider dataProvider; 10 | 11 | setUp(() { 12 | dataProvider = TestDataProvider(); 13 | StaticCached(dataProvider).clearCache(); 14 | }); 15 | 16 | test('cache should be static', () { 17 | final firstCachedClass = StaticCached(dataProvider); 18 | final secondsCachedClass = StaticCached(dataProvider); 19 | 20 | final cachedValue = firstCachedClass.cachedValue(); 21 | final cachedValueFromAnotherInstance = secondsCachedClass.cachedValue(); 22 | 23 | expect(cachedValue, equals(cachedValueFromAnotherInstance)); 24 | }); 25 | 26 | test('stream should emit between instances', () async { 27 | final firstCachedClass = StaticCached(dataProvider); 28 | final secondsCachedClass = StaticCached(dataProvider); 29 | 30 | final queue = StreamQueue(secondsCachedClass.cachedValueCacheStream()); 31 | final next = Future.microtask(() => queue.next); 32 | await Future.delayed(Duration.zero); 33 | 34 | final cachedValue = firstCachedClass.cachedValue(); 35 | 36 | expect(cachedValue, equals(await next)); 37 | }); 38 | 39 | test('peek cache should be static', () { 40 | final firstCachedClass = StaticCached(dataProvider); 41 | final secondsCachedClass = StaticCached(dataProvider); 42 | 43 | final cachedValue = firstCachedClass.cachedValueCachePeek(); 44 | final cachedValueFromAnotherInstance = 45 | secondsCachedClass.cachedValueCachePeek(); 46 | 47 | expect(cachedValue, equals(cachedValueFromAnotherInstance)); 48 | }); 49 | 50 | test('cache should be static', () { 51 | final firstCachedClass = StaticCached(dataProvider); 52 | final secondsCachedClass = StaticCached(dataProvider); 53 | 54 | final cachedValue = firstCachedClass.cachedValueGetter; 55 | final cachedValueFromAnotherInstance = 56 | secondsCachedClass.cachedValueGetter; 57 | 58 | expect(cachedValue, equals(cachedValueFromAnotherInstance)); 59 | }); 60 | 61 | test('stream should emit between instances', () async { 62 | final firstCachedClass = StaticCached(dataProvider); 63 | final secondsCachedClass = StaticCached(dataProvider); 64 | 65 | final queue = 66 | StreamQueue(secondsCachedClass.cachedValueGetterCacheStream()); 67 | final next = Future.microtask(() => queue.next); 68 | await Future.delayed(Duration.zero); 69 | 70 | final cachedValue = firstCachedClass.cachedValueGetter; 71 | 72 | expect(cachedValue, equals(await next)); 73 | }); 74 | 75 | test('peek cache should be static', () { 76 | final firstCachedClass = StaticCached(dataProvider); 77 | final secondsCachedClass = StaticCached(dataProvider); 78 | 79 | firstCachedClass.cachedValueGetter; 80 | 81 | final cachedValue = firstCachedClass.cachedValueGetterCachePeek(); 82 | final cachedValueFromAnotherInstance = 83 | secondsCachedClass.cachedValueGetterCachePeek(); 84 | 85 | expect(cachedValue, equals(cachedValueFromAnotherInstance)); 86 | }); 87 | 88 | test( 89 | 'deletes cache method should clear cache if function returns with value', 90 | () { 91 | final cachedClass = StaticCached(dataProvider); 92 | final cachedValue = cachedClass.cachedValue(); 93 | cachedClass.deleteCachedValue(); 94 | final secondCachedValue = cachedClass.cachedValue(); 95 | 96 | expect(cachedValue != secondCachedValue, true); 97 | }); 98 | 99 | test( 100 | 'deletes cache method should not clear cache if function returns with error', 101 | () { 102 | final cachedClass = StaticCached(dataProvider); 103 | final cachedValue = cachedClass.cachedValue(); 104 | try { 105 | cachedClass.deleteCachedValueFail(); 106 | } catch (e) { 107 | // 108 | } 109 | 110 | final secondCachedValue = cachedClass.cachedValue(); 111 | 112 | expect(cachedValue != secondCachedValue, false); 113 | }); 114 | }); 115 | } 116 | -------------------------------------------------------------------------------- /packages/cached/test/persistent_storage_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:cached_annotation/cached_annotation.dart'; 4 | import 'package:path/path.dart' as path; 5 | import 'package:source_gen_test/source_gen_test.dart'; 6 | 7 | import 'utils/test_storage.dart'; 8 | 9 | Future main() async { 10 | PersistentStorageHolder.storage = TestStorage(); 11 | 12 | initializeBuildLogTracking(); 13 | const expectedAnnotatedTests = { 14 | 'StaticPersistedRepository', 15 | 'NonStaticPersistedRepository', 16 | 'CachedPersistentStorageError', 17 | 'CachedWithClearAllPersistentStorageError', 18 | 'CachedWithClearPersistentStorageError', 19 | 'CachedWithDeletePersistentStorageError', 20 | 'NonStaticNestedGenericType', 21 | 'StaticNestedGenericType', 22 | 'DirectPersistentStoargeRepository', 23 | 'LazyPersistentStorage', 24 | 'AllParamsPersistentStorage', 25 | }; 26 | 27 | final reader = await initializeLibraryReaderForDirectory( 28 | path.join('test', 'inputs'), 29 | 'persistent_storage_test_input.dart', 30 | ); 31 | 32 | testAnnotatedElements( 33 | reader, 34 | const CachedGenerator(config: Config()), 35 | expectedAnnotatedTests: expectedAnnotatedTests, 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /packages/cached/test/should_cache_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | const expectedAnnotatedTests = { 9 | 'ShouldCacheIncompatibleTypes', 10 | 'ShouldCacheAsyncAnnotated', 11 | 'ShouldCacheAsyncCondition', 12 | 'ShouldCacheReturnsVoid', 13 | 'AlwaysCache', 14 | 'NeverCache', 15 | 'Parameters', 16 | 'AnnotatedMethodHasKey', 17 | 'StaticCache', 18 | }; 19 | 20 | final reader = await initializeLibraryReaderForDirectory( 21 | path.join('test', 'inputs'), 22 | 'should_cache_generation_test_input.dart', 23 | ); 24 | 25 | testAnnotatedElements( 26 | reader, 27 | const CachedGenerator(config: Config()), 28 | expectedAnnotatedTests: expectedAnnotatedTests, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/cached/test/streamed_cache_method_generation_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached/src/cached_generator.dart'; 2 | import 'package:cached/src/config.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:source_gen_test/source_gen_test.dart'; 5 | 6 | Future main() async { 7 | initializeBuildLogTracking(); 8 | 9 | const expectedAnnotatedTests = { 10 | 'StreamedCacheMethodReturnType', 11 | 'MethodShouldExists', 12 | 'MethodShouldHaveCachedAnnotation', 13 | 'MethodShouldHaveSyncReturnTypeInStream', 14 | 'MethodShouldHaveSameParams', 15 | 'MethodShouldHaveSameParamsNullable', 16 | 'MethodShouldHaveSameParamsNoParams', 17 | 'MethodShouldHaveSameParamsWithoutIgnore', 18 | 'MethodShouldHaveSameParamsWithoutIgnoreCache', 19 | 'SimpleMethod', 20 | 'FutureMethod', 21 | 'EmitLastValue', 22 | 'Parameters', 23 | 'DuplicateTarget', 24 | 'ShouldBeAbstract', 25 | 'NullableReturnType', 26 | 'NullableReturnTypeWithLastValue', 27 | 'StreamCacheWithCacheKey', 28 | 'StaticCache', 29 | }; 30 | 31 | final reader = await initializeLibraryReaderForDirectory( 32 | path.join('test', 'inputs'), 33 | 'streamed_cache_method_generation_test_input.dart', 34 | ); 35 | 36 | testAnnotatedElements( 37 | reader, 38 | const CachedGenerator(config: Config()), 39 | expectedAnnotatedTests: expectedAnnotatedTests, 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /packages/cached/test/utils/test_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | class TestStorage extends CachedStorage { 4 | @override 5 | Future> read(String key) async => {}; 6 | 7 | @override 8 | Future write(String key, Map data) async {} 9 | 10 | @override 11 | Future delete(String key) async {} 12 | 13 | @override 14 | Future deleteAll() async {} 15 | } 16 | -------------------------------------------------------------------------------- /packages/cached/test/utils/test_storage_to_write.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | class TestStorageWithWrite extends CachedStorage { 4 | TestStorageWithWrite(this.storedValue); 5 | 6 | final Map> storedValue; 7 | 8 | @override 9 | Future> read(String key) async => storedValue[key]!; 10 | 11 | @override 12 | Future write(String key, Map data) async { 13 | storedValue[key] = data; 14 | } 15 | 16 | @override 17 | Future delete(String key) async {} 18 | 19 | @override 20 | Future deleteAll() async {} 21 | } 22 | -------------------------------------------------------------------------------- /packages/cached/test/utils/test_storage_with_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | 3 | class TestStorageWithData extends CachedStorage { 4 | TestStorageWithData(this.storedValue); 5 | 6 | final Map> storedValue; 7 | 8 | @override 9 | Future> read(String key) async => storedValue[key]!; 10 | 11 | @override 12 | Future write(String key, Map data) async {} 13 | 14 | @override 15 | Future delete(String key) async {} 16 | 17 | @override 18 | Future deleteAll() async {} 19 | } 20 | -------------------------------------------------------------------------------- /packages/cached/test/utils/test_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | class TestDataProvider { 4 | final Random _random = Random(); 5 | 6 | int getCurrentTimestamp() => DateTime.now().millisecondsSinceEpoch; 7 | 8 | int getRandomValue() => _random.nextInt(99999); 9 | 10 | Future fetchRandomValue() => Future.value(getRandomValue()); 11 | 12 | Future fetchCurrentTimestamp() => Future.value(getCurrentTimestamp()); 13 | } 14 | -------------------------------------------------------------------------------- /packages/cached_annotation/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | -------------------------------------------------------------------------------- /packages/cached_annotation/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 097d3313d8e2c7f901932d63e537c1acefb87800 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /packages/cached_annotation/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.7.1 2 | * Bump min sdk version to 3.6.0 3 | 4 | ## 1.7.0 5 | * Fix not working peristent storage with the getters 6 | * Add `@DirectPersistentCached()` annotation - avoid store cached data in generated classes by cached, data will be stored only in external storage 7 | * Add `@LazyPersistentCached()` annotation - initialize cache from external storage only after the first method call 8 | * Mark `@Cached(persistentStorage: )` parameter as @Deprecated 9 | * Update documentation 10 | 11 | ## 1.6.0 12 | * Add `@Cached(persistentStorage: )` parameter, which allows you to turn on cache persisting with usage of external storage. 13 | 14 | ## 1.5.0 15 | * Add `@Cached(where: )` parameter - function triggered before caching the value for conditional caching. 16 | 17 | ## 1.4.0 18 | * Add `@DeletesCache()` annotation 19 | * Make parameters anonymous in `@CachePeek()` and `@CacheKey()` annotations 20 | * Add support for getters for `@Cached()` annotation 21 | 22 | ## 1.3.0 23 | * Add `@PeakCache()` annotation 24 | * Fix generating `@StreamedCache()` with ignored parameters 25 | 26 | ## 1.2.0 27 | * Add `@StreamedCache()` annotation 28 | 29 | ## 1.1.0 30 | * Add `@IgnoreCache()` annotation 31 | * Add `@CacheKey()` annotation 32 | 33 | ## 1.0.0 34 | * Initial version of cached_annotation library 35 | -------------------------------------------------------------------------------- /packages/cached_annotation/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Iteo.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/cached_annotation/README.md: -------------------------------------------------------------------------------- 1 | Annotations for [cached].\ 2 | This package does nothing without [cached]. 3 | 4 | [cached]: https://pub.dartlang.org/packages/cached -------------------------------------------------------------------------------- /packages/cached_annotation/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:linteo/analysis_options.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_annotating_with_dynamic: false 6 | comment_references: false 7 | prefer_constructors_over_static_methods: false 8 | avoid_print: false 9 | avoid_classes_with_only_static_members: false 10 | avoid_setters_without_getters: false 11 | 12 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/cached_annotation.dart: -------------------------------------------------------------------------------- 1 | /// Annotations for [cached].\ 2 | /// This package does nothing without [cached]. 3 | /// 4 | /// [cached]: https://pub.dartlang.org/packages/cached 5 | library cached_annotation; 6 | 7 | export 'dart:async'; 8 | 9 | export 'src/cache_key.dart'; 10 | export 'src/cache_peek.dart'; 11 | export 'src/cached.dart'; 12 | export 'src/clear_all_cached.dart'; 13 | export 'src/clear_cached.dart'; 14 | export 'src/deletes_cache.dart'; 15 | export 'src/direct_persistent_cached.dart'; 16 | export 'src/ignore.dart'; 17 | export 'src/ignore_cache.dart'; 18 | export 'src/lazy_persistent_cached.dart'; 19 | export 'src/persistent_cached.dart'; 20 | export 'src/persistent_storage/cached_storage.dart'; 21 | export 'src/persistent_storage/not_implemented_storage.dart'; 22 | export 'src/persistent_storage/persistent_storage_holder.dart'; 23 | export 'src/streamed_cache.dart'; 24 | export 'src/with_cache.dart'; 25 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/cache_key.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | typedef CacheKeyGeneratorFunc = String Function(dynamic); 4 | 5 | /// {@template cached.cache_key} 6 | /// ### CacheKey 7 | /// 8 | /// Annotation for `Cached` package. 9 | /// 10 | /// Annotation that must be above a field in a method and must contain constant 11 | /// function that will return cache key for provided field value 12 | /// 13 | /// ### Example 14 | /// 15 | /// Use @CacheKey annotation 16 | /// 17 | /// ```dart 18 | /// @cached 19 | /// Future getInt( 20 | /// @CacheKey(cacheKeyGenerator: exampleCacheFunction) int test) async { 21 | /// await Future.delayed(Duration(milliseconds: 20)); 22 | /// return test; 23 | /// } 24 | /// 25 | /// String exampleCacheFunction(dynamic value) { 26 | /// return value.toString(); 27 | /// } 28 | /// ``` 29 | /// {@endtemplate} 30 | @Target({TargetKind.parameter}) 31 | class CacheKey { 32 | const CacheKey(this.cacheKeyGenerator); 33 | 34 | final CacheKeyGeneratorFunc cacheKeyGenerator; 35 | } 36 | 37 | /// const instance of [CacheKey] that uses [iterableCacheKeyGenerator] 38 | const iterableCacheKey = CacheKey(iterableCacheKeyGenerator); 39 | 40 | /// Calculates good hashcode for list of values 41 | String iterableCacheKeyGenerator(dynamic l) { 42 | if (l == null || l is! Iterable) { 43 | return l.hashCode.toString(); 44 | } 45 | 46 | return Object.hashAll(l).toString(); 47 | } 48 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/cache_peek.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// {@template cached.cache_peek} 4 | /// ### CachePeek 5 | /// 6 | /// Annotation for `Cached` package. 7 | /// 8 | /// Throws an [InvalidGenerationSourceError] 9 | /// * if more then one method is targeting [Cached] method cache 10 | /// * if method return type is incorrect 11 | /// * if method has different parameters then target function (excluding 12 | /// [Ignore], [IgnoreCache]) 13 | /// * if method is not abstract 14 | /// 15 | /// Example 16 | /// 17 | /// Use @CachePeek annotation 18 | /// 19 | /// In this example, peekSthDataCache will return cache data from getDataWithCached method 20 | /// 21 | /// ```dart 22 | /// @CachePeek(methodName: "getDataWithCached") 23 | /// SomeResponseType? peekSthDataCache(); 24 | /// ``` 25 | /// 26 | /// {@endtemplate} 27 | @Target({TargetKind.method}) 28 | class CachePeek { 29 | /// {@macro cached.cache_peek} 30 | const CachePeek( 31 | this.methodName, 32 | ); 33 | 34 | /// Name of method to be cache peek 35 | final String methodName; 36 | } 37 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// const instance of [Cached] 4 | const cached = Cached(); 5 | 6 | /// {@template cached.cached} 7 | /// ### Cached 8 | /// 9 | /// Annotation for `Cached` package. 10 | /// 11 | /// Method decorator that flag it as needing to be processed 12 | /// by `Cached` code generator. 13 | /// 14 | /// Throws an [InvalidGenerationSourceError] 15 | /// * if we have multiple [IgnoreCache] annotations in method 16 | /// 17 | /// ### Example 18 | /// 19 | /// Use @cached annotation 20 | /// 21 | /// ```dart 22 | /// @cached 23 | /// Future getSthData() { 24 | /// return dataSource.getData(); 25 | /// } 26 | /// ``` 27 | /// 28 | /// or with parameters 29 | /// 30 | /// ```dart 31 | /// @Cached(syncWrite: true, ttl: 30, limit: 10, where: _checkIfShouldCache,) 32 | /// Future getSthData() { 33 | /// return dataSource.getData(); 34 | /// } 35 | /// ``` 36 | /// 37 | /// if you want to clear cache method use [ClearCached] 38 | /// or [ClearAllCached] annotation 39 | /// {@endtemplate} 40 | @Target({TargetKind.method, TargetKind.getter}) 41 | class Cached { 42 | /// {@macro cached.cached} 43 | const Cached({ 44 | this.limit, 45 | this.syncWrite, 46 | this.ttl, 47 | this.where, 48 | @Deprecated( 49 | 'This feature is deprecated and will be removed in the next release. ' 50 | 'Use `@PersistentCached` annotation instead of @Cached.', 51 | ) 52 | this.persistentStorage, 53 | }); 54 | 55 | /// limit how many results for different method call 56 | /// arguments combination will be cached. 57 | /// Default value null, means no limit. 58 | final int? limit; 59 | 60 | /// time to live. In seconds. 61 | /// Set how long cache will be alive. 62 | /// Default value is set to null, means infinitive ttl. 63 | final int? ttl; 64 | 65 | /// Affects only async methods (those one that returns Future) 66 | /// If set to true first method call will be cached, 67 | /// and if following ( the same ) call will occur, 68 | /// all of them will get result from the first call. 69 | /// Default value is set to `false`. 70 | final bool? syncWrite; 71 | 72 | /// Function triggered before caching the value. Should return `bool` 73 | /// 74 | /// if returns `true`: value will be cached, 75 | /// if returns `false`: value wil be ignored 76 | /// 77 | /// Useful to signal that a certain result must not be cached, 78 | /// but @IgnoreCache is not enough 79 | /// (e.g. condition whether or not to cache known once acquiring data) 80 | final Function? where; 81 | 82 | /// Defines optional usage of external persistent storage (e.g. shared 83 | /// preferences) 84 | /// 85 | /// If set to `true` in order to work, you have to set 86 | /// `PersistentStorageHolder.storage` in your main.dart file 87 | /// 88 | /// Important: 89 | /// If you want to utilize persistent storage, all methods which use 90 | /// Cached library's annotations has to be async 91 | @Deprecated( 92 | 'This feature is deprecated and will be removed in the next release. ' 93 | 'Use `@PersistentCached` annotation instead of @Cached.', 94 | ) 95 | final bool? persistentStorage; 96 | } 97 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/clear_all_cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// const instance of [ClearAllCached] 4 | const clearAllCached = ClearAllCached(); 5 | 6 | /// {@template cached.clear_all_cached} 7 | /// ### ClearAllCache 8 | /// 9 | /// Annotation for `Cached` package. 10 | /// 11 | /// Throws an [InvalidGenerationSourceError] 12 | /// * if we have too many `clearAllCached` annotation, only one can be 13 | /// * if method don't return bool, Future or not a void 14 | /// 15 | /// ### Example 16 | /// 17 | /// Use @clearAllCached annotation 18 | /// all methods with @cached annotation will be cleared 19 | /// 20 | /// ```dart 21 | /// @clearAllCached 22 | /// void clearAll(); 23 | /// ``` 24 | /// 25 | /// if you want to clear a specify cache method use [ClearCached] annotation 26 | /// {@endtemplate} 27 | @Target({TargetKind.method}) 28 | class ClearAllCached { 29 | /// {@macro cached.clear_all_cached} 30 | const ClearAllCached(); 31 | } 32 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/clear_cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// const instance of [ClearCached] 4 | /// with default arguments 5 | const clearCached = ClearCached(); 6 | 7 | /// {@template cached.clear_cached} 8 | /// ### ClearCache 9 | /// 10 | /// Annotation for `Cached` package. 11 | /// 12 | /// Throws an [InvalidGenerationSourceError] 13 | /// * if method with @cached annotation doesn’t exist 14 | /// * if method to pair doesn’t exist 15 | /// * if method don't return bool, Future or not a void, Future 16 | /// 17 | /// ### Example 18 | /// 19 | /// We have a method with @Cached() annotation 20 | /// 21 | /// ```dart 22 | /// @Cached() 23 | /// Future getSthData() { 24 | /// return dataSource.getData(); 25 | /// } 26 | /// ``` 27 | /// 28 | /// to clear cache this method, we just add `clear` phrase before name method 29 | /// 30 | /// ```dart 31 | /// @clearCached 32 | /// void clearGetSthData(); 33 | /// ``` 34 | /// 35 | /// or we can use annotation with argument, then the method name doesn't matter, 36 | /// we just need to add the name of the method we want to clean to the argument 37 | /// 38 | /// ```dart 39 | /// @ClearCached("getSthData") 40 | /// void clearMe(); 41 | /// ``` 42 | /// {@endtemplate} 43 | @Target({TargetKind.method}) 44 | class ClearCached { 45 | /// {@macro cached.clear_cached} 46 | const ClearCached([this.methodName]); 47 | 48 | /// Name of method to be cache clear 49 | final String? methodName; 50 | } 51 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/deletes_cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// {@template cached.deletes_cache} 4 | /// #### DeletesCache 5 | /// 6 | /// Annotation for for 'Cached package'. 7 | /// 8 | /// Throws an [InvalidGenerationSourceError] 9 | /// * if method with @cached annotation doesn't exist 10 | /// * if no target method names are specified 11 | /// * if specified target methods are invalid 12 | /// * if annotated class is abstract 13 | /// 14 | /// ### Example 15 | /// 16 | /// If there's a method with @Cached() annotation 17 | /// 18 | /// ```dart 19 | /// @Cached() 20 | /// Future getSthData() { 21 | /// return dataSource.getData(); 22 | /// } 23 | /// ``` 24 | /// 25 | /// If you have a method that alters the cached data, then to clear the cache 26 | /// of affected methods on successful operation just place the '@DeletesCache' 27 | /// annotation before method declaration, and specify methods that are 28 | /// affected by the use of this method in method arguments 29 | /// 30 | /// ```dart 31 | /// @DeletesCache(['getSthData']) 32 | /// Future performOperation() { 33 | /// ... 34 | /// return data; 35 | /// } 36 | /// ``` 37 | /// 38 | /// The cache of the specified methods will be cleared if the method annotated 39 | /// with @DeletesCache completes without throwing an error, 40 | /// but if an error occurs, the cache is not deleted and the error is rethrown. 41 | /// 42 | /// {@endtemplate} 43 | @Target({TargetKind.method, TargetKind.getter}) 44 | class DeletesCache { 45 | /// {@macro cached.deletes_cache} 46 | const DeletesCache(this.methodNames); 47 | 48 | /// Name of methods whose cache is altered by this method 49 | final List methodNames; 50 | } 51 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/direct_persistent_cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/src/persistent_cached.dart'; 2 | import 'package:meta/meta_meta.dart'; 3 | 4 | /// const instance of [DirectPersistentCached] 5 | const directPersistentCached = DirectPersistentCached(); 6 | 7 | /// {@template cached.direct_cached} 8 | /// ### DirectPersistentCached 9 | /// 10 | /// Annotation for `Cached` package. 11 | /// 12 | /// Method decorator that flag it as needing to be processed 13 | /// by `Cached` code generator. 14 | /// 15 | /// Throws an [InvalidGenerationSourceError] 16 | /// * if we have multiple [IgnoreCache] annotations in method 17 | /// 18 | /// Defines usage of external persistent storage (e.g. shared preferences) 19 | /// 20 | /// You have to set `PersistentStorageHolder.storage` in your main.dart file 21 | /// 22 | /// Important: 23 | /// If you want to utilize persistent storage, all methods which use 24 | /// Cached library's annotations has to be async 25 | /// ### Example 26 | /// 27 | /// Use @directPersistentCached annotation 28 | /// 29 | /// ```dart 30 | /// @DirectPersistentCached() 31 | /// Future getSthData() { 32 | /// return dataSource.getData(); 33 | /// } 34 | /// ``` 35 | /// 36 | /// if you want to clear cache method use [ClearCached] 37 | /// or [ClearAllCached] annotation 38 | /// {@endtemplate} 39 | @Target({TargetKind.method, TargetKind.getter}) 40 | class DirectPersistentCached extends PersistentCached { 41 | /// {@macro cached.persistentCached} 42 | const DirectPersistentCached() : super(); 43 | } 44 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/ignore.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// const instance of [Ignore] 4 | const ignore = Ignore(); 5 | 6 | /// {@template cached.ignore} 7 | /// ### Ignore 8 | /// 9 | /// Annotation for `Cached` package. 10 | /// 11 | /// Annotation that must be above a field in a method 12 | /// Arguments with @ignore annotations will be ignored while generating cache key 13 | /// 14 | /// ### Example 15 | /// 16 | /// Use @ignore annotation 17 | /// 18 | /// ```dart 19 | /// @cached 20 | /// Future getInt(@ignore String param) { 21 | /// return Future.value(1); 22 | /// } 23 | /// ``` 24 | /// {@endtemplate} 25 | @Target({TargetKind.parameter}) 26 | class Ignore { 27 | /// {@macro cached.ignore} 28 | const Ignore(); 29 | } 30 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/ignore_cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// const instance of [IgnoreCache] 4 | const ignoreCache = IgnoreCache(); 5 | 6 | /// {@template cached.ignore_cache} 7 | /// ### IgnoreCache 8 | /// 9 | /// Annotation for `Cached` package. 10 | /// 11 | /// Annotation that must be above a field in a method and must be bool, 12 | /// if true the cache will be ignored 13 | /// 14 | /// ### Example 15 | /// 16 | /// Use @ignoreCache annotation 17 | /// 18 | /// ```dart 19 | /// @cached 20 | /// Future getInt(String param, {@ignoreCache bool ignoreCache = false}) { 21 | /// return Future.value(1); 22 | /// } 23 | /// ``` 24 | /// 25 | /// or with `useCacheOnError` in the annotation and if set `true` 26 | /// then return the last cached value when an error occurs. 27 | /// 28 | /// ```dart 29 | /// Future getInt(String param, {@IgnoreCache(useCacheOnError: true) bool ignoreCache = false}) { 30 | /// return Future.value(1); 31 | /// } 32 | /// ``` 33 | /// {@endtemplate} 34 | @Target({TargetKind.parameter}) 35 | class IgnoreCache { 36 | /// {@macro cached.ignore_cache} 37 | const IgnoreCache({ 38 | this.useCacheOnError, 39 | }); 40 | 41 | /// If set `true` then return the last cached value when an error occurs. 42 | final bool? useCacheOnError; 43 | } 44 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/lazy_persistent_cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/src/persistent_cached.dart'; 2 | import 'package:meta/meta_meta.dart'; 3 | 4 | /// const instance of [LazyPersistentCached] 5 | const lazyPersistentCached = LazyPersistentCached(); 6 | 7 | /// {@template cached.lazy_cached} 8 | /// ### LazyPersistentCached 9 | /// 10 | /// Annotation for `Cached` package. 11 | /// 12 | /// Method decorator that flag it as needing to be processed 13 | /// by `Cached` code generator. 14 | /// 15 | /// Throws an [InvalidGenerationSourceError] 16 | /// * if we have multiple [IgnoreCache] annotations in method 17 | /// 18 | /// Defines usage of external persistent storage (e.g. shared preferences) 19 | /// 20 | /// You have to set `PersistentStorageHolder.storage` in your main.dart file 21 | /// 22 | /// Important: 23 | /// If you want to utilize persistent storage, all methods which use 24 | /// Cached library's annotations has to be async 25 | /// ### Example 26 | /// 27 | /// Use @lazyPersistentCached annotation 28 | /// 29 | /// ```dart 30 | /// @LazyPersistentCached() 31 | /// Future getSthData() { 32 | /// return dataSource.getData(); 33 | /// } 34 | /// ``` 35 | /// 36 | /// if you want to clear cache method use [ClearCached] 37 | /// or [ClearAllCached] annotation 38 | /// {@endtemplate} 39 | @Target({TargetKind.method, TargetKind.getter}) 40 | class LazyPersistentCached extends PersistentCached { 41 | /// {@macro cached.persistentCached} 42 | const LazyPersistentCached({ 43 | super.limit, 44 | super.ttl, 45 | super.syncWrite, 46 | super.where, 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/persistent_cached.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/cached_annotation.dart'; 2 | import 'package:meta/meta_meta.dart'; 3 | 4 | /// const instance of [PersistentCached] 5 | const persistentCached = PersistentCached(); 6 | 7 | /// {@template cached.cached} 8 | /// ### PersistentCached 9 | /// 10 | /// Annotation for `Cached` package. 11 | /// 12 | /// Method decorator that flag it as needing to be processed 13 | /// by `Cached` code generator. 14 | /// 15 | /// Throws an [InvalidGenerationSourceError] 16 | /// * if we have multiple [IgnoreCache] annotations in method 17 | /// 18 | /// Defines usage of external persistent storage (e.g. shared preferences) 19 | /// 20 | /// You have to set `PersistentStorageHolder.storage` in your main.dart file 21 | /// 22 | /// Important: 23 | /// If you want to utilize persistent storage, all methods which use 24 | /// Cached library's annotations has to be async 25 | /// ### Example 26 | /// 27 | /// Use @persistentCached annotation 28 | /// 29 | /// ```dart 30 | /// @persistentCached 31 | /// Future getSthData() { 32 | /// return dataSource.getData(); 33 | /// } 34 | /// ``` 35 | /// 36 | /// or with parameters 37 | /// 38 | /// ```dart 39 | /// @PersistentCached() 40 | /// Future getSthData() { 41 | /// return dataSource.getData(); 42 | /// } 43 | /// ``` 44 | /// 45 | /// if you want to clear cache method use [ClearCached] 46 | /// or [ClearAllCached] annotation 47 | /// {@endtemplate} 48 | @Target({TargetKind.method, TargetKind.getter}) 49 | class PersistentCached extends Cached { 50 | /// {@macro cached.persistentCached} 51 | const PersistentCached({ 52 | super.limit, 53 | super.ttl, 54 | super.syncWrite, 55 | super.where, 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/persistent_storage/cached_storage.dart: -------------------------------------------------------------------------------- 1 | abstract class CachedStorage { 2 | Future> read(String key); 3 | 4 | Future write(String key, Map data); 5 | 6 | Future delete(String key); 7 | 8 | Future deleteAll(); 9 | } 10 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/persistent_storage/not_implemented_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/src/persistent_storage/cached_storage.dart'; 2 | 3 | const _message = 'CachedStorage is not set. In main.dart add: ' 4 | 'PersistentStorageHolder.storage = YourStorage(); ' 5 | 'See the documentation and project examples for more code snippets.'; 6 | 7 | class NotImplementedStorage extends CachedStorage { 8 | @override 9 | Future delete(String key) async { 10 | throw UnimplementedError(_message); 11 | } 12 | 13 | @override 14 | Future deleteAll() async { 15 | throw UnimplementedError(_message); 16 | } 17 | 18 | @override 19 | Future> read(String key) async { 20 | throw UnimplementedError(_message); 21 | } 22 | 23 | @override 24 | Future write(String key, Map data) async { 25 | throw UnimplementedError(_message); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/persistent_storage/persistent_storage_holder.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_annotation/src/persistent_storage/cached_storage.dart'; 2 | import 'package:cached_annotation/src/persistent_storage/not_implemented_storage.dart'; 3 | 4 | class PersistentStorageHolder { 5 | static CachedStorage _storage = NotImplementedStorage(); 6 | 7 | static bool get isStorageSet => _storage is! NotImplementedStorage; 8 | 9 | static set storage(CachedStorage value) { 10 | _storage = value; 11 | } 12 | 13 | static Future write(String key, Map data) { 14 | return _storage.write(key, data); 15 | } 16 | 17 | static Future> read(String key) { 18 | return _storage.read(key); 19 | } 20 | 21 | static Future delete(String key) { 22 | return _storage.delete(key); 23 | } 24 | 25 | static Future deleteAll() { 26 | return _storage.deleteAll(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/streamed_cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// {@template cached.streamed_cache} 4 | /// ### StreamedCache 5 | /// 6 | /// Annotation for `Cached` package. 7 | /// 8 | /// Throws an [InvalidGenerationSourceError] 9 | /// * if more then one method is targeting [Cached] method cache 10 | /// * if method return type is incorrect 11 | /// * if method has different parameters then target function (excluding 12 | /// [Ignore], [IgnoreCache]) 13 | /// * if method is not abstract 14 | /// 15 | /// ### Example 16 | /// 17 | /// Use `@StreamedCache` annotation: 18 | /// 19 | /// In this example, cachedStream will return stream of cache updates from cachedMethod 20 | /// 21 | /// ```dart 22 | /// @withCache 23 | /// abstract mixin class EmitLastValue { 24 | /// factory EmitLastValue() = _EmitLastValue; 25 | /// 26 | /// @cached 27 | /// int cachedMethod() { 28 | /// return 1; 29 | /// } 30 | /// 31 | /// @StreamedCache(methodName: "cachedMethod", emitLastValue: true) 32 | /// Stream cachedStream(); 33 | /// } 34 | /// ``` 35 | /// 36 | /// {@endtemplate} 37 | @Target({TargetKind.method}) 38 | class StreamedCache { 39 | /// {@macro cached.cached} 40 | const StreamedCache({ 41 | required this.methodName, 42 | this.emitLastValue = false, 43 | }); 44 | 45 | /// Name of class method. 46 | final String methodName; 47 | 48 | /// If true, last value from cache (if exists) will be emitted. 49 | final bool emitLastValue; 50 | } 51 | 52 | /// {@template cached.stream_event_id} 53 | /// Class that is used by generated code to identify events in stream. 54 | /// 55 | /// This class should not be used outside of generated code 56 | /// {@endtemplate} 57 | class StreamEventIdentifier { 58 | /// {@macro cached.stream_event_id} 59 | const StreamEventIdentifier({ 60 | this.instance, 61 | this.paramsKey, 62 | }); 63 | 64 | /// Instance 65 | final T? instance; 66 | 67 | /// Key 68 | final String? paramsKey; 69 | } 70 | -------------------------------------------------------------------------------- /packages/cached_annotation/lib/src/with_cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta_meta.dart'; 2 | 3 | /// const instance of [WithCache] 4 | const withCache = WithCache(); 5 | 6 | /// {@template cached.with_cache} 7 | /// ### WithCache 8 | /// 9 | /// Annotation for `Cached` package. 10 | /// 11 | /// Annotating a class with `@WithCache` will flag 12 | /// it as a needing to be processed by `Cached` code generator. 13 | /// 14 | /// It can take one additional boolean parameter `useStaticCache`. 15 | /// If this parameter is set to true, generator will generate 16 | /// cached class with static cache. 17 | /// It means each instance of this class will have access 18 | /// to the same cache. Default value is set to `false` 19 | /// 20 | /// Throws an [InvalidGenerationSourceError] 21 | /// * if class has to many constructors. Class can have only one constructor 22 | /// 23 | /// ### Example 24 | /// 25 | /// Use @withCache annotation 26 | /// 27 | /// ```dart 28 | /// @withCache 29 | /// abstract mixin class Gen implements _$Gen { 30 | /// ... 31 | /// } 32 | /// ``` 33 | /// 34 | /// or with parameters 35 | /// 36 | /// ``` 37 | /// @WithCache(useStaticCache: true) 38 | /// abstract mixin class Gen implements _$Gen { 39 | /// ... 40 | /// } 41 | /// ``` 42 | /// 43 | /// do not forget add 44 | /// ```dart 45 | /// part '.cached.dart'; 46 | /// ``` 47 | /// 48 | /// if you want to cache method use [Cached] annotation 49 | /// {@endtemplate} 50 | @Target({TargetKind.classType}) 51 | class WithCache { 52 | /// {@macro cached.with_cache} 53 | const WithCache({ 54 | this.useStaticCache, 55 | }); 56 | 57 | /// If this parameter is set to true, 58 | /// generator will generate cached class with static cache. 59 | /// It means each instance of this class will have access 60 | /// to the same cache. Default value is set to `false`. 61 | final bool? useStaticCache; 62 | } 63 | -------------------------------------------------------------------------------- /packages/cached_annotation/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cached_annotation 2 | description: > 3 | Annotations for the Cached code-generator. 4 | This package does nothing without Cached. 5 | version: 1.7.1 6 | repository: https://github.com/Iteo/cached 7 | issue_tracker: https://github.com/Iteo/cached/issues 8 | homepage: https://github.com/Iteo/cached 9 | documentation: https://github.com/Iteo/cached/blob/master/packages/cached/README.md 10 | 11 | environment: 12 | sdk: ">=3.6.0 <4.0.0" 13 | 14 | dependencies: 15 | meta: ^1.7.0 16 | 17 | dev_dependencies: 18 | linteo: ^1.0.1 19 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | ansi_styles: 5 | dependency: transitive 6 | description: 7 | name: ansi_styles 8 | sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "0.3.2+1" 12 | args: 13 | dependency: transitive 14 | description: 15 | name: args 16 | sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.4.2" 20 | async: 21 | dependency: transitive 22 | description: 23 | name: async 24 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.11.0" 28 | boolean_selector: 29 | dependency: transitive 30 | description: 31 | name: boolean_selector 32 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.1.1" 36 | charcode: 37 | dependency: transitive 38 | description: 39 | name: charcode 40 | sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.3.1" 44 | cli_launcher: 45 | dependency: transitive 46 | description: 47 | name: cli_launcher 48 | sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "0.3.1" 52 | cli_util: 53 | dependency: transitive 54 | description: 55 | name: cli_util 56 | sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "0.4.1" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.18.0" 68 | conventional_commit: 69 | dependency: transitive 70 | description: 71 | name: conventional_commit 72 | sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "0.6.0+1" 76 | file: 77 | dependency: transitive 78 | description: 79 | name: file 80 | sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "6.1.4" 84 | glob: 85 | dependency: transitive 86 | description: 87 | name: glob 88 | sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "2.1.2" 92 | graphs: 93 | dependency: transitive 94 | description: 95 | name: graphs 96 | sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "2.3.1" 100 | http: 101 | dependency: transitive 102 | description: 103 | name: http 104 | sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "1.2.0" 108 | http_parser: 109 | dependency: transitive 110 | description: 111 | name: http_parser 112 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "4.0.2" 116 | io: 117 | dependency: transitive 118 | description: 119 | name: io 120 | sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "1.0.4" 124 | json_annotation: 125 | dependency: transitive 126 | description: 127 | name: json_annotation 128 | sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "4.8.1" 132 | matcher: 133 | dependency: transitive 134 | description: 135 | name: matcher 136 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "0.12.16+1" 140 | melos: 141 | dependency: "direct dev" 142 | description: 143 | name: melos 144 | sha256: "96e64bbade5712c3f010137e195bca9f1b351fac34ab1f322af492ae34032067" 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "3.4.0" 148 | meta: 149 | dependency: transitive 150 | description: 151 | name: meta 152 | sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" 153 | url: "https://pub.dev" 154 | source: hosted 155 | version: "1.12.0" 156 | mustache_template: 157 | dependency: transitive 158 | description: 159 | name: mustache_template 160 | sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c 161 | url: "https://pub.dev" 162 | source: hosted 163 | version: "2.0.0" 164 | path: 165 | dependency: transitive 166 | description: 167 | name: path 168 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 169 | url: "https://pub.dev" 170 | source: hosted 171 | version: "1.9.0" 172 | platform: 173 | dependency: transitive 174 | description: 175 | name: platform 176 | sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" 177 | url: "https://pub.dev" 178 | source: hosted 179 | version: "3.1.4" 180 | pool: 181 | dependency: transitive 182 | description: 183 | name: pool 184 | sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" 185 | url: "https://pub.dev" 186 | source: hosted 187 | version: "1.5.1" 188 | process: 189 | dependency: transitive 190 | description: 191 | name: process 192 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" 193 | url: "https://pub.dev" 194 | source: hosted 195 | version: "4.2.4" 196 | prompts: 197 | dependency: transitive 198 | description: 199 | name: prompts 200 | sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" 201 | url: "https://pub.dev" 202 | source: hosted 203 | version: "2.0.0" 204 | pub_semver: 205 | dependency: transitive 206 | description: 207 | name: pub_semver 208 | sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" 209 | url: "https://pub.dev" 210 | source: hosted 211 | version: "2.1.4" 212 | pub_updater: 213 | dependency: transitive 214 | description: 215 | name: pub_updater 216 | sha256: b06600619c8c219065a548f8f7c192b3e080beff95488ed692780f48f69c0625 217 | url: "https://pub.dev" 218 | source: hosted 219 | version: "0.3.1" 220 | pubspec: 221 | dependency: transitive 222 | description: 223 | name: pubspec 224 | sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e 225 | url: "https://pub.dev" 226 | source: hosted 227 | version: "2.3.0" 228 | quiver: 229 | dependency: transitive 230 | description: 231 | name: quiver 232 | sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 233 | url: "https://pub.dev" 234 | source: hosted 235 | version: "3.2.1" 236 | source_span: 237 | dependency: transitive 238 | description: 239 | name: source_span 240 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 241 | url: "https://pub.dev" 242 | source: hosted 243 | version: "1.10.0" 244 | stack_trace: 245 | dependency: transitive 246 | description: 247 | name: stack_trace 248 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 249 | url: "https://pub.dev" 250 | source: hosted 251 | version: "1.11.1" 252 | stream_channel: 253 | dependency: transitive 254 | description: 255 | name: stream_channel 256 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 257 | url: "https://pub.dev" 258 | source: hosted 259 | version: "2.1.2" 260 | string_scanner: 261 | dependency: transitive 262 | description: 263 | name: string_scanner 264 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 265 | url: "https://pub.dev" 266 | source: hosted 267 | version: "1.2.0" 268 | term_glyph: 269 | dependency: transitive 270 | description: 271 | name: term_glyph 272 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 273 | url: "https://pub.dev" 274 | source: hosted 275 | version: "1.2.1" 276 | test_api: 277 | dependency: transitive 278 | description: 279 | name: test_api 280 | sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" 281 | url: "https://pub.dev" 282 | source: hosted 283 | version: "0.7.0" 284 | typed_data: 285 | dependency: transitive 286 | description: 287 | name: typed_data 288 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 289 | url: "https://pub.dev" 290 | source: hosted 291 | version: "1.3.2" 292 | uri: 293 | dependency: transitive 294 | description: 295 | name: uri 296 | sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" 297 | url: "https://pub.dev" 298 | source: hosted 299 | version: "1.0.0" 300 | web: 301 | dependency: transitive 302 | description: 303 | name: web 304 | sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" 305 | url: "https://pub.dev" 306 | source: hosted 307 | version: "0.4.2" 308 | yaml: 309 | dependency: transitive 310 | description: 311 | name: yaml 312 | sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" 313 | url: "https://pub.dev" 314 | source: hosted 315 | version: "3.1.2" 316 | yaml_edit: 317 | dependency: transitive 318 | description: 319 | name: yaml_edit 320 | sha256: "1579d4a0340a83cf9e4d580ea51a16329c916973bffd5bd4b45e911b25d46bfd" 321 | url: "https://pub.dev" 322 | source: hosted 323 | version: "2.1.1" 324 | sdks: 325 | dart: ">=3.6.0 <4.0.0" 326 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cached_workspace 2 | 3 | environment: 4 | sdk: ">=3.6.0 <=4.0.0" 5 | 6 | dev_dependencies: 7 | melos: ^3.0.1 8 | -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fast fail the script on failures. 4 | set -e 5 | 6 | dart pub global activate coverage 7 | 8 | dart test --coverage="coverage" 9 | 10 | format_coverage --lcov --in=coverage --out=coverage.lcov --packages=.dart_tool/package_config.json --report-on=lib --------------------------------------------------------------------------------