From 8700f307a7d1efd5d0bff0ab96b16385fd8bb10b Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Wed, 18 Oct 2017 23:46:00 -0400 Subject: [PATCH 1/8] sapling.rb: Rubocop and refactor updates - Fix several rubocop violations - Update functionality to account for gardner refactor --- lib/sapling.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/sapling.rb b/lib/sapling.rb index 2025207..eef7142 100644 --- a/lib/sapling.rb +++ b/lib/sapling.rb @@ -8,19 +8,20 @@ Dir[File.join(__dir__, 'sapling', '*.rb')].each { |file| require file } # The main Sapling interface. class Sapling < Thor desc 'read TREE', 'Load and traverse the TREE' - def read(tree) - exit unless verify_tree(tree) + def read(file) puts 'Welcome to Sapling, a Dialogue Tree Utility.' - speaker = Dialogue::Speaker.new(YAML.load_file(tree), false) + exit unless verify_tree(file) + tree = Gardner::Plot.new(YAML.load_file(file), false) + speaker = Dialogue::Speaker.new(tree) speaker.conversation end desc 'edit TREE', 'Edit a new or existing TREE' - def edit(tree = '') + def edit(file = '') puts 'Welcome to Sapling, a Dialogue Tree Utility.' if !tree.empty? - puts "Loading tree: #{tree}" - exit unless verify_tree(tree) + puts "Loading tree: #{file}" + exit unless verify_tree(file) gardner = Planter::Spade.new(YAML.load_file(tree, false)) else puts 'Creating a new tree!' From 6e16b250a5839765437290f4021f43aae5306e8c Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Wed, 18 Oct 2017 23:46:52 -0400 Subject: [PATCH 2/8] gardner.rb: Rubocop fixes and refactor changes - Fix several rubocop problems - Update module to more fully encapsulate all dialogue tree handling - Add Plot class for storing all tree data - Add Digiplot class for (eventual) tree editing --- lib/sapling/gardner.rb | 116 +++++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/lib/sapling/gardner.rb b/lib/sapling/gardner.rb index e6184bf..1512076 100644 --- a/lib/sapling/gardner.rb +++ b/lib/sapling/gardner.rb @@ -2,47 +2,95 @@ require 'yaml' # Gardner is the module for working with a dialogue tree file module Gardner + # The Plot class handles a specific tree file. It provides functionality for + # parsing trunks and branches, and provides these as class attributes. + class Plot + # The trunk and branches instance variables + attr_reader :branches, :tree, :trunk - # Parse the tree array into an array of numbered branches, and ordered leaves. - # - # @param tree [Array] The dialogue tree - # @return [Array] An array of numbered branches, with numbered leaves - def self.prune_branches(tree) - branches = { 0 => { "desc" => "Thanks for using Sapling!" } } - tree.each do |b| - branches[b["branch"]["number"]] = { - "desc" => b["branch"]["text"], - "options" => prune_leaves(b["branch"]["leaf"]) } + # Initialize a new Plot from a tree file + # + # @param tree [File] The dialogue tree file + def initialize(file) + @tree = file + prune_trunk + prune_branches end - return branches - end - - # Parse the leaves of a branch into a numbered hash of options. - # - # @param leaves [Array] The option of leaf hashes - # @return [Hash] A numbered hash of options - def self.prune_leaves(leaves) - x = 1 - options = {} - - return options if leaves.nil? - - leaves.each do |l| - options[x] = { l["text"] => l["branch"] } - x += 1 + # Parse the tree array into an array of numbered branches, and ordered + # leaves. + # + # @param tree [File] The dialogue tree + # @return [Array] An array of numbered branches, with numbered leaves + def prune_branches + @branches = { 0 => { 'desc' => 'Thanks for using Sapling!' } } + @tree.each do |b| + @branches[b['branch']['number']] = { + 'desc' => b['branch']['text'], + 'options' => prune_leaves(b['branch']['leaf']) + } + end end - return options + # Parse the leaves of a branch into a numbered hash of options. + # + # @param leaves [Array] The option of leaf hashes + # @return [Hash] A numbered hash of options + def prune_leaves(leaves) + x = 1 + options = {} + + return options if leaves.nil? + + leaves.each do |l| + options[x] = { l['text'] => l['branch'] } + x += 1 + end + + options + end + + # Parse the trunk of the tree. + # + # @return [Array] The trunk, and the remainder of the tree + def prune_trunk + @trunk = @tree.shift + end end - # Parse the trunk of the tree. - # - # @param tree [Hash] The entire tree - # @return [Array] The trunk, and the remainder of the tree - def self.prune_trunk(tree) - trunk = tree.shift + # Digiplot represents a Plot used for editing. The Digiplot functions exactly + # like a Plot, except with additional functionality for over-writing existing + # branches, leaves, and the trunk. + class Digiplot < Plot + # Duplicate the "old" trunk and branches, for restoration purposes + attr_reader :old_branches, :old_trunk - return [trunk,tree] + # Enable editing for the trunk + attr_writer :trunk + + # Initialize a Digiplot just like a Plot, but also copy the trunk and + # branches to "old" instance variables. + def initialize + super + @old_trunk = @trunk + @old_branches = @branches + end + + # Change a branch + # + # @param branch [Integer] the number of the branch to be edited + def branch=(branch, text) + @branches[branch]['desc'] = text + end + + # Change a leaf on a branch, grasshopper + # + # @param branch [Integer] the number of the branch to be edited + # @param leaf [Integer] the number of the leaf to be edited + # @param text [String] the new text for the leaf + # @param target [Integer] the branch number target for the leaf option + def leaf=(branch, leaf, text, target) + @branches[branch]['options'][leaf] = { text => target } + end end end From e85a8828511b4d888836a56c0a5005d9f396a446 Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Thu, 19 Oct 2017 00:19:14 -0400 Subject: [PATCH 3/8] dialogue.rb: Rubocop fixes and refactor changes - Fix several Rubocop problems - Update module for gardner refactor - Update all tree references to now reference Gardner::Plot object --- lib/sapling/dialogue.rb | 76 +++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/lib/sapling/dialogue.rb b/lib/sapling/dialogue.rb index 5ccc010..67fc10f 100644 --- a/lib/sapling/dialogue.rb +++ b/lib/sapling/dialogue.rb @@ -2,16 +2,15 @@ require_relative './gardner' # Dialogue is the module for traversing an existing tree. module Dialogue - # Format and display the trunk # # @param trunk [Hash] The trunk hash # @param debug [Boolean] The status of showing debug information - def self.display_trunk(trunk, debug=false) - 40.times { print "-" } + def self.display_trunk(trunk, debug = false) + 40.times { print '-' } puts "\n[ Trunk ]\n" if debug - puts "\n#{trunk["trunk"]}" - 40.times { print "-" } + puts "\n#{trunk['trunk']}" + 40.times { print '-' } puts "\n" end @@ -20,11 +19,11 @@ module Dialogue # @param branch [Hash] A branch data set # @param branch_no [Integer] The branch number # @param debug [Boolean] Status of showing debug information - def self.display_branch(branch, branch_no, debug=false) + def self.display_branch(branch, branch_no, debug = false) puts "\n[ Branch: #{branch_no} ]" if debug - puts "\n#{branch["desc"]}\n\n" + puts "\n#{branch['desc']}\n\n" - branch["options"].each_pair do |k,v| + branch['options'].each_pair do |k, v| puts "\t#{k}: #{v.keys[0]}" puts "\t\t[ Goes to branch #{v.values[0]} ]\n" if debug end @@ -32,30 +31,27 @@ module Dialogue # Speaker holds the functionality for going through a dialogue tree. class Speaker - # The file, which should be a dialogue tree YAML file. - attr_accessor :file + # The tree, an instance of Gardner::Plot + attr_reader :tree # Status of verbose/debug mode. True = on; false = off. - attr_accessor :debug + attr_reader :debug - def initialize(file="", debug=false) - @file = file + def initialize(tree, debug = false) + @tree = tree @debug = debug end # Conversation handles navigating the tree, until the option to end is # reached. - def conversation() - tree = Gardner.prune_trunk(@file) - - Dialogue.display_trunk(tree[0], false) - branches = Gardner.prune_branches(tree[1]) + def conversation + Dialogue.display_trunk(@tree.trunk, @debug) next_branch = 1 - until next_branch == 0 do - next_branch = talk(branches[next_branch], next_branch) + until next_branch.zero? + next_branch = talk(@tree.branches[next_branch], next_branch) end - puts "\n#{branches[0]["desc"]}" + puts "\n#{@tree.branches[0]['desc']}" exit end @@ -65,25 +61,18 @@ module Dialogue # @param branch_no [Integer] The branch number # @return [Integer] The number of the next branch def talk(branch, branch_no) - # If there are no options on this branch, we assume it's a terminal - # branch. Return 0, and end the program. - if branch["options"].empty? - puts "\n#{branch["desc"]}\n\n" - return 0 - end + return 0 if terminal?(branch) Dialogue.display_branch(branch, branch_no, @debug) response = get_response(branch) - unless response == 0 - puts "\n" - 10.times { print "*" } - puts "\n(Your choice: #{branch["options"][response].keys[0]})" - response = branch["options"][response].values[0].to_i + unless response.zero? + puts "(Your choice: #{branch['options'][response].keys[0]})" + response = branch['options'][response].values[0].to_i end - return response + response end # Get a response for the displayed branch @@ -91,20 +80,33 @@ module Dialogue # @param branch [Hash] A branch data set # @return [Integer] the next branch def get_response(branch) - valid_options = branch["options"].keys.join(", ") + valid_options = branch['options'].keys.join(', ') print "\n[#{valid_options}]> " STDOUT.flush response = STDIN.gets.chomp.to_i - until branch["options"].keys.include?(response) or response == 0 - print "[## Invalid options. " + until branch['options'].keys.include?(response) || response.zero? + print '[## Invalid options. ' print "Valid options are #{valid_options}, or 0 to exit." print "\n[#{valid_options}]> " response = STDIN.gets.chomp.to_i end - return response + response + end + + # Check if a branch is terminal + # + # @param branch [Hash] A branch data set + # @return [Boolean] true if the branch is terminal, false otherwise + def terminal?(branch) + if branch['options'].empty? + puts "\n#{branch['desc']}\n\n" + return true + end + + false end end end From bd45e60bb7b69ef89b5d9bf8248446b446891a7d Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Thu, 19 Oct 2017 00:20:21 -0400 Subject: [PATCH 4/8] utility.rb: Rubocop fixes --- lib/sapling/utility.rb | 44 ++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/sapling/utility.rb b/lib/sapling/utility.rb index 576122a..fa5af53 100644 --- a/lib/sapling/utility.rb +++ b/lib/sapling/utility.rb @@ -7,34 +7,32 @@ # documentation. # The default trunk text of a new tree -SKELE_TRUNK_TEXT = "Welcome to the Sapling Editor. For details, please see the -documentation!" +SKELE_TRUNK_TEXT = 'Welcome to the Sapling Editor. For details, please see the +documentation!'.freeze # The default first-branch text of a new tree -SKELE_BRANCH_TEXT = "The first branch is always shown by default. It should act -as the introduction to the story. From here, the user enters your world!" +SKELE_BRANCH_TEXT = 'The first branch is always shown by default. It should act +as the introduction to the story. From here, the user enters your world!'.freeze # The default first-leaf text of the first branch of a new tree. The leaf points # to it's own branch. The only way out of the program is to either force-quit or # reply with option 0. -SKELE_LEAF_TEXT = "Each branch can have any number of leaves, which represent +SKELE_LEAF_TEXT = 'Each branch can have any number of leaves, which represent the options a user has on that branch. Each leaf points to another branch, or -can point to branch 0 to immediately exit." +can point to branch 0 to immediately exit.'.freeze # The final tree SKELETON_TREE = [ - {"trunk" => "#{SKELE_TRUNK_TEXT}"}, - {"branch" => { - "number" => 1, - "text" => "#{SKELE_BRANCH_TEXT}", - "leaf" => [{ - "text" => "#{SKELE_LEAF_TEXT}", - "branch" => 1 - }] - } - } -] - + { 'trunk' => SKELE_TRUNK_TEXT.to_s }, + { 'branch' => { + 'number' => 1, + 'text' => SKELE_BRANCH_TEXT.to_s, + 'leaf' => [{ + 'text' => SKELE_LEAF_TEXT.to_s, + 'branch' => 1 + }] + } } +].freeze # Verify that a file is a dialogue tree file. # @@ -44,13 +42,13 @@ def verify_tree(file) results = [] begin tree = YAML.load_file(file) - results << tree[0].keys.include?("trunk") - results << tree[1]["branch"].keys.include?("number") - results << tree[1]["branch"].keys.include?("text") - results << tree[1]["branch"].keys.include?("leaf") + results << tree[0].keys.include?('trunk') + results << tree[1]['branch'].keys.include?('number') + results << tree[1]['branch'].keys.include?('text') + results << tree[1]['branch'].keys.include?('leaf') rescue puts "Sorry chummer, I don't think this is a tree." - puts "Verify your YAML file is formatted properly." + puts 'Verify your YAML file is formatted properly.' results << false end From 54f23625a85009228b373d3a5cc59b1f7aa3a537 Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Thu, 19 Oct 2017 00:21:03 -0400 Subject: [PATCH 5/8] sapling.rb: Fix argument passing Accidentially moved the debug argument to the Gardner invokation, instead of leaving with the Dialogue invokation. --- lib/sapling.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sapling.rb b/lib/sapling.rb index eef7142..901094b 100644 --- a/lib/sapling.rb +++ b/lib/sapling.rb @@ -11,8 +11,8 @@ class Sapling < Thor def read(file) puts 'Welcome to Sapling, a Dialogue Tree Utility.' exit unless verify_tree(file) - tree = Gardner::Plot.new(YAML.load_file(file), false) - speaker = Dialogue::Speaker.new(tree) + tree = Gardner::Plot.new(YAML.load_file(file)) + speaker = Dialogue::Speaker.new(tree, false) speaker.conversation end From c6c9522a1840324198f46e9b1cc7145b428e6051 Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Thu, 19 Oct 2017 00:26:35 -0400 Subject: [PATCH 6/8] Update gem --- sapling-dialogue.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sapling-dialogue.gemspec b/sapling-dialogue.gemspec index 99c9d8d..d48fac4 100644 --- a/sapling-dialogue.gemspec +++ b/sapling-dialogue.gemspec @@ -1,10 +1,10 @@ Gem::Specification.new do |s| s.name = 'sapling-dialogue' - s.version = '0.1.0' + s.version = '0.1.1' s.executables << 'sapling' - s.date = '2017-10-14' + s.date = '2017-10-19' s.summary = 'A Dialogue Tree Utility' - s.description = 'Create, edit, and traverse Dialogue trees' + s.description = 'Create, edit, and traverse dialogue trees' s.authors = ['Bill Niblock'] s.email = 'azulien@gmail.com' s.files = Dir['lib/**/*.rb'] + Dir['bin/*'] From ce2eb89b4487781f604fa7104782603f7219f774 Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Thu, 19 Oct 2017 00:44:41 -0400 Subject: [PATCH 7/8] dialogue.rb: Rubocop ABC fixes --- lib/sapling/dialogue.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/sapling/dialogue.rb b/lib/sapling/dialogue.rb index 67fc10f..f981f52 100644 --- a/lib/sapling/dialogue.rb +++ b/lib/sapling/dialogue.rb @@ -84,16 +84,15 @@ module Dialogue print "\n[#{valid_options}]> " STDOUT.flush - response = STDIN.gets.chomp.to_i + response = STDIN.gets.chomp - until branch['options'].keys.include?(response) || response.zero? - print '[## Invalid options. ' - print "Valid options are #{valid_options}, or 0 to exit." - print "\n[#{valid_options}]> " - response = STDIN.gets.chomp.to_i + until valid_options.include?(response) || response.to_i.zero? + print "[## Invalid options. Valid options are #{valid_options}," \ + "or 0 to exit.\n[#{valid_options}]> " + response = STDIN.gets.chomp end - response + response.to_i end # Check if a branch is terminal From a85673673959c575624634407a33817e0e05023f Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Thu, 19 Oct 2017 00:48:15 -0400 Subject: [PATCH 8/8] Update Gem -> Version 0.1.2 --- sapling-dialogue.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapling-dialogue.gemspec b/sapling-dialogue.gemspec index d48fac4..3aebab3 100644 --- a/sapling-dialogue.gemspec +++ b/sapling-dialogue.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'sapling-dialogue' - s.version = '0.1.1' + s.version = '0.1.2' s.executables << 'sapling' s.date = '2017-10-19' s.summary = 'A Dialogue Tree Utility'