From 3c274d2b370a41551c8fd9cc899e147a37b6b4c7 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sun, 23 Jul 2017 23:13:52 -0500 Subject: [PATCH 1/8] wip: API compatibility with StringScanner --- spec/io_scanner_spec.cr | 275 ++++++++++++++++++ .../displacement/m_word_offset_spec.cr | 1 - src/typhar/io_scanner.cr | 123 ++++++-- 3 files changed, 373 insertions(+), 26 deletions(-) create mode 100644 spec/io_scanner_spec.cr diff --git a/spec/io_scanner_spec.cr b/spec/io_scanner_spec.cr new file mode 100644 index 0000000..d2925db --- /dev/null +++ b/spec/io_scanner_spec.cr @@ -0,0 +1,275 @@ +require "./spec_helper" + +describe Typhar::IOScanner, "#scan" do + it "returns the string matched and advances the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.scan(/\w+\s/).should eq("this ") + s.scan(/\w+\s/).should eq("is ") + s.scan(/\w+\s/).should eq("a ") + s.scan(/\w+/).should eq("string") + end + + it "returns nil if it can't match from the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("test string")) + s.scan(/\w+/).should_not be_nil # => "test" + s.scan(/\w+/).should be_nil + s.scan(/\s\w+/).should_not be_nil # => " string" + s.scan(/.*/).should_not be_nil # => "" + end +end + +describe Typhar::IOScanner, "#scan_until" do + it "returns the string matched and advances the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("test string")) + s.scan_until(/tr/).should eq("test str") + s.offset.should eq(8) + s.scan_until(/g/).should eq("ing") + end + + it "returns nil if it can't match from the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("test string")) + s.offset = 8 + s.scan_until(/tr/).should be_nil + end +end + +describe Typhar::IOScanner, "#skip" do + it "advances the offset but does not returns the string matched" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + + s.skip(/\w+\s/).should eq(5) + s.offset.should eq(5) + s[0]?.should_not be_nil + + s.skip(/\d+/).should eq(nil) + s.offset.should eq(5) + + s.skip(/\w+\s/).should eq(3) + s.offset.should eq(8) + + s.skip(/\w+\s/).should eq(2) + s.offset.should eq(10) + + s.skip(/\w+/).should eq(6) + s.offset.should eq(16) + end +end + +describe Typhar::IOScanner, "#skip_until" do + it "advances the offset but does not returns the string matched" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + + s.skip_until(/not/).should eq(nil) + s.offset.should eq(0) + s[0]?.should be_nil + + s.skip_until(/a\s/).should eq(10) + s.offset.should eq(10) + s[0]?.should_not be_nil + + s.skip_until(/ng/).should eq(6) + s.offset.should eq(16) + end +end + +describe Typhar::IOScanner, "#eos" do + it "it is true when the offset is at the end" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.eos?.should eq(false) + s.skip(/(\w+\s?){4}/) + s.eos?.should eq(true) + end +end + +describe Typhar::IOScanner, "#check" do + it "returns the string matched but does not advances the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.offset = 5 + + s.check(/\w+\s/).should eq("is ") + s.offset.should eq(5) + s.check(/\w+\s/).should eq("is ") + s.offset.should eq(5) + end + + it "returns nil if it can't match from the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("test string")) + s.check(/\d+/).should be_nil + end +end + +describe Typhar::IOScanner, "#check_until" do + it "returns the string matched and advances the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("test string")) + s.check_until(/tr/).should eq("test str") + s.offset.should eq(0) + s.check_until(/g/).should eq("test string") + s.offset.should eq(0) + end + + it "returns nil if it can't match from the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("test string")) + s.offset = 8 + s.check_until(/tr/).should be_nil + end +end + +describe Typhar::IOScanner, "#rest" do + it "returns the remainder of the string from the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.rest.should eq("this is a string") + + s.scan(/this is a /) + s.rest.should eq("string") + + s.scan(/string/) + s.rest.should eq("") + end +end + +describe Typhar::IOScanner, "#[]" do + it "allows access to subgroups of the last match" do + s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39")) + regex = /(?\w+) (?\w+) (?\d+)/ + s.scan(regex).should eq("Fri Dec 12") + s[0].should eq("Fri Dec 12") + s[1].should eq("Fri") + s[2].should eq("Dec") + s[3].should eq("12") + s["wday"].should eq("Fri") + s["month"].should eq("Dec") + s["day"].should eq("12") + end + + it "raises when there is no last match" do + s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39")) + s.scan(/this is not there/) + + expect_raises { s[0] } + end + + it "raises when there is no subgroup" do + s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39")) + regex = /(?\w+) (?\w+) (?\d+)/ + s.scan(regex) + + s[0].should_not be_nil + expect_raises { s[5] } + expect_raises { s["something"] } + end +end + +describe Typhar::IOScanner, "#[]?" do + it "allows access to subgroups of the last match" do + s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39")) + result = s.scan(/(?\w+) (?\w+) (?\d+)/) + + result.should eq("Fri Dec 12") + s[0]?.should eq("Fri Dec 12") + s[1]?.should eq("Fri") + s[2]?.should eq("Dec") + s[3]?.should eq("12") + s["wday"]?.should eq("Fri") + s["month"]?.should eq("Dec") + s["day"]?.should eq("12") + end + + it "returns nil when there is no last match" do + s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39")) + s.scan(/this is not there/) + + s[0]?.should be_nil + end + + it "raises when there is no subgroup" do + s = Typhar::IOScanner.new(::IO::Memory.new("Fri Dec 12 1975 14:39")) + s.scan(/(?\w+) (?\w+) (?\d+)/) + + s[0].should_not be_nil + s[5]?.should be_nil + s["something"]?.should be_nil + end +end + +describe Typhar::IOScanner, "#string" do + it { Typhar::IOScanner.new(::IO::Memory.new("foo")).string.should eq("foo") } +end + +describe Typhar::IOScanner, "#offset" do + it "returns the current position" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.offset.should eq(0) + s.scan(/\w+/) + s.offset.should eq(4) + end +end + +describe Typhar::IOScanner, "#offset=" do + it "sets the current position" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.offset = 5 + s.scan(/\w+/).should eq("is") + end + + it "raises on negative positions" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + expect_raises(IndexError) { s.offset = -2 } + end +end + +describe Typhar::IOScanner, "#inspect" do + it "has information on the scanner" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.inspect.should eq(%(#)) + s.scan(/\w+\s/) + s.inspect.should eq(%(#)) + s.scan(/\w+\s/) + s.inspect.should eq(%(#)) + s.scan(/\w+\s\w+/) + s.inspect.should eq(%(#)) + end + + it "works with small strings" do + s = Typhar::IOScanner.new(::IO::Memory.new("hi")) + s.inspect.should eq(%(#)) + s.scan(/\w\w/) + s.inspect.should eq(%(#)) + end +end + +describe Typhar::IOScanner, "#peek" do + it "shows the next len characters without advancing the offset" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.offset.should eq(0) + s.peek(4).should eq("this") + s.offset.should eq(0) + s.peek(7).should eq("this is") + s.offset.should eq(0) + end +end + +describe Typhar::IOScanner, "#reset" do + it "resets the scan offset to the beginning and clears the last match" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.scan_until(/str/) + s[0]?.should_not be_nil + s.offset.should_not eq(0) + + s.reset + s[0]?.should be_nil + s.offset.should eq(0) + end +end + +describe Typhar::IOScanner, "#terminate" do + it "moves the scan offset to the end of the string and clears the last match" do + s = Typhar::IOScanner.new(::IO::Memory.new("this is a string")) + s.scan_until(/str/) + s[0]?.should_not be_nil + s.eos?.should eq(false) + + s.terminate + s[0]?.should be_nil + s.eos?.should eq(true) + end +end \ No newline at end of file diff --git a/spec/strategies/displacement/m_word_offset_spec.cr b/spec/strategies/displacement/m_word_offset_spec.cr index 0a5f85b..e232c58 100644 --- a/spec/strategies/displacement/m_word_offset_spec.cr +++ b/spec/strategies/displacement/m_word_offset_spec.cr @@ -14,7 +14,6 @@ describe Typhar::DisplacementStrategies::MWordOffset do it "adds the configured offset to the StringScanner#offset" do m_word_offsetter.advance_to_next!(source_text_scanner) - source_text_scanner.last_match.not_nil![0].should eq("test") source_text_scanner.offset.should eq(16) end end diff --git a/src/typhar/io_scanner.cr b/src/typhar/io_scanner.cr index b70bc47..91d3d17 100644 --- a/src/typhar/io_scanner.cr +++ b/src/typhar/io_scanner.cr @@ -1,6 +1,7 @@ module Typhar class IOScanner @last_match : ::Regex::MatchData? + @line = "" @line_offset = 0 @line_start_offset : Int32 | Int64 = 0 @line_size = 0 @@ -12,32 +13,68 @@ module Typhar def initialize(@io : Typhar::IO) end - def initialize(io : IOScanner) - @io = io.io - @last_match = io.last_match + def [](index) + @last_match.not_nil![index] end - def scan_until(pattern) - last_match = @last_match + def []?(index) + @last_match.try(&.[index]?) + end - if last_match - last_match = scan_next(pattern, last_match.post_match) - end + def eos? + io.closed? || offset >= size + end - unless last_match - each_line { |line| break if scan_next(pattern, line) } - end + def peek(*args) + end - @last_match + def rest + io.gets_to_end end - def each_line - io.each_line do |line| - @line_start_offset = io.pos - line.bytesize - @line_offset = 0 - @line_size = line.size - yield line - end + def string + @line + end + + def reset + @line = "" + @line_offset = 0 + @line_start_offset = 0 + @line_size = 0 + @last_match = nil + io.rewind + end + + def terminate + @last_match = nil + @line_offset = @line_size + io.close + end + + def check(pattern) + match(pattern, advance: false, options: Regex::Options::ANCHORED) + end + + def check_until(pattern) + match(pattern, advance: false, options: Regex::Options::None) + end + + def skip(pattern : Regex) + match = scan(pattern) + match.size if match + end + + def skip_until(pattern) + match = scan_until(pattern) + match.size if match + end + + def scan(pattern) + match(pattern, advance: true, options: Regex::Options::ANCHORED) + end + + def scan_until(pattern) + match(pattern, advance: true, options: Regex::Options::None) end def offset @@ -51,6 +88,11 @@ module Typhar end end + def offset=(position) + raise IndexError.new unless position >= 0 + io.pos = position + end + def pos offset end @@ -68,20 +110,51 @@ module Typhar stream << "#" end - private def scan_next(pattern, str) - if m = pattern.match(str) - @line_offset += m.string.bytesize - m.post_match.size - @last_match = m + private def match(pattern, advance = true, options = Regex::Options::ANCHORED) + last_match = @last_match + last_match_str = nil + + if last_match + last_match_str = line_match(pattern, advance: true, options: Regex::Options::None) + end + + unless last_match + each_line(advance) do |line| + @line = line + last_match_str = line_match(pattern, advance: true, options: Regex::Options::None) + break if last_match_str + end + end + + last_match_str + end + + private def line_match(pattern, advance = true, options = Regex::Options::ANCHORED) + match = pattern.match_at_byte_index(@line, @line_offset, options) + if match + start = @line_offset + new_byte_offset = match.byte_end(0).to_i + @line_offset = new_byte_offset if advance + + @last_match = match + @line.byte_slice(start, new_byte_offset - start) else @last_match = nil end + end - @last_match + private def each_line(advance = true) + io.each_line do |line| + @line_start_offset = io.pos - line.bytesize + @line_offset = 0 + @line_size = line.size + yield line + end end end end From 8a9bef5c08aec237a26198ddafa5a69532dea26b Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Mon, 24 Jul 2017 20:32:53 -0500 Subject: [PATCH 2/8] Add peek, rest, string, and pos= --- src/typhar/io_scanner.cr | 43 +++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/typhar/io_scanner.cr b/src/typhar/io_scanner.cr index 91d3d17..a915c33 100644 --- a/src/typhar/io_scanner.cr +++ b/src/typhar/io_scanner.cr @@ -25,23 +25,20 @@ module Typhar io.closed? || offset >= size end - def peek(*args) + def peek(len) + @line[offset, len] end def rest - io.gets_to_end + @line[@line_offset, @line_size] + io.gets_to_end end def string - @line + @line + io.gets_to_end end def reset - @line = "" - @line_offset = 0 - @line_start_offset = 0 - @line_size = 0 - @last_match = nil + reset_line_match! io.rewind end @@ -90,6 +87,8 @@ module Typhar def offset=(position) raise IndexError.new unless position >= 0 + reset_line_match! + @line_start_offset = position io.pos = position end @@ -97,6 +96,10 @@ module Typhar offset end + def pos=(position) + self.offset = position + end + def size case _io = io when ::IO::FileDescriptor @@ -107,26 +110,26 @@ module Typhar end def inspect(stream : ::IO) - stream << "#" end - private def match(pattern, advance = true, options = Regex::Options::ANCHORED) + private def match(pattern, **kwargs) last_match = @last_match last_match_str = nil if last_match - last_match_str = line_match(pattern, advance: true, options: Regex::Options::None) + last_match_str = line_match(pattern, **kwargs) end unless last_match - each_line(advance) do |line| + each_line do |line| @line = line - last_match_str = line_match(pattern, advance: true, options: Regex::Options::None) + last_match_str = line_match(pattern, **kwargs) break if last_match_str end end @@ -148,7 +151,7 @@ module Typhar end end - private def each_line(advance = true) + private def each_line io.each_line do |line| @line_start_offset = io.pos - line.bytesize @line_offset = 0 @@ -156,5 +159,13 @@ module Typhar yield line end end + + private def reset_line_match! + @last_match = nil + @line_start_offset = 0 + @line_offset = 0 + @line = "" + @line_size = 0 + end end end From fa175bc5bfe4a405b09093eff070aa62ac18a9b6 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Mon, 24 Jul 2017 21:34:37 -0500 Subject: [PATCH 3/8] Refactoring around maintaining line state --- src/typhar/io_scanner.cr | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/typhar/io_scanner.cr b/src/typhar/io_scanner.cr index a915c33..3afebe1 100644 --- a/src/typhar/io_scanner.cr +++ b/src/typhar/io_scanner.cr @@ -4,7 +4,6 @@ module Typhar @line = "" @line_offset = 0 @line_start_offset : Int32 | Int64 = 0 - @line_size = 0 getter io getter last_match @@ -30,7 +29,7 @@ module Typhar end def rest - @line[@line_offset, @line_size] + io.gets_to_end + rest_of_line + io.gets_to_end end def string @@ -44,7 +43,7 @@ module Typhar def terminate @last_match = nil - @line_offset = @line_size + @line_offset = line_size io.close end @@ -75,11 +74,8 @@ module Typhar end def offset - line_start_offset = @line_start_offset - line_offset = @line_offset - - if line_start_offset > 0 || line_offset > 0 - line_start_offset + line_offset + if has_offset? + @line_start_offset + @line_offset else io.pos end @@ -118,6 +114,18 @@ module Typhar stream << ">" end + private def has_offset? + @line_start_offset > 0 || @line_offset > 0 + end + + private def rest_of_line + @line[@line_offset, line_size] + end + + private def line_size + @line.size + end + private def match(pattern, **kwargs) last_match = @last_match last_match_str = nil @@ -128,7 +136,6 @@ module Typhar unless last_match each_line do |line| - @line = line last_match_str = line_match(pattern, **kwargs) break if last_match_str end @@ -155,7 +162,7 @@ module Typhar io.each_line do |line| @line_start_offset = io.pos - line.bytesize @line_offset = 0 - @line_size = line.size + @line = line yield line end end @@ -165,7 +172,6 @@ module Typhar @line_start_offset = 0 @line_offset = 0 @line = "" - @line_size = 0 end end end From b0985cc1ea78c90608750050a7bc54794d4f4b39 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Mon, 24 Jul 2017 23:37:14 -0500 Subject: [PATCH 4/8] Should probably close the file after opening it, huh? --- src/typhar/cli.cr | 20 +++++++++++++------- src/typhar/transformer.cr | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/typhar/cli.cr b/src/typhar/cli.cr index dc053ab..cdb1b18 100644 --- a/src/typhar/cli.cr +++ b/src/typhar/cli.cr @@ -49,16 +49,22 @@ module Typhar def run plaintext_scanner = ::IO::Memory.new(args.message) - source_file = File.open(args.source_text_file) displacement_strategy = options.displacement_strategy replacement_strategy = options.replacement_strategy - transformer = Typhar::Transformer.new( - plaintext_scanner, - source_file, - displacement_strategy_for(displacement_strategy, plaintext_scanner, options), - replacement_strategy_for(replacement_strategy, options) - ).transform + begin + source_file = File.open(args.source_text_file) + + transformer = Typhar::Transformer.new( + plaintext_scanner, + source_file, + displacement_strategy_for(displacement_strategy, plaintext_scanner, options), + replacement_strategy_for(replacement_strategy, options) + ).transform(STDOUT) + puts + ensure + source_file.close if source_file + end end private def displacement_strategy_for(strategy, plaintext_scanner, options) diff --git a/src/typhar/transformer.cr b/src/typhar/transformer.cr index 9044fda..f5602b2 100644 --- a/src/typhar/transformer.cr +++ b/src/typhar/transformer.cr @@ -13,7 +13,7 @@ module Typhar ) end - def transform(io : ::IO = STDOUT) : ::IO + def transform(io) : ::IO current_offset = 0 source_scanner = IOScanner.new(@source_stream) From 5e18e81661d275207eab8902030c5e9e69ba3e3c Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Mon, 24 Jul 2017 23:39:08 -0500 Subject: [PATCH 5/8] IOScanner: read the first line from IO on initialize --- src/typhar/io_scanner.cr | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/typhar/io_scanner.cr b/src/typhar/io_scanner.cr index 3afebe1..10a48ad 100644 --- a/src/typhar/io_scanner.cr +++ b/src/typhar/io_scanner.cr @@ -10,6 +10,7 @@ module Typhar forward_missing_to io def initialize(@io : Typhar::IO) + @line = @io.read_line end def [](index) @@ -74,11 +75,7 @@ module Typhar end def offset - if has_offset? - @line_start_offset + @line_offset - else - io.pos - end + @line_start_offset + @line_offset end def offset=(position) @@ -108,9 +105,7 @@ module Typhar def inspect(stream : ::IO) stream << "#" end @@ -127,14 +122,13 @@ module Typhar end private def match(pattern, **kwargs) - last_match = @last_match last_match_str = nil - if last_match + if line = @line last_match_str = line_match(pattern, **kwargs) end - unless last_match + unless last_match_str each_line do |line| last_match_str = line_match(pattern, **kwargs) break if last_match_str From 600ab3bd0ab425059140e19b43dc947ccf397eb4 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Tue, 25 Jul 2017 20:50:42 -0500 Subject: [PATCH 6/8] dependency update --- shard.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shard.lock b/shard.lock index 6c95fd4..26ef7e6 100644 --- a/shard.lock +++ b/shard.lock @@ -2,17 +2,17 @@ version: 1.0 shards: callback: github: mosop/callback - version: 0.6.2 + version: 0.6.3 cli: github: mosop/cli - version: 0.6.9 + version: 0.6.10 optarg: github: mosop/optarg - version: 0.5.7 + version: 0.5.8 string_inflection: github: mosop/string_inflection - version: 0.2.0 + version: 0.2.1 From b68bcbc5b4c07ba672828fcbfc5345c75ad23f00 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sun, 6 Aug 2017 21:05:37 -0500 Subject: [PATCH 7/8] Fix remaining failing test cases for StringScanner compatiblility --- src/typhar.cr | 1 - src/typhar/io_scanner.cr | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typhar.cr b/src/typhar.cr index d204bf8..1749e37 100644 --- a/src/typhar.cr +++ b/src/typhar.cr @@ -1,6 +1,5 @@ require "cli" require "secure_random" -require "string_scanner" require "./typhar/*" module Typhar diff --git a/src/typhar/io_scanner.cr b/src/typhar/io_scanner.cr index 10a48ad..dca97f4 100644 --- a/src/typhar/io_scanner.cr +++ b/src/typhar/io_scanner.cr @@ -105,7 +105,8 @@ module Typhar def inspect(stream : ::IO) stream << "#" end From 3711112aa22518cca28d219c4e24aad9753d1a02 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sun, 6 Aug 2017 21:08:29 -0500 Subject: [PATCH 8/8] Unify seed values for displacement and replacement --- src/typhar/cli.cr | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/typhar/cli.cr b/src/typhar/cli.cr index cdb1b18..2f811d3 100644 --- a/src/typhar/cli.cr +++ b/src/typhar/cli.cr @@ -13,6 +13,8 @@ module Typhar end class Encode < ::Cli::Command + @seed : UInt32? + class Options arg "source_text_file", required: true, desc: "source text file" arg "message", required: true, desc: "message" @@ -47,29 +49,23 @@ module Typhar caption "encode message" end - def run + def run(io = STDOUT) plaintext_scanner = ::IO::Memory.new(args.message) displacement_strategy = options.displacement_strategy replacement_strategy = options.replacement_strategy + source_file = File.open(args.source_text_file) - begin - source_file = File.open(args.source_text_file) - - transformer = Typhar::Transformer.new( - plaintext_scanner, - source_file, - displacement_strategy_for(displacement_strategy, plaintext_scanner, options), - replacement_strategy_for(replacement_strategy, options) - ).transform(STDOUT) - puts - ensure - source_file.close if source_file - end + transformer = Typhar::Transformer.new( + plaintext_scanner, + source_file, + displacement_strategy_for(displacement_strategy, plaintext_scanner, options), + replacement_strategy_for(replacement_strategy, options) + ).transform(io) + ensure + source_file.close if source_file end private def displacement_strategy_for(strategy, plaintext_scanner, options) - seed = options.seed.empty? ? generate_seed : options.seed.to_u32 - case strategy when "word-offset" word_offset = options.word_offset.to_i @@ -83,8 +79,6 @@ module Typhar end private def replacement_strategy_for(strategy, options) - seed = options.seed.empty? ? generate_seed : options.seed.to_u32 - case strategy when "n-shifter" codepoint_shift = options.codepoint_shift.to_i @@ -94,8 +88,14 @@ module Typhar end end + private def seed + @seed ||= options.seed.empty? ? generate_seed : options.seed.to_u32 + end + private def generate_seed - SecureRandom.hex(4).to_u32(16) + s = SecureRandom.hex(4).to_u32(16) + STDERR.puts "INFO: Using #{s} as seed" + s end end