poefy 1.1.1 → 2.0.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/bin/poefy CHANGED
@@ -1,310 +1,347 @@
1
- #!/usr/bin/env ruby
2
- # Encoding: UTF-8
3
-
4
- ################################################################################
5
- # Use Poefy::Poem to make a poem from the command line.
6
- ################################################################################
7
-
8
- require 'optparse'
9
-
10
- require_relative '../lib/poefy.rb'
11
-
12
- Poefy.console = true
13
- Poefy.require_db
14
-
15
- ################################################################################
16
-
17
- # List the corpora & descriptions in a nice table format.
18
- def corpora
19
- output = Poefy.corpora_with_desc
20
- width = output.keys.max_by(&:length).length
21
- output.map do |key, value|
22
- sprintf "%-#{width}s %s", key, value
23
- end
24
- end
25
-
26
- # Create a new Poefy::Poem object, and catch any exceptions thrown.
27
- def poefy_new corpus, options = nil
28
- Poefy::Poem.new corpus, options
29
- rescue Poefy::DatabaseError => e
30
- STDERR.puts e.console_msg
31
- exit 1
32
- end
33
-
34
- ################################################################################
35
-
36
- def parse_options
37
- options = {}
38
-
39
- # Set up variables used later.
40
- forms = Poefy.poetic_forms
41
- forms_by_4 = forms.each_slice(4).to_a.map { |i| i.join ', ' }
42
- rhyme_docs = " This is the most important argument.
43
- All other form strings are based on this.
44
- Each token represents a line.
45
- (Token examples: 'a', 'b', 'A1', ' ')
46
- Letters indicate rhymes, so all 'a' or 'A' lines have the same rhyme.
47
- (Example, limerick: 'aabba')
48
- Uppercase letter lines will be duplicated exactly.
49
- This is used to create refrain lines.
50
- (Example, rondeau: 'aabba aabR aabbaR')
51
- Numbers after a capital letter indicate which specific line to repeat.
52
- (Example, villanelle: 'A1bA2 abA1 abA2 abA1 abA2 abA1A2'"
53
-
54
- # Get all of the command-line options.
55
- optparse = OptionParser.new do |opts|
56
-
57
- # Set a banner, displayed at the top of the help screen.
58
- program_info = %[ Poefy, Line-Based Poem Generator
59
- Version #{Poefy.version_number} - #{Poefy.version_date}
60
- https://github.com/nossidge/poefy
61
- Paul Thompson - nossidge@gmail.com
62
- ].gsub(' ',' ')
63
-
64
- usage = %[Usage: poefy shakespeare -m < shakespeare_sonnets.txt
65
- poefy shakespeare -d "The sonnets of Shakespeare"
66
- poefy shakespeare sonnet
67
- poefy spoke haiku
68
- poefy therese -r 'abab cdcd efef gg' -i '0101 0101 0011 01'
69
- poefy whitman -r 'A1bA2 abA1 abA2 abA1 abA2 abA1A2'
70
- poefy -Lc
71
- poefy -f
72
- ].gsub(' ',' ')
73
-
74
- list = 'Corpora: ' + corpora.join("\n ")
75
-
76
- opts.banner = program_info + "\n" + usage + "\n" + list + "\n\n"
77
-
78
- # These will be further validated within the class.
79
- opts.on('-f', '--form [STRING]',
80
- "A named poetic form for the output\n" +
81
- ' ' * 37 + "Specifies rhyme, indent and syllable\n" +
82
- ' ' * 37 + "One of:\n" +
83
- ' ' * 39 + forms_by_4.join("\n" + ' ' * 39)) do |s|
84
- if s.nil?
85
- puts forms
86
- exit 0
87
- end
88
- options[:form] = s
89
- end
90
- opts.on('-r', '--rhyme STRING', "(See 'Description of rhyme string' below)") do |s|
91
- options[:rhyme] = s
92
- end
93
- opts.on('-i', '--indent STRING', "Indentation of each line") do |s|
94
- options[:indent] = s
95
- end
96
- opts.on('-s', '--syllable STRING',
97
- "Apply syllable constraints to certain lines") do |s|
98
- options[:syllable] = s
99
- end
100
- opts.on('-x', '--regex STRING',
101
- "Apply regex constraints to certain lines") do |s|
102
- options[:regex] = s
103
- end
104
-
105
- # Options for acrostic poems.
106
- opts.on('-a', '--acrostic STRING',
107
- "Generate an acrostic on a certain word") do |s|
108
- options[:acrostic] = s
109
- end
110
- opts.on('-A', '--acrostic_x STRING',
111
- "Generate an acrostic with better handling of 'x'") do |s|
112
- options[:acrostic_x] = s
113
- end
114
-
115
- # Handle proper sentence structure.
116
- opts.separator nil
117
- opts.on('-p', '--proper',
118
- "Ensure first word is not 'and but or nor yet'\n" +
119
- ' ' * 39 + "and final line ends with closing punctuation'\n" +
120
- ' ' * 39 + "Defaults to ON -- Use this option to DISABLE") do
121
- options[:proper] = false
122
- end
123
- opts.on('-c', '--capital',
124
- "Capitalise the first letter of each line") do
125
- options[:transform] = proc { |line| line.capitalize }
126
- end
127
-
128
- # Poem output options.
129
- opts.separator nil
130
- opts.on('-n', '--number INTEGER',
131
- "Number of poems to generate") do |n|
132
- options[:number] = n.to_i
133
- end
134
-
135
- # Corpus options.
136
- opts.separator nil
137
- opts.on('-m', '--make [STRING]',
138
- "Make new or overwrite existing corpus with piped input\n" + ' ' * 39 +
139
- "Argument is a description of the corpus") do |s|
140
- options[:make_corpus] = true
141
- options[:corpus_desc] = s
142
- end
143
- opts.on('-d', '--desc STRING',
144
- "Overwrite the description of the corpus") do |s|
145
- options[:corpus_desc] = s
146
- end
147
- opts.on('-l', '--local',
148
- "(SQLite only) Default is to use database files from /data/\n" + ' ' * 39 +
149
- "With this option, paths are relative to working directory") do
150
- options[:local] = true
151
- end
152
-
153
- # Database internals.
154
- opts.separator nil
155
- opts.on('-L', '--list [C|D]',
156
- "List all the installed corpora\n" + ' ' * 39 +
157
- "Append 'c' or 'd' to list just the corpora or descriptions") do |s|
158
- s ||= ' '
159
- if s[0].casecmp('c').zero?
160
- puts Poefy.corpora
161
- elsif s[0].casecmp('d').zero?
162
- puts Poefy.corpora_with_desc.values
163
- else
164
- puts corpora
165
- end
166
- exit 0
167
- end
168
- opts.on('-D', '--database [pg|sqlite3]',
169
- "Display the database implementation setting\n" + ' ' * 39 +
170
- "Append 'pg' or 'sqlite3' to change programs") do |s|
171
- s ||= ' '
172
- if s[0].casecmp('p').zero?
173
- Poefy.database_type = 'pg'
174
- elsif s[0].casecmp('s').zero?
175
- Poefy.database_type = 'sqlite3'
176
- end
177
- puts Poefy.database_type
178
- exit 0
179
- end
180
-
181
- # Help output.
182
- opts.separator nil
183
- opts.on('-h', '--help', 'Display this help screen' ) do
184
- puts opts
185
- exit 0
186
- end
187
- opts.on('-v', '--version', 'Display the version number' ) do
188
- puts "poefy #{Poefy.version_number} (#{Poefy.version_date})"
189
- exit 0
190
- end
191
-
192
- opts.separator nil
193
- opts.separator "Description of rhyme string:"
194
- opts.separator rhyme_docs
195
- opts.separator nil
196
- opts.separator "All of this is much better documented in README.md"
197
- end
198
-
199
- # Parse the options and show errors on failure.
200
- begin
201
- optparse.parse! ARGV
202
- rescue OptionParser::ParseError => e
203
- puts e
204
- exit 1
205
- end
206
-
207
- options
208
- end
209
-
210
- ################################################################################
211
-
212
- # Parse the options to shift the ARGV list.
213
- options = parse_options
214
-
215
- # Read data lines from STDIN.
216
- data = (not STDIN.tty? and not STDIN.closed?) ? STDIN.read : nil
217
-
218
- # Corpus name is the first argument.
219
- first_arg = ARGV.first
220
- if first_arg.nil?
221
- STDERR.puts "ERROR: Please specify a corpus name to read from/to"
222
- exit 1
223
- end
224
-
225
- # Poetic form name is the second argument, if it exists.
226
- second_arg = (ARGV.length > 1) ? ARGV[1] : ''
227
- options[:form] = second_arg if second_arg != ''
228
-
229
- # If we need to make a corpus.
230
- # Exit the program after corpus is generated.
231
- if options[:make_corpus]
232
-
233
- # It's okay if there's an error with an existing database, because
234
- # we're creating a new one. So we can swallow any errors here.
235
- begin
236
- poefy = Poefy::Poem.new first_arg
237
- rescue Poefy::DatabaseError
238
- end
239
-
240
- if data
241
- poefy.make_database data, options[:corpus_desc], true
242
- poefy.close
243
- exit 0
244
- else
245
- STDERR.puts 'ERROR: Need text input to generate a corpus'
246
- STDERR.puts ' Please pipe some data into the program'
247
- exit 1
248
- end
249
- end
250
-
251
- # If we need to update a corpus description.
252
- # Exit the program after corpus is generated.
253
- if options[:corpus_desc]
254
- poefy = poefy_new first_arg
255
- begin
256
- poefy.corpus.desc = options[:corpus_desc]
257
- poefy.close
258
- exit 0
259
- rescue
260
- STDERR.puts "ERROR: Corpus '#{first_arg}' does not yet exist"
261
- exit 1
262
- end
263
- end
264
-
265
- # If the second argument is 'rhyme', then output all
266
- # lines that rhyme with the word.
267
- if second_arg == 'rhyme'
268
- poefy = poefy_new first_arg
269
- third_arg = (ARGV.length > 2) ? ARGV[2] : nil
270
- fourth_arg = (ARGV.length > 3) ? ARGV[3] : nil
271
- puts poefy.corpus.rhymes(third_arg, fourth_arg)
272
- exit 0
273
- end
274
-
275
- # If there is piped data, or the second argument is a file,
276
- # then use that as the poetic_form.
277
- if data or File.exists?(second_arg)
278
- options[:form_from_text] = (data || second_arg)
279
- end
280
-
281
- # Create poefy object using the options.
282
- begin
283
- poefy = poefy_new first_arg, options
284
- rescue Poefy::DatabaseError => e
285
- STDERR.puts e.console_msg
286
- exit 1
287
- end
288
-
289
- # Make the correct number of poems, and output them.
290
- number = options[:number] || 1
291
- number.times do |i|
292
-
293
- # Exit the program if a Poefy error is raised.
294
- begin
295
- poem = poefy.poem
296
- rescue Poefy::Error => e
297
- STDERR.puts e.console_msg
298
- exit 1
299
- end
300
-
301
- if poem
302
- puts poem
303
- puts nil if i < number - 1
304
- end
305
- end
306
-
307
- # Close the database connection.
308
- poefy.close
309
-
310
- ################################################################################
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ ################################################################################
5
+ # Use Poefy::Poem to make a poem from the command line.
6
+ ################################################################################
7
+
8
+ require 'optparse'
9
+
10
+ require_relative '../lib/poefy.rb'
11
+
12
+ Poefy.console = true
13
+ Poefy.require_db
14
+
15
+ ################################################################################
16
+
17
+ # List the corpora & descriptions in a nice table format.
18
+ def corpora
19
+ output = Poefy.corpora_with_desc
20
+ width = output.keys.max_by(&:length).length
21
+ output.map do |key, value|
22
+ sprintf "%-#{width}s %s", key, value
23
+ end
24
+ end
25
+
26
+ # Create a new Poefy::Poem object, and catch any exceptions thrown.
27
+ def poefy_new corpus, options = nil
28
+ Poefy::Poem.new corpus, options
29
+ rescue Poefy::DatabaseError => e
30
+ STDERR.puts e.console_msg
31
+ exit 1
32
+ end
33
+
34
+ ################################################################################
35
+
36
+ def parse_options
37
+ options = {}
38
+
39
+ # Set up variables used later.
40
+ forms = Poefy.poetic_forms
41
+ forms_by_4 = forms.each_slice(4).to_a.map { |i| i.join ', ' }
42
+ rhyme_docs = " This is the most important argument.
43
+ All other form strings are based on this.
44
+ Each token represents a line.
45
+ (Token examples: 'a', 'b', 'A1', ' ')
46
+ Letters indicate rhymes, so all 'a' or 'A' lines have the same rhyme.
47
+ (Example, limerick: 'aabba')
48
+ Uppercase letter lines will be duplicated exactly.
49
+ This is used to create refrain lines.
50
+ (Example, rondeau: 'aabba aabR aabbaR')
51
+ Numbers after a capital letter indicate which specific line to repeat.
52
+ (Example, villanelle: 'A1bA2 abA1 abA2 abA1 abA2 abA1A2'"
53
+
54
+ # Get all of the command-line options.
55
+ optparse = OptionParser.new do |opts|
56
+
57
+ # Set a banner, displayed at the top of the help screen.
58
+ program_info = %[ Poefy, Line-Based Poem Generator
59
+ Version #{Poefy.version_number} - #{Poefy.version_date}
60
+ https://github.com/nossidge/poefy
61
+ Paul Thompson - nossidge@gmail.com
62
+ ].gsub(' ',' ')
63
+
64
+ usage = %[Usage: poefy shakespeare -m < shakespeare_sonnets.txt
65
+ poefy shakespeare -d "The sonnets of Shakespeare"
66
+ poefy shakespeare sonnet
67
+ poefy spoke haiku
68
+ poefy therese -r 'abab cdcd efef gg' -i '0101 0101 0011 01'
69
+ poefy whitman -r 'A1bA2 abA1 abA2 abA1 abA2 abA1A2'
70
+ poefy -Lc
71
+ poefy -f
72
+ ].gsub(' ',' ')
73
+
74
+ list = 'Corpora: ' + corpora.join("\n ")
75
+
76
+ opts.banner = program_info + "\n" + usage + "\n" + list + "\n\n"
77
+
78
+ # These will be further validated within the class.
79
+ opts.on('-f', '--form [STRING]',
80
+ "A named poetic form for the output\n" +
81
+ ' ' * 37 + "Specifies rhyme, indent and syllable\n" +
82
+ ' ' * 37 + "One of:\n" +
83
+ ' ' * 39 + forms_by_4.join("\n" + ' ' * 39)) do |s|
84
+ if s.nil?
85
+ puts forms
86
+ exit 0
87
+ end
88
+ options[:form] = s
89
+ end
90
+ opts.on('-r', '--rhyme STRING', "(See 'Description of rhyme string' below)") do |s|
91
+ options[:rhyme] = s
92
+ end
93
+ opts.on('-i', '--indent STRING', "Indentation of each line") do |s|
94
+ options[:indent] = s
95
+ end
96
+ opts.on('-s', '--syllable STRING',
97
+ "Apply syllable constraints to certain lines") do |s|
98
+ options[:syllable] = s
99
+ end
100
+ opts.on('-x', '--regex STRING',
101
+ "Apply regex constraints to certain lines") do |s|
102
+ options[:regex] = s
103
+ end
104
+
105
+ # Options for acrostic poems.
106
+ opts.on('-a', '--acrostic STRING',
107
+ "Generate an acrostic on a certain word") do |s|
108
+ options[:acrostic] = s
109
+ end
110
+ opts.on('-A', '--acrostic_x STRING',
111
+ "Generate an acrostic with better handling of 'x'") do |s|
112
+ options[:acrostic_x] = s
113
+ end
114
+
115
+ # Handle proper sentence structure.
116
+ opts.separator nil
117
+ opts.on('-p', '--proper',
118
+ "Ensure first word is not 'and but or nor yet'\n" +
119
+ ' ' * 39 + "and final line ends with closing punctuation'\n" +
120
+ ' ' * 39 + "Defaults to ON -- Use this option to DISABLE") do
121
+ options[:proper] = false
122
+ end
123
+ opts.on('-c', '--capital',
124
+ "Capitalise the first letter of each line") do
125
+ options[:transform] = proc do |line|
126
+ regex = /[a-zA-Z]/
127
+ line[regex] = line[regex].upcase if line[regex]
128
+ line
129
+ end
130
+ end
131
+
132
+ # Poem output options.
133
+ opts.separator nil
134
+ opts.on('-n', '--number INTEGER',
135
+ "Number of poems to generate") do |n|
136
+ options[:number] = n.to_i
137
+ end
138
+
139
+ # Corpus options.
140
+ opts.separator nil
141
+ opts.on('-m', '--make [STRING]',
142
+ "Make new or overwrite existing corpus with piped input\n" + ' ' * 39 +
143
+ "Argument is a description of the corpus") do |s|
144
+ options[:make_corpus] = true
145
+ options[:corpus_desc] = s
146
+ end
147
+ opts.on('-d', '--desc STRING',
148
+ "Overwrite the description of the corpus") do |s|
149
+ options[:corpus_desc] = s
150
+ end
151
+ opts.on('-l', '--local',
152
+ "(SQLite only) Default is to use database files from /data/\n" + ' ' * 39 +
153
+ "With this option, paths are relative to working directory") do
154
+ options[:local] = true
155
+ end
156
+
157
+ # Database internals.
158
+ opts.separator nil
159
+ opts.on('-L', '--list [C|D]',
160
+ "List all the installed corpora\n" + ' ' * 39 +
161
+ "Append 'c' or 'd' to list just the corpora or descriptions") do |s|
162
+ s ||= ' '
163
+ if s[0].casecmp('c').zero?
164
+ puts Poefy.corpora
165
+ elsif s[0].casecmp('d').zero?
166
+ puts Poefy.corpora_with_desc.values
167
+ else
168
+ puts corpora
169
+ end
170
+ exit 0
171
+ end
172
+ opts.on('-D', '--database [pg|sqlite3]',
173
+ "Display the database implementation setting\n" + ' ' * 39 +
174
+ "Append 'pg' or 'sqlite3' to change programs") do |s|
175
+ s ||= ' '
176
+ if s[0].casecmp('p').zero?
177
+ Poefy.database_type = 'pg'
178
+ elsif s[0].casecmp('s').zero?
179
+ Poefy.database_type = 'sqlite3'
180
+ end
181
+ puts Poefy.database_type
182
+ exit 0
183
+ end
184
+
185
+ # Help output.
186
+ opts.separator nil
187
+ opts.on('-h', '--help', 'Display this help screen' ) do
188
+ puts opts
189
+ exit 0
190
+ end
191
+ opts.on('-v', '--version', 'Display the version number' ) do
192
+ puts "poefy #{Poefy.version_number} (#{Poefy.version_date})"
193
+ exit 0
194
+ end
195
+
196
+ opts.separator nil
197
+ opts.separator "Description of rhyme string:"
198
+ opts.separator rhyme_docs
199
+ opts.separator nil
200
+ opts.separator "All of this is much better documented in README.md"
201
+ end
202
+
203
+ # Parse the options and show errors on failure.
204
+ begin
205
+ optparse.parse! ARGV
206
+ rescue OptionParser::ParseError => e
207
+ puts e
208
+ exit 1
209
+ end
210
+
211
+ options
212
+ end
213
+
214
+ ################################################################################
215
+
216
+ # Parse the options to shift the ARGV list.
217
+ options = parse_options
218
+
219
+ # Read data lines from STDIN.
220
+ data = (not STDIN.tty? and not STDIN.closed?) ? STDIN.read : nil
221
+
222
+ # Corpus name is the first argument.
223
+ first_arg = ARGV.first
224
+ if first_arg.nil?
225
+ STDERR.puts "ERROR: Please specify a corpus name to read from/to"
226
+ exit 1
227
+ end
228
+
229
+ # Poetic form name is the second argument, if it exists.
230
+ second_arg = (ARGV.length > 1) ? ARGV[1] : ''
231
+ options[:form] = second_arg if second_arg != ''
232
+
233
+ # If we need to make a corpus.
234
+ # Exit the program after corpus is generated.
235
+ if options[:make_corpus]
236
+
237
+ # It's okay if there's an error with an existing database, because
238
+ # we're creating a new one. So we can swallow any errors here.
239
+ begin
240
+ poefy = Poefy::Poem.new first_arg
241
+ rescue Poefy::DatabaseError
242
+ end
243
+
244
+ if data
245
+ poefy.make_database data, options[:corpus_desc], true
246
+ poefy.close
247
+ exit 0
248
+ else
249
+ STDERR.puts 'ERROR: Need text input to generate a corpus'
250
+ STDERR.puts ' Please pipe some data into the program'
251
+ exit 1
252
+ end
253
+ end
254
+
255
+ # If we need to update a corpus description.
256
+ # Exit the program after corpus is generated.
257
+ if options[:corpus_desc]
258
+ poefy = poefy_new first_arg
259
+ begin
260
+ poefy.corpus.desc = options[:corpus_desc]
261
+ poefy.close
262
+ exit 0
263
+ rescue
264
+ STDERR.puts "ERROR: Corpus '#{first_arg}' does not yet exist"
265
+ exit 1
266
+ end
267
+ end
268
+
269
+ # If the second argument is 'rhyme', then output all
270
+ # lines that rhyme with the word.
271
+ if second_arg == 'rhyme'
272
+ poefy = poefy_new first_arg
273
+ rhyme_word = (ARGV.length > 2) ? ARGV[2] : nil
274
+ rhyme_key = (ARGV.length > 3) ? ARGV[3] : nil
275
+
276
+ # If the value for 'rhyme_key' is instead 'json', then set that bool.
277
+ is_output_json = (rhyme_key.to_s.downcase == 'json')
278
+ rhyme_key = nil if is_output_json
279
+
280
+ # This will return an array of elements.
281
+ # If the 'rhyme_key' is nil, it will be an array of hashes.
282
+ # If the 'rhyme_key' is not, it will be an array of strings.
283
+ lines = poefy.corpus.rhymes(rhyme_word, rhyme_key)
284
+
285
+ if is_output_json or !rhyme_key.nil?
286
+ puts lines
287
+ else
288
+ # Convert the array of hashes to an array of arrays.
289
+ lines.map! { |i| i.values }
290
+
291
+ # Find the max length of each column.
292
+ max_len = []
293
+ lines.each do |row|
294
+ row.each.with_index do |col, index|
295
+ max_len[index] ||= 0
296
+ if col.length > max_len[index]
297
+ max_len[index] = col.length
298
+ end
299
+ end
300
+ end
301
+
302
+ # Output to console as whitespace delimited.
303
+ lines.each do |i|
304
+ pform = "%-#{max_len[0]+3}s %-#{max_len[1]+3}s "
305
+ pform += "%-#{max_len[2]+3}s %-#{max_len[3]}s\n"
306
+ printf pform, *i
307
+ end
308
+ end
309
+ exit 0
310
+ end
311
+
312
+ # If there is piped data, or the second argument is a file,
313
+ # then use that as the poetic_form.
314
+ if data or File.exist?(second_arg)
315
+ options[:form_from_text] = (data || second_arg)
316
+ end
317
+
318
+ # Create poefy object using the options.
319
+ begin
320
+ poefy = poefy_new first_arg, options
321
+ rescue Poefy::DatabaseError => e
322
+ STDERR.puts e.console_msg
323
+ exit 1
324
+ end
325
+
326
+ # Make the correct number of poems, and output them.
327
+ number = options[:number] || 1
328
+ number.times do |i|
329
+
330
+ # Exit the program if a Poefy error is raised.
331
+ begin
332
+ poem = poefy.poem
333
+ rescue Poefy::Error => e
334
+ STDERR.puts e.console_msg
335
+ exit 1
336
+ end
337
+
338
+ if poem
339
+ puts poem
340
+ puts nil if i < number - 1
341
+ end
342
+ end
343
+
344
+ # Close the database connection.
345
+ poefy.close
346
+
347
+ ################################################################################