├── .ruby-version ├── .gitignore ├── .bundle └── config ├── .rspec ├── .envrc ├── Gemfile ├── Rakefile ├── bin ├── rake ├── ldiff ├── rspec ├── bundler ├── rubocop ├── htmldiff ├── ruby-parse └── ruby-rewrite ├── spec ├── spec_helper.rb └── gilded_rose_spec.rb ├── .rubocop.yml ├── README.md └── lib └── gilded_rose.rb /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.2.4 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Gemfile.lock 2 | -------------------------------------------------------------------------------- /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_BIN: bin 3 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # Enables the project's binstubs 2 | PATH_add bin 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rake" 4 | gem "rspec" 5 | gem "rubocop" 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rspec/core/rake_task" 2 | require "rubocop/rake_task" 3 | 4 | RuboCop::RakeTask.new 5 | RSpec::Core::RakeTask.new(:spec) 6 | 7 | task :default => [:rubocop, :spec] 8 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rake' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("rake", "rake") 17 | -------------------------------------------------------------------------------- /bin/ldiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'ldiff' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("diff-lcs", "ldiff") 17 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rspec' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("rspec-core", "rspec") 17 | -------------------------------------------------------------------------------- /bin/bundler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'bundler' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("bundler", "bundler") 17 | -------------------------------------------------------------------------------- /bin/rubocop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rubocop' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("rubocop", "rubocop") 17 | -------------------------------------------------------------------------------- /bin/htmldiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'htmldiff' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("diff-lcs", "htmldiff") 17 | -------------------------------------------------------------------------------- /bin/ruby-parse: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'ruby-parse' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("parser", "ruby-parse") 17 | -------------------------------------------------------------------------------- /bin/ruby-rewrite: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'ruby-rewrite' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require "pathname" 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require "rubygems" 14 | require "bundler/setup" 15 | 16 | load Gem.bin_path("parser", "ruby-rewrite") 17 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.expect_with :rspec do |expectations| 3 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 4 | end 5 | 6 | config.mock_with :rspec do |mocks| 7 | mocks.verify_partial_doubles = true 8 | end 9 | 10 | config.filter_run :focus 11 | config.run_all_when_everything_filtered = true 12 | 13 | config.disable_monkey_patching! 14 | config.warnings = true 15 | 16 | if config.files_to_run.one? 17 | config.default_formatter = "doc" 18 | end 19 | 20 | config.order = :random 21 | Kernel.srand config.seed 22 | end 23 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.2 3 | 4 | Exclude: 5 | - "bin/**/*" 6 | - "spec/**/*" 7 | - "lib/gilded_rose.rb" 8 | 9 | Metrics/LineLength: 10 | Max: 120 11 | 12 | # We like to use the hash rocket in rake files. 13 | Style/HashSyntax: 14 | Exclude: 15 | - "Rakefile" 16 | 17 | # We will use double quotes everywhere. 18 | Style/StringLiterals: 19 | EnforcedStyle: double_quotes 20 | 21 | # We are not going to optimize by freezing strings. 22 | Style/MutableConstant: 23 | Enabled: false 24 | 25 | # We are not going to distinguish between fail and raise. 26 | Style/SignalException: 27 | Enabled: false 28 | 29 | # We are not going to line up parameters that span more than one line. 30 | Style/AlignParameters: 31 | Enabled: false 32 | 33 | # We are okay with using extend self instead of module_function. 34 | Style/ModuleFunction: 35 | Enabled: false 36 | 37 | # We are going to skip top-level class documentation for katas. 38 | Style/Documentation: 39 | Enabled: false 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gilded Rose Kata in Ruby 2 | ======================== 3 | 4 | The Gilded Rose Kata in Ruby following the style from the [Sandi Metz](https://twitter.com/sandimetz) 5 | presentation [All the Little Things](https://www.youtube.com/watch?v=8bZh5LMaSmE). 6 | 7 | The objective of this kata is to practice refactoring. This kata motivates the need for refactoring as follows: 8 | 9 | * The implementation is spaghetti code, and difficult to glean context. 10 | * The tests of the current implementation pass, but there are a number of pending tests. 11 | * You are asked to implement the code to get the pending tests to pass. 12 | * This isn't easy, because the implementation is so messy. 13 | * Consider using the passing tests to refactor the code to make it more habitable before implementing new functionality. 14 | Also known as a [preparatory refactoring](http://martinfowler.com/articles/workflowsOfRefactoring/#preparatory). 15 | 16 | ## Where to Start 17 | 18 | Begin refactoring the existing `lib/gilded_rose.rb` class. 19 | 20 | ## Original Requirements 21 | 22 | Here are the [requirements](https://github.com/jimweirich/gilded_rose_kata#original-description-of-the-gilded-rose) 23 | as given in the original version of the kata. It may help you to understand the problem domain if you know 24 | that the [Gilded Rose](http://wowwiki.wikia.com/wiki/Gilded_Rose) is an inn from the World of Warcraft. 25 | -------------------------------------------------------------------------------- /lib/gilded_rose.rb: -------------------------------------------------------------------------------- 1 | class GildedRose 2 | attr_reader :name, :days_remaining, :quality 3 | 4 | def initialize(name:, days_remaining:, quality:) 5 | @name = name 6 | @days_remaining = days_remaining 7 | @quality = quality 8 | end 9 | 10 | def tick 11 | if @name != "Aged Brie" and @name != "Backstage passes to a TAFKAL80ETC concert" 12 | if @quality > 0 13 | if @name != "Sulfuras, Hand of Ragnaros" 14 | @quality = @quality - 1 15 | end 16 | end 17 | else 18 | if @quality < 50 19 | @quality = @quality + 1 20 | if @name == "Backstage passes to a TAFKAL80ETC concert" 21 | if @days_remaining < 11 22 | if @quality < 50 23 | @quality = @quality + 1 24 | end 25 | end 26 | if @days_remaining < 6 27 | if @quality < 50 28 | @quality = @quality + 1 29 | end 30 | end 31 | end 32 | end 33 | end 34 | if @name != "Sulfuras, Hand of Ragnaros" 35 | @days_remaining = @days_remaining - 1 36 | end 37 | if @days_remaining < 0 38 | if @name != "Aged Brie" 39 | if @name != "Backstage passes to a TAFKAL80ETC concert" 40 | if @quality > 0 41 | if @name != "Sulfuras, Hand of Ragnaros" 42 | @quality = @quality - 1 43 | end 44 | end 45 | else 46 | @quality = @quality - @quality 47 | end 48 | else 49 | if @quality < 50 50 | @quality = @quality + 1 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/gilded_rose_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | require_relative "../lib/gilded_rose" 4 | 5 | RSpec.describe GildedRose do 6 | context "Normal Item" do 7 | it "before sell date" do 8 | gilded_rose = GildedRose.new(name: "Normal Item", days_remaining: 5, quality: 10) 9 | 10 | gilded_rose.tick 11 | 12 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 9) 13 | end 14 | 15 | it "on sell date" do 16 | gilded_rose = GildedRose.new(name: "Normal Item", days_remaining: 0, quality: 10) 17 | 18 | gilded_rose.tick 19 | 20 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 8) 21 | end 22 | 23 | it "after sell date" do 24 | gilded_rose = GildedRose.new(name: "Normal Item", days_remaining: -10, quality: 10) 25 | 26 | gilded_rose.tick 27 | 28 | expect(gilded_rose).to have_attributes(days_remaining: -11, quality: 8) 29 | end 30 | 31 | it "of zero quality" do 32 | gilded_rose = GildedRose.new(name: "Normal Item", days_remaining: 5, quality: 0) 33 | 34 | gilded_rose.tick 35 | 36 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 0) 37 | end 38 | end 39 | 40 | context "Aged Brie" do 41 | it "before sell date" do 42 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: 5, quality: 10) 43 | 44 | gilded_rose.tick 45 | 46 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 11) 47 | end 48 | 49 | it "with max quality" do 50 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: 5, quality: 50) 51 | 52 | gilded_rose.tick 53 | 54 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 50) 55 | end 56 | 57 | it "on sell date" do 58 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: 0, quality: 10) 59 | 60 | gilded_rose.tick 61 | 62 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 12) 63 | end 64 | 65 | it "on sell date near max quality" do 66 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: 0, quality: 49) 67 | 68 | gilded_rose.tick 69 | 70 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 50) 71 | end 72 | 73 | it "on sell date with max quality" do 74 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: 0, quality: 50) 75 | 76 | gilded_rose.tick 77 | 78 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 50) 79 | end 80 | 81 | it "after sell date" do 82 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: -10, quality: 10) 83 | 84 | gilded_rose.tick 85 | 86 | expect(gilded_rose).to have_attributes(days_remaining: -11, quality: 12) 87 | end 88 | 89 | it "after sell date with max quality" do 90 | gilded_rose = GildedRose.new(name: "Aged Brie", days_remaining: -10, quality: 50) 91 | 92 | gilded_rose.tick 93 | 94 | expect(gilded_rose).to have_attributes(days_remaining: -11, quality: 50) 95 | end 96 | end 97 | 98 | context "Sulfuras" do 99 | it "before sell date" do 100 | gilded_rose = GildedRose.new(name: "Sulfuras, Hand of Ragnaros", days_remaining: 5, quality: 80) 101 | 102 | gilded_rose.tick 103 | 104 | expect(gilded_rose).to have_attributes(days_remaining: 5, quality: 80) 105 | end 106 | 107 | it "on sell date" do 108 | gilded_rose = GildedRose.new(name: "Sulfuras, Hand of Ragnaros", days_remaining: 0, quality: 80) 109 | 110 | gilded_rose.tick 111 | 112 | expect(gilded_rose).to have_attributes(days_remaining: 0, quality: 80) 113 | end 114 | 115 | it "after sell date" do 116 | gilded_rose = GildedRose.new(name: "Sulfuras, Hand of Ragnaros", days_remaining: -10, quality: 80) 117 | 118 | gilded_rose.tick 119 | 120 | expect(gilded_rose).to have_attributes(days_remaining: -10, quality: 80) 121 | end 122 | end 123 | 124 | context "Backstage Pass" do 125 | it "long before sell date" do 126 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 11, quality: 10) 127 | 128 | gilded_rose.tick 129 | 130 | expect(gilded_rose).to have_attributes(days_remaining: 10, quality: 11) 131 | end 132 | 133 | it "long before sell date at max quality" do 134 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 11, quality: 50) 135 | 136 | gilded_rose.tick 137 | 138 | expect(gilded_rose).to have_attributes(days_remaining: 10, quality: 50) 139 | end 140 | 141 | it "medium close to sell date upper bound" do 142 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 10, quality: 10) 143 | 144 | gilded_rose.tick 145 | 146 | expect(gilded_rose).to have_attributes(days_remaining: 9, quality: 12) 147 | end 148 | 149 | it "medium close to sell date upper bound at max quality" do 150 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 10, quality: 50) 151 | 152 | gilded_rose.tick 153 | 154 | expect(gilded_rose).to have_attributes(days_remaining: 9, quality: 50) 155 | end 156 | 157 | it "medium close to sell date lower bound" do 158 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 6, quality: 10) 159 | 160 | gilded_rose.tick 161 | 162 | expect(gilded_rose).to have_attributes(days_remaining: 5, quality: 12) 163 | end 164 | 165 | it "medium close to sell date lower bound at max quality" do 166 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 6, quality: 50) 167 | 168 | gilded_rose.tick 169 | 170 | expect(gilded_rose).to have_attributes(days_remaining: 5, quality: 50) 171 | end 172 | 173 | it "very close to sell date upper bound" do 174 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 5, quality: 10) 175 | 176 | gilded_rose.tick 177 | 178 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 13) 179 | end 180 | 181 | it "very close to sell date upper bound at max quality" do 182 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 5, quality: 50) 183 | 184 | gilded_rose.tick 185 | 186 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 50) 187 | end 188 | 189 | it "very close to sell date lower bound" do 190 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 1, quality: 10) 191 | 192 | gilded_rose.tick 193 | 194 | expect(gilded_rose).to have_attributes(days_remaining: 0, quality: 13) 195 | end 196 | 197 | it "very close to sell date lower bound at max quality" do 198 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 1, quality: 50) 199 | 200 | gilded_rose.tick 201 | 202 | expect(gilded_rose).to have_attributes(days_remaining: 0, quality: 50) 203 | end 204 | 205 | it "on sell date" do 206 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: 0, quality: 10) 207 | 208 | gilded_rose.tick 209 | 210 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 0) 211 | end 212 | 213 | it "after sell date" do 214 | gilded_rose = GildedRose.new(name: "Backstage passes to a TAFKAL80ETC concert", days_remaining: -10, quality: 10) 215 | 216 | gilded_rose.tick 217 | 218 | expect(gilded_rose).to have_attributes(days_remaining: -11, quality: 0) 219 | end 220 | end 221 | 222 | context "Conjured Mana" do 223 | xit "before sell date" do 224 | gilded_rose = GildedRose.new(name: "Conjured Mana Cake", days_remaining: 5, quality: 10) 225 | 226 | gilded_rose.tick 227 | 228 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 8) 229 | end 230 | 231 | xit "before sell date at zero quality" do 232 | gilded_rose = GildedRose.new(name: "Conjured Mana Cake", days_remaining: 5, quality: 0) 233 | 234 | gilded_rose.tick 235 | 236 | expect(gilded_rose).to have_attributes(days_remaining: 4, quality: 0) 237 | end 238 | 239 | xit "on sell date" do 240 | gilded_rose = GildedRose.new(name: "Conjured Mana Cake", days_remaining: 0, quality: 10) 241 | 242 | gilded_rose.tick 243 | 244 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 6) 245 | end 246 | 247 | xit "on sell date at zero quality" do 248 | gilded_rose = GildedRose.new(name: "Conjured Mana Cake", days_remaining: 0, quality: 0) 249 | 250 | gilded_rose.tick 251 | 252 | expect(gilded_rose).to have_attributes(days_remaining: -1, quality: 0) 253 | end 254 | 255 | xit "after sell date" do 256 | gilded_rose = GildedRose.new(name: "Conjured Mana Cake", days_remaining: -10, quality: 10) 257 | 258 | gilded_rose.tick 259 | 260 | expect(gilded_rose).to have_attributes(days_remaining: -11, quality: 6) 261 | end 262 | 263 | xit "after sell date at zero quality" do 264 | gilded_rose = GildedRose.new(name: "Conjured Mana Cake", days_remaining: -10, quality: 0) 265 | 266 | gilded_rose.tick 267 | 268 | expect(gilded_rose).to have_attributes(days_remaining: -11, quality: 0) 269 | end 270 | end 271 | end 272 | --------------------------------------------------------------------------------