poefy 0.6.0 → 0.6.1
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/README.md +16 -7
- data/bin/poefy +45 -19
- data/lib/poefy.rb +2 -0
- data/lib/poefy/core_extensions/array.rb +64 -0
- data/lib/poefy/database.rb +15 -4
- data/lib/poefy/poefy_gen_base.rb +11 -3
- data/lib/poefy/poetic_form_from_text.rb +62 -26
- data/lib/poefy/string_manipulation.rb +60 -6
- data/lib/poefy/version.rb +2 -2
- data/poefy.gemspec +1 -0
- data/spec/poefy_spec.rb +99 -0
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0defb6d9a94a5162826c5815c0f9f1aac7b98c91
|
4
|
+
data.tar.gz: a8c83d5c423ea918252c47a2aa74897d44ad2c72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a09920a71f8feced4ddd7a079c06f8ea2c763f7fa84adb47da81d075d993d94b4ba65e8e1889d3be57081c634f04edb77fee46a3beaf414cdf6ea58bf229eb09
|
7
|
+
data.tar.gz: 5ee1495dab167b26d4ec006b3fadd18e93a85d483e2730f061080dc6cd32988c0d50d5fca542b52d47b48a6abfc9b15e3c30939c477067a238097a525802f988
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ The code rather hackily uses system to call `sqlite3`, so make sure you have tha
|
|
38
38
|
|
39
39
|
Make a poefy database from a text file:
|
40
40
|
|
41
|
-
$ poefy shakespeare < shakespeare_sonnets.txt
|
41
|
+
$ poefy shakespeare -o < shakespeare_sonnets.txt
|
42
42
|
|
43
43
|
Now, whenever you want to make poems using Shakespeare's lines, you can just use `poefy shakespeare` and it will read from the already created database:
|
44
44
|
|
@@ -147,11 +147,11 @@ Specify syllable count allowed for each line. There's a few valid forms it can t
|
|
147
147
|
|
148
148
|
If the string is just one number, all lines will be that number of syllables long.
|
149
149
|
|
150
|
-
$ poefy whitman -s'10'
|
150
|
+
$ poefy whitman sonnet -s'10'
|
151
151
|
|
152
152
|
If the string is comma delimited, all lines will be any of those numbers of syllables long.
|
153
153
|
|
154
|
-
$ poefy whitman -s'9,10,11'
|
154
|
+
$ poefy whitman sonnet -s'9,10,11'
|
155
155
|
|
156
156
|
If the string is an array, each element corresponds to a line in the output. This will skip blank lines.
|
157
157
|
|
@@ -164,7 +164,8 @@ If the string is a hash, the key will be used to match the line number.
|
|
164
164
|
|
165
165
|
$ poefy whitman -r'aabba' -s'{1:8,2:8,3:5,4:5,5:8}'
|
166
166
|
$ poefy whitman -r'aabba' -s'{1:[8,9],2:[8,9],3:[4,5,6],4:[4,5,6],5:[8,9]}'
|
167
|
-
$ poefy whitman -r'aabba' -s'{0:[8,9],3:[4,5,6],4:[4,5,6]}'
|
167
|
+
$ poefy whitman -r'aabba' -s'{0: [8,9],3: [4,5,6],4: [4,5,6]}'
|
168
|
+
$ poefy whitman -r'aabba' -s'{0=>[8,9],3=>[4,5,6],4=>[4,5,6]}'
|
168
169
|
|
169
170
|
In the hash form, any lines not explicitly specified will use the value of the '0' key. If there is no '0' key, the lines will be ignored.
|
170
171
|
|
@@ -188,7 +189,7 @@ If the string is just one regex, all lines will be forced to match that regex.
|
|
188
189
|
$ poefy whitman sonnet -x'^[A-Z].*$'
|
189
190
|
$ poefy whitman sonnet -x'^[^e]*$' -s0
|
190
191
|
|
191
|
-
If the string is a hash, the key will be used to match the line number. Unlike in the `syllable` string, you must use ruby's `=>` key identifier. Also, you must put the regex inside `/slashes/`.
|
192
|
+
If the string is a hash, the key will be used to match the line number. Unlike in the `syllable` string, you must use ruby's `=>` key identifier, and not `:` as in JSON. Also, you must put the regex inside `/slashes/`.
|
192
193
|
|
193
194
|
Example, to ensure the first line always starts with capitalisation:
|
194
195
|
|
@@ -213,7 +214,7 @@ You must also beware of repeated lines (uppercase letters in the rhyme string).
|
|
213
214
|
|
214
215
|
#### Option `-A` or `--acrostic_x`
|
215
216
|
|
216
|
-
This does the same as `-a`, but with special workarounds for 'x'. In the case that a line needs to match
|
217
|
+
This does the same as `-a`, but with special workarounds for 'x'. In the case that a line needs to match /^x/, it will instead match /^ex/ and replace with 'eX'. It will also use indentation to line-up the letters vertically:
|
217
218
|
|
218
219
|
$ poefy whitman -s8 -r abcbdd -A taxman
|
219
220
|
|
@@ -269,12 +270,18 @@ You can do the same thing for the other keys: `rhyme`, `final_word`, and `syllab
|
|
269
270
|
|
270
271
|
#### Special case: poetic form from text file
|
271
272
|
|
272
|
-
If
|
273
|
+
If you pipe in text and don't use the `-o` option to create a database, then the output will be a poem with the same structure as the file. This can also be accomplished if the second argument is a reference to a text file. So, assuming you have a `lyrics` script that will return song lines for you:
|
274
|
+
|
275
|
+
$ lyrics 'carly rae jepsen' 'call me maybe' | tee jep.txt | poefy whitman
|
276
|
+
$ poefy whitman < jep.txt
|
277
|
+
$ poefy whitman jep.txt
|
273
278
|
|
274
279
|
The program will scan by line, looking for rhyme, syllables and repeated lines. It will then build up a constraint hash and use that as the poetic form.
|
275
280
|
|
276
281
|
Any line that is bracketed in `[square]` or `{curly}` braces will be duplicated exactly in the output. This is for lines such as "chorus" or "1st verse" descriptions. This seems to work nicely with lyrics from genius.com.
|
277
282
|
|
283
|
+
Also, any indentation will be preserved, assuming 2 spaces per "indent".
|
284
|
+
|
278
285
|
Here's an example of a song that can be sung to the same tune as "[I Want to Hold Your Hand][1]", but using lyrics from all Beatles songs:
|
279
286
|
|
280
287
|
````
|
@@ -366,6 +373,8 @@ puts poefy.poem ({ form: :sonnet, regex: '^[A-Z].*$' })
|
|
366
373
|
puts poefy.poem ({ form: :sonnet, acrostic: 'pauldpthompson' })
|
367
374
|
puts poefy.poem ({ form: 'sonnet', indent: '01010101001101' })
|
368
375
|
puts poefy.poem ({ form: 'sonnet', proper: false })
|
376
|
+
puts poefy.poem ({ form_from_text: 'how_do_i_love_thee.txt' })
|
377
|
+
puts poefy.poem ({ form_from_text: 'how_do_i_love_thee.txt', syllable: 0 })
|
369
378
|
```
|
370
379
|
|
371
380
|
All options can be specified at object initialisation, and subsequent poems will use those options as default:
|
data/bin/poefy
CHANGED
@@ -46,8 +46,11 @@ def parse_options
|
|
46
46
|
poefy whitman -r 'A1bA2 abA1 abA2 abA1 abA2 abA1A2'
|
47
47
|
].gsub(' ',' ')
|
48
48
|
|
49
|
-
databases = Poefy.all_databases
|
50
|
-
|
49
|
+
databases = (Poefy.all_databases - ['test'])
|
50
|
+
.each_slice(4).to_a
|
51
|
+
.map { |i| i.join ', ' }
|
52
|
+
.join("\n" + ' ' * 21)
|
53
|
+
databases = 'Databases available: ' + databases
|
51
54
|
|
52
55
|
opts.banner = program_info + "\n" + usage + "\n" + databases + "\n\n"
|
53
56
|
|
@@ -93,6 +96,13 @@ def parse_options
|
|
93
96
|
options[:proper] = false
|
94
97
|
end
|
95
98
|
|
99
|
+
# Repeat options.
|
100
|
+
opts.separator nil
|
101
|
+
opts.on('-n', '--number INTEGER',
|
102
|
+
"Number of poems to generate") do |n|
|
103
|
+
options[:number] = n.to_i
|
104
|
+
end
|
105
|
+
|
96
106
|
# Database options.
|
97
107
|
opts.separator nil
|
98
108
|
opts.on('-o', '--overwrite',
|
@@ -145,8 +155,8 @@ data = (not STDIN.tty? and not STDIN.closed?) ? STDIN.read : nil
|
|
145
155
|
# Database is the first argument.
|
146
156
|
first_arg = ARGV.first
|
147
157
|
if first_arg.nil?
|
148
|
-
puts "ERROR: Please specify a database to read from
|
149
|
-
exit
|
158
|
+
STDERR.puts "ERROR: Please specify a database to read from / to"
|
159
|
+
exit 1
|
150
160
|
end
|
151
161
|
|
152
162
|
# If the first argument is 'make_dbs', then make
|
@@ -179,34 +189,50 @@ end
|
|
179
189
|
|
180
190
|
# Poetic form name is the second argument, if it exists.
|
181
191
|
second_arg = (ARGV.length > 1) ? ARGV[1] : ''
|
182
|
-
options[:form] = second_arg
|
183
|
-
|
184
|
-
#
|
185
|
-
|
192
|
+
options[:form] = second_arg if second_arg != ''
|
193
|
+
|
194
|
+
# If we need to make a database.
|
195
|
+
# Exit the program after database is generated.
|
196
|
+
if options[:overwrite]
|
197
|
+
poefy = Poefy::PoefyGen.new first_arg
|
198
|
+
if data
|
199
|
+
poefy.make_database data, true
|
200
|
+
poefy.close
|
201
|
+
exit 0
|
202
|
+
else
|
203
|
+
STDERR.puts 'ERROR: Need text input to generate a database'
|
204
|
+
STDERR.puts ' Please pipe some data into the program'
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
end
|
186
208
|
|
187
209
|
# If the second argument is 'rhyme', then output all
|
188
210
|
# lines that rhyme with the word.
|
189
211
|
if second_arg == 'rhyme'
|
212
|
+
poefy = Poefy::PoefyGen.new first_arg
|
190
213
|
third_arg = (ARGV.length > 2) ? ARGV[2] : nil
|
191
214
|
fourth_arg = (ARGV.length > 3) ? ARGV[3] : nil
|
192
215
|
puts poefy.rhymes(third_arg, fourth_arg)
|
193
216
|
exit 0
|
194
217
|
end
|
195
218
|
|
196
|
-
# If the second argument is a file,
|
197
|
-
|
198
|
-
if File.exists?(second_arg)
|
199
|
-
|
219
|
+
# If there is piped data, or the second argument is a file,
|
220
|
+
# then use that as the poetic_form.
|
221
|
+
if data or File.exists?(second_arg)
|
222
|
+
options[:form_from_text] = (data || second_arg)
|
200
223
|
end
|
201
224
|
|
202
|
-
# Create
|
203
|
-
|
204
|
-
poefy.make_database data, options[:overwrite]
|
225
|
+
# Create poefy object using the options.
|
226
|
+
poefy = Poefy::PoefyGen.new first_arg, options
|
205
227
|
|
206
|
-
# Make
|
207
|
-
|
208
|
-
|
209
|
-
|
228
|
+
# Make the correct number of poems, and output them.
|
229
|
+
number = options[:number] || 1
|
230
|
+
number.times do |i|
|
231
|
+
poem = poefy.poem
|
232
|
+
if poem
|
233
|
+
puts poem
|
234
|
+
puts nil if i < number - 1
|
235
|
+
end
|
210
236
|
end
|
211
237
|
|
212
238
|
# Close the database connection.
|
data/lib/poefy.rb
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
|
13
13
|
require 'ruby_rhymes'
|
14
14
|
require 'wordfilter'
|
15
|
+
require 'humanize'
|
15
16
|
require 'tempfile'
|
16
17
|
require 'sqlite3'
|
17
18
|
require 'timeout'
|
@@ -26,6 +27,7 @@ require_relative 'poefy/string_manipulation.rb'
|
|
26
27
|
require_relative 'poefy/handle_error.rb'
|
27
28
|
require_relative 'poefy/database.rb'
|
28
29
|
require_relative 'poefy/conditional_satisfaction.rb'
|
30
|
+
require_relative 'poefy/core_extensions/array.rb'
|
29
31
|
|
30
32
|
################################################################################
|
31
33
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Encoding: UTF-8
|
3
|
+
|
4
|
+
################################################################################
|
5
|
+
# Monkey patch the Array class.
|
6
|
+
################################################################################
|
7
|
+
|
8
|
+
# [array] is the same array as [self], but ordered by closeness to the index.
|
9
|
+
# Optionally pass an integer, for results for just that index element.
|
10
|
+
# Returns a Struct, or an array of Structs, in the form:
|
11
|
+
# .index => original index
|
12
|
+
# .value => original element
|
13
|
+
# .array => self array minus value, ordered by closeness to index
|
14
|
+
# Example usage:
|
15
|
+
# lines = (1..4).to_a * 2
|
16
|
+
# puts lines.by_distance
|
17
|
+
# puts lines.by_distance(3)
|
18
|
+
# lines.by_distance(3).each { ... }
|
19
|
+
module Poefy
|
20
|
+
module CoreExtensions
|
21
|
+
|
22
|
+
# Output struct for #by_distance method.
|
23
|
+
# Array is the most useful data, but index and value are also kept.
|
24
|
+
IndexValueArray = Struct.new(:index, :value, :array) do
|
25
|
+
alias_method :to_a, :array
|
26
|
+
include Enumerable
|
27
|
+
def each &block
|
28
|
+
array.each do |i|
|
29
|
+
block.call i
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Array
|
35
|
+
|
36
|
+
def by_distance index = nil
|
37
|
+
if index.nil?
|
38
|
+
self.map.with_index do |value, index|
|
39
|
+
self.by_distance index
|
40
|
+
end
|
41
|
+
else
|
42
|
+
others, counter = [], 0
|
43
|
+
loop do
|
44
|
+
counter += 1
|
45
|
+
below_index = index - counter
|
46
|
+
below_index = nil if below_index < 0
|
47
|
+
below = self[below_index] if below_index
|
48
|
+
above = self[index + counter]
|
49
|
+
others << below if below
|
50
|
+
others << above if above
|
51
|
+
break if !above and !below
|
52
|
+
end
|
53
|
+
IndexValueArray.new(index, self[index], others)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Array
|
61
|
+
include Poefy::CoreExtensions::Array
|
62
|
+
end
|
63
|
+
|
64
|
+
################################################################################
|
data/lib/poefy/database.rb
CHANGED
@@ -158,17 +158,28 @@ module Poefy
|
|
158
158
|
def save_sql_import_file lines
|
159
159
|
sql_lines = []
|
160
160
|
lines.map do |line|
|
161
|
+
|
162
|
+
# Don't add the line if it contains a blacklisted? substring.
|
161
163
|
next if Wordfilter.blacklisted? line
|
164
|
+
|
165
|
+
# Format the line for SQL parsing.
|
162
166
|
line_ = format_sql_string line
|
163
|
-
final = line.to_phrase.last_word.downcase rescue ''
|
164
167
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
+
# Get the phrase info for the line.
|
169
|
+
phrase = phrase_info line
|
170
|
+
syll = phrase[:syllables]
|
171
|
+
rhymes = phrase[:rhymes]
|
172
|
+
final_ = format_sql_string phrase[:last_word]
|
173
|
+
|
174
|
+
# There may be more than one rhyme, so add a database
|
175
|
+
# record for each rhyme.
|
176
|
+
rhymes.each do |rhyme|
|
168
177
|
rhyme_ = format_sql_string rhyme
|
169
178
|
sql_lines << "\"#{line_}\"\t#{syll}\t\"#{final_}\"\t\"#{rhyme_}\""
|
170
179
|
end
|
171
180
|
end
|
181
|
+
|
182
|
+
# Save the SQL spec to a temporary file, and return the filename.
|
172
183
|
sql_file = tmpfile
|
173
184
|
File.open(sql_file, 'w') { |fo| fo.puts sql_lines }
|
174
185
|
sql_file
|
data/lib/poefy/poefy_gen_base.rb
CHANGED
@@ -18,11 +18,13 @@ module Poefy
|
|
18
18
|
|
19
19
|
# Make a database using the given lines.
|
20
20
|
def make_database input, overwrite = @overwrite
|
21
|
+
lines = validate_lines input
|
22
|
+
lines.map(&:strip!)
|
21
23
|
@db.close if @db
|
22
24
|
if overwrite
|
23
|
-
@db.make_new!
|
25
|
+
@db.make_new! lines
|
24
26
|
else
|
25
|
-
@db.make_new
|
27
|
+
@db.make_new lines
|
26
28
|
end
|
27
29
|
end
|
28
30
|
def make_database! input
|
@@ -43,7 +45,6 @@ module Poefy
|
|
43
45
|
|
44
46
|
# If lines is not an array, assume string and split on newlines.
|
45
47
|
lines = lines.respond_to?(:each) ? lines : lines.split("\n")
|
46
|
-
lines.map(&:strip!)
|
47
48
|
lines
|
48
49
|
end
|
49
50
|
|
@@ -77,6 +78,13 @@ module Poefy
|
|
77
78
|
input, output = poetic_form, {}
|
78
79
|
form_string = get_valid_form input[:form]
|
79
80
|
|
81
|
+
# Apply ':form_from_text' before any others.
|
82
|
+
if input[:form_from_text]
|
83
|
+
lines = validate_lines input[:form_from_text]
|
84
|
+
form = poetic_form_from_text lines
|
85
|
+
input = form.merge input
|
86
|
+
end
|
87
|
+
|
80
88
|
# Handle obvious inputs.
|
81
89
|
output[:form] = form_string if form_string
|
82
90
|
output[:rhyme] = input[:rhyme] if input[:rhyme]
|
@@ -10,16 +10,29 @@ module Poefy
|
|
10
10
|
module PoeticFormFromText
|
11
11
|
|
12
12
|
# Read a song lyric file, output a poetic_form that matches its form.
|
13
|
-
def poetic_form_from_text
|
14
|
-
|
13
|
+
def poetic_form_from_text lines
|
14
|
+
|
15
|
+
# If lines is not an array, assume string and split on newlines.
|
16
|
+
lines = lines.respond_to?(:each) ? lines : lines.split("\n")
|
17
|
+
|
18
|
+
# Remove duplicate '' elements that are neighbours in the array.
|
19
|
+
# https://genius.com/The-monkees-im-a-believer-lyrics
|
20
|
+
prev_line = ''
|
21
|
+
lines.map! do |i|
|
22
|
+
out = (i == '' && prev_line == '') ? nil : i
|
23
|
+
prev_line = i
|
24
|
+
out
|
25
|
+
end
|
26
|
+
lines.compact!
|
15
27
|
|
16
28
|
# For refrains, we don't care about the lines exactly, just
|
17
29
|
# the structure. So we can delete punctuation and downcase.
|
18
30
|
lines = lines.map do |line|
|
19
|
-
{
|
20
|
-
|
21
|
-
|
22
|
-
|
31
|
+
hash = {}
|
32
|
+
hash[:orig] = line
|
33
|
+
hash[:strip] = line.strip
|
34
|
+
hash[:downcase] = line.strip.gsub(/[[:punct:]]/, '').downcase
|
35
|
+
hash
|
23
36
|
end
|
24
37
|
|
25
38
|
# Find all the lines that are duplicated.
|
@@ -38,27 +51,26 @@ module Poefy
|
|
38
51
|
hash = {}
|
39
52
|
|
40
53
|
# Text of the line.
|
41
|
-
hash[:
|
54
|
+
hash[:strip] = line[:strip]
|
42
55
|
hash[:downcase] = line[:downcase]
|
43
56
|
|
57
|
+
# Get the phrase info for the line.
|
58
|
+
phrase = phrase_info line[:strip]
|
59
|
+
|
44
60
|
# Misc details.
|
45
61
|
hash[:num] = index + 1
|
46
|
-
hash[:syllable] =
|
47
|
-
hash[:last_word] =
|
62
|
+
hash[:syllable] = phrase[:syllables]
|
63
|
+
hash[:last_word] = phrase[:last_word]
|
64
|
+
hash[:indent] = (line[:orig].length - line[:orig].lstrip.length) / 2
|
48
65
|
|
49
|
-
# The rhyme for the line.
|
50
|
-
|
51
|
-
rhyme_tag = get_rhymes(hash[:downcase]).first
|
52
|
-
hash[:rhyme_tag] = rhyme_tag || ' '
|
53
|
-
hash[:rhyme_letter] = rhyme_tag
|
54
|
-
hash[:rhyme] = ' ' if hash[:downcase] == ''
|
66
|
+
# The rhyme tag array for the line.
|
67
|
+
hash[:rhyme_tags] = phrase[:rhymes]
|
55
68
|
|
56
69
|
# Map [:refrain] and [:exact].
|
57
70
|
# (They are mutually exclusive)
|
58
71
|
# If it needs to be an exact line, we don't need rhyme tokens.
|
59
|
-
if bracketed?(line[:
|
60
|
-
hash[:exact] = line[:
|
61
|
-
hash[:rhyme] = ' '
|
72
|
+
if bracketed?(line[:strip])
|
73
|
+
hash[:exact] = line[:strip]
|
62
74
|
hash[:rhyme_letter] = nil
|
63
75
|
hash[:syllable] = 0
|
64
76
|
elsif refrains.keys.include?(line[:downcase])
|
@@ -68,14 +80,32 @@ module Poefy
|
|
68
80
|
hash
|
69
81
|
end
|
70
82
|
|
71
|
-
|
72
|
-
|
83
|
+
# [:rhyme_tags] may well contain more than one rhyme tag.
|
84
|
+
# e.g. 'wind' rhymes with 'sinned' and 'find'.
|
85
|
+
# So we will compare this array against the rhymes of each
|
86
|
+
# other line in the array, to find the correct one to use.
|
87
|
+
# We will work from the closest lines, until we find a match.
|
88
|
+
lines.each.with_index do |line, index|
|
89
|
+
|
90
|
+
# Compare each other rhyme tag, order by closeness.
|
91
|
+
found_rhyme = line[:rhyme_tags].first
|
92
|
+
if line[:rhyme_tags].length > 1
|
93
|
+
lines.by_distance(index).each do |i|
|
94
|
+
i[:rhyme_tags].each do |tag|
|
95
|
+
if line[:rhyme_tags].include?(tag)
|
96
|
+
found_rhyme = tag
|
97
|
+
break
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
73
102
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
103
|
+
# If we haven't found the rhyme, then it doesn't matter,
|
104
|
+
# just use the first in the tag array.
|
105
|
+
lines[index][:rhyme_tags] = *found_rhyme
|
106
|
+
lines[index][:rhyme_tag] = found_rhyme
|
107
|
+
lines[index][:rhyme_letter] = found_rhyme
|
108
|
+
end
|
79
109
|
|
80
110
|
# Split into separate sections, [:rhyme] and [:syllable].
|
81
111
|
rhyme = lines.map do |line|
|
@@ -92,9 +122,15 @@ module Poefy
|
|
92
122
|
syllable[index+1] = line[:syllable] if line[:syllable] > 0
|
93
123
|
end
|
94
124
|
|
125
|
+
# Has to be a single character, so 9 is the maximum.
|
126
|
+
indent = lines.map do |line|
|
127
|
+
line[:indent] >= 9 ? 9 : line[:indent]
|
128
|
+
end.join
|
129
|
+
|
95
130
|
poetic_form = {
|
96
131
|
rhyme: rhyme,
|
97
|
-
syllable: syllable
|
132
|
+
syllable: syllable,
|
133
|
+
indent: indent
|
98
134
|
}
|
99
135
|
poetic_form
|
100
136
|
end
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
require 'ruby_rhymes'
|
9
9
|
require 'wordfilter'
|
10
|
+
require 'humanize'
|
10
11
|
|
11
12
|
################################################################################
|
12
13
|
|
@@ -27,14 +28,67 @@ module Poefy
|
|
27
28
|
(text.gsub(/[[:punct:]]/,'').scan(/^[^ ]+/).first rescue '') || ''
|
28
29
|
end
|
29
30
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
31
|
+
# Return info that is returned using 'ruby_rhymes' '#to_phrase'
|
32
|
+
# But also account for numbers and initialisms.
|
33
|
+
def phrase_info text
|
34
|
+
input = humanize_instr text
|
35
|
+
phrase = input.to_phrase rescue nil
|
36
|
+
return { rhymes: [], syllables: 0, last_word: '' } if phrase.nil?
|
37
|
+
last_word = phrase.last_word.downcase rescue ''
|
38
|
+
rhy = phrase.rhymes.keys rescue []
|
39
|
+
rhy = rhyme_initialism(input) if rhy.empty?
|
40
|
+
syl = syllables_correct text rescue 0
|
41
|
+
{ rhymes: rhy, syllables: syl, last_word: last_word }
|
33
42
|
end
|
34
43
|
|
35
|
-
#
|
36
|
-
|
37
|
-
|
44
|
+
# Humanize every number in the text.
|
45
|
+
# This will not work for floats.
|
46
|
+
# It will also break emoticons, but GIGO.
|
47
|
+
def humanize_instr text
|
48
|
+
output = text
|
49
|
+
loop do
|
50
|
+
num = output[/\d+/]
|
51
|
+
break if not num
|
52
|
+
output.sub!(num, num.to_i.humanize)
|
53
|
+
end
|
54
|
+
output
|
55
|
+
end
|
56
|
+
|
57
|
+
# We will only call this method if there are no dictionary rhymes.
|
58
|
+
# If the last word is uppercase, then assume it's an initialism.
|
59
|
+
# Get the last letter and rhyme that.
|
60
|
+
# Else, return an empty array, as normal.
|
61
|
+
def rhyme_initialism text
|
62
|
+
output = []
|
63
|
+
last_word = text.split.last
|
64
|
+
if last_word and last_word == last_word.upcase
|
65
|
+
letter = last_word.scan(/[A-Z]/).last
|
66
|
+
output = letter.to_phrase.rhymes.keys rescue []
|
67
|
+
end
|
68
|
+
output
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get the correct syllable count, even for initialisms.
|
72
|
+
# e.g. "Flew in from Miami Beach BOAC"
|
73
|
+
# "I'm back in the U.S.S.R"
|
74
|
+
def syllables_correct text
|
75
|
+
syll_count = 0
|
76
|
+
|
77
|
+
# This is similar to how 'ruby_rhymes' splits to word.
|
78
|
+
# But that gem ignores case, which we need.
|
79
|
+
text.gsub(/[^A-Z ']/i,'').split.each do |word|
|
80
|
+
|
81
|
+
# If the word has no rhymes, and it is uppercase, then assume
|
82
|
+
# it's an initialism and count each letter's syllables.
|
83
|
+
if word.to_phrase.rhyme_keys.empty? and word == word.upcase
|
84
|
+
word.gsub(/[^A-Z]/,'').split('').each do |i|
|
85
|
+
syll_count += i.to_phrase.syllables
|
86
|
+
end
|
87
|
+
else
|
88
|
+
syll_count += word.to_phrase.syllables
|
89
|
+
end
|
90
|
+
end
|
91
|
+
syll_count
|
38
92
|
end
|
39
93
|
|
40
94
|
# Final line must close with sentence-end punctuation.
|
data/lib/poefy/version.rb
CHANGED
data/poefy.gemspec
CHANGED
@@ -30,4 +30,5 @@ Gem::Specification.new do |s|
|
|
30
30
|
s.add_runtime_dependency('sqlite3', '~> 1.3', '>= 1.3.13')
|
31
31
|
s.add_runtime_dependency('ruby_rhymes', '~> 0.1', '>= 0.1.2')
|
32
32
|
s.add_runtime_dependency('wordfilter', '~> 0.2', '>= 0.2.6')
|
33
|
+
s.add_runtime_dependency('humanize', '~> 1.4', '>= 1.4.0')
|
33
34
|
end
|
data/spec/poefy_spec.rb
CHANGED
@@ -618,6 +618,105 @@ describe Poefy::PoefyGen do
|
|
618
618
|
end
|
619
619
|
end
|
620
620
|
|
621
|
+
##############################################################################
|
622
|
+
|
623
|
+
describe "using the form_from_text option" do
|
624
|
+
before(:all) do
|
625
|
+
@file = "#{@root}/data/i_want_to_hold_your_hand.txt"
|
626
|
+
end
|
627
|
+
|
628
|
+
it "should use the exact poetic form 1" do
|
629
|
+
poefy = Poefy::PoefyGen.new(:beatles, {
|
630
|
+
form_from_text: @file
|
631
|
+
})
|
632
|
+
poem = poefy.poem
|
633
|
+
expect(poem.count).to be 46
|
634
|
+
end
|
635
|
+
|
636
|
+
it "should use the exact poetic form 2" do
|
637
|
+
poefy = Poefy::PoefyGen.new :beatles
|
638
|
+
poem = poefy.poem({
|
639
|
+
form_from_text: @file
|
640
|
+
})
|
641
|
+
expect(poem.count).to be 46
|
642
|
+
end
|
643
|
+
|
644
|
+
it "should correctly modify the poetic form 1" do
|
645
|
+
poefy = Poefy::PoefyGen.new(:beatles, {
|
646
|
+
form_from_text: @file,
|
647
|
+
syllable: 6
|
648
|
+
})
|
649
|
+
poem = poefy.poem
|
650
|
+
expect(poem.count).to be 46
|
651
|
+
end
|
652
|
+
|
653
|
+
it "should correctly modify the poetic form 2" do
|
654
|
+
poefy = Poefy::PoefyGen.new :beatles
|
655
|
+
poem = poefy.poem({
|
656
|
+
form_from_text: @file,
|
657
|
+
syllable: 6
|
658
|
+
})
|
659
|
+
expect(poem.count).to be 46
|
660
|
+
end
|
661
|
+
|
662
|
+
it "should correctly modify the poetic form 3" do
|
663
|
+
poefy = Poefy::PoefyGen.new(:beatles, {
|
664
|
+
form_from_text: @file
|
665
|
+
})
|
666
|
+
poem = poefy.poem({
|
667
|
+
syllable: 6
|
668
|
+
})
|
669
|
+
expect(poem.count).to be 46
|
670
|
+
end
|
671
|
+
|
672
|
+
it "should correctly replace the poetic form" do
|
673
|
+
poefy = Poefy::PoefyGen.new(:beatles, {
|
674
|
+
syllable: 6
|
675
|
+
})
|
676
|
+
poem = poefy.poem({
|
677
|
+
form_from_text: @file
|
678
|
+
})
|
679
|
+
expect(poem.count).to be 46
|
680
|
+
end
|
681
|
+
|
682
|
+
############################################################################
|
683
|
+
|
684
|
+
describe "private method #poetic_form_from_text" do
|
685
|
+
|
686
|
+
# Singleton which includes the method.
|
687
|
+
# Make the private methods public.
|
688
|
+
let(:obj) do
|
689
|
+
class Sing
|
690
|
+
include Poefy::PoeticFormFromText
|
691
|
+
include Poefy::StringManipulation
|
692
|
+
public *private_instance_methods
|
693
|
+
end.new
|
694
|
+
end
|
695
|
+
|
696
|
+
it "80 should rhyme with weighty" do
|
697
|
+
lines = ["Lorem ipsum dolor weighty", "Lorem ipsum dolor 80"]
|
698
|
+
form = obj.poetic_form_from_text(lines)
|
699
|
+
expect(form[:rhyme].uniq.count).to be 1
|
700
|
+
end
|
701
|
+
it "80 should not rhyme with shoe" do
|
702
|
+
lines = ["Lorem ipsum dolor shoe", "Lorem ipsum dolor 80"]
|
703
|
+
form = obj.poetic_form_from_text(lines)
|
704
|
+
expect(form[:rhyme].uniq.count).to_not be 1
|
705
|
+
end
|
706
|
+
it "2 should rhyme with shoe" do
|
707
|
+
lines = ["Lorem ipsum dolor shoe", "Lorem ipsum dolor 2"]
|
708
|
+
form = obj.poetic_form_from_text(lines)
|
709
|
+
expect(form[:rhyme].uniq.count).to be 1
|
710
|
+
end
|
711
|
+
it "2 should not rhyme with weighty" do
|
712
|
+
lines = ["Lorem ipsum dolor weighty", "Lorem ipsum dolor 2"]
|
713
|
+
form = obj.poetic_form_from_text(lines)
|
714
|
+
expect(form[:rhyme].uniq.count).to_not be 1
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
end
|
719
|
+
|
621
720
|
end
|
622
721
|
|
623
722
|
################################################################################
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: poefy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Thompson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -112,6 +112,26 @@ dependencies:
|
|
112
112
|
- - ">="
|
113
113
|
- !ruby/object:Gem::Version
|
114
114
|
version: 0.2.6
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: humanize
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '1.4'
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.4.0
|
125
|
+
type: :runtime
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.4'
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 1.4.0
|
115
135
|
description: Create poems from an input text file, by generating and querying a SQLite
|
116
136
|
database describing each line. Poems are created using a template to select lines
|
117
137
|
from the database, according to closing rhyme, syllable count, and regex matching.
|
@@ -137,6 +157,7 @@ files:
|
|
137
157
|
- data/whitman_leaves.txt
|
138
158
|
- lib/poefy.rb
|
139
159
|
- lib/poefy/conditional_satisfaction.rb
|
160
|
+
- lib/poefy/core_extensions/array.rb
|
140
161
|
- lib/poefy/database.rb
|
141
162
|
- lib/poefy/generation.rb
|
142
163
|
- lib/poefy/handle_error.rb
|
@@ -169,7 +190,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
190
|
version: '0'
|
170
191
|
requirements: []
|
171
192
|
rubyforge_project:
|
172
|
-
rubygems_version: 2.
|
193
|
+
rubygems_version: 2.5.2
|
173
194
|
signing_key:
|
174
195
|
specification_version: 4
|
175
196
|
summary: Create rhyming poetry by rearranging lines of text
|