succubus 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
[](https://travis-ci.org/asilano/succubus)
|
3
|
+
[](https://travis-ci.org/asilano/succubus)[](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
|