From ef3f925a53f1e3b277d91ed33a1bdafd040cbf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:38:05 -0800 Subject: [PATCH] Add ETL exercise --- config.json | 8 ++ exercises/practice/etl/.docs/instructions.md | 27 +++++ exercises/practice/etl/.docs/introduction.md | 16 +++ exercises/practice/etl/.meta/Example.cfc | 22 ++++ exercises/practice/etl/.meta/ExampleTest.cfc | 7 ++ exercises/practice/etl/.meta/config.json | 19 ++++ exercises/practice/etl/.meta/tests.toml | 22 ++++ exercises/practice/etl/Etl.cfc | 13 +++ exercises/practice/etl/EtlTest.cfc | 56 ++++++++++ exercises/practice/etl/TestRunner.cfc | 103 +++++++++++++++++++ exercises/practice/etl/box.json | 8 ++ exercises/practice/etl/index.cfm | 37 +++++++ 12 files changed, 338 insertions(+) create mode 100644 exercises/practice/etl/.docs/instructions.md create mode 100644 exercises/practice/etl/.docs/introduction.md create mode 100644 exercises/practice/etl/.meta/Example.cfc create mode 100644 exercises/practice/etl/.meta/ExampleTest.cfc create mode 100644 exercises/practice/etl/.meta/config.json create mode 100644 exercises/practice/etl/.meta/tests.toml create mode 100644 exercises/practice/etl/Etl.cfc create mode 100644 exercises/practice/etl/EtlTest.cfc create mode 100644 exercises/practice/etl/TestRunner.cfc create mode 100644 exercises/practice/etl/box.json create mode 100644 exercises/practice/etl/index.cfm diff --git a/config.json b/config.json index 3667bb5..0643e8b 100644 --- a/config.json +++ b/config.json @@ -125,6 +125,14 @@ "prerequisites": [], "difficulty": 1 }, + { + "slug": "etl", + "name": "ETL", + "uuid": "562a030c-681d-4e53-85eb-c181e73ac950", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, { "slug": "flatten-array", "name": "Flatten Array", diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md new file mode 100644 index 0000000..802863b --- /dev/null +++ b/exercises/practice/etl/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to change the data format of letters and their point values in the game. + +Currently, letters are stored in groups based on their score, in a one-to-many mapping. + +- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T", +- 2 points: "D", "G", +- 3 points: "B", "C", "M", "P", +- 4 points: "F", "H", "V", "W", "Y", +- 5 points: "K", +- 8 points: "J", "X", +- 10 points: "Q", "Z", + +This needs to be changed to store each individual letter with its score in a one-to-one mapping. + +- "a" is worth 1 point. +- "b" is worth 3 points. +- "c" is worth 3 points. +- "d" is worth 2 points. +- etc. + +As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case. + +~~~~exercism/note +If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite. +~~~~ diff --git a/exercises/practice/etl/.docs/introduction.md b/exercises/practice/etl/.docs/introduction.md new file mode 100644 index 0000000..5be6514 --- /dev/null +++ b/exercises/practice/etl/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +You work for a company that makes an online multiplayer game called Lexiconia. + +To play the game, each player is given 13 letters, which they must rearrange to create words. +Different letters have different point values, since it's easier to create words with some letters than others. + +The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well. + +Different languages need to support different point values for letters. +The point values are determined by how often letters are used, compared to other letters in that language. + +For example, the letter 'C' is quite common in English, and is only worth 3 points. +But in Norwegian it's a very rare letter, and is worth 10 points. + +To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game. diff --git a/exercises/practice/etl/.meta/Example.cfc b/exercises/practice/etl/.meta/Example.cfc new file mode 100644 index 0000000..8a38d83 --- /dev/null +++ b/exercises/practice/etl/.meta/Example.cfc @@ -0,0 +1,22 @@ +/** +* Here is an example solution for the Etl exercise +*/ +component { + + /** + * @returns + */ + function transform( legacy ) { + var results = {}; + + legacy.each( function( score, letters ) { + for(letter in letters) { + letter = LCase(letter); + results[letter] = score; + } + }); + + return results; + } + +} \ No newline at end of file diff --git a/exercises/practice/etl/.meta/ExampleTest.cfc b/exercises/practice/etl/.meta/ExampleTest.cfc new file mode 100644 index 0000000..3eb30e4 --- /dev/null +++ b/exercises/practice/etl/.meta/ExampleTest.cfc @@ -0,0 +1,7 @@ +component extends="EtlTest" { + + function beforeAll(){ + SUT = createObject( 'Solution' ); + } + +} \ No newline at end of file diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json new file mode 100644 index 0000000..51b7716 --- /dev/null +++ b/exercises/practice/etl/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "Etl.cfc" + ], + "test": [ + "EtlTest.cfc" + ], + "example": [ + ".meta/Example.cfc" + ] + }, + "blurb": "Change the data format for scoring a game to more easily add other languages.", + "source": "Based on an exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu" +} diff --git a/exercises/practice/etl/.meta/tests.toml b/exercises/practice/etl/.meta/tests.toml new file mode 100644 index 0000000..e937107 --- /dev/null +++ b/exercises/practice/etl/.meta/tests.toml @@ -0,0 +1,22 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[78a7a9f9-4490-4a47-8ee9-5a38bb47d28f] +description = "single letter" + +[60dbd000-451d-44c7-bdbb-97c73ac1f497] +description = "single score with multiple letters" + +[f5c5de0c-301f-4fdd-a0e5-df97d4214f54] +description = "multiple scores with multiple letters" + +[5db8ea89-ecb4-4dcd-902f-2b418cc87b9d] +description = "multiple scores with differing numbers of letters" diff --git a/exercises/practice/etl/Etl.cfc b/exercises/practice/etl/Etl.cfc new file mode 100644 index 0000000..ba3f4a5 --- /dev/null +++ b/exercises/practice/etl/Etl.cfc @@ -0,0 +1,13 @@ +/** +* Your implementation of the Etl exercise +*/ +component { + + /** + * @returns + */ + function transform( legacy ) { + // Implement me here + } + +} \ No newline at end of file diff --git a/exercises/practice/etl/EtlTest.cfc b/exercises/practice/etl/EtlTest.cfc new file mode 100644 index 0000000..a95113b --- /dev/null +++ b/exercises/practice/etl/EtlTest.cfc @@ -0,0 +1,56 @@ +component extends="testbox.system.BaseSpec" { + + function beforeAll(){ + SUT = createObject( 'Etl' ); + } + + function run(){ + + describe( "My Etl class", function(){ + + it( 'single letter', function(){ + expect( SUT.transform( legacy={"1":["A"]} ) ).toBe( {"a":1} ); + }); + + it( 'single score with multiple letters', function(){ + expect( SUT.transform( legacy={"1":["A", "E", "I", "O", "U"]} ) ).toBe( {"a":1, + "e":1, + "i":1, + "o":1, + "u":1} ); + }); + + it( 'multiple scores with multiple letters', function(){ + expect( SUT.transform( + legacy={"1":["A", "E"], + "2":["D", "G"]} ) ).toBe( {"a":1,"d":2,"e":1,"g":2} ); + }); + + it( 'multiple scores with differing numbers of letters', function(){ + expect( SUT.transform( + legacy={"1":["A", "E", "I", "O", "U", "L", "N", "R", "S", "T"], + "2":["D", "G"], + "3":["B", "C", "M", "P"], + "4":["F", "H", "V", "W", "Y"], + "5":["K"], + "8":["J", "X"], + "10":["Q", "Z"]} ) ).toBe( {"a":1, "b":3, + "c":3, "d":2, + "e":1, "f":4, + "g":2, "h":4, + "i":1, "j":8, + "k":5, "l":1, + "m":3, "n":1, + "o":1, "p":3, + "q":10, "r":1, + "s":1, "t":1, + "u":1, "v":4, + "w":4, "x":8, + "y":4, "z":10} ); + }); + + }); + + } + +} \ No newline at end of file diff --git a/exercises/practice/etl/TestRunner.cfc b/exercises/practice/etl/TestRunner.cfc new file mode 100644 index 0000000..762b153 --- /dev/null +++ b/exercises/practice/etl/TestRunner.cfc @@ -0,0 +1,103 @@ +/** +* I am a CommandBox task runner which you can use to test your implementation of this exercise against the +* provided test suite. To use me, open the CommandBox CLI and run this: +* +* CommandBox> task run TestRunner +* +* To start up a test watcher that will automatically rerun the test suite every time you save a file change, run this: +* +* CommandBox> task run TestRunner --watcher +* +*/ +component { + + /** + * @solution Runs the tests against the solution + * @watcher Start up a file watch that re-runs the tests on file changes. Use Ctrl-C to stop + */ + function run( boolean solution=false, boolean watcher=false ) { + + ensureTestBox(); + + if( watcher ) { + + // Tabula rasa + command( 'cls' ).run(); + + // Start watcher + watch() + .paths( '*.cfc' ) + .inDirectory( getCWD() ) + .withDelay( 500 ) + .onChange( function() { + + // Clear the screen + command( 'cls' ) + .run(); + + // This is neccessary so changes to tests get picked up right away. + pagePoolClear(); + + runTests( solution ); + + } ) + .start(); + + } else { + runTests( solution ); + } + + } + + /** + * Make sure the TestBox framework is installed + */ + private function ensureTestBox() { + var excerciseRoot = getCWD(); + var testBoxRoot = excerciseRoot & '/testbox'; + + if( !directoryExists( testBoxRoot ) ) { + + print.boldYellowLine( 'Installing some missing dependencies for you!' ).toConsole(); + command( 'install' ) + .inWorkingDirectory( excerciseRoot ) + .run(); + } + + // Bootstrap TestBox framework + filesystemUtil.createMapping( '/testbox', testBoxRoot ); + } + + /** + * Invoke TestBox to run the test suite + */ + private function runTests( boolean solution=false ) { + + // Create TestBox and run the tests + testData = new testbox.system.TestBox() + .runRaw( directory = { + // Find all CFCs... + mapping = filesystemUtil.makePathRelative( getCWD() ), + // ... in this directory ... + recurse = false, + // ... whose name ends in "test" + filter = function( path ) { + return path.reFind( ( solution ? 'Solution' : '' ) & 'Test.cfc$' ); + } + } ) + .getMemento(); + + // Print out the results with ANSI formatting for the CLI + getInstance( 'CLIRenderer@testbox-commands' ) + .render( print, testData, true ); + + print.toConsole(); + + // Set proper exit code + if( testData.totalFail || testData.totalError ) { + setExitCode( 1 ); + } + } + +} + diff --git a/exercises/practice/etl/box.json b/exercises/practice/etl/box.json new file mode 100644 index 0000000..3b95f7e --- /dev/null +++ b/exercises/practice/etl/box.json @@ -0,0 +1,8 @@ +{ + "dependencies":{ + "testbox":"^2.5.0+107" + }, + "installPaths":{ + "testbox":"testbox/" + } +} \ No newline at end of file diff --git a/exercises/practice/etl/index.cfm b/exercises/practice/etl/index.cfm new file mode 100644 index 0000000..bb28af8 --- /dev/null +++ b/exercises/practice/etl/index.cfm @@ -0,0 +1,37 @@ + + + + + // get a list of all CFCs in this folder whose name looks like "XXXTest.cfc" + // And turn it into compnent path relative to the web root + url.bundles = directoryList( + path=expandPath( '/' ), + filter='*Test.cfc' ) + .map( function( path ) { + return path + .replaceNoCase( expandPath( '/' ), '' ) + .left( -4 ) + } ) + .toList(); + + + + + + + + Oops, you don't have TestBox installed yet! Please run box install from the root of your exercise folder and refresh this page. +