From a544a1f747a1404ea42ac075db477cf1e214bc01 Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Mon, 1 Jun 2020 14:22:28 +0200 Subject: [PATCH] Add an a sample CLI app that uses null safety (#55) --- null_safety/calculate_lix/.gitignore | 12 ++ .../calculate_lix/.idea/calculate_lix.iml | 14 +++ .../.idea/codeStyles/Project.xml | 116 ++++++++++++++++++ null_safety/calculate_lix/.idea/modules.xml | 8 ++ .../Run_main_with_test_data.xml | 8 ++ null_safety/calculate_lix/.vscode/launch.json | 20 +++ null_safety/calculate_lix/CHANGELOG.md | 3 + null_safety/calculate_lix/LICENSE | 26 ++++ null_safety/calculate_lix/README.md | 77 ++++++++++++ .../calculate_lix/analysis_options.yaml | 3 + null_safety/calculate_lix/bin/main.dart | 39 ++++++ null_safety/calculate_lix/lib/lix.dart | 86 +++++++++++++ null_safety/calculate_lix/pubspec.lock | 14 +++ null_safety/calculate_lix/pubspec.yaml | 17 +++ .../calculate_lix/text/lorem-ipsum.txt | 19 +++ 15 files changed, 462 insertions(+) create mode 100644 null_safety/calculate_lix/.gitignore create mode 100644 null_safety/calculate_lix/.idea/calculate_lix.iml create mode 100644 null_safety/calculate_lix/.idea/codeStyles/Project.xml create mode 100644 null_safety/calculate_lix/.idea/modules.xml create mode 100644 null_safety/calculate_lix/.idea/runConfigurations/Run_main_with_test_data.xml create mode 100644 null_safety/calculate_lix/.vscode/launch.json create mode 100644 null_safety/calculate_lix/CHANGELOG.md create mode 100644 null_safety/calculate_lix/LICENSE create mode 100644 null_safety/calculate_lix/README.md create mode 100644 null_safety/calculate_lix/analysis_options.yaml create mode 100644 null_safety/calculate_lix/bin/main.dart create mode 100644 null_safety/calculate_lix/lib/lix.dart create mode 100644 null_safety/calculate_lix/pubspec.lock create mode 100644 null_safety/calculate_lix/pubspec.yaml create mode 100644 null_safety/calculate_lix/text/lorem-ipsum.txt diff --git a/null_safety/calculate_lix/.gitignore b/null_safety/calculate_lix/.gitignore new file mode 100644 index 0000000..4de48e3 --- /dev/null +++ b/null_safety/calculate_lix/.gitignore @@ -0,0 +1,12 @@ +# Files and directories used by the Dart SDK +.dart_tool/ +.packages +build/ +doc/api/ + +# Android Studio configuration +.idea/ +!.idea/runConfigurations/Run_main_with_test_data.xml + +# VSCode configuration +.vscode/settings.json diff --git a/null_safety/calculate_lix/.idea/calculate_lix.iml b/null_safety/calculate_lix/.idea/calculate_lix.iml new file mode 100644 index 0000000..0fd729f --- /dev/null +++ b/null_safety/calculate_lix/.idea/calculate_lix.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/null_safety/calculate_lix/.idea/codeStyles/Project.xml b/null_safety/calculate_lix/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/null_safety/calculate_lix/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/null_safety/calculate_lix/.idea/modules.xml b/null_safety/calculate_lix/.idea/modules.xml new file mode 100644 index 0000000..65ec1fc --- /dev/null +++ b/null_safety/calculate_lix/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/null_safety/calculate_lix/.idea/runConfigurations/Run_main_with_test_data.xml b/null_safety/calculate_lix/.idea/runConfigurations/Run_main_with_test_data.xml new file mode 100644 index 0000000..59a7502 --- /dev/null +++ b/null_safety/calculate_lix/.idea/runConfigurations/Run_main_with_test_data.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/null_safety/calculate_lix/.vscode/launch.json b/null_safety/calculate_lix/.vscode/launch.json new file mode 100644 index 0000000..8918f3b --- /dev/null +++ b/null_safety/calculate_lix/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Dart", + "program": "bin/main.dart", + "request": "launch", + "type": "dart", + "vmAdditionalArgs": [ + "--enable-experiment=non-nullable", + ], + "args": [ + "text/lorem-ipsum.txt" + ] + } + ] +} \ No newline at end of file diff --git a/null_safety/calculate_lix/CHANGELOG.md b/null_safety/calculate_lix/CHANGELOG.md new file mode 100644 index 0000000..532bcd2 --- /dev/null +++ b/null_safety/calculate_lix/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version diff --git a/null_safety/calculate_lix/LICENSE b/null_safety/calculate_lix/LICENSE new file mode 100644 index 0000000..b9cb482 --- /dev/null +++ b/null_safety/calculate_lix/LICENSE @@ -0,0 +1,26 @@ +Copyright 2020, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/null_safety/calculate_lix/README.md b/null_safety/calculate_lix/README.md new file mode 100644 index 0000000..6456c98 --- /dev/null +++ b/null_safety/calculate_lix/README.md @@ -0,0 +1,77 @@ +# Null safety example: A CLI-app for calculating lix + +This is a small code example of an app that calculates the 'lix' readability +index for a text file. The implementation uses the new Dart null safety feature, +and is meant to demonstrate how this feature works in a practical example, +as well as serve as a demonstration of how to configure and run code with null +safety at it's current tech preview stage. + +## Running the example code + +The code works only with the first tech preview of null safety, Dart SDK version +`2.9.0-13.0.dev`. You will need to download a copy of this Dart SDK even if you +have a Flutter or Dart SDK installed already, and you'll want to use this +preview SDK only for experimenting with null safety. Specifically, do not use it +for any kind of production coding. + +### Dart preview SDK installation + + 1. Download the tech preview 1 build is version **`2.9.0-13.0.dev`** from the + dev-channel in the SDK archive: + https://dart.dev/tools/sdk/archive#dev-channel + + 1. Unzip the SDK to a folder, e.g. `/Users/michael/dev/preview/dart-sdk` or + `C:\Users\michael\dev\preview\dart-sdk\` + +### Running from the terminal/command-prompt + +Because null safety is still in tech preview, we need to pass a so-called +'experiment flag' when invoking and Dart command in the terminal, which looks +like this: `--enable-experiment=non-nullable`. + +To run the main app, type these commands in the terminal/command-prompt: + + - Windows: + - `cd \null_safety\calculate_lix\` + - `C:\Users\michael\dev\preview\dart-sdk\bin\dart --enable-experiment=non-nullable bin\main.dart text\lorem-ipsum.txt` + - macOS/Linux: + - `cd /null_safety/calculate_lix/` + - `/Users/michael/dev/preview/dart-sdk/bin/dart --enable-experiment=non-nullable bin/main.dart text/lorem-ipsum.txt` + +### Running from VSCode + +This example contains a launch configuration for VSCode that runs +`bin/main.dart` passing both the experimental flag, so to run the sample in +VSCode: + + 1. Edit your VSCode configuration to point to one additional Dart SDK, the + preview SDK we just downloaded. See [details + here](https://dartcode.org/docs/quickly-switching-between-sdk-versions/) + for what values to put in Code > Preferences > Settings. + + 1. Invoke File > Open, and select the `calculate_lix` folder + + 1. Tell VSCode to use the preview Dart SDK: Open `bin/main.dart` and then + locate the 'Dart: ' selector in the status bar at the + bottom, and select `Dart: 2.9.0-13.0.dev`. + + 1. Press F5 and the project should run and print a message in the Debug + Console. + + +### Running from Android Studio + + 1. Start Android Studio + + 1. Select Open Project, and select the `calculate_lix` folder + + 1. Open the file `bin/main.dart` in the code editor + + 1. Select 'Open Dart Settings' in the top banner + + 1. Select both 'Enable Dart support' checkmarks at the top and bottom of the dialog. + + 1. Under Dart SDK specify the path to the Dart preview SDK (2.9.0-13.0.dev). Click OK. + + 1. Select Run > Run and the project should run and print a message in the Run + pane. diff --git a/null_safety/calculate_lix/analysis_options.yaml b/null_safety/calculate_lix/analysis_options.yaml new file mode 100644 index 0000000..4163582 --- /dev/null +++ b/null_safety/calculate_lix/analysis_options.yaml @@ -0,0 +1,3 @@ +analyzer: + enable-experiment: + - non-nullable diff --git a/null_safety/calculate_lix/bin/main.dart b/null_safety/calculate_lix/bin/main.dart new file mode 100644 index 0000000..adac1a3 --- /dev/null +++ b/null_safety/calculate_lix/bin/main.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:calculate_lix/lix.dart'; + +void main(List arguments) { + // Parse the arguments; we expect a single argument containing the file name. + if (arguments.length != 1) { + printUsage(); + exit(64); + } else { + // Get the file name. + // + // TIP: We're using null safe versions of the core libraries, and the type + // of `arguments` is `List` which means that arguments cannot contain null + // elements. As a result, the Dart analyzer knows that `fileName` isn't + // null. + final fileName = arguments[0]; + print("Calculating Lix of '$fileName'"); + + // Calculate lix. + try { + final l = Lix.fromString(File(fileName).readAsStringSync()); + print("Lix is: ${l.readability}, ${l.describe()} to read (" + "words: ${l.words}, long words: ${l.longWords}, " + "periods: ${l.periods})."); + } catch (ArgumentError) { + print( + 'Invalid input, could not calculate lix!\nThe input text must contain at least one full sentence.'); + } + } +} + +void printUsage() { + print('Usage: calculate_lix '); +} diff --git a/null_safety/calculate_lix/lib/lix.dart b/null_safety/calculate_lix/lib/lix.dart new file mode 100644 index 0000000..2183219 --- /dev/null +++ b/null_safety/calculate_lix/lib/lix.dart @@ -0,0 +1,86 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +class Lix { + // Based on: + // https://readabilityformulas.com/the-LIX-readability-formula.php. + int words; // Number of words in general. + + int longWords; // Number of words with more than 6 characters. + + int periods; // Number of periods. + + // TIP: `readability` isn't passed to the constructor, but calculated. By + // adding the late keyword we tell the analyzer that it will be initialized + // later in the program. + late int readability; // The calculated lix readability index . + + // TIP: Because the fields are all non-nullable, the constructor must + // initialize them all. We can either set a default value, or like here make + // the fields 'required', meaning that a value must be passed to the + // constructor. 'required' is a new keyword introduced as part of null safety + // which replaces the previous '@required' annotatation. + Lix({ + required this.words, + required this.longWords, + required this.periods, + }) { + readability = this._calculate(); + } + + factory Lix.fromString(String text) { + // Count periods: . : ; ! ? + var periods = (RegExp(r"[.:;!?]").allMatches(text).length); + + // Count words. + // + // TIP: We're using null safe versions of the core libraries, so it's clear + // from the signature of split() that it returns a non-null result. + var allWords = text.split(RegExp(r"\W+")); + var words = allWords.length; + var longWords = allWords.where((w) => w.length > 6).toList().length; + + return Lix(words: words, longWords: longWords, periods: periods); + } + + // TIP: Notice how we declare a non-nullable uninitalized `result` variable, + // yet we can return is as a non-nullable result without getting an error. + // + // This is possible due to "definite assignement": The Dart analyzer + // determines that a value has definitely been assigned before we return. Try + // removing the assignment to `result` in our of the if/else code branches, + // and notice how an error then appears in the return statement. + int _calculate() { + int result; + + if (words == 0 || periods == 0) { + throw (ArgumentError('Text must contain at least one full sentence.')); + } else { + final sentenceLength = words / periods; + final wordLength = (longWords * 100) / words; + result = (sentenceLength + wordLength).round(); + } + + return result; + } + + // TIP: Notice how flow analysis knows that every branch returns a value for + // `readability` and so it's ok to have a non-nullable return type (try + // removing the last else-statement). + String describe() { + if (readability > 0 && readability < 20) { + return 'very easy'; + } else if (readability < 30) { + return 'easy'; + } else if (readability < 40) { + return 'a little hard'; + } else if (readability < 50) { + return 'hard'; + } else if (readability < 60) { + return 'very hard'; + } else { + return 'unknown'; + } + } +} diff --git a/null_safety/calculate_lix/pubspec.lock b/null_safety/calculate_lix/pubspec.lock new file mode 100644 index 0000000..e1c1cdf --- /dev/null +++ b/null_safety/calculate_lix/pubspec.lock @@ -0,0 +1,14 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + collection: + dependency: "direct main" + description: + path: "." + ref: null_safety + resolved-ref: "6ef56d3e8cfa4d347cbf70321251c5bb87acc9a1" + url: "https://github.com/dart-lang/collection" + source: git + version: "1.14.13-dev" +sdks: + dart: ">=2.9.0-13.0.dev <3.0.0" diff --git a/null_safety/calculate_lix/pubspec.yaml b/null_safety/calculate_lix/pubspec.yaml new file mode 100644 index 0000000..1008a01 --- /dev/null +++ b/null_safety/calculate_lix/pubspec.yaml @@ -0,0 +1,17 @@ +name: calculate_lix +description: A command-line application demonstrating use of null safety. +version: 1.0.0 + +environment: + sdk: '>=2.9.0-13.0.dev <3.0.0' + +dependencies: + collection: 1.14.13-dev + +# Temporary override as the null safety enabled versions of this package is +# not yet published to pub.dev. +dependency_overrides: + collection: + git: + url: https://github.com/dart-lang/collection + ref: null_safety diff --git a/null_safety/calculate_lix/text/lorem-ipsum.txt b/null_safety/calculate_lix/text/lorem-ipsum.txt new file mode 100644 index 0000000..80a197c --- /dev/null +++ b/null_safety/calculate_lix/text/lorem-ipsum.txt @@ -0,0 +1,19 @@ +Lorem. ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in +culpa qui officia deserunt mollit anim id est laborum. + +Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, +turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis +sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus +et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut +ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt +sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. +Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, +consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl +adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque +nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, +laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, +feugiat in, orci. In hac habitasse platea dictumst.