diff --git a/common-lisp-parachute/README.md b/common-lisp-parachute/README.md new file mode 100644 index 00000000..9c542d3b --- /dev/null +++ b/common-lisp-parachute/README.md @@ -0,0 +1,79 @@ +# gilded-rose + +## Setup + +### Install quicklisp + +To run this project install quicklisp (if not already done): + - download https://beta.quicklisp.org/quicklisp.lisp + - load it with your common lisp implementation. The example with sbcl: + > sbcl --load quicklisp.lisp + - run the command + > (quicklisp-quickstart:install) + in your common lisp implementation + - run the command + > (ql:add-to-init-file) + in your common lisp implementation + +### Install project +Copy the project-folder containing this file into /quicklisp/local-projects/ that has been created when installing quicklisp. +This is the root directory for quicklisp to search for the gilded-rose.asd file which defines the system (project) and its dependencies. +The quicklisp-folder is usually created in your home-directory. + +### Work with the project + +Now you can load the project with +> (ql:quickload "gilded-rose") +in your common lisp-implementation and run the tests with +> (asdf:test-system "gilded-rose") +. + +You can stub and mock functions and methods with the cl-mock-library which is already included in the system definition of the test-system: + +(with-mocks () + (answer (call-previous)) + ( ) + (is (invocations '))) + +## Documentation + +Hi and welcome to team Gilded Rose. As you know, we are a small inn +with a prime location in a prominent city ran by a friendly +innkeeper named Allison. We also buy and sell only the finest goods. +Unfortunately, our goods are constantly degrading in quality as they +approach their sell by date. We have a system in place that updates +our inventory for us. It was developed by a no-nonsense type named +Leeroy, who has moved on to new adventures. Your task is to add the +new feature to our system so that we can begin selling a new +category of items. + +First an introduction to our system: + - All items have a SellIn value which denotes the number of days we have to sell the item + - All items have a Quality value which denotes how valuable the item is + - At the end of each day our system lowers both values for every item + +Pretty simple, right? Well this is where it gets interesting: + - Once the sell by date has passed, Quality degrades twice as fast +The Quality of an item is never negative + - "Aged Brie" actually increases in Quality the older it gets + - The Quality of an item is never more than 50 + - "Sulfuras", being a legendary item, never has to be sold or decreases in Quality + - "Backstage passes", like aged brie, increases in Quality as it's + SellIn value approaches; Quality increases by 2 when there are 10 + days or less and by 3 when there are 5 days or less but Quality + drops to 0 after the concert + +We have recently signed a supplier of conjured items. This requires an update to our system: + - "Conjured" items degrade in Quality twice as fast as normal items + +Feel free to make any changes to the UpdateQuality method and add +any new code as long as everything still works correctly. However, +do not alter the Item class or Items property as those belong to the +goblin in the corner who will insta-rage and one-shot you as he +doesn't believe in shared code ownership (you can make the +UpdateQuality method and Items property static if you like, we'll +cover for you). +Just for clarification, an item can never have its Quality increase +above 50, however "Sulfuras" is a legendary item and as such its +Quality is 80 and it never alters. + diff --git a/common-lisp-parachute/gilded-rose.asd b/common-lisp-parachute/gilded-rose.asd new file mode 100644 index 00000000..5985f942 --- /dev/null +++ b/common-lisp-parachute/gilded-rose.asd @@ -0,0 +1,20 @@ +;;;; gilded-rose.asd + +(defsystem "gilded-rose" + :description "Gilded Rose is a small inn selling the finest goods." + :author "Leeroy " + :version "1.0.0" + :pathname "source/" + :components ((:file "package") + (:file "gilded-rose" :depends-on ("package"))) + :in-order-to ((test-op (test-op "gilded-rose/tests")))) + +(defsystem "gilded-rose/tests" + :description "Unit tests for gilded-rose-package" + :author "Leeroy " + :version "1.0.0" + :depends-on ("gilded-rose" "parachute" "cl-mock") + :pathname "tests/" + :components ((:file "package") + (:file "tests" :depends-on ("package"))) + :perform (test-op (o c) (symbol-call :parachute :test :gilded-rose-tests))) diff --git a/common-lisp-parachute/source/gilded-rose.lisp b/common-lisp-parachute/source/gilded-rose.lisp new file mode 100644 index 00000000..575c86a2 --- /dev/null +++ b/common-lisp-parachute/source/gilded-rose.lisp @@ -0,0 +1,128 @@ +;; Hi and welcome to team Gilded Rose. As you know, we are a small inn +;; with a prime location in a prominent city ran by a friendly +;; innkeeper named Allison. We also buy and sell only the finest goods. +;; Unfortunately, our goods are constantly degrading in quality as they +;; approach their sell by date. We have a system in place that updates +;; our inventory for us. It was developed by a no-nonsense type named +;; Leeroy, who has moved on to new adventures. Your task is to add the +;; new feature to our system so that we can begin selling a new +;; category of items. + +;; First an introduction to our system: +;; All items have a SellIn value which denotes the number of days we have to sell the item +;; All items have a Quality value which denotes how valuable the item is +;; At the end of each day our system lowers both values for every item +;; Pretty simple, right? Well this is where it gets interesting: +;; Once the sell by date has passed, Quality degrades twice as fast +;; The Quality of an item is never negative +;; "Aged Brie" actually increases in Quality the older it gets +;; The Quality of an item is never more than 50 +;; "Sulfuras", being a legendary item, never has to be sold or decreases in Quality +;; "Backstage passes", like aged brie, increases in Quality as it's +;; SellIn value approaches; Quality increases by 2 when there are 10 +;; days or less and by 3 when there are 5 days or less but Quality +;; drops to 0 after the concert + +;; We have recently signed a supplier of conjured items. This requires an update to our system: +;; "Conjured" items degrade in Quality twice as fast as normal items +;; Feel free to make any changes to the UpdateQuality method and add +;; any new code as long as everything still works correctly. However, +;; do not alter the Item class or Items property as those belong to the +;; goblin in the corner who will insta-rage and one-shot you as he +;; doesn't believe in shared code ownership (you can make the +;; UpdateQuality method and Items property static if you like, we'll +;; cover for you). +;; Just for clarification, an item can never have its Quality increase +;; above 50, however "Sulfuras" is a legendary item and as such its +;; Quality is 80 and it never alters. + +;; https://github.com/emilybache/GildedRose-Refactoring-Kata + +;; Common Lisp version: Rainer Joswig, joswig@lisp.de, 2016 +;; Minor modifications (independent of CL impl.): Manfred Bergmann, 2022 +;; Adaption to asdf: Nico Simoski, 2023 + +;;; ================================================================ +;;; Code + +(in-package :gilded-rose) + +;;; Class ITEM + +(defclass item () + ((name :initarg :name :type string) + (sell-in :initarg :sell-in :type integer) + (quality :initarg :quality :type integer))) + +(defmethod to-string ((i item)) + (with-slots (name quality sell-in) i + (format nil "~a, ~a, ~a" name sell-in quality))) + +;;; Class gilded-rose + +(defclass gilded-rose () + ((items :initarg :items))) + +(defmethod update-quality ((gr gilded-rose)) + (with-slots (items) gr + (dotimes (i (length items)) + (with-slots (name quality sell-in) + (elt items i) + (if (and (not (equalp name "Aged Brie")) + (not (equalp name "Backstage passes to a TAFKAL80ETC concert"))) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (when (< quality 50) + (setf quality (+ quality 1)) + (when (equalp name "Backstage passes to a TAFKAL80ETC concert") + (if (< sell-in 11) + (if (< quality 50) + (setf quality (+ quality 1)))) + (if (< sell-in 6) + (if (< quality 50) + (setf quality (+ quality 1))))))) + + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf sell-in (- sell-in 1))) + + (if (< sell-in 0) + (if (not (equalp name "Aged Brie")) + (if (not (equalp name "Backstage passes to a TAFKAL80ETC concert")) + (if (> quality 0) + (if (not (equalp name "Sulfuras, Hand of Ragnaros")) + (setf quality (- quality 1)))) + (setf quality (- quality quality))) + (if (< quality 50) + (setf quality (+ quality 1))))))))) + +;;; Example + +(defun run-gilded-rose (days) + (write-line "OMGHAI!") + (let* ((descriptions '(("+5 Dexterity Vest" 10 20) + ("Aged Brie" 2 0) + ("Elixir of the Mongoose" 5 7) + ("Sulfuras, Hand of Ragnaros" 0 80) + ("Sulfuras, Hand of Ragnaros" -1 80) + ("Backstage passes to a TAFKAL80ETC concert" 15 20) + ("Backstage passes to a TAFKAL80ETC concert" 10 49) + ("Backstage passes to a TAFKAL80ETC concert" 5 49) + ;; this conjured item does not work properly yet + ("Conjured Mana Cake" 3 6))) + (items (loop :for (name sell-in quality) :in descriptions + :collect (make-instance 'item + :name name + :sell-in sell-in + :quality quality))) + (app (make-instance 'gilded-rose :items items))) + (dotimes (i days) + (format t "-------- day ~a --------~%" i) + (format t "name, sell-in, quality~%") + (dolist (item items) + (write-line (to-string item))) + (terpri) + (update-quality app)))) + +;;; ================================================================ +;;; EOF diff --git a/common-lisp-parachute/source/package.lisp b/common-lisp-parachute/source/package.lisp new file mode 100644 index 00000000..c94fed86 --- /dev/null +++ b/common-lisp-parachute/source/package.lisp @@ -0,0 +1,14 @@ +;;;; package.lisp + +(defpackage :gilded-rose + (:use :cl) + (:export :run-gilded-rose + :gilded-rose + :update-quality + :items + :item + :name + :sell-in + :quality)) + + diff --git a/common-lisp-parachute/tests/package.lisp b/common-lisp-parachute/tests/package.lisp new file mode 100644 index 00000000..ae158148 --- /dev/null +++ b/common-lisp-parachute/tests/package.lisp @@ -0,0 +1,12 @@ +;;;; package.lisp + +(defpackage :gilded-rose-tests + (:use :cl :gilded-rose) + (:import-from :parachute + :define-test + :is) + (:import-from :cl-mock + :with-mocks + :answer + :call-previous + :invocations)) diff --git a/common-lisp-parachute/tests/tests.lisp b/common-lisp-parachute/tests/tests.lisp new file mode 100644 index 00000000..d1752acf --- /dev/null +++ b/common-lisp-parachute/tests/tests.lisp @@ -0,0 +1,12 @@ +(in-package :gilded-rose-tests) + +(define-test gilded-rose-testsuite) + +(define-test "An item in gilded-rose is updated." + :parent gilded-rose-testsuite + (let* ((an-item (make-instance 'item :name "An item" :sell-in 1 :quality 1)) + (some-items (list an-item)) + (my-app (make-instance 'gilded-rose :items some-items))) + (update-quality my-app) + (is equal (slot-value (first (slot-value my-app 'items)) 'name) "Fix me"))) +