diff --git a/README.md b/README.md index 2586809..e1497e1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,976 @@ # ruby-patterns Examples of Patterns in Ruby -Pd: I'd like recommend you visit my last gem worked: [Ate](https://github.com/TheBlasfem/ate), is a new template engine for Ruby, is minimalist and simple to use, hope you enjoy it! +## Table of Contents + + 1. [Adapter](#adapter) + 1. [Builder](#builder) + 1. [Command](#command) + 1. [Composite](#composite) + 1. [Decorator](#decorator) + 1. [Facade](#facade) + 1. [Factory](#factory) + 1. [Interpreter](#interpreter) + 1. [Iterator](#iterator) + 1. [Observer](#observer) + 1. [Proxy](#proxy) + 1. [Singleton](#singleton) + 1. [State](#state) + 1. [Strategy](#strategy) + 1. [Template](#template) + +## Adapter + - Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.[[link](#adapter)] + + ```ruby + class Quest + attr_accessor :difficulty, :hero + + def initialize(difficulty) + @difficulty = difficulty + @hero = nil + end + + def finish + @hero.exp += calculate_experience + end + + def calculate_experience + @difficulty * 50 / @hero.level + end + end + + class Hero + attr_accessor :level, :exp, :quests + + def initialize + @level = 1 + @exp = 0 + @quests = [] + end + + def take_quest(quest) + @quests << (quest.hero = self) + end + + def finish_quest(quest) + quest.finish + @quests.delete quest + end + end + + class OldQuest + attr_accessor :hero, :difficulty, :experience + + def initialize + @difficulty = 3 + @experience = 10 + end + + def done + difficulty * experience + end + end + + class QuestAdapter + attr_accessor :hero + + def initialize(old_quest, difficulty) + @old_quest = old_quest + @old_quest.difficulty = difficulty + @hero = nil + end + + def finish + @hero.exp += @old_quest.done + end + end + + # Usage + hero = Hero.new + quest = Quest.new 5 + hero.take_quest quest + hero.finish_quest quest + puts hero.exp + # => 250 + some_old_quest = OldQuest.new + old_quest_adapted = QuestAdapter.new(some_old_quest, 5) + hero.take_quest old_quest_adapted + hero.finish_quest old_quest_adapted + puts hero.exp + # => 300 + ``` + +**[Back to top](#table-of-contents)** + +## Builder + - Separate the construction of a complex object from its representation so that the same construction process can create different representations.[[link](#builder)] + + ```ruby + class BoardBuilder + def initialize(width, height) + @board = Board.new + @board.width = width + @board.height = height + @board.tiles = [] + @board.monsters = [] + end + + def add_tiles(n) + n.times{ @board.tiles << Tile.new } + end + + def add_monsters(n) + n.times{ @board.monsters << Monster.new } + end + + def board + @board + end + end + + class Board + attr_accessor :width, :height, :tiles, :monsters + def initialize + end + end + + class Tile; end + class Monster; end + + # Usage + builder = BoardBuilder.new 2, 3 + puts builder.board + # => Board Object + board = builder.board + puts board.width + # => 2 + builder.add_tiles(3) + builder.add_monsters(2) + puts board.tiles.size + # => 3 + puts board.monsters.size + # => 2 + ``` + +**[Back to top](#table-of-contents)** + +## Command + - Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.[[link](#command)] + + ```ruby + class Turn + def initialize + @commands = [] + end + + def run_command(command) + command.execute + @commands << command + end + + def undo_command + @commands.pop.unexecute + end + end + + class Hero + attr_accessor :money, :health + + def initialize + @money = 0 + @health = 100 + end + end + + class GetMoneyCommand + def initialize(hero) + @hero = hero + end + + def execute + @hero.money += 10 + end + + def unexecute + @hero.money -= 10 + end + end + + class HealCommand + def initialize(hero) + @hero = hero + end + + def execute + @hero.health += 10 + end + + def unexecute + @hero.health -= 10 + end + end + + # Usage + hero = Hero.new + get_money = GetMoneyCommand.new hero + heal = HealCommand.new hero + turn = Turn.new + turn.run_command(heal) + puts hero.health + # => 110 + turn.run_command(get_money) + puts hero.money + # => 10 + turn.undo_command + puts hero.money + # => 0 + ``` + +**[Back to top](#table-of-contents)** + +## Composite + - Composition over inheritance. Compose objects into tree structures to represent part-whole hierarchies.[[link](#composite)] + + ```ruby + class CompositeQuest + def initialize + @tasks = [] + end + + def <<(task) + @tasks << task + end + + def reward + @tasks.inject(0){ |sum, task| sum += task.reward } + end + end + + class MegaQuest < CompositeQuest + end + + class Quest < CompositeQuest + end + + class MonsterTask + attr_reader :reward + + def initialize + @reward = 100 + end + end + + class PuzzleTask + attr_reader :reward + + def initialize + @reward = 200 + end + end + + # Usage + quest1 = Quest.new + quest1 << MonsterTask.new + quest1 << PuzzleTask.new + puts quest1.reward + # => 300 + + quest2 = Quest.new + quest2 << MonsterTask.new + quest2 << PuzzleTask.new + megaquest = MegaQuest.new + megaquest << quest1 + megaquest << quest2 + puts megaquest.reward + # => 600 + ``` + +**[Back to top](#table-of-contents)** + +## Decorator + - Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.[[link](#decorator)] + + ```ruby + class ItemDecorator + def initialize(item) + @item = item + end + # this needs to be delegated with other efective way + def use + @item.use + end + end + + class MagicItemDecorator < ItemDecorator + def price + @item.price * 3 + end + + def description + @item.description + "Magic" + end + end + + class MasterpieceItemDecorator < ItemDecorator + def price + @item.price * 2 + end + + def description + @item.description + "Masterpiece" + end + end + + class Item + attr_reader :price, :description + def initialize + @price = 10 + @description = "Item " + end + + def use + "do something" + end + end + + # Usage + item = Item.new + magic_item = MagicItemDecorator.new(item) + puts magic_item.price + # => 30 + puts magic_item.description + # => Item Magic + + masterpiece_item = MasterpieceItemDecorator.new(item) + puts masterpiece_item.price + # => 20 + puts masterpiece_item.description + # => Item Masterpiece + + # all next lines puts "do something" + item.use + magic_item.use + masterpiece_item.use + ``` + +**[Back to top](#table-of-contents)** + +## Facade + - The goal of the Facade Pattern is to provide a unified interface to a set of interfaces in a subsystem. This means you'd just have some object that can send back other objects.[[link](#facade)] + + ```ruby + class Hero + attr_reader :name + + def initialize(name) + @name = name + end + + def join(level) + puts "#{self.name} join #{level}\n" + end + + def attack(enemy) + puts "#{self.name} kick #{enemy}\n" + end + end + + class Enemy + attr_reader :name + + def initialize(name) + @name = name + end + + def dead(hero) + puts "#{self.name} killed by #{hero}" + end + end + + class Level + attr_reader :stage + + def initialize(stage) + @stage = stage + end + + def to_s + stage + end + end + + class GameFacade + attr_reader :hero, :enemy, :level + + def initialize + @hero = Hero.new('Sonic') + @enemy = Enemy.new('Eggman') + @level = Level.new('Green Hill') + end + + def start_game + hero.join(level) + hero.attack(enemy.name) + enemy.dead(hero.name) + end + end + + game = GameFacade.new + game.start_game + # => Sonic join Green Hill + # Sonic kick Eggman + # Eggman killed by Sonic + ``` + +**[Back to top](#table-of-contents)** + +## Factory + - Define an interface for creating an object, but let subclasses decide which class to instantiate.[[link](#factory)] + + ```ruby + class Party + attr_reader :members + def initialize(factory) + @members = [] + @factory = factory + end + + def add_warriors(n) + n.times{ @members << @factory.create_warrior } + end + + def add_mages(n) + n.times{ @members << @factory.create_mage } + end + end + + class HeroFactory + def create_warrior + Warrior.new + end + + def create_mage + Mage.new + end + end + + class Hero + def initialize + end + end + + class Warrior < Hero + end + + class Mage < Hero + end + + # Usage + party = Party.new(HeroFactory.new) + party.add_warriors(3) + party.add_mages(2) + puts party.members.size + # => 5 + puts party.members.count{ |member| member.class == "Mage" } + # => 2 + ``` + +**[Back to top](#table-of-contents)** + +## Interpreter + - This pattern provides an interpreter to deal with an abstract language. Using classes we can understand the inputs for parse them.[[link](#interpreter)] + + ```ruby + class Word + def initialize(value) + @value = value + end + + def execute + @value + end + end + + class Plus + def initialize(first, second) + @first = first + @second = second + end + + def execute + @first.execute + @second.execute + end + end + + class Minus + def initialize(first, second) + @first = first + @second = second + end + + def execute + index = @first.execute =~ /#{@second.execute}/ + second_index = index + @second.execute.length + @first.execute[0,index] + @first.execute[second_index..-1] + end + end + + class Interpreter + def self.parse(input) + @waiting_second_word = false + words = [] + operations = [] + input.split.each_with_index do |value| + if value =~ /^[^+-].*/ && !@waiting_second_word + words << Word.new(value) + else + if symbol = operations.pop() + first = words.size > 1 ? Word.new(words.map(&:execute).join(" ")) : + words.pop + second = Word.new(value) + case symbol + when /\A\+/ + words << Word.new(Plus.new(first, second).execute) + when /\A\-/ + words << Word.new(Minus.new(first, second).execute) + end + @waiting_second_word = false + else + @waiting_second_word = true + operations << value + end + end + end + words.pop.execute + end + end + + puts Interpreter.parse("NA + NA + NA + BATMAN") + #=> NANANABATMAN + + puts Interpreter.parse("you know nothing Jon Snow - nothing") + #=> you know Jon Snow + + puts Interpreter.parse("hello + world - llowo") + #=>herld + ``` + +**[Back to top](#table-of-contents)** + +## Iterator + - Iterator helps you to iterate through a complex object using an iterator method.[[link](#iterator)] + + ```ruby + class Parent + attr_reader :first_name + + def initialize(first_name, gender) + @first_name = first_name + @gender = gender + end + end + + class Child < Parent + end + + class Family + def initialize(surname) + @surname = surname + @children = [] + end + + def add_father(first_name) + @father = Parent.new first_name, "M" + end + + def add_mother(first_name) + @mother = Parent.new first_name, "F" + end + + def add_child(first_name, gender) + @children << Child.new(first_name, gender) + end + + def each_member + [@father, @mother, @children].flatten.each do |member| + yield member + end + end + end + + # Usage + family = Family.new "Jackson" + family.add_father("Robert") + family.add_mother("Susan") + family.add_child("Lucas", "M") + family.add_child("James", "M") + family.add_child("Rose", "F") + family.each_member{ |member| puts member.first_name } + # => Robert + # Susan + # Lucas + # James + # Rose + ``` + +**[Back to top](#table-of-contents)** + +## Observer + - Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.[[link](#observer)] + + ```ruby + module Observable + attr_reader :observers + + def initialize(attrs = {}) + @observers = [] + end + + def add_observer(observer) + @observers << observer + end + + def notify_observers + @observers.each{ |observer| observer.update } + end + end + + class Tile + include Observable + + def initialize(attrs = {}) + super + @cursed = attrs.fetch(:cursed, false) + end + + def cursed? + @cursed + end + + def activate_curse + notify_observers + end + end + + class Hero + attr_reader :health + + def initialize + @cursed = false + @health = 10 + end + + def damage(hit) + @health -= hit + end + + def cursed? + @cursed + end + + def discover(tile) + if tile.cursed? + @cursed = true + tile.add_observer(self) + end + end + + def update + damage(4) + end + end + + # Usage + hero = Hero.new + tile = Tile.new cursed: true + hero.discover(tile) + tile.activate_curse + puts hero.health + # => 6 + puts hero.cursed? + # => true + ``` + +**[Back to top](#table-of-contents)** + +## Proxy + - Provide a surrogate or placeholder for another object to control access to it.[[link](#proxy)] + + ```ruby + require 'forwardable' + + class Hero + attr_accessor :keywords + + def initialize + @keywords = [] + end + end + + class ComputerProxy + # Forwardable allows objects to run methods on behalf + # of it's members, in this case the Computer object + extend Forwardable + + # We delegate the ComputerProxy's use of + # the Computer object's add method + def_delegators :real_object, :add + + def initialize(hero) + @hero = hero + end + + def execute + check_access + real_object.execute + end + + def check_access + unless @hero.keywords.include?(:computer) + raise "You have no access" + end + end + + def real_object + @real_object ||= Computer.new + end + end + + class Computer + def initialize + @queue = [] + end + + def add(command) + @queue << command + end + + def execute + "executing commands" + end + end + + # Usage + hero = Hero.new + proxy = ComputerProxy.new(hero) + proxy.add("some command") + proxy.execute + # => raise error + hero.keywords << :computer + proxy.execute + # => executing commands + ``` + +**[Back to top](#table-of-contents)** + +## Singleton + - Define a unique instance of an object.[[link](#singleton)] + + ```ruby + class HeroFactory + @@instance = nil + + def self.instance + @@instance ||= HeroFactory.send(:new) + end + + def create_warrior + Warrior.new + end + + def create_mage + Mage.new + end + + private_class_method :new + end + + # Usage + factory = HeroFactory.instance + another_factory = HeroFactory.instance + puts factory == another_factory + # => true + HeroFactory.new + # => Raise Exception + ``` + +**[Back to top](#table-of-contents)** + +## State + - This pattern tries to simplify complicated control flows changing an object's behavior dynamically.[[link](#state)] + + ```ruby + class Operation + attr_reader :state + def initialize + @state = OperationOpenState.new + end + + def trigger(state) + @state = @state.next(state) + end + end + + class OperationOpenState + def next(state) + if valid?(state) + OperationPendingPaymentState.new + else + raise IllegalStateJumpError + end + end + + def valid?(state) + state == :pending_payment + end + end + + class OperationPendingPaymentState + def next(state) + OperationConfirmState.new if valid?(state) + end + + def valid?(state) + state == :confirm + end + end + class IllegalStateJumpError < StandardError; end + class OperationConfirmState; end + + #Usage + operation = Operation.new + puts operation.state.class + #=> OperationOpenState + operation.trigger :pending_payment + puts operation.state.class + #=> OperationPendingPaymentState + operation.trigger :confirm + puts operation.state.class + #=> OperationConfirmState + + operation = Operation.new + operation.trigger :confirm + #=> raise IllegalStateJumpError + ``` + +**[Back to top](#table-of-contents)** + +## Strategy + - Define a family of algorithms, encapsulate each one, and make them interchangeable.[[link](#strategy)] + + ```ruby + class Hero + attr_reader :damage, :health, :skills + attr_accessor :printer + + def initialize(printer) + @damage = 10 + @health = 5 + @printer = printer + @skills = [:stealth, :driving, :intimidation] + end + + def print_stats + if block_given? + yield(damage, health, skills) + else + printer.print(damage, health, skills) + end + end + end + + class BattleStats + def print(damage, health, skills) + "Damage: #{damage}\nHealth: #{health}" + end + end + + class SkillStats + def print(damage, health, skills) + skills.inject(""){ |result, skill| result + skill.to_s.capitalize + "\n" } + end + end + + # Usage + Hero.new(BattleStats.new).print_stats + # => Damage: 10 + # Health: 5 + + Hero.new(SkillStats.new).print_stats + # => Stealth + # Driving + # Intimidation + + Hero.new(any_printer).print_stats do |damage, health, skills| + "Looks: I'm printing a customize message about my hero with damage #{damage} and number of skills: #{skills.size}" + end + ``` + +**[Back to top](#table-of-contents)** + +## Template + - Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template methods lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.[[link](#template)] + + ```ruby + class Hero + attr_reader :damage, :abilities + + def initialize + @damage = damage_rating + @abilities = occupation_abilities + end + + def greet + greeting = ["Hello"] + greeting << unique_greeting_line + greeting + end + + def unique_greeting_line + raise "You must define unique_greeting_line" + end + + def damage_rating + 10 + end + + def occupation_abilities + [] + end + + def attack + "Atack dealing #{damage} damage" + end + end + + class Warrior < Hero + def damage_rating + 15 + end + + def occupation_abilities + [:strike] + end + + def unique_greeting_line + "Warrior is ready to fight!" + end + end + + class Mage < Hero + def damage_rating + 7 + end + + def occupation_abilities + [:magic_spell] + end + + def unique_greeting_line + "Mage is ready to make powerful spells!" + end + end + ``` + +**[Back to top](#table-of-contents)** diff --git a/command.rb b/command.rb index ef2ece9..f18d759 100644 --- a/command.rb +++ b/command.rb @@ -4,6 +4,7 @@ class Turn def initialize @commands = [] end + def run_command(command) command.execute @commands << command @@ -15,7 +16,8 @@ def undo_command end class Hero - attr_reader :money, :health + attr_accessor :money, :health + def initialize @money = 0 @health = 100 @@ -63,4 +65,4 @@ def unexecute # => 10 turn.undo_command puts hero.money -# => 0 \ No newline at end of file +# => 0 diff --git a/composite.rb b/composite.rb index e904629..7f23581 100644 --- a/composite.rb +++ b/composite.rb @@ -37,16 +37,16 @@ def initialize # Usage quest1 = Quest.new -quest1 << MonsterTask -quest1 << PuzzleTask +quest1 << MonsterTask.new +quest1 << PuzzleTask.new puts quest1.reward # => 300 quest2 = Quest.new -quest2 << MonsterTask -quest2 << PuzzleTask +quest2 << MonsterTask.new +quest2 << PuzzleTask.new megaquest = MegaQuest.new megaquest << quest1 megaquest << quest2 puts megaquest.reward -# => 600 \ No newline at end of file +# => 600 diff --git a/decorator.rb b/decorator.rb index e2b1d7a..f7714b8 100644 --- a/decorator.rb +++ b/decorator.rb @@ -1,4 +1,4 @@ -# Attach additional responsabilities to an object dinamically. Decorators +# Attach additional responsibilities to an object dynamically. Decorators # provide a flexible alternative to subclassing for extending functionality class ItemDecorator def initialize(item) @@ -44,13 +44,13 @@ def use # Usage item = Item.new -magic_item = MagicItemDecorator(item) +magic_item = MagicItemDecorator.new(item) puts magic_item.price # => 30 puts magic_item.description # => Item Magic -masterpiece_item = MasterpieceItemDecorator(item) +masterpiece_item = MasterpieceItemDecorator.new(item) puts masterpiece_item.price # => 20 puts masterpiece_item.description @@ -59,4 +59,4 @@ def use # all next lines puts "do something" item.use magic_item.use -masterpiece_item.use \ No newline at end of file +masterpiece_item.use diff --git a/facade.rb b/facade.rb new file mode 100644 index 0000000..a46dec7 --- /dev/null +++ b/facade.rb @@ -0,0 +1,61 @@ +class Hero + attr_reader :name + + def initialize(name) + @name = name + end + + def join(level) + puts "#{self.name} join #{level}\n" + end + + def attack(enemy) + puts "#{self.name} kick #{enemy}\n" + end +end + +class Enemy + attr_reader :name + + def initialize(name) + @name = name + end + + def dead(hero) + puts "#{self.name} killed by #{hero}" + end +end + +class Level + attr_reader :stage + + def initialize(stage) + @stage = stage + end + + def to_s + stage + end +end + +class GameFacade + attr_reader :hero, :enemy, :level + + def initialize + @hero = Hero.new('Sonic') + @enemy = Enemy.new('Eggman') + @level = Level.new('Green Hill') + end + + def start_game + hero.join(level) + hero.attack(enemy.name) + enemy.dead(hero.name) + end +end + +game = GameFacade.new +game.start_game +# => Sonic join Green Hill +# Sonic kick Eggman +# Eggman killed by Sonic \ No newline at end of file diff --git a/interpreter.rb b/interpreter.rb new file mode 100644 index 0000000..fee34ee --- /dev/null +++ b/interpreter.rb @@ -0,0 +1,73 @@ +#This pattern provides an interpreter to deal with an abstract language. Using classes we can understand the inputs for parse them. +class Word + def initialize(value) + @value = value + end + + def execute + @value + end +end + +class Plus + def initialize(first, second) + @first = first + @second = second + end + + def execute + @first.execute + @second.execute + end +end + +class Minus + def initialize(first, second) + @first = first + @second = second + end + + def execute + index = @first.execute =~ /#{@second.execute}/ + second_index = index + @second.execute.length + @first.execute[0,index] + @first.execute[second_index..-1] + end +end + +class Interpreter + def self.parse(input) + @waiting_second_word = false + words = [] + operations = [] + input.split.each_with_index do |value| + if value =~ /^[^+-].*/ && !@waiting_second_word + words << Word.new(value) + else + if symbol = operations.pop() + first = words.size > 1 ? Word.new(words.map(&:execute).join(" ")) : + words.pop + second = Word.new(value) + case symbol + when /\A\+/ + words << Word.new(Plus.new(first, second).execute) + when /\A\-/ + words << Word.new(Minus.new(first, second).execute) + end + @waiting_second_word = false + else + @waiting_second_word = true + operations << value + end + end + end + words.pop.execute + end +end + +puts Interpreter.parse("NA + NA + NA + BATMAN") +#=> NANANABATMAN + +puts Interpreter.parse("you know nothing Jon Snow - nothing") +#=> you know Jon Snow + +puts Interpreter.parse("hello + world - llowo") +#=>herld \ No newline at end of file diff --git a/iterator.rb b/iterator.rb index 80bb1d6..de96be6 100644 --- a/iterator.rb +++ b/iterator.rb @@ -1,5 +1,5 @@ # Iterator helps you to iterate through a complex object using -# an interator method +# an iterator method class Parent attr_reader :first_name @@ -28,7 +28,7 @@ def add_mother(first_name) end def add_child(first_name, gender) - @children << Child.new first_name, gender + @children << Child.new(first_name, gender) end def each_member @@ -50,4 +50,4 @@ def each_member # Susan # Lucas # James -# Rose \ No newline at end of file +# Rose diff --git a/observer.rb b/observer.rb index 4329918..381d305 100644 --- a/observer.rb +++ b/observer.rb @@ -23,7 +23,7 @@ class Tile def initialize(attrs = {}) super - @cursed = attrs.fetch(:cursed, :false) + @cursed = attrs.fetch(:cursed, false) end def cursed? @@ -71,4 +71,4 @@ def update puts hero.health # => 6 puts hero.cursed? -# => true \ No newline at end of file +# => true diff --git a/proxy.rb b/proxy.rb index 64dd523..4b98a11 100644 --- a/proxy.rb +++ b/proxy.rb @@ -1,3 +1,5 @@ +require 'forwardable' + # Provide a surrogate or placeholder for another object to control # access to it class Hero @@ -9,8 +11,12 @@ def initialize end class ComputerProxy + # Forwardable allows objects to run methods on behalf + # of it's members, in this case the Computer object extend Forwardable + # We delegate the ComputerProxy's use of + # the Computer object's add method def_delegators :real_object, :add def initialize(hero) @@ -37,7 +43,7 @@ class Computer def initialize @queue = [] end - + def add(command) @queue << command end @@ -55,4 +61,4 @@ def execute # => raise error hero.keywords << :computer proxy.execute -# => executing commands \ No newline at end of file +# => executing commands diff --git a/state.rb b/state.rb new file mode 100644 index 0000000..57ed48d --- /dev/null +++ b/state.rb @@ -0,0 +1,52 @@ +#This pattern try to simplify complicate control flows changing an object's behaviour dynamically +class Operation + attr_reader :state + def initialize + @state = OperationOpenState.new + end + + def trigger(state) + @state = @state.next(state) + end +end + +class OperationOpenState + def next(state) + if valid?(state) + OperationPendingPaymentState.new + else + raise IllegalStateJumpError + end + end + + def valid?(state) + state == :pending_payment + end +end + +class OperationPendingPaymentState + def next(state) + OperationConfirmState.new if valid?(state) + end + + def valid?(state) + state == :confirm + end +end +class IllegalStateJumpError < StandardError; end +class OperationConfirmState; end + +#Usage +operation = Operation.new +puts operation.state.class +#=> OperationOpenState +operation.trigger :pending_payment +puts operation.state.class +#=> OperationPendingPaymentState +operation.trigger :confirm +puts operation.state.class +#=> OperationConfirmState + +operation = Operation.new +operation.trigger :confirm +#=> raise IllegalStateJumpError diff --git a/strategy.rb b/strategy.rb index 01b78f6..bb90cbe 100644 --- a/strategy.rb +++ b/strategy.rb @@ -34,18 +34,15 @@ def print(damage, health, skills) end # Usage -Hero.new(BattleStats.new) -Hero.print_stats +Hero.new(BattleStats.new).print_stats # => Damage: 10 # Health: 5 -Hero.new(SkillStats.new) -Hero.print_stats +Hero.new(SkillStats.new).print_stats # => Stealth # Driving # Intimidation -Hero.new(any_printer) -Hero.print_stats do |damage, health, skills| +Hero.new(any_printer).print_stats do |damage, health, skills| "Looks: I'm printing a customize message about my hero with damage #{damage} and number of skills: #{skills.size}" -end \ No newline at end of file +end