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