From 407bab18140a6f1b879adbe7989086e5d3b1d878 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 09:32:09 -0800 Subject: [PATCH] Add strain exercise --- config.json | 8 ++ .../practice/strain/.docs/instructions.md | 29 +++++ exercises/practice/strain/.meta/Example.cfc | 27 ++++ .../practice/strain/.meta/ExampleTest.cfc | 7 + exercises/practice/strain/.meta/config.json | 19 +++ exercises/practice/strain/.meta/tests.toml | 52 ++++++++ exercises/practice/strain/Strain.cfc | 19 +++ exercises/practice/strain/StrainTest.cfc | 120 ++++++++++++++++++ exercises/practice/strain/TestRunner.cfc | 103 +++++++++++++++ exercises/practice/strain/box.json | 8 ++ exercises/practice/strain/index.cfm | 37 ++++++ 11 files changed, 429 insertions(+) create mode 100644 exercises/practice/strain/.docs/instructions.md create mode 100644 exercises/practice/strain/.meta/Example.cfc create mode 100644 exercises/practice/strain/.meta/ExampleTest.cfc create mode 100644 exercises/practice/strain/.meta/config.json create mode 100644 exercises/practice/strain/.meta/tests.toml create mode 100644 exercises/practice/strain/Strain.cfc create mode 100644 exercises/practice/strain/StrainTest.cfc create mode 100644 exercises/practice/strain/TestRunner.cfc create mode 100644 exercises/practice/strain/box.json create mode 100644 exercises/practice/strain/index.cfm diff --git a/config.json b/config.json index 0a227cb..5f26420 100644 --- a/config.json +++ b/config.json @@ -331,6 +331,14 @@ "prerequisites": [], "difficulty": 1 }, + { + "slug": "strain", + "name": "Strain", + "uuid": "5a1a2fa9-e8d0-4784-ba0c-c53fb8405e26", + "practices": [], + "prerequisites": [], + "difficulty": 2 + }, { "slug": "sum-of-multiples", "name": "Sum of Multiples", diff --git a/exercises/practice/strain/.docs/instructions.md b/exercises/practice/strain/.docs/instructions.md new file mode 100644 index 0000000..3469ae6 --- /dev/null +++ b/exercises/practice/strain/.docs/instructions.md @@ -0,0 +1,29 @@ +# Instructions + +Implement the `keep` and `discard` operation on collections. +Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false. + +For example, given the collection of numbers: + +- 1, 2, 3, 4, 5 + +And the predicate: + +- is the number even? + +Then your keep operation should produce: + +- 2, 4 + +While your discard operation should produce: + +- 1, 3, 5 + +Note that the union of keep and discard is all the elements. + +The functions may be called `keep` and `discard`, or they may need different names in order to not clash with existing functions or concepts in your language. + +## Restrictions + +Keep your hands off that filter/reject/whatchamacallit functionality provided by your standard library! +Solve this one yourself using other basic tools instead. diff --git a/exercises/practice/strain/.meta/Example.cfc b/exercises/practice/strain/.meta/Example.cfc new file mode 100644 index 0000000..e5d3ab1 --- /dev/null +++ b/exercises/practice/strain/.meta/Example.cfc @@ -0,0 +1,27 @@ +/** +* Here is an example solution for the Strain exercise +*/ +component { + + /** + * @returns + */ + function keep(list, predicate) { + var results = []; + for (elem in list) { + if (predicate(elem)) { + arrayAppend(results, elem); + } + } + + return results; + } + + /** + * @returns + */ + function discard(list, predicate) { + var newPredicate = function( x ) {return not predicate(x); } + return keep(list, newPredicate); + } +} \ No newline at end of file diff --git a/exercises/practice/strain/.meta/ExampleTest.cfc b/exercises/practice/strain/.meta/ExampleTest.cfc new file mode 100644 index 0000000..a592d2e --- /dev/null +++ b/exercises/practice/strain/.meta/ExampleTest.cfc @@ -0,0 +1,7 @@ +component extends="StrainTest" { + + function beforeAll(){ + SUT = createObject( 'Solution' ); + } + +} \ No newline at end of file diff --git a/exercises/practice/strain/.meta/config.json b/exercises/practice/strain/.meta/config.json new file mode 100644 index 0000000..9bbd191 --- /dev/null +++ b/exercises/practice/strain/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "Strain.cfc" + ], + "test": [ + "StrainTest.cfc" + ], + "example": [ + ".meta/Example.cfc" + ] + }, + "blurb": "Implement the `keep` and `discard` operation on collections. Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false.", + "source": "Conversation with James Edward Gray II", + "source_url": "http://graysoftinc.com/" +} diff --git a/exercises/practice/strain/.meta/tests.toml b/exercises/practice/strain/.meta/tests.toml new file mode 100644 index 0000000..3a617b4 --- /dev/null +++ b/exercises/practice/strain/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[26af8c32-ba6a-4eb3-aa0a-ebd8f136e003] +description = "keep on empty list returns empty list" + +[f535cb4d-e99b-472a-bd52-9fa0ffccf454] +description = "keeps everything" + +[950b8e8e-f628-42a8-85e2-9b30f09cde38] +description = "keeps nothing" + +[92694259-6e76-470c-af87-156bdf75018a] +description = "keeps first and last" + +[938f7867-bfc7-449e-a21b-7b00cbb56994] +description = "keeps neither first nor last" + +[8908e351-4437-4d2b-a0f7-770811e48816] +description = "keeps strings" + +[2728036b-102a-4f1e-a3ef-eac6160d876a] +description = "keeps lists" + +[ef16beb9-8d84-451a-996a-14e80607fce6] +description = "discard on empty list returns empty list" + +[2f42f9bc-8e06-4afe-a222-051b5d8cd12a] +description = "discards everything" + +[ca990fdd-08c2-4f95-aa50-e0f5e1d6802b] +description = "discards nothing" + +[71595dae-d283-48ca-a52b-45fa96819d2f] +description = "discards first and last" + +[ae141f79-f86d-4567-b407-919eaca0f3dd] +description = "discards neither first nor last" + +[daf25b36-a59f-4f29-bcfe-302eb4e43609] +description = "discards strings" + +[a38d03f9-95ad-4459-80d1-48e937e4acaf] +description = "discards lists" diff --git a/exercises/practice/strain/Strain.cfc b/exercises/practice/strain/Strain.cfc new file mode 100644 index 0000000..0ad6303 --- /dev/null +++ b/exercises/practice/strain/Strain.cfc @@ -0,0 +1,19 @@ +/** +* Your implementation of the Strain exercise +*/ +component { + + /** + * @returns + */ + function keep(list, predicate) { + // Implement me here + } + + /** + * @returns + */ + function discard(list, predicate) { + // Implement me here + } +} \ No newline at end of file diff --git a/exercises/practice/strain/StrainTest.cfc b/exercises/practice/strain/StrainTest.cfc new file mode 100644 index 0000000..3298a1a --- /dev/null +++ b/exercises/practice/strain/StrainTest.cfc @@ -0,0 +1,120 @@ +component extends="testbox.system.BaseSpec" { + + function beforeAll(){ + SUT = createObject( 'Strain' ); + } + + function alwaysTrue( x ) { return true; } + + function alwaysFalse( x ) { return false; } + + function isOdd( x ) { return x % 2 == 1; } + + function isEven( x ) { return x % 2 == 0; } + + function containsFive( x ) { return arrayContains(x, 5); } + + function startWithZ( x ) { return left(x, 1) == 'z'; } + + function run(){ + + describe( "My Strain class", function(){ + + it( 'keep on empty list returns empty list', function(){ + expect( SUT.keep( list=[], predicate=alwaysTrue ) ).toBe( [] ); + }); + + it( 'keeps everything', function(){ + expect( SUT.keep( list=[1, 3, 5], predicate=alwaysTrue ) ).toBe( [1,3,5] ); + }); + + it( 'keeps nothing', function(){ + expect( SUT.keep( list=[1, 3, 5], predicate=alwaysFalse) ).toBe( [] ); + }); + + it( 'keeps first and last', function(){ + expect( SUT.keep( list=[1, 2, 3], predicate=isOdd ) ).toBe( [1,3] ); + }); + + it( 'keeps neither first nor last', function(){ + expect( SUT.keep( list=[1, 2, 3], predicate=isEven ) ).toBe( [2] ); + }); + + it( 'keeps strings', function(){ + expect( SUT.keep( + list=["apple", + "zebra", + "banana", + "zombies", + "cherimoya", + "zealot"], + predicate=startWithZ ) ).toBe( ["zebra","zombies","zealot"] ); + }); + + it( 'keeps lists', function(){ + expect( SUT.keep( + list=[[1, 2, 3], + [5, 5, 5], + [5, 1, 2], + [2, 1, 2], + [1, 5, 2], + [2, 2, 1], + [1, 2, 5]], + predicate=containsFive ) ).toBe( [[5,5,5], + [5,1,2], + [1,5,2], + [1,2,5]] ); + }); + + it( 'discard on empty list returns empty list', function(){ + expect( SUT.discard( list=[], predicate=alwaysTrue ) ).toBe( [] ); + }); + + it( 'discards everything', function(){ + expect( SUT.discard( list=[1, 3, 5], predicate=alwaysTrue ) ).toBe( [] ); + }); + + it( 'discards nothing', function(){ + expect( SUT.discard( list=[1, 3, 5], predicate=alwaysFalse ) ).toBe( [1,3,5] ); + }); + + it( 'discards first and last', function(){ + expect( SUT.discard( list=[1, 2, 3], predicate=isOdd ) ).toBe( [2] ); + }); + + it( 'discards neither first nor last', function(){ + expect( SUT.discard( list=[1, 2, 3], predicate=isEven ) ).toBe( [1,3] ); + }); + + it( 'discards strings', function(){ + expect( SUT.discard( + list=["apple", + "zebra", + "banana", + "zombies", + "cherimoya", + "zealot"], + predicate=startWithZ ) ).toBe( ["apple", + "banana", + "cherimoya"] ); + }); + + it( 'discards lists', function(){ + expect( SUT.discard( + list=[[1, 2, 3], + [5, 5, 5], + [5, 1, 2], + [2, 1, 2], + [1, 5, 2], + [2, 2, 1], + [1, 2, 5]], + predicate=containsFive ) ).toBe( [[1,2,3], + [2,1,2], + [2,2,1]] ); + }); + + }); + + } + +}containsFive \ No newline at end of file diff --git a/exercises/practice/strain/TestRunner.cfc b/exercises/practice/strain/TestRunner.cfc new file mode 100644 index 0000000..762b153 --- /dev/null +++ b/exercises/practice/strain/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/strain/box.json b/exercises/practice/strain/box.json new file mode 100644 index 0000000..3b95f7e --- /dev/null +++ b/exercises/practice/strain/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/strain/index.cfm b/exercises/practice/strain/index.cfm new file mode 100644 index 0000000..bb28af8 --- /dev/null +++ b/exercises/practice/strain/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. +