Skip to content

Commit

Permalink
Add exercise (#589)
Browse files Browse the repository at this point in the history
  • Loading branch information
meatball133 authored Jan 29, 2024
1 parent 887b933 commit 4ae0a5d
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@
"concepts": ["bit-manipulation"],
"prerequisites": ["binary-octal-hexadecimal"],
"status": "beta"
},
{
"slug": "task-handler",
"name": "Task Handler",
"uuid": "2acbe0f3-bb3b-4eb7-bd6f-fd760e3816b0",
"concepts": ["procs-blocks"],
"prerequisites": ["array-methods"],
"status": "beta"
}
],
"practice": [
Expand Down
21 changes: 21 additions & 0 deletions exercises/concept/task-handler/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Hints

## General

- To access and define instance variables you write `@` followed by the name of the variable.

## 1. Initialize `TaskHandler`

- To create a `Proc` use the `-> (args) { block }` syntax.
- When a `Proc` is defined for an instance variable, it needs to be explicitly typed.
This can be done by adding `: Proc(<arg_type>, <return_type>)` after the variable name.
Or `-> (arg_1 : <arg_type>) : <return_type> { block }` syntax.
- The `Proc` should compare if the argument is greater than or equal to 0.

## 2. Update the task condition logic

- The method should take a block as an argument and assign it to the `@task_condition_logic` instance variable.

## 3. Execute the next task

- To execute a `Proc` you can use the `call` method which accepts the arguments for the proc.
56 changes: 56 additions & 0 deletions exercises/concept/task-handler/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Instructions

You are developing a content management system for a website that hosts articles, blog posts, and other written content.
You have been tasked with implementing a task handler that will process tasks in a priority queue.
The task handler will receive tasks and based on the load on the server and their priority will decide which task to execute next.
If the server is under heavy load, then the priority number has to be higher for the task to be executed.
The server will dynamically change the load based on the load.

## 1. Initialize `TaskHandler`

The TaskHandler needs to be initialized with a queue and a default load_level.

Implement the initializer of the class `TaskHandler` that takes an `Array` of `String`s of the tasks names as an argument and storing it in an instance variable called `@tasks`.
The initializer should also create the instance variable `@task_condition_logic` that stores a `Proc` that takes an `Int32` and returns true if the argument is equal to or greater than the 0 and false if it is less than 0.

```crystal
task_handler = TaskHandler.new(["task1", "task2", "task3"])
# => #<TaskHandler:0x7fd8d4cfbf00 @tasks=["task1", "task2", "task3"], @task_condition_logic=#<Proc(Int32, Bool):0x562536a4a330>>
```

## 2. Update the task condition logic

The server has to be able to update the task condition logic based on the load of the server.

Implement the method `TaskHandler#update_task_condition_logic` that takes a block as an argument and the block should be stored in the instance variable `@task_condition_logic`.

```crystal
task_handler = TaskHandler.new(["task1", "task2", "task3"])
task_handler.update_task_condition_logic { |load| load > 1 }
# => #<TaskHandler:0x7f068118cf00 @tasks=["task1", "task2", "task3"], @task_condition_logic=#<Proc(Int32, Bool):0x562df99d0370>>
```

## 3. Execute the next task

The server has to be able to execute the next task in the queue.

The first task in the `@tasks` array is the next task to be executed.
The task should be executed if the priority level of the task given returns true when passed to the `@task_condition_logic` proc.

It shall return messages based on if the task was executed or not.
If the task was executed it should return: `"Completed <task>"`.
If the task was not executed it should return: `"Could not complete <task>"`.

If the task was executed it should be removed from the `@tasks` array.

Implement the method `TaskHandler#execute_next_task` that takes the priority level as `Int32` of the next task as an argument and returns a `String` based on if the task was executed or not.

```crystal
task_handler = TaskHandler.new(["task1", "task2", "task3"])
task_handler.update_task_condition_logic { |load| load > 1 }
task_handler.execute_next_task(2)
# => "Completed task1"
task_handler.execute_next_task(1)
# => "Could not complete task2"
```
1 change: 1 addition & 0 deletions exercises/concept/task-handler/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TODO
16 changes: 16 additions & 0 deletions exercises/concept/task-handler/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"authors": ["meatball133"],
"files": {
"solution": [
"src/task_handler.cr"
],
"test": [
"spec/task_handler_spec.cr"
],
"exemplar": [
".meta/src/exemplar.cr"
]
},
"blurb": "Learn about Procs & blocks by implementing a simple task handler.",
"icon": "coordinate-transformation"
}
19 changes: 19 additions & 0 deletions exercises/concept/task-handler/.meta/src/exemplar.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class TaskHandler
def initialize(tasks : Array(String))
@tasks = tasks
@task_condition_logic = ->(priority : Int32) : Bool { priority >= 0 }
end

def update_task_condition_logic(&block : Int32 -> Bool)
@task_condition_logic = block
end

def execute(priority : Int32)
if @task_condition_logic.call(priority)
completed = @tasks.shift
"Completed #{completed}"
else
"Could not complete #{@tasks[0]}"
end
end
end
60 changes: 60 additions & 0 deletions exercises/concept/task-handler/spec/task_handler_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require "spec"
require "../src/*"

def check_proc(proc, num)
proc.call(num).should eq true
proc.call(num - 1).should eq false
end

describe TaskHandler do
describe "initialize" do
it "should initialize with a task" do
handler = TaskHandler.new(["task1", "task2"])
handler.@tasks.should eq ["task1", "task2"]
handler.@task_condition_logic.should be_a Proc(Int32, Bool)
check_proc(handler.@task_condition_logic, 0)
end
end

describe "update_task_condition_logic" do
it "should update the task condition logic" do
handler = TaskHandler.new(["task1", "task2"])
handler.update_task_condition_logic { |priority| priority > 2 }
check_proc(handler.@task_condition_logic, 3)
end

it "should be able to update the task condition logic multiple times" do
handler = TaskHandler.new(["task1", "task2"])
handler.update_task_condition_logic { |priority| priority > 2 }
check_proc(handler.@task_condition_logic, 3)
handler.update_task_condition_logic { |priority| priority > 0 }
check_proc(handler.@task_condition_logic, 1)
end
end

describe "execute" do
it "should add a task to the task list" do
handler = TaskHandler.new(["task1", "task2"])
handler.execute(1).should eq "Completed task1"
end

it "should be able to execute multiple tasks" do
handler = TaskHandler.new(["task1", "task2"])
handler.execute(1).should eq "Completed task1"
handler.execute(1).should eq "Completed task2"
end

it "should not run if the task condition logic is not met" do
handler = TaskHandler.new(["task1", "task2"])
handler.update_task_condition_logic { |priority| priority > 2 }
handler.execute(2).should eq "Could not complete task1"
end

it "should be able to update the condition after one task is completed" do
handler = TaskHandler.new(["task1", "task2"])
handler.execute(1).should eq "Completed task1"
handler.update_task_condition_logic { |priority| priority > 2 }
handler.execute(1).should eq "Could not complete task2"
end
end
end
13 changes: 13 additions & 0 deletions exercises/concept/task-handler/src/task_handler.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class TaskHandler
def initialize(tasks : Array(String))
raise "Please implement the TaskHandler#initialize method"
end

def update_task_condition_logic(&block : Int32 -> Bool)
raise "Please implement the TaskHandler#update_task_condition_logic method"
end

def execute(priority : Int32)
raise "Please implement the TaskHandler#execute method"
end
end

0 comments on commit 4ae0a5d

Please sign in to comment.