dunmanifestin 1.1.3 → 1.2.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.
- checksums.yaml +4 -4
- data/bin/dunmanifestin +0 -2
- data/lib/dunmanifestin.rb +0 -1
- data/lib/dunmanifestin/array.rb +4 -25
- data/lib/dunmanifestin/churn.rb +35 -0
- data/lib/dunmanifestin/custom_inflections.rb +1 -0
- data/lib/dunmanifestin/genre.rb +31 -0
- data/lib/dunmanifestin/palette.rb +88 -32
- data/lib/dunmanifestin/phrase.rb +79 -139
- data/lib/dunmanifestin/terminator.rb +65 -27
- metadata +7 -11
- data/lib/dunmanifestin/generator.rb +0 -46
- data/lib/dunmanifestin/list.rb +0 -31
- data/lib/dunmanifestin/list_loader.rb +0 -20
- data/lib/dunmanifestin/manifestation.rb +0 -30
- data/lib/dunmanifestin/member.rb +0 -18
- data/lib/dunmanifestin/string.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe034e91906b9ceaa0c86e037a01edfb69ad6c737ab9fdbb626becd610bc234b
|
4
|
+
data.tar.gz: 820efff91dd927269b89fee2462747d303870b97b3648957c20c524768da0b30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a0bfbe9e373a9ae7730a5d1db22bd6b4fbbe46e4fe69a9cdaefe7e15b70f913d12c4bf45ae9dc51b5575ddb0c1ebe61e9bddf8aaf3346d67de380807be6f006
|
7
|
+
data.tar.gz: 40ee4e7f6a31d3310af75923e9dd5d34fbad1698bbe5703801de62189d07236886d8ad5c60c0de7181fd305b03010d096640068387787837b415097ea9e519c8
|
data/bin/dunmanifestin
CHANGED
@@ -12,8 +12,6 @@ user_demands = Optimist::options do
|
|
12
12
|
opt :phrase, "Specify a phrase or list, e.g. 'The [animal] sat on the [article]' or '[weapon]'", type: :string, short: '-p'
|
13
13
|
opt :chomp, "remove the trailing newline from the output", short: '-o'
|
14
14
|
opt :file, "Read a file as the phrase", type: :string, short: '-f'
|
15
|
-
opt :coarse_seed, "Set this number to encourage phrases to resolve somewhat predictably", type: :integer, short: "-s"
|
16
|
-
opt :fine_seed, "Set this number (alongside -s) to lock phrases into resolving completely predictably", type: :integer, short: "-e"
|
17
15
|
end
|
18
16
|
|
19
17
|
begin
|
data/lib/dunmanifestin.rb
CHANGED
@@ -7,5 +7,4 @@ require_relative 'dunmanifestin/string'
|
|
7
7
|
require_relative 'dunmanifestin/custom_inflections'
|
8
8
|
require_relative 'dunmanifestin/phrase'
|
9
9
|
require_relative 'dunmanifestin/manifestation'
|
10
|
-
require_relative 'dunmanifestin/list_loader'
|
11
10
|
require_relative 'dunmanifestin/terminator'
|
data/lib/dunmanifestin/array.rb
CHANGED
@@ -1,30 +1,9 @@
|
|
1
|
-
require_relative 'integer'
|
2
|
-
|
3
1
|
class Array
|
4
|
-
|
5
|
-
|
6
|
-
class << self
|
7
|
-
attr_accessor :coarse_seed, :fine_seed, :diversity, :recurrences
|
8
|
-
|
9
|
-
def meta_random
|
10
|
-
@meta_random ||= @fine_seed ? Random.new(@fine_seed) : Random.new
|
11
|
-
end
|
12
|
-
|
13
|
-
def randoms
|
14
|
-
@coarse_seed ||= meta_random.rand(Integer::MAX)
|
15
|
-
@recurrences ||= 500
|
16
|
-
@randoms ||= @recurrences.times.map do |i|
|
17
|
-
Random.new(@coarse_seed + (i % (@diversity||Integer::MAX)))
|
18
|
-
end
|
19
|
-
end
|
2
|
+
def even_elements
|
3
|
+
select.each_with_index { |_, i| i.even? }
|
20
4
|
end
|
21
5
|
|
22
|
-
def
|
23
|
-
|
24
|
-
# p $count += 1
|
25
|
-
self.sample(
|
26
|
-
1,
|
27
|
-
random: randoms.sample(1, random: meta_random).pop
|
28
|
-
).pop
|
6
|
+
def odd_elements
|
7
|
+
select.each_with_index { |_, i| i.odd? }
|
29
8
|
end
|
30
9
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Churn
|
2
|
+
# A churn is a data structure that stores a set of things.
|
3
|
+
# You can randomly pick one of those things by calling its
|
4
|
+
# sample() method.
|
5
|
+
#
|
6
|
+
# The wrinkle is that the set of sampleable things is not
|
7
|
+
# closed. The churn will sometimes, at random, generate a
|
8
|
+
# totally new item. You can also explicitly request a
|
9
|
+
# novel item by calling generate(). Any new items
|
10
|
+
# generated are added to the churn and may be picked on
|
11
|
+
# future calls to sample().
|
12
|
+
#
|
13
|
+
# Control how often the churn generates new things using
|
14
|
+
# the novelty: parameter to initialize().
|
15
|
+
|
16
|
+
def initialize novelty:
|
17
|
+
@novelty = novelty
|
18
|
+
@generated = 0
|
19
|
+
@slots = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def sample
|
23
|
+
return yield if @novelty == 0
|
24
|
+
r = (rand * (@novelty + @slots.length)).floor
|
25
|
+
@slots[r] ||= yield
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate
|
29
|
+
return yield if @novelty == 0
|
30
|
+
@slots[@generated] ||= yield
|
31
|
+
value = @slots[@generated]
|
32
|
+
@generated += 1
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Genre
|
2
|
+
GAP_BETWEEN_PALETTES = /\n(?=\|)/
|
3
|
+
|
4
|
+
def self.from_directories paths
|
5
|
+
new palettes_from paths
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.palettes_from paths
|
9
|
+
paths.flat_map do |path|
|
10
|
+
Dir[File.join(path, '**', '*.pal')].flat_map do |filename|
|
11
|
+
File.read(filename)
|
12
|
+
.split(GAP_BETWEEN_PALETTES)
|
13
|
+
.map do |textual_palette|
|
14
|
+
Palette.new(textual_palette, filename)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize palette_list
|
21
|
+
self.palettes = Hash[palette_list.map { |p| [p.name, p] }]
|
22
|
+
end
|
23
|
+
|
24
|
+
def palette_named name
|
25
|
+
palettes[name] || NullPalette.new(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
attr_accessor :palettes
|
31
|
+
end
|
@@ -1,42 +1,98 @@
|
|
1
|
+
require_relative './phrase'
|
2
|
+
require_relative './churn'
|
3
|
+
|
4
|
+
class NullPalette
|
5
|
+
def initialize name
|
6
|
+
@name = name
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
@name
|
11
|
+
end
|
12
|
+
|
13
|
+
def sample _, _, _
|
14
|
+
"{#{@name} ??}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
1
18
|
class Palette
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
19
|
+
GAP_BETWEEN_LISTS = /\n(?=\|)/
|
20
|
+
PALETTE_TITLE = /^\|(.*?)\n/
|
21
|
+
COMMENT = %r{//.*$}
|
22
|
+
REPETITION_PREFIX = /^\d+@/
|
23
|
+
|
24
|
+
def initialize raw_text, filename
|
25
|
+
@raw_text = raw_text
|
26
|
+
@filename = filename
|
27
|
+
end
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
|
29
|
+
def name
|
30
|
+
return file_basename if not titled?
|
31
|
+
lines.first.sub(/\|(\w+).*$/, '\1')
|
32
|
+
end
|
15
33
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
34
|
+
def sample genre, inflections = [], constraints = []
|
35
|
+
if inflections.any?
|
36
|
+
# don't use the churn if there are inflections, since
|
37
|
+
# the inflected text would be cached by the churn and
|
38
|
+
# could then `recur` in places where we don't want any
|
39
|
+
# inflections.
|
40
|
+
phrases.sample.reify genre, inflections
|
41
|
+
else
|
42
|
+
if constraints.include? :recur
|
43
|
+
churn.sample { phrases.sample.reify genre }
|
20
44
|
else
|
21
|
-
|
22
|
-
# These comments should be tests.
|
23
|
-
body.gsub!(PALETTE_TITLE, '')
|
45
|
+
churn.generate { phrases.sample.reify genre }
|
24
46
|
end
|
25
|
-
|
26
|
-
# This metaprogramming is mysterious to me.
|
27
|
-
qlass = declare_phrase_class list_name
|
28
|
-
# Where is 'list' defined?
|
29
|
-
qlass.list body
|
30
|
-
rescue NoMethodError
|
31
47
|
end
|
48
|
+
rescue NoMethodError
|
49
|
+
"{#{name} ??}"
|
50
|
+
end
|
51
|
+
|
32
52
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
53
|
+
|
54
|
+
def file_basename
|
55
|
+
File.basename(@filename, '.pal')
|
56
|
+
end
|
57
|
+
|
58
|
+
def churn
|
59
|
+
@churn ||= Churn.new novelty: novelty
|
60
|
+
end
|
61
|
+
|
62
|
+
def novelty
|
63
|
+
lines.first.sub(/^.*\*(\d+).*$/, '\1').to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
def phrases
|
67
|
+
@phrases ||= begin
|
68
|
+
lines[valid_phrase_range]
|
69
|
+
.map { |line| line.sub(COMMENT, '') }
|
70
|
+
.map(&:strip)
|
71
|
+
.reject(&:empty?)
|
72
|
+
.flat_map { |line|
|
73
|
+
if line =~ REPETITION_PREFIX
|
74
|
+
[line.sub(REPETITION_PREFIX, '')] * line.to_i
|
75
|
+
else
|
76
|
+
[line]
|
77
|
+
end
|
78
|
+
}
|
79
|
+
.map(&Phrase.method(:new))
|
40
80
|
end
|
41
81
|
end
|
82
|
+
|
83
|
+
def valid_phrase_range
|
84
|
+
titled? ? 1..-1 : 0..-1
|
85
|
+
end
|
86
|
+
|
87
|
+
def lines
|
88
|
+
@lines ||= text.gsub(/>>(.+?)<</m, '').split("\n") | text.scan(/>>(.+?)<</m).flatten
|
89
|
+
end
|
90
|
+
|
91
|
+
def text
|
92
|
+
@text ||= @raw_text.strip
|
93
|
+
end
|
94
|
+
|
95
|
+
def titled?
|
96
|
+
lines[0][0] == '|'
|
97
|
+
end
|
42
98
|
end
|
data/lib/dunmanifestin/phrase.rb
CHANGED
@@ -2,175 +2,115 @@ require 'active_support/inflector'
|
|
2
2
|
require_relative 'custom_inflections'
|
3
3
|
require_relative 'array'
|
4
4
|
|
5
|
-
class
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
new_list = new_list.split_on_newlines_and_strip if new_list.is_a?(String) unless multiline_document
|
10
|
-
new_list = [new_list] if multiline_document
|
11
|
-
|
12
|
-
new_list.map! do |line|
|
13
|
-
multiplier_regex = /^\d+@/
|
14
|
-
multiplier = line.match(multiplier_regex).to_s.to_i
|
15
|
-
multiplier = 1 if multiplier < 1
|
16
|
-
[line.gsub(multiplier_regex, '')] * multiplier
|
17
|
-
end.flatten!.reject! { |i| i.nil? || i.empty? }
|
5
|
+
class NullPhrase
|
6
|
+
def initialize dsl_string
|
7
|
+
@dsl_string = dsl_string
|
8
|
+
end
|
18
9
|
|
19
|
-
|
10
|
+
def reify _, _
|
11
|
+
@dsl_string
|
20
12
|
end
|
13
|
+
end
|
21
14
|
|
22
|
-
|
15
|
+
class Phrase
|
16
|
+
def initialize dsl_string
|
23
17
|
raise "Try again." unless dsl_string
|
24
|
-
|
18
|
+
@dsl_string = dsl_string
|
25
19
|
end
|
26
20
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
@variable_classes = []
|
31
|
-
@inflection_delegates = {
|
32
|
-
:plural => [],
|
33
|
-
:possessive => [],
|
34
|
-
:article => [],
|
35
|
-
:capitalize => [],
|
21
|
+
def reify genre, requested_inflections = []
|
22
|
+
vals = variables.map { |variable|
|
23
|
+
variable.reify genre, requested_inflections
|
36
24
|
}
|
25
|
+
inflect requested_inflections, constant_segments.zip(vals).flatten.join('')
|
26
|
+
end
|
37
27
|
|
38
|
-
|
39
|
-
|
40
|
-
parsed_dsl[:variables].each_with_index do |variable, i|
|
41
|
-
rough_var_class = variable[:rough_variable_class]
|
28
|
+
private
|
42
29
|
|
43
|
-
|
44
|
-
|
45
|
-
|
30
|
+
def variables
|
31
|
+
parsed_dsl[:variables]
|
32
|
+
end
|
46
33
|
|
47
|
-
|
34
|
+
def constant_segments
|
35
|
+
parsed_dsl[:constant_segments]
|
36
|
+
end
|
48
37
|
|
49
|
-
|
50
|
-
|
51
|
-
|
38
|
+
def parsed_dsl
|
39
|
+
@parsed_dsl ||= begin
|
40
|
+
tokens = @dsl_string.split(/[\[\]]/)
|
41
|
+
constant_segments = tokens.even_elements
|
42
|
+
variables = tokens.odd_elements
|
52
43
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
44
|
+
{
|
45
|
+
:variables => variables.map(&Variable.method(:new)),
|
46
|
+
:constant_segments => constant_segments
|
47
|
+
}
|
58
48
|
end
|
59
|
-
|
60
|
-
@to_s_proc = -> {
|
61
|
-
self.variables.each_with_index do |variable, i|
|
62
|
-
inflections[i].each { |inflection| variable.inflect inflection }
|
63
|
-
end
|
64
|
-
template.zip(self.variables).flatten.map(&:to_s).join('')
|
65
|
-
}
|
66
49
|
end
|
67
50
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
# },
|
75
|
-
# { :rough_variable_class => 'noun',
|
76
|
-
# :inflections_to_delegate => [:plural],
|
77
|
-
# :inflections_to_apply => [:possessive]
|
78
|
-
# }
|
79
|
-
# ],
|
80
|
-
# :template => ["", " "]
|
81
|
-
# }
|
82
|
-
#
|
83
|
-
tokens = dsl_string.split(/[\[\]]/)
|
84
|
-
template = []; variables = [];
|
85
|
-
|
86
|
-
tokens.each_with_index do |token, i|
|
87
|
-
if i % 2 == 0
|
88
|
-
template << token
|
89
|
-
else
|
90
|
-
variables << token
|
91
|
-
end
|
92
|
-
end
|
51
|
+
def inflect inflections, string
|
52
|
+
plural = inflections.include? :plural
|
53
|
+
article = inflections.include? :article
|
54
|
+
possessive = inflections.include? :possessive
|
55
|
+
capitalize = inflections.include? :capitalize
|
56
|
+
titleize = inflections.include? :titleize
|
93
57
|
|
94
|
-
|
58
|
+
# Good, now turn this into a stateless function.
|
59
|
+
string = string.pluralize if plural && variables.none?(&:delegated_plural?)
|
60
|
+
string = (string =~ /s$/) ? "#{string}'" : "#{string}'s" if plural && possessive && variables.none?(&:delegated_plural?)
|
61
|
+
string = "#{string}'s" if !plural && possessive && variables.none?(&:delegated_possessive?)
|
62
|
+
string = (string =~ /^[aeiou]/i) ? "an #{string}" : "a #{string}" if !plural && article && variables.none?(&:delegated_article?)
|
63
|
+
string = string[0].capitalize + string[1 .. -1] if capitalize
|
64
|
+
string = string.titleize if titleize
|
95
65
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
inflections_to_delegate = []
|
100
|
-
inflections_to_apply = []
|
66
|
+
string
|
67
|
+
end
|
68
|
+
end
|
101
69
|
|
102
|
-
|
103
|
-
|
104
|
-
|
70
|
+
class Variable
|
71
|
+
def initialize dsl_string
|
72
|
+
components = dsl_string.split(/\b/)
|
73
|
+
self.palette_name = components.shift
|
74
|
+
|
75
|
+
self.inflections_delegated_to_me = []
|
76
|
+
self.demanded_inflections = []
|
77
|
+
self.constraints = []
|
78
|
+
|
79
|
+
components.each_with_index do |v, k|
|
80
|
+
case components[k-1]
|
81
|
+
when '#'
|
82
|
+
inflections_delegated_to_me << v.to_sym
|
83
|
+
when '.'
|
84
|
+
demanded_inflections << v.to_sym
|
85
|
+
when ':'
|
86
|
+
constraints << v.to_sym
|
105
87
|
end
|
106
|
-
|
107
|
-
hash[:variables] << {
|
108
|
-
:rough_variable_class => rough_var_class,
|
109
|
-
:inflections_to_delegate => inflections_to_delegate,
|
110
|
-
:inflections_to_apply => inflections_to_apply,
|
111
|
-
}
|
112
88
|
end
|
113
|
-
|
114
|
-
hash
|
115
|
-
end
|
116
|
-
|
117
|
-
def variables
|
118
|
-
@variables ||= @variable_classes.map(&:new)
|
119
89
|
end
|
120
90
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def titleize?; !!@titleize end
|
126
|
-
|
127
|
-
def titleize!
|
128
|
-
delegates = @inflection_delegates[:capitalize]
|
129
|
-
delegates.each { |delegate| variables[delegate].capitalize! }
|
91
|
+
def reify genre, inflections_of_parent_phrase
|
92
|
+
inherited_inflections = inflections_of_parent_phrase & inflections_delegated_to_me
|
93
|
+
inflections = demanded_inflections | inherited_inflections
|
94
|
+
genre.palette_named(palette_name).sample genre, inflections, constraints
|
130
95
|
end
|
131
96
|
|
132
|
-
def
|
133
|
-
|
134
|
-
self.instance_variable_set("@#{inflection}", true)
|
135
|
-
return (titleize! && self) if inflection == :titleize
|
136
|
-
|
137
|
-
delegates = @inflection_delegates[inflection]
|
138
|
-
delegates.each { |delegate| variables[delegate].inflect inflection }
|
139
|
-
self
|
140
|
-
rescue NoMethodError
|
141
|
-
puts "==> Failed to inflect: .#{inflection}?"
|
142
|
-
puts "==> Valid inflections are: plural, possessive, article, capitalize, titleize"
|
143
|
-
self
|
97
|
+
def delegated_plural?
|
98
|
+
inflections_delegated_to_me.include? :plural
|
144
99
|
end
|
145
100
|
|
146
|
-
def
|
147
|
-
|
101
|
+
def delegated_article?
|
102
|
+
inflections_delegated_to_me.include? :article
|
148
103
|
end
|
149
104
|
|
150
|
-
def
|
151
|
-
|
152
|
-
string = string.pluralize if plural? && @inflection_delegates[:plural].empty?
|
153
|
-
string = (string =~ /s$/) ? "#{string}'" : "#{string}'s" if plural? && possessive? && @inflection_delegates[:possessive].empty?
|
154
|
-
string = "#{string}'s" if !plural? && possessive? && @inflection_delegates[:possessive].empty?
|
155
|
-
string = (string =~ /^[aeiou]/i) ? "an #{string}" : "a #{string}" if !plural? && article? && @inflection_delegates[:article].empty?
|
156
|
-
string = string[0].capitalize + string[1 .. -1] if capitalize?
|
157
|
-
string = string.titleize if titleize?
|
158
|
-
|
159
|
-
string
|
105
|
+
def delegated_possessive?
|
106
|
+
inflections_delegated_to_me.include? :possessive
|
160
107
|
end
|
161
108
|
|
162
109
|
private
|
163
110
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
170
|
-
end
|
171
|
-
RUBY
|
172
|
-
|
173
|
-
eval(class_definition)
|
174
|
-
end
|
175
|
-
|
111
|
+
attr_accessor \
|
112
|
+
:demanded_inflections,
|
113
|
+
:palette_name,
|
114
|
+
:inflections_delegated_to_me,
|
115
|
+
:constraints
|
176
116
|
end
|
@@ -1,41 +1,27 @@
|
|
1
|
+
require_relative 'genre'
|
1
2
|
require_relative 'phrase'
|
2
|
-
require_relative '
|
3
|
-
require_relative 'string'
|
3
|
+
require_relative 'palette'
|
4
4
|
|
5
5
|
class Terminator
|
6
|
-
def initialize(
|
7
|
-
@list_loader = list_loader
|
6
|
+
def initialize(shell: Shell)
|
8
7
|
@shell = shell
|
9
8
|
end
|
10
9
|
|
11
10
|
def address demands
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Array.recurrences = 20
|
16
|
-
phrase_string = phrasing(demands[:phrase], demands[:file])
|
17
|
-
root_phrase_class = Class.new(Phrase) { list phrase_string, !!demands[:file] }
|
18
|
-
list_loader.load demands[:genre]
|
19
|
-
print interactive_banner if demands[:interactive]
|
20
|
-
|
21
|
-
construction_loop(
|
22
|
-
root_phrase_class,
|
23
|
-
(demands[:count] || 1),
|
24
|
-
demands[:chomp],
|
25
|
-
demands[:copy],
|
26
|
-
demands[:interactive]
|
27
|
-
)
|
11
|
+
self.demands = demands
|
12
|
+
print interactive_banner if interactive
|
13
|
+
construction_loop
|
28
14
|
end
|
29
15
|
|
30
16
|
private
|
31
|
-
DEFAULT_ROOT_PHRASEAGE = '[root]'
|
32
|
-
attr_accessor :list_loader, :shell
|
33
17
|
|
34
|
-
|
18
|
+
attr_accessor :shell, :demands
|
19
|
+
|
20
|
+
def construction_loop
|
35
21
|
fulltext = ""
|
36
22
|
while true
|
37
23
|
volume.times do
|
38
|
-
text =
|
24
|
+
text = Phrase.new(phrasing).reify genre
|
39
25
|
fulltext += "\n#{text}"
|
40
26
|
text += "\n" unless chomp
|
41
27
|
shell.puts text
|
@@ -43,13 +29,21 @@ class Terminator
|
|
43
29
|
|
44
30
|
`echo #{fulltext.inspect} | pbcopy $1` if copy
|
45
31
|
|
46
|
-
break unless
|
32
|
+
break unless interactive
|
47
33
|
break if get_from_prompt == 'quit'
|
48
34
|
end
|
49
35
|
end
|
50
36
|
|
51
|
-
def
|
52
|
-
|
37
|
+
def genre
|
38
|
+
@genre ||= Genre.from_directories genre_directories
|
39
|
+
end
|
40
|
+
|
41
|
+
def genre_directories
|
42
|
+
([default_genre_path] + demanded_genres).reject(&:empty?)
|
43
|
+
end
|
44
|
+
|
45
|
+
def phrasing
|
46
|
+
demanded_phrase || demanded_file_contents || '[root]'
|
53
47
|
end
|
54
48
|
|
55
49
|
def get_from_prompt
|
@@ -63,6 +57,50 @@ class Terminator
|
|
63
57
|
[bar, message, bar].join "\n"
|
64
58
|
end
|
65
59
|
|
60
|
+
def chomp
|
61
|
+
demands[:chomp]
|
62
|
+
end
|
63
|
+
|
64
|
+
def copy
|
65
|
+
demands[:copy]
|
66
|
+
end
|
67
|
+
|
68
|
+
def volume
|
69
|
+
demands[:count] || 1
|
70
|
+
end
|
71
|
+
|
72
|
+
def interactive
|
73
|
+
demands[:interactive]
|
74
|
+
end
|
75
|
+
|
76
|
+
def demanded_genres
|
77
|
+
demands[:genre] ? demands[:genre].split(':') : []
|
78
|
+
end
|
79
|
+
|
80
|
+
def demanded_file
|
81
|
+
demands[:file]
|
82
|
+
end
|
83
|
+
|
84
|
+
def demanded_file_contents
|
85
|
+
demanded_file && File.read(demanded_file)
|
86
|
+
end
|
87
|
+
|
88
|
+
def demanded_phrase
|
89
|
+
demands[:phrase]
|
90
|
+
end
|
91
|
+
|
92
|
+
def coarse_seed
|
93
|
+
demands[:coarse_seed]
|
94
|
+
end
|
95
|
+
|
96
|
+
def fine_seed
|
97
|
+
demands[:fine_seed]
|
98
|
+
end
|
99
|
+
|
100
|
+
def default_genre_path
|
101
|
+
File.join(*%W(#{File.dirname(__FILE__)} .. .. default-genre))
|
102
|
+
end
|
103
|
+
|
66
104
|
module Shell
|
67
105
|
class << self
|
68
106
|
def method_missing method, *args
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dunmanifestin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- gavmor
|
8
8
|
- benchristel
|
9
9
|
- AlexLerman
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2019-04-
|
13
|
+
date: 2019-04-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -83,7 +83,7 @@ dependencies:
|
|
83
83
|
description: The point of Dunmanifestin [is] to inject chaos into humans' cliched
|
84
84
|
ideas or, at least, let them be hunter-gatherers instead of farmers of culture.
|
85
85
|
-- B. Christel
|
86
|
-
email:
|
86
|
+
email: morgan.gavin@gmail.com
|
87
87
|
executables:
|
88
88
|
- dunmanifestin
|
89
89
|
extensions: []
|
@@ -98,18 +98,14 @@ files:
|
|
98
98
|
- default-genre/verb.pal
|
99
99
|
- lib/dunmanifestin.rb
|
100
100
|
- lib/dunmanifestin/array.rb
|
101
|
+
- lib/dunmanifestin/churn.rb
|
101
102
|
- lib/dunmanifestin/custom_inflections.rb
|
102
|
-
- lib/dunmanifestin/
|
103
|
+
- lib/dunmanifestin/genre.rb
|
103
104
|
- lib/dunmanifestin/integer.rb
|
104
|
-
- lib/dunmanifestin/list.rb
|
105
|
-
- lib/dunmanifestin/list_loader.rb
|
106
|
-
- lib/dunmanifestin/manifestation.rb
|
107
|
-
- lib/dunmanifestin/member.rb
|
108
105
|
- lib/dunmanifestin/palette.rb
|
109
106
|
- lib/dunmanifestin/phrase.rb
|
110
|
-
- lib/dunmanifestin/string.rb
|
111
107
|
- lib/dunmanifestin/terminator.rb
|
112
|
-
homepage: http://github.com/
|
108
|
+
homepage: http://github.com/gavmor/dunmanifestin
|
113
109
|
licenses:
|
114
110
|
- MIT
|
115
111
|
metadata: {}
|
@@ -1,46 +0,0 @@
|
|
1
|
-
class Generator
|
2
|
-
def self.from_file path
|
3
|
-
new File.open(path).readlines.map(&:chomp).reject { |line| line.empty? }.unshift('root')
|
4
|
-
end
|
5
|
-
|
6
|
-
def initialize lines
|
7
|
-
lines.inject do |list, line|
|
8
|
-
create_or_append_list(list, line)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def lists
|
13
|
-
@lists ||= {}
|
14
|
-
end
|
15
|
-
|
16
|
-
def generate kind
|
17
|
-
kind = kind.to_s
|
18
|
-
list = lists[kind] || lists['|' + kind]
|
19
|
-
return "{#{kind}?}" unless list
|
20
|
-
list.sample.to_s
|
21
|
-
end
|
22
|
-
|
23
|
-
def manifest phrase
|
24
|
-
phrase.to_s
|
25
|
-
#phrase.listerpolate(self)
|
26
|
-
end
|
27
|
-
|
28
|
-
def method_missing slug
|
29
|
-
generate slug
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def create_or_append_list list, line
|
35
|
-
if line.is_name?
|
36
|
-
list = List.new(name: line, universe: self)
|
37
|
-
else
|
38
|
-
list << line
|
39
|
-
end
|
40
|
-
|
41
|
-
list
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class NoSuchList < StandardError
|
46
|
-
end
|
data/lib/dunmanifestin/list.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
class List
|
2
|
-
attr_accessor :name
|
3
|
-
|
4
|
-
def initialize opts={}
|
5
|
-
@name = opts.fetch(:name, 'default')
|
6
|
-
@members = opts.fetch(:members, [])
|
7
|
-
@universe = opts.fetch(:universe)
|
8
|
-
@universe.lists[name] = self
|
9
|
-
puts members
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
def << rough_member
|
14
|
-
pieces = rough_member.split('@')
|
15
|
-
|
16
|
-
weight = pieces.length > 1 ? pieces.shift.to_i : 1
|
17
|
-
|
18
|
-
attrs = {body: pieces.pop, list: self, universe: universe}
|
19
|
-
attrs.merge!(frequency: pieces.pop) if pieces.any?
|
20
|
-
member = Member.new(attrs)
|
21
|
-
|
22
|
-
weight.times { members << member }
|
23
|
-
end
|
24
|
-
|
25
|
-
def sample
|
26
|
-
members.sample
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
attr_accessor :members, :universe
|
31
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require_relative 'phrase'
|
2
|
-
require_relative 'string'
|
3
|
-
require_relative 'palette'
|
4
|
-
|
5
|
-
class ListLoader
|
6
|
-
DEFAULT_GENRE = File.join(*%W(#{File.dirname(__FILE__)} .. .. default-genre))
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def load super_genre=''
|
10
|
-
[DEFAULT_GENRE, (super_genre || '').split(":")]
|
11
|
-
.flatten.compact.each(&method(:load_genre))
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def load_genre dirname
|
17
|
-
Dir[File.join(dirname, '**' '*.pal')].each(&Palette.method(:expose))
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class Manifestation
|
2
|
-
attr_accessor :text
|
3
|
-
|
4
|
-
def initialize desired
|
5
|
-
@text = manifestation of: desired[:phrase], from: desired[:genre]
|
6
|
-
end
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def concatenate_lists_in genre
|
11
|
-
unless system("find ./lists/#{genre}/* -exec cat {} > #{genre}.list + 2>&1")
|
12
|
-
puts "There is no lists/#{genre} directory in which to find lists."
|
13
|
-
raise StandardError
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def manifestation desire
|
18
|
-
concatenate_lists_in(desire[:from])
|
19
|
-
|
20
|
-
world = Generator.from_file "#{desire[:from]}.list"
|
21
|
-
|
22
|
-
text = world.manifest desire[:of]
|
23
|
-
while text.length > 500
|
24
|
-
text = world.manifest desire[:of]
|
25
|
-
end
|
26
|
-
|
27
|
-
text
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
data/lib/dunmanifestin/member.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
class Member
|
2
|
-
attr_reader :body, :list
|
3
|
-
|
4
|
-
def initialize(opts={})
|
5
|
-
@list = opts.fetch(:list)
|
6
|
-
@freq = opts.fetch(:frequency, 1)
|
7
|
-
@body = opts.fetch(:body)
|
8
|
-
@universe = opts.fetch(:universe)
|
9
|
-
end
|
10
|
-
|
11
|
-
def to_s
|
12
|
-
body.listerpolate(universe).to_s
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
attr_reader :universe
|
18
|
-
end
|
data/lib/dunmanifestin/string.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
class String
|
4
|
-
def is_name?
|
5
|
-
self[0] == '|'
|
6
|
-
end
|
7
|
-
|
8
|
-
def listerpolate universe
|
9
|
-
body = self
|
10
|
-
|
11
|
-
while body.match(/\[\w+\]/) do
|
12
|
-
slug = body.match(/\[\w+\]/).to_s
|
13
|
-
phrase = universe.send(slug.gsub('[', '').gsub(']', ''))
|
14
|
-
body = body.sub(slug, phrase)
|
15
|
-
end
|
16
|
-
|
17
|
-
body
|
18
|
-
end
|
19
|
-
|
20
|
-
alias :original_capitalize :capitalize
|
21
|
-
|
22
|
-
def capitalize
|
23
|
-
case_mapping = {
|
24
|
-
"ä" => "Ä",
|
25
|
-
"ö" => "Ö",
|
26
|
-
}
|
27
|
-
|
28
|
-
s = original_capitalize
|
29
|
-
case_mapping.each_pair do |lower, upper|
|
30
|
-
s.gsub!(%r{^#{lower}}, upper)
|
31
|
-
end
|
32
|
-
s
|
33
|
-
end
|
34
|
-
|
35
|
-
def split_on_newlines_and_strip
|
36
|
-
split(/\s*\n\s*/).reject(&:empty?)
|
37
|
-
end
|
38
|
-
end
|