succubus 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -1
- data/Rakefile +3 -1
- data/lib/succubus.rb +35 -69
- data/lib/succubus/generator.rb +82 -0
- data/lib/succubus/grammar.rb +79 -0
- data/lib/succubus/result.rb +39 -0
- data/lib/succubus/version.rb +2 -1
- data/spec/grammar_spec.rb +23 -9
- data/spec/spec_helper.rb +27 -0
- data/test/support/fixed_random.rb +23 -24
- metadata +36 -10
- data/test/support/subset_asserts.rb +0 -49
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
_A random generator based on a generalised Backus-Naur Form grammar_
|
2
2
|
|
3
|
-
[![Build Status](https://travis-ci.org/asilano/succubus.png?branch=master)](https://travis-ci.org/asilano/succubus)
|
3
|
+
[![Build Status](https://travis-ci.org/asilano/succubus.png?branch=master)](https://travis-ci.org/asilano/succubus)[![Coverage Status](https://coveralls.io/repos/asilano/succubus/badge.png?branch=master)](https://coveralls.io/r/asilano/succubus)
|
4
4
|
|
5
5
|
**Succubus** is a generator which takes stochastic paths through a
|
6
6
|
generalised Backus-Naur Form grammar to produce random text. For instance, the following:
|
data/Rakefile
CHANGED
data/lib/succubus.rb
CHANGED
@@ -1,77 +1,43 @@
|
|
1
|
+
require 'succubus/grammar'
|
2
|
+
require 'succubus/generator'
|
3
|
+
require 'succubus/result'
|
4
|
+
|
5
|
+
# @author Chris Howlett
|
1
6
|
module Succubus
|
7
|
+
# Raised by {Grammar#initialize Grammar.new} if the grammar defined in the
|
8
|
+
# supplied block cannot be parsed.
|
9
|
+
#
|
10
|
+
# @!attribute [r] errors
|
11
|
+
# @return [Array<String>] the errors encountered during parsing
|
2
12
|
class ParseError < RuntimeError
|
3
|
-
|
4
|
-
end
|
5
|
-
|
6
|
-
class ExecuteError < RuntimeError
|
7
|
-
attr_accessor :errors, :partial
|
8
|
-
end
|
9
|
-
|
10
|
-
class Grammar
|
11
|
-
def initialize(&block)
|
12
|
-
@rules = {}
|
13
|
-
@errors = []
|
14
|
-
define_singleton_method(:create, block)
|
15
|
-
create
|
16
|
-
class << self ; undef_method :create ; end
|
17
|
-
|
18
|
-
unless @errors.empty?
|
19
|
-
pe = ParseError.new("Errors found parsing")
|
20
|
-
pe.errors = @errors
|
21
|
-
raise pe
|
22
|
-
end
|
23
|
-
end
|
13
|
+
attr_reader :errors
|
24
14
|
|
25
|
-
|
26
|
-
|
27
|
-
@errors
|
28
|
-
@rules[name] = choices
|
29
|
-
end
|
30
|
-
|
31
|
-
def execute(start)
|
32
|
-
gen = Generator.new(@rules)
|
33
|
-
gen.run(start)
|
34
|
-
|
35
|
-
unless gen.errors.empty?
|
36
|
-
ee = ExecuteError.new("Errors found executing")
|
37
|
-
ee.errors = gen.errors
|
38
|
-
ee.partial = gen.result
|
39
|
-
raise ee
|
40
|
-
end
|
41
|
-
|
42
|
-
gen.result
|
15
|
+
# @private
|
16
|
+
def set_errors(errors)
|
17
|
+
@errors = errors
|
43
18
|
end
|
44
19
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
20
|
+
|
21
|
+
# Raised by {Grammar#execute} if the {Generator} encountered any errors
|
22
|
+
# while executing the grammar.
|
23
|
+
#
|
24
|
+
# @!attribute [r] errors
|
25
|
+
# @return [Array<String>] the errors encountered during execution
|
26
|
+
# @!attribute [r] partial
|
27
|
+
# @return [Result] as much of the result as the {Generator} was able
|
28
|
+
# to produce. Typically, this will be a string with missing rules
|
29
|
+
# surrounded by +!!missing!!+. For instance, +"My !!pet!! is nice"+
|
30
|
+
class ExecuteError < RuntimeError
|
31
|
+
attr_reader :errors, :partial
|
32
|
+
|
33
|
+
# @private
|
34
|
+
def set_errors(errors)
|
35
|
+
@errors = errors
|
58
36
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
return "!!#{rule}!!"
|
64
|
-
end
|
65
|
-
|
66
|
-
local_res = ""
|
67
|
-
@rules[rule].sample.scan(/(.*?(?<!\\)(?:\\\\)*)(<.*?>|$)/) do |match|
|
68
|
-
local_res << match[0]
|
69
|
-
unless match[1].empty?
|
70
|
-
local_res << invoke(match[1].match(/<(.*?)>/)[1].to_sym)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
return local_res
|
37
|
+
|
38
|
+
# @private
|
39
|
+
def set_partial(partial)
|
40
|
+
@partial = partial
|
75
41
|
end
|
76
42
|
end
|
77
|
-
end
|
43
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Succubus
|
2
|
+
# Class which handles running through a {Grammar} and producing a
|
3
|
+
# {Result} string.
|
4
|
+
#
|
5
|
+
# @private
|
6
|
+
class Generator
|
7
|
+
|
8
|
+
# Run through a given {Grammar}, starting at a given rule. Optionally
|
9
|
+
# use a given random seed.
|
10
|
+
#
|
11
|
+
# @param [Grammar] grammar the {Grammar} to generate a string from
|
12
|
+
# @param [Symbol] rule the name of the rule to start generation from
|
13
|
+
# @param [Integer] seed optional random seed. Defaults to +nil+, which will
|
14
|
+
# make the {Generator} use a random seed
|
15
|
+
def self.run(grammar, rule, seed=nil)
|
16
|
+
new(grammar, seed).run(rule)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new {Generator}, ready to run through the supplied {Grammar},
|
20
|
+
# optionally with the given random seed.
|
21
|
+
#
|
22
|
+
# @param [Grammar] grammar the {Grammar} to generate a string from
|
23
|
+
# @param [Integer] seed optional random seed. Defaults to +nil+, which will
|
24
|
+
# make the {Generator} use a random seed
|
25
|
+
def initialize(grammar, seed=nil)
|
26
|
+
@rules = grammar.rules
|
27
|
+
|
28
|
+
@seed = seed
|
29
|
+
unless @seed
|
30
|
+
srand()
|
31
|
+
@seed = rand(0xffffffff)
|
32
|
+
end
|
33
|
+
srand(@seed)
|
34
|
+
|
35
|
+
@errors = []
|
36
|
+
end
|
37
|
+
|
38
|
+
# Produce a random {Result} string from the Generator's {Grammar}
|
39
|
+
#
|
40
|
+
# @param [Symbol] rule the name of the rule to start generation from
|
41
|
+
# @return [Result] the {Result} string produced
|
42
|
+
def run(rule)
|
43
|
+
@result = Result.new(@seed, invoke(rule))
|
44
|
+
@result.set_errors(@errors)
|
45
|
+
|
46
|
+
return @result
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
# Execute a single rule all the way down, returning the +String+ randomly
|
51
|
+
# produced from it
|
52
|
+
#
|
53
|
+
# @param [Symbol] rule the name of the rule to execute
|
54
|
+
# @return [String] the string produced
|
55
|
+
def invoke(rule)
|
56
|
+
# Check the rule is defined; error if so
|
57
|
+
unless @rules.include? rule
|
58
|
+
@errors << "No such rule: #{rule}"
|
59
|
+
return "!!#{rule}!!"
|
60
|
+
end
|
61
|
+
|
62
|
+
local_res = ""
|
63
|
+
|
64
|
+
# Pick an option. We can't use Array#sample, as its implementation differs
|
65
|
+
# between Ruby versions, so random seeds aren't portable.
|
66
|
+
choices = @rules[rule]
|
67
|
+
choice = choices[rand(choices.length)]
|
68
|
+
|
69
|
+
# Scan it for all instances of (unescaped) <rules>
|
70
|
+
# Each match will be an array containing the portion of string before
|
71
|
+
# the <rule>; and the <rule> itself (which may be empty on the last match)
|
72
|
+
choice.scan(/(.*?(?<!\\)(?:\\\\)*)(<.*?>|$)/) do |match|
|
73
|
+
local_res << match[0]
|
74
|
+
unless match[1].empty?
|
75
|
+
local_res << invoke(match[1].match(/<(.*?)>/)[1].to_sym)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return local_res
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Succubus
|
2
|
+
# Class which contains the rules describing a grammar from
|
3
|
+
# which to generate random strings.
|
4
|
+
#
|
5
|
+
# {Grammar} provides the vast majority of the user interface
|
6
|
+
# to Succubus.
|
7
|
+
#
|
8
|
+
# @!attribute [r] last_seed
|
9
|
+
# @return [Integer] the random seed used by the last call to {#execute}
|
10
|
+
# @!attribute [r] rules
|
11
|
+
# @return [Hash<Symbol => String>] the rules describing this grammar
|
12
|
+
class Grammar
|
13
|
+
attr_reader :last_seed
|
14
|
+
attr_reader :rules
|
15
|
+
|
16
|
+
# Create a new {Grammar} object. {#initialize Grammar.new} should be passed
|
17
|
+
# a block defining the rules for the grammar. +self+ in the block will be
|
18
|
+
# the newly-created {Grammar} instance
|
19
|
+
#
|
20
|
+
# @yield Block defining the grammar. +self+ is the newly-created {Grammar},
|
21
|
+
# so bare calls to {#add_rule} work as expected.
|
22
|
+
# @raise [ParseError] if errors were encountered parsing the grammar in the
|
23
|
+
# supplied block
|
24
|
+
def initialize(&block)
|
25
|
+
@rules = {}
|
26
|
+
@errors = []
|
27
|
+
define_singleton_method(:create, block)
|
28
|
+
create
|
29
|
+
class << self ; undef_method :create ; end
|
30
|
+
|
31
|
+
unless @errors.empty?
|
32
|
+
pe = ParseError.new("Errors found parsing")
|
33
|
+
pe.set_errors(@errors)
|
34
|
+
raise pe
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Define a new rule into the {Grammar}.
|
39
|
+
#
|
40
|
+
# {#add_rule} is usually called from the block parameter of {#initialize Grammar.new};
|
41
|
+
# however, it can be called on an existing {Grammar} object. Errors encountered by
|
42
|
+
# {#add_rule} will only be reported when called via {#initialize Grammar.new}.
|
43
|
+
#
|
44
|
+
# @param [#to_sym] name name of the rule
|
45
|
+
# @param [Array<String>] choices possible replacements for the rule. When +<name>+ is
|
46
|
+
# encountered during execution of the {Grammar}, +<name>+ will be replaced by one of
|
47
|
+
# the strings in +choices+ chosen uniformly at random.
|
48
|
+
def add_rule(name, *choices)
|
49
|
+
name = name.to_sym
|
50
|
+
@errors << "Duplicate rule definition: #{name}" if @rules.include? name
|
51
|
+
@rules[name] = choices
|
52
|
+
end
|
53
|
+
|
54
|
+
# Execute the grammar, producing a random {Result} string.
|
55
|
+
#
|
56
|
+
# The rule named +start+ is examined, and one of that rule's choices chosen. Then, for each
|
57
|
+
# rule reference tag +<name>+ in that choice, the tag is recursively replaced with one of
|
58
|
+
# the choices for the rule named +name+.
|
59
|
+
#
|
60
|
+
# @param [#to_sym] start name of the rule to start execution at
|
61
|
+
# @param [Integer] seed optional random seed. Defaults to +nil+, which will
|
62
|
+
# cause a random seed to be used
|
63
|
+
#
|
64
|
+
# @raise [ExecuteError] if errors were encountered while executing the grammar
|
65
|
+
def execute(start, seed=nil)
|
66
|
+
result = Generator.run(self, start.to_sym, seed)
|
67
|
+
|
68
|
+
unless result.errors.empty?
|
69
|
+
ee = ExecuteError.new("Errors found executing")
|
70
|
+
ee.set_errors(result.errors)
|
71
|
+
ee.set_partial(result)
|
72
|
+
raise ee
|
73
|
+
end
|
74
|
+
|
75
|
+
@last_seed = result.random_seed
|
76
|
+
result
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Succubus
|
2
|
+
# Class containing the result of executing a {Grammar}.
|
3
|
+
#
|
4
|
+
# Result is a subclass of String, so it can be used exactly as if it were a String.
|
5
|
+
# Its main purpose is to provide access to metadata about the execution that
|
6
|
+
# produced it - namely the +random_seed+ used, and any +errors+ produced.
|
7
|
+
#
|
8
|
+
# @!attribute [r] random_seed
|
9
|
+
# @return [Integer] The random seed used to generate this Result.
|
10
|
+
# {#random_seed} can be passed to {Grammar#execute} to exactly repeat the process
|
11
|
+
# that generated this {Result} (for possible debugging purposes, or reporting issues)
|
12
|
+
# @!attribute [r] errors
|
13
|
+
# @return [Array<String>] Any errors that occurred during the generation
|
14
|
+
# of this {Result}.
|
15
|
+
class Result < String
|
16
|
+
attr_reader :random_seed, :errors
|
17
|
+
|
18
|
+
# Create a new Result
|
19
|
+
#
|
20
|
+
# @private
|
21
|
+
#
|
22
|
+
# @param [Integer] seed The random seed used to generate this result
|
23
|
+
# @param [Array] args Passed through to +super+
|
24
|
+
# @param [Proc] block Passed through to +super+
|
25
|
+
def initialize(seed, *args, &block)
|
26
|
+
@random_seed = seed
|
27
|
+
super(*args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Set the errors encountered creating this Result
|
31
|
+
#
|
32
|
+
# @private
|
33
|
+
#
|
34
|
+
# @param [Array<String>] errors the errors encountered
|
35
|
+
def set_errors(errors)
|
36
|
+
@errors = errors
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/succubus/version.rb
CHANGED
data/spec/grammar_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require File.dirname(__FILE__) + '/../test/support/fixed_random'
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
3
2
|
require 'succubus'
|
4
3
|
|
5
4
|
describe Succubus::Grammar do
|
@@ -50,13 +49,27 @@ describe Succubus::Grammar do
|
|
50
49
|
end
|
51
50
|
|
52
51
|
it "should produce a known string given pre-chosen randomness" do
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
# From ["I have a <colour> <pet>", "<pet>s look best in <colour>"], choose "<pet>s look best in <colour>"
|
53
|
+
expect_random 2, 1
|
54
|
+
# From ["cat", "dog"], choose "cat"
|
55
|
+
expect_random 2, 0
|
56
|
+
# From ["black", "white"], choose "black"
|
57
|
+
expect_random 2, 0
|
56
58
|
|
57
59
|
@grammar.execute(:base).must_equal "cats look best in black"
|
58
60
|
|
59
|
-
|
61
|
+
queue_must_be_used
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should always produce the same string given a known seed" do
|
65
|
+
result = @grammar.execute(:base)
|
66
|
+
result.random_seed.wont_be_nil
|
67
|
+
|
68
|
+
seed = result.random_seed
|
69
|
+
100.times { @grammar.execute(:base, seed).must_equal result }
|
70
|
+
|
71
|
+
# We know from manual runs that the seed 42 produces "I have a white cat"
|
72
|
+
100.times { @grammar.execute(:base, 42).must_equal "I have a white cat" }
|
60
73
|
end
|
61
74
|
end
|
62
75
|
|
@@ -72,7 +85,7 @@ describe Succubus::Grammar do
|
|
72
85
|
end
|
73
86
|
|
74
87
|
ex = bad_parse.must_raise Succubus::ParseError
|
75
|
-
ex.errors.
|
88
|
+
ex.errors.must_equal_contents ["Duplicate rule definition: colour", "Duplicate rule definition: base"]
|
76
89
|
end
|
77
90
|
|
78
91
|
it "should fail to execute when rules are missing" do
|
@@ -81,10 +94,11 @@ describe Succubus::Grammar do
|
|
81
94
|
add_rule :colour, "black", "white"
|
82
95
|
end
|
83
96
|
|
84
|
-
|
97
|
+
# From ["black", "white"], choose "black"
|
98
|
+
expect_random 2, 0
|
85
99
|
|
86
100
|
ex = proc {grammar.execute(:base)}.must_raise Succubus::ExecuteError
|
87
|
-
ex.errors.
|
101
|
+
ex.errors.must_equal_contents ["No such rule: size", "No such rule: pet"]
|
88
102
|
ex.partial.must_equal "I have a !!size!! black !!pet!!"
|
89
103
|
end
|
90
104
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear! do
|
3
|
+
add_filter '/test/'
|
4
|
+
add_filter '/spec/'
|
5
|
+
add_filter '/examples/'
|
6
|
+
end
|
7
|
+
require 'minitest/autorun'
|
8
|
+
require 'minitest/great_expectations'
|
9
|
+
|
10
|
+
if false
|
11
|
+
require 'turn'
|
12
|
+
Turn.config do |c|
|
13
|
+
# use one of output formats:
|
14
|
+
# :outline - turn's original case/test outline mode [default]
|
15
|
+
# :progress - indicates progress with progress bar
|
16
|
+
# :dotted - test/unit's traditional dot-progress mode
|
17
|
+
# :pretty - new pretty reporter
|
18
|
+
# :marshal - dump output as YAML (normal run mode only)
|
19
|
+
# :cue - interactive testing
|
20
|
+
c.format = :outline
|
21
|
+
# turn on invoke/execute tracing, enable full backtrace
|
22
|
+
c.trace = true
|
23
|
+
# use humanized test names (works only with :outline format)
|
24
|
+
c.natural = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
require File.dirname(__FILE__) + '/../test/support/fixed_random'
|
@@ -1,40 +1,39 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/subset_asserts'
|
2
|
-
|
3
1
|
module MiniTest::Assertions
|
4
|
-
@@
|
2
|
+
@@queued_random = []
|
5
3
|
|
6
|
-
def self.
|
7
|
-
def self.
|
4
|
+
def self.queued_random; @@queued_random; end
|
5
|
+
def self.next_random; @@queued_random.shift; end
|
8
6
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
7
|
+
def expect_random(max, choice)
|
8
|
+
assert_includes (0...max), choice, "Can't queue rand(#{max.inspect}) => #{choice}; choice not in [0-1)"
|
9
|
+
unless max.nil?
|
10
|
+
assert_kind_of Integer, choice, "Can't queue rand(#{max}) => #{choice}; choice not an integer"
|
11
|
+
end
|
12
|
+
@@queued_random << {:max => max, :choice => choice}
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
assert_empty @@
|
15
|
+
def queue_must_be_used
|
16
|
+
assert_empty @@queued_random, "Expected to have used all queued random choices: #{@@queued_random.length} left"
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
|
-
unless
|
20
|
-
|
21
|
-
def
|
22
|
-
queued = MiniTest::Assertions.
|
23
|
-
if !queued.empty? && queued[0][:
|
24
|
-
expected = MiniTest::Assertions.
|
25
|
-
|
26
|
-
ret = ret[0] if n.nil?
|
27
|
-
return ret
|
20
|
+
unless Kernel.method_defined? :rand_with_predefined_values
|
21
|
+
module Kernel
|
22
|
+
def rand_with_predefined_values(max=nil, &block)
|
23
|
+
queued = MiniTest::Assertions.queued_random
|
24
|
+
if !queued.empty? && queued[0][:max] == max
|
25
|
+
expected = MiniTest::Assertions.next_random
|
26
|
+
return expected[:choice]
|
28
27
|
else
|
29
|
-
if
|
30
|
-
|
28
|
+
if max
|
29
|
+
rand_without_predefined_values(max, &block)
|
31
30
|
else
|
32
|
-
|
31
|
+
rand_without_predefined_values(&block)
|
33
32
|
end
|
34
33
|
end
|
35
34
|
end
|
36
|
-
alias_method :
|
37
|
-
alias_method :
|
35
|
+
alias_method :rand_without_predefined_values, :rand
|
36
|
+
alias_method :rand, :rand_with_predefined_values
|
38
37
|
|
39
38
|
end
|
40
39
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: succubus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirement: &20243712 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,15 +21,32 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: *20243712
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: minitest
|
27
|
+
requirement: &20243460 !ruby/object:Gem::Requirement
|
25
28
|
none: false
|
26
29
|
requirements:
|
27
30
|
- - ! '>='
|
28
31
|
- !ruby/object:Gem::Version
|
29
32
|
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *20243460
|
30
36
|
- !ruby/object:Gem::Dependency
|
31
|
-
name: minitest
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
37
|
+
name: minitest-great_expectations
|
38
|
+
requirement: &20243208 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *20243208
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: turn
|
49
|
+
requirement: &20242956 !ruby/object:Gem::Requirement
|
33
50
|
none: false
|
34
51
|
requirements:
|
35
52
|
- - ! '>='
|
@@ -37,12 +54,18 @@ dependencies:
|
|
37
54
|
version: '0'
|
38
55
|
type: :development
|
39
56
|
prerelease: false
|
40
|
-
version_requirements:
|
57
|
+
version_requirements: *20242956
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: coveralls
|
60
|
+
requirement: &20242704 !ruby/object:Gem::Requirement
|
41
61
|
none: false
|
42
62
|
requirements:
|
43
63
|
- - ! '>='
|
44
64
|
- !ruby/object:Gem::Version
|
45
65
|
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *20242704
|
46
69
|
description: ! " Succubus is a generator which takes stochastic paths through a
|
47
70
|
\n generalised Backus-Naur Form grammar to produce random text.\n\n See the
|
48
71
|
examples for details.\n"
|
@@ -51,13 +74,16 @@ executables: []
|
|
51
74
|
extensions: []
|
52
75
|
extra_rdoc_files: []
|
53
76
|
files:
|
77
|
+
- lib/succubus/generator.rb
|
78
|
+
- lib/succubus/grammar.rb
|
79
|
+
- lib/succubus/result.rb
|
54
80
|
- lib/succubus/version.rb
|
55
81
|
- lib/succubus.rb
|
56
82
|
- Rakefile
|
57
83
|
- README.md
|
58
84
|
- test/support/fixed_random.rb
|
59
|
-
- test/support/subset_asserts.rb
|
60
85
|
- spec/grammar_spec.rb
|
86
|
+
- spec/spec_helper.rb
|
61
87
|
homepage: https://github.com/asilano/succubus
|
62
88
|
licenses: []
|
63
89
|
post_install_message:
|
@@ -78,11 +104,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
104
|
version: '0'
|
79
105
|
requirements: []
|
80
106
|
rubyforge_project:
|
81
|
-
rubygems_version: 1.8.
|
107
|
+
rubygems_version: 1.8.16
|
82
108
|
signing_key:
|
83
109
|
specification_version: 3
|
84
110
|
summary: A random generator based on a generalised Backus-Naur Form grammar
|
85
111
|
test_files:
|
86
112
|
- test/support/fixed_random.rb
|
87
|
-
- test/support/subset_asserts.rb
|
88
113
|
- spec/grammar_spec.rb
|
114
|
+
- spec/spec_helper.rb
|
@@ -1,49 +0,0 @@
|
|
1
|
-
module MiniTest::Assertions
|
2
|
-
def assert_subset(subset, superset, msg = nil)
|
3
|
-
sub = subset.to_a.dup
|
4
|
-
supe = superset.to_a.dup
|
5
|
-
msg ||= "#{sub.inspect} is not a subset of #{supe.inspect}"
|
6
|
-
failed = false
|
7
|
-
sub.each do |elem|
|
8
|
-
if supe.index(elem)
|
9
|
-
supe.delete_at(supe.index(elem) || li.length)
|
10
|
-
else
|
11
|
-
failed = true
|
12
|
-
break
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
assert(!failed, msg)
|
17
|
-
end
|
18
|
-
|
19
|
-
def assert_disjoint(left, right, msg = nil)
|
20
|
-
left_a = left.to_a.dup
|
21
|
-
right_a = right.to_a.dup
|
22
|
-
msg ||= "#{left_a.inspect} and #{right_a.inspect} are not disjoint"
|
23
|
-
failed = false
|
24
|
-
left_a.each do |elem|
|
25
|
-
if right_a.index(elem)
|
26
|
-
failed = true
|
27
|
-
break
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
assert(!failed, msg)
|
32
|
-
end
|
33
|
-
|
34
|
-
def assert_same_elements(a1, a2, msg = nil)
|
35
|
-
[:select, :inject, :size].each do |m|
|
36
|
-
[a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
|
37
|
-
end
|
38
|
-
|
39
|
-
assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
|
40
|
-
assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
|
41
|
-
|
42
|
-
assert_equal(a1h, a2h, msg)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
module MiniTest::Expectations
|
47
|
-
infect_an_assertion :assert_subset, :must_be_subset_of
|
48
|
-
infect_an_assertion :assert_same_elements, :must_have_same_elements_as
|
49
|
-
end
|