poefy-pg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 562ca9a5d0e1e8fc84fce3f1d2cc1191f67b72b8
4
+ data.tar.gz: 532339860c2f1ad84288c8232162b31de8854154
5
+ SHA512:
6
+ metadata.gz: 8a95524ade873f8ff08785a8d4abdb94bb808f15c92fb73bf5fb6daa11f6c9f62f288ef86d44fc305f186a31eb6ea2369927f9471a769b00bb88c6a713cfa270
7
+ data.tar.gz: 1f7f41a631894cb7191805f0d8024290dc2757c0f1fdcd2c10ba4d3c6a69dbf4f5341407d5174262ce1f8ec00c0ef146339dea24cf540d688d713b35580863b6
data/.gitignore ADDED
@@ -0,0 +1,77 @@
1
+
2
+ ################################################################################
3
+ # Ruby specific files
4
+
5
+ *.gem
6
+ *.rbc
7
+ /.config
8
+ /coverage/
9
+ /InstalledFiles
10
+ /pkg/
11
+ /spec/reports/
12
+ /test/tmp/
13
+ /test/version_tmp/
14
+ /tmp/
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+
21
+ ## Documentation cache and generated files:
22
+ /.yardoc/
23
+ /_yardoc/
24
+ /doc/
25
+ /rdoc/
26
+
27
+ ## Environment normalisation:
28
+ /.bundle/
29
+ /vendor/bundle
30
+ /lib/bundler/man/
31
+
32
+ # for a library or gem, you might want to ignore these files since the code is
33
+ # intended to run in multiple environments; otherwise, check them in:
34
+ Gemfile.lock
35
+ .ruby-version
36
+ .ruby-gemset
37
+
38
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
+ .rvmrc
40
+
41
+ ################################################################################
42
+ # Rails stuff
43
+
44
+ # Ignore all logfiles and tempfiles.
45
+ /log/*
46
+ /tmp/*
47
+ !/log/.keep
48
+ !/tmp/.keep
49
+
50
+ # Ignore Byebug command history file.
51
+ .byebug_history
52
+
53
+ # Ignore application configuration
54
+ /config/application.yml
55
+
56
+ ################################################################################
57
+ # System and config files
58
+ desktop.ini
59
+ .agignore
60
+ .ignore
61
+ *.lnk
62
+
63
+ ################################################################################
64
+ # App specific files
65
+
66
+ # Development files
67
+ work*.rb
68
+ /~/
69
+
70
+ # Config files
71
+ settings.yml
72
+
73
+ # Data files
74
+ /data/
75
+
76
+ # Output files
77
+ /output/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (C) 2017 Paul Thompson
2
+
3
+ This program is free software: you can redistribute it and/or modify
4
+ it under the terms of the GNU General Public License as published by
5
+ the Free Software Foundation, either version 3 of the License, or
6
+ (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful,
9
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ GNU General Public License for more details.
12
+
13
+ Full text of this licence: <https://www.gnu.org/licenses/gpl.html>
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # poefy-pg
2
+
3
+ by Paul Thompson - nossidge@gmail.com
4
+
5
+ PostgreSQL interface for the 'poefy' gem.
6
+
7
+ Please see the [poefy](https://github.com/nossidge/poefy) documentation.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.verbose = false
6
+ end
7
+ task :default => :spec
8
+ task :test => :spec
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ module Poefy
5
+
6
+ module Pg
7
+
8
+ def self.version_number
9
+ major = 0
10
+ minor = 1
11
+ tiny = 0
12
+ pre = nil
13
+
14
+ string = [major, minor, tiny, pre].compact.join('.')
15
+ Gem::Version.new string
16
+ end
17
+
18
+ def self.version_date
19
+ '2017-10-02'
20
+ end
21
+
22
+ end
23
+
24
+ end
data/lib/poefy/pg.rb ADDED
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ ################################################################################
5
+ # Extend 'Database' class for connecting to a PostgreSQL database.
6
+ # These methods are specific to PostgreSQL.
7
+ # Other databases should be implemented in separate gems.
8
+ ################################################################################
9
+
10
+ require 'pg'
11
+
12
+ ################################################################################
13
+
14
+ module Poefy
15
+
16
+ class Database
17
+
18
+ # Open a connection, execute a query, close the connection.
19
+ def self.single_exec! sql, sql_args = nil
20
+ output = nil
21
+ begin
22
+ con = PG.connect(
23
+ :dbname => 'poefy',
24
+ :user => 'poefy',
25
+ :password => 'poefy'
26
+ )
27
+ output = if sql_args
28
+ con.exec(sql, [*sql_args]).values
29
+ else
30
+ con.exec(sql).values
31
+ end
32
+ ensure
33
+ con.close if con
34
+ end
35
+ output
36
+ end
37
+
38
+ # List all tables in the database.
39
+ # Does not include tables used for testing.
40
+ def self.list
41
+ rs = Database::single_exec! <<-SQL
42
+ SELECT table_name
43
+ FROM information_schema.tables
44
+ WHERE table_schema = 'public';
45
+ SQL
46
+ rs.flatten.reject do |i|
47
+ i.start_with?('spec_')
48
+ end.sort - ['test']
49
+ end
50
+
51
+ # Get the description of a table.
52
+ def self.desc table_name
53
+ begin
54
+ sql = "SELECT obj_description($1::regclass, 'pg_class');"
55
+ single_exec!(sql, [table_name]).flatten.first.to_s
56
+ rescue
57
+ ''
58
+ end
59
+ end
60
+
61
+ # List all database files and their descriptions.
62
+ def self.list_with_desc
63
+ Database::list.map do |i|
64
+ begin
65
+ [i, Database::desc(i)]
66
+ rescue
67
+ [i, '']
68
+ end
69
+ end.to_h
70
+ end
71
+
72
+ ############################################################################
73
+
74
+ # This is the type of database that is being used.
75
+ # It is also used as a signifier that a database has been specified.
76
+ def type
77
+ 'pg'
78
+ end
79
+
80
+ # Get/set the description of the table.
81
+ def desc
82
+ Database::desc table
83
+ end
84
+ def desc=(description)
85
+ safe_desc = description.to_s.gsub("'","''")
86
+ execute! "COMMENT ON TABLE #{table} IS '#{safe_desc}';"
87
+ end
88
+
89
+ # The number of lines in the table.
90
+ def count
91
+ return 0 if not exists?
92
+ sql = "SELECT COUNT(*) AS num FROM #{table};"
93
+ execute!(sql).first['num'].to_i
94
+ end
95
+
96
+ # See if the table exists or not.
97
+ # Attempt to access table, and return false on error.
98
+ def exists?
99
+ open_connection
100
+ @db.exec("SELECT $1::regclass", [*table])
101
+ true
102
+ rescue PG::UndefinedTable
103
+ false
104
+ end
105
+
106
+ # Get all rhyming lines for the word.
107
+ def rhymes word, key = nil
108
+ return nil if word.nil?
109
+
110
+ sql = <<-SQL
111
+ SELECT rhyme, final_word, syllables, line
112
+ FROM #{table}
113
+ WHERE rhyme = $1
114
+ ORDER BY rhyme, final_word, syllables, line
115
+ SQL
116
+ output = word.to_phrase.rhymes.keys.map do |rhyme|
117
+ execute!(sql, [rhyme]).to_a
118
+ end.flatten
119
+
120
+ if !key.nil? and %w[rhyme final_word syllables line].include?(key)
121
+ output.map!{ |i| i[key] }
122
+ end
123
+ output
124
+ end
125
+
126
+ private
127
+
128
+ # The name of the table.
129
+ def table
130
+ @name
131
+ end
132
+
133
+ # Create a new table.
134
+ def new_connection
135
+ open_connection
136
+ end
137
+
138
+ # Open a connection to the database.
139
+ def open_connection
140
+ @db ||= PG.connect(
141
+ :dbname => 'poefy',
142
+ :user => 'poefy',
143
+ :password => 'poefy'
144
+ )
145
+ end
146
+
147
+ # Execute a query.
148
+ def execute! sql, *args
149
+ db.exec sql, *args
150
+ end
151
+
152
+ # Insert an array of lines.
153
+ def insert_lines table_name, rows
154
+ sql = "INSERT INTO #{table_name} VALUES ( $1, $2, $3, $4 )"
155
+ db.transaction do |db_tr|
156
+ rows.each do |line|
157
+ db_tr.exec sql, line
158
+ end
159
+ end
160
+ end
161
+
162
+ ##########################################################################
163
+
164
+ # Create the table and the index.
165
+ def create_table table_name, description = nil
166
+ index_name = 'idx_' + table_name
167
+ execute! <<-SQL
168
+ SET client_min_messages TO WARNING;
169
+ DROP INDEX IF EXISTS #{index_name};
170
+ DROP TABLE IF EXISTS #{table_name};
171
+ CREATE TABLE #{table_name} (
172
+ line TEXT,
173
+ syllables SMALLINT,
174
+ final_word TEXT,
175
+ rhyme TEXT
176
+ );
177
+ CREATE INDEX #{index_name} ON #{table_name} (
178
+ rhyme, final_word, line
179
+ );
180
+ SQL
181
+ self.desc = description
182
+ end
183
+
184
+ ##########################################################################
185
+
186
+ # Define SQL of the stored procedures.
187
+ def sprocs_sql_hash
188
+ sql = {}
189
+ sql[:rbc] = <<-SQL
190
+ SELECT rhyme, COUNT(rhyme) AS count
191
+ FROM (
192
+ SELECT rhyme, final_word, COUNT(final_word) AS wc
193
+ FROM #{table}
194
+ GROUP BY rhyme, final_word
195
+ ) AS sub_table
196
+ GROUP BY rhyme
197
+ HAVING COUNT(rhyme) >= $1
198
+ SQL
199
+ sql[:rbcs] = <<-SQL
200
+ SELECT rhyme, COUNT(rhyme) AS count
201
+ FROM (
202
+ SELECT rhyme, final_word, COUNT(final_word) AS wc
203
+ FROM #{table}
204
+ WHERE syllables BETWEEN $1 AND $2
205
+ GROUP BY rhyme, final_word
206
+ ) AS sub_table
207
+ GROUP BY rhyme
208
+ HAVING COUNT(rhyme) >= $3
209
+ SQL
210
+ sql[:la] = <<-SQL
211
+ SELECT line, syllables, final_word, rhyme
212
+ FROM #{table} WHERE rhyme = $1
213
+ SQL
214
+ sql[:las] = <<-SQL
215
+ SELECT line, syllables, final_word, rhyme
216
+ FROM #{table} WHERE rhyme = $1
217
+ AND syllables BETWEEN $2 AND $3
218
+ SQL
219
+ sql
220
+ end
221
+
222
+ # Create the stored procedures in the database.
223
+ def create_sprocs
224
+ sprocs_sql_hash.each do |key, value|
225
+ db.prepare key.to_s, value
226
+ end
227
+ rescue
228
+ handle_error \
229
+ "ERROR: Database table structure is invalid.\n" +
230
+ " Please manually DROP the corrupt table and recreate it."
231
+ end
232
+
233
+ # Find rhymes and counts greater than a certain length.
234
+ def sproc_rhymes_by_count rhyme_count
235
+ rs = db.exec_prepared 'rbc', [rhyme_count]
236
+ rs.values.map do |row|
237
+ {
238
+ 'rhyme' => row[0],
239
+ 'count' => row[1].to_i
240
+ }
241
+ end
242
+ end
243
+
244
+ # Also adds syllable selection.
245
+ def sproc_rhymes_by_count_syllables rhyme_count, syllable_min_max
246
+ arg_array = [
247
+ syllable_min_max[:min],
248
+ syllable_min_max[:max],
249
+ rhyme_count
250
+ ]
251
+ rs = db.exec_prepared 'rbcs', arg_array
252
+ rs.values.map do |row|
253
+ {
254
+ 'rhyme' => row[0],
255
+ 'count' => row[1].to_i
256
+ }
257
+ end
258
+ end
259
+
260
+ # Find all lines for a certain rhyme.
261
+ def sproc_lines_by_rhyme rhyme
262
+ rs = db.exec_prepared 'la', [rhyme]
263
+ rs.values.map do |row|
264
+ {
265
+ 'line' => row[0],
266
+ 'syllables' => row[1].to_i,
267
+ 'final_word' => row[2],
268
+ 'rhyme' => row[3]
269
+ }
270
+ end
271
+ end
272
+
273
+ # Also adds syllable selection.
274
+ def sproc_lines_by_rhyme_syllables rhyme, syllable_min_max
275
+ arg_array = [
276
+ rhyme,
277
+ syllable_min_max[:min],
278
+ syllable_min_max[:max]
279
+ ]
280
+ rs = db.exec_prepared 'las', arg_array
281
+ rs.values.map do |row|
282
+ {
283
+ 'line' => row[0],
284
+ 'syllables' => row[1].to_i,
285
+ 'final_word' => row[2],
286
+ 'rhyme' => row[3]
287
+ }
288
+ end
289
+ end
290
+
291
+ end
292
+
293
+ end
294
+
295
+ ################################################################################
data/poefy-pg.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # Encoding: UTF-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'poefy/pg/version.rb'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'poefy-pg'
9
+ s.authors = ['Paul Thompson']
10
+ s.email = ['nossidge@gmail.com']
11
+
12
+ s.summary = %q{PostgreSQL interface for the 'poefy' gem}
13
+ s.description = %q{PostgreSQL interface for the 'poefy' gem}
14
+ s.homepage = 'https://github.com/nossidge/poefy-pg'
15
+
16
+ s.version = Poefy::Pg.version_number
17
+ s.date = Poefy::Pg.version_date
18
+ s.license = 'GPL-3.0'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.require_paths = ['lib']
23
+
24
+ s.add_development_dependency('bundler', '~> 1.13')
25
+ s.add_development_dependency('rake', '~> 10.0')
26
+ s.add_development_dependency('rspec', '~> 3.0')
27
+ s.add_development_dependency('ruby_rhymes', '~> 0.1')
28
+
29
+ s.add_runtime_dependency('poefy', '~> 1.0', '>= 1.0.0')
30
+ s.add_runtime_dependency('pg', '~> 0.21', '>= 0.21.0')
31
+ end
@@ -0,0 +1,682 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ ################################################################################
5
+
6
+ describe Poefy::Poem, "-- Postgres" do
7
+
8
+ before(:all) do
9
+ require 'poefy/pg'
10
+ @root = Poefy.root
11
+ dbs = %w{spec_test_tiny spec_shakespeare spec_whitman}
12
+ dbs.each do |db_name|
13
+ Poefy::Database.single_exec! <<-SQL
14
+ SET client_min_messages TO WARNING;
15
+ DROP TABLE IF EXISTS #{db_name};
16
+ SQL
17
+ end
18
+ end
19
+
20
+ after(:all) do
21
+ dbs = %w{spec_test_tiny spec_shakespeare spec_whitman}
22
+ dbs.each do |db_name|
23
+ Poefy::Database.single_exec! <<-SQL
24
+ SET client_min_messages TO WARNING;
25
+ DROP TABLE IF EXISTS #{db_name};
26
+ SQL
27
+ end
28
+ end
29
+
30
+ ##############################################################################
31
+
32
+ describe "using tiny dataset 'spec_test_tiny'" do
33
+ corpus = "spec_test_tiny"
34
+
35
+ # Create a small corpus of a few rhymes.
36
+ text_array = %w{man plan flan can dan fish dish wish bee sea tree flea}
37
+ text_array.map!{ |i| 'a ' + i }
38
+ text_string = text_array.join("\n")
39
+ row_count = text_array.count
40
+
41
+ before(:each) do
42
+ @poefy = Poefy::Poem.new(corpus, { proper: false })
43
+ end
44
+ after(:each) do
45
+ @poefy.close
46
+ end
47
+ it "initialised object not nil" do
48
+ expect(@poefy).to_not be_nil
49
+ end
50
+
51
+ # Create corpora in the three different ways.
52
+ describe "@poefy#make_database!" do
53
+
54
+ it "Use array of strings" do
55
+ @poefy.make_database! text_array
56
+ expect(@poefy.corpus.exists?).to be true
57
+ expect(@poefy.corpus.count).to be row_count
58
+ poem = @poefy.poem({ rhyme: 'aabb' })
59
+ expect(poem.count).to be 4
60
+ end
61
+
62
+ it "Use one long newline delimited string" do
63
+ @poefy.make_database! text_string
64
+ expect(@poefy.corpus.exists?).to be true
65
+ expect(@poefy.corpus.count).to be row_count
66
+ poem = @poefy.poem({ rhyme: 'aabb' })
67
+ expect(poem.count).to be 4
68
+ end
69
+
70
+ it "Use text lines from a file" do
71
+
72
+ # Create a temp file.
73
+ tmp = Tempfile.new('spec-', Poefy.root + '/spec')
74
+ text_path = tmp.path
75
+ tmp.write text_string
76
+ tmp.close
77
+
78
+ @poefy.make_database! text_path
79
+ expect(@poefy.corpus.exists?).to be true
80
+ expect(@poefy.corpus.count).to be row_count
81
+ poem = @poefy.poem({ rhyme: 'aabb' })
82
+ expect(poem.count).to be 4
83
+
84
+ # Delete the temp file.
85
+ tmp.delete
86
+ end
87
+ end
88
+
89
+ # Make sure that the description can be updated as specified
90
+ # and that it doesn't cause SQL injection.
91
+ describe "corpus description using #desc=" do
92
+ it "@poefy.corpus.desc is initially empty" do
93
+ expect(@poefy.corpus.desc).to eq ''
94
+ end
95
+
96
+ values = [
97
+ "test",
98
+ " -- test",
99
+ "; -- test",
100
+ "test' -- ",
101
+ "test'' -- ",
102
+ "'test' -- ",
103
+ "'test'' -- ",
104
+ "Shakespeare's sonnets",
105
+ "Shakespeare's -- sonnets",
106
+ "Shakespeare's; -- sonnets",
107
+ "test' ; INSERT INTO spec_test_tiny VALUES('foo') -- ",
108
+ "105 OR 1=1",
109
+ "' or ''='"
110
+ ]
111
+ values.each do |value|
112
+ it "@poefy.corpus.desc = #{value}" do
113
+ @poefy.corpus.desc = value
114
+ expect(@poefy.corpus.desc).to eq value
115
+ expect(@poefy.corpus.count).to be row_count
116
+ end
117
+ end
118
+ end
119
+
120
+ describe ":rhyme option" do
121
+
122
+ describe "should return nil" do
123
+ it "blank, no argument" do
124
+ poem = @poefy.poem
125
+ expect(poem).to be_nil
126
+ end
127
+ it "({ })" do
128
+ poem = @poefy.poem ({ })
129
+ expect(poem).to be_nil
130
+ end
131
+ it "({ rhyme: nil })" do
132
+ poem = @poefy.poem ({ rhyme: nil })
133
+ expect(poem).to be_nil
134
+ end
135
+ it "({ rhyme: ' ' })" do
136
+ poem = @poefy.poem ({ rhyme: ' ' })
137
+ expect(poem).to be_nil
138
+ end
139
+ it "({ rhyme: '' })" do
140
+ poem = @poefy.poem ({ rhyme: '' })
141
+ expect(poem).to be_nil
142
+ end
143
+ end
144
+
145
+ describe "should return correct number of lines" do
146
+ rhymes = %w{a b z A aa ab zz AA AB AA1 A1 B1 Z1 AB1 A1A1A1A1B1B1B1B1B1}
147
+ rhymes += ['A1A1A1 A1A1A1 B1B1B1B1B1B1','a b c a b c']
148
+ rhymes += [' abc','abc ',' abc ']
149
+ rhymes += ['n aaa n','n aXXXa N1']
150
+ rhymes.each do |i|
151
+ it "({ rhyme: '#{i}' })" do
152
+ poem = @poefy.poem ({ rhyme: i })
153
+ expect(poem.count).to be i.gsub(/[0-9]/,'').length
154
+ end
155
+ end
156
+ end
157
+
158
+ describe "should accept characters other than number" do
159
+ rhymes = %w{. , : .. ., ,, :: (()) @ ~ <<>< A1A1A1...a;}
160
+ rhymes.each do |i|
161
+ it "({ rhyme: '#{i}' })" do
162
+ poem = @poefy.poem ({ rhyme: i })
163
+ expect(poem.count).to be i.gsub(/[0-9]/,'').length
164
+ end
165
+ end
166
+ end
167
+
168
+ describe "should be nil if can't parse rhyme string" do
169
+ rhymes = %w{a1 b1 ab1 Ab1 AAAAABb1 1 1111 1122 11221 ;;::1. }
170
+ rhymes += ['AA Bb1','11 11','11 1 1','..1.']
171
+ rhymes.each do |i|
172
+ it "({ rhyme: '#{i}' })" do
173
+ poem = @poefy.poem ({ rhyme: i })
174
+ expect(poem).to be_nil
175
+ end
176
+ end
177
+ end
178
+
179
+ describe "should be nil if can't complete rhyme string" do
180
+ rhymes = %w{aaaaaa abcd aaaaabbbbb}
181
+ rhymes.each do |i|
182
+ it "({ rhyme: '#{i}' })" do
183
+ poem = @poefy.poem ({ rhyme: i })
184
+ expect(poem).to be_nil
185
+ end
186
+ end
187
+ end
188
+
189
+ describe "should correctly repeat uppercase lines" do
190
+ lines = 200
191
+ it "({ rhyme: 'A' * #{lines} })" do
192
+ poem = @poefy.poem ({ rhyme: 'A' * lines })
193
+ expect(poem.count).to be lines
194
+ expect(poem.uniq.count).to be 1
195
+ end
196
+ it "({ rhyme: ('A'..'C').to_a.map { |i| i * #{lines} }.join })" do
197
+ rhyme = ('A'..'C').to_a.map { |i| i * lines }.join
198
+ poem = @poefy.poem ({ rhyme: rhyme })
199
+ expect(poem.count).to be lines * 3
200
+ expect(poem.uniq.count).to be 3
201
+ end
202
+ end
203
+
204
+ describe "should be nil if can't complete repeating rhyme string" do
205
+ lines = 200
206
+ it "({ rhyme: ('A'..'D').to_a.map { |i| i * #{lines} }.join })" do
207
+ rhyme = ('A'..'D').to_a.map { |i| i * lines }.join
208
+ poem = @poefy.poem ({ rhyme: rhyme })
209
+ expect(poem).to be_nil
210
+ end
211
+ end
212
+
213
+ end
214
+
215
+ describe ":form option" do
216
+
217
+ describe "should return correct number of lines" do
218
+ it "({ form: :default })" do
219
+ poem = @poefy.poem ({ form: :default })
220
+ expect(poem.count).to be 1
221
+ end
222
+ end
223
+
224
+ describe "should be nil if given a named form it can't fulfil" do
225
+ it "({ form: 'sonnet' })" do
226
+ poem = @poefy.poem ({ form: 'sonnet' })
227
+ expect(poem).to be_nil
228
+ end
229
+ it "({ form: :villanelle })" do
230
+ poem = @poefy.poem ({ form: :villanelle })
231
+ expect(poem).to be_nil
232
+ end
233
+ end
234
+
235
+ describe "should be nil if given a junk named form" do
236
+ it "({ form: 'sonnet_junk' })" do
237
+ poem = @poefy.poem ({ form: 'sonnet_junk' })
238
+ expect(poem).to be_nil
239
+ end
240
+ it "({ form: :not_a_form })" do
241
+ poem = @poefy.poem ({ form: :not_a_form })
242
+ expect(poem).to be_nil
243
+ end
244
+ it "({ form: :not_a_form, indent: '0010' })" do
245
+ poem = @poefy.poem ({ form: :not_a_form, indent: '0010' })
246
+ expect(poem).to be_nil
247
+ end
248
+ end
249
+
250
+ describe "should be valid if given a junk named form, and a rhyme" do
251
+ it "({ form: :not_a_form, rhyme: 'abcb' })" do
252
+ poem = @poefy.poem ({ form: :not_a_form, rhyme: 'abcb' })
253
+ expect(poem.count).to be 4
254
+ end
255
+ end
256
+
257
+ describe "should overwrite a named form if another option is specified" do
258
+ it "({ form: 'default', rhyme: 'ab' })" do
259
+ poem = @poefy.poem ({ form: 'default', rhyme: 'ab' })
260
+ expect(poem.count).to be 2
261
+ end
262
+ it "({ form: :villanelle, rhyme: 'abcb' })" do
263
+ poem = @poefy.poem ({ form: :villanelle, rhyme: 'abcb' })
264
+ expect(poem.count).to be 4
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ ##############################################################################
271
+
272
+ describe "using dataset 'spec_shakespeare'" do
273
+
274
+ file_txt = "shakespeare_sonnets.txt"
275
+ file_db = "spec_shakespeare"
276
+
277
+ # All the Shakespeare lines are pentameter, so some forms should fail.
278
+ forms = Poefy::PoeticForms::POETIC_FORMS
279
+ forms_fail = [:limerick, :haiku, :common, :ballad, :double_dactyl]
280
+ forms_pass = forms.keys - forms_fail
281
+
282
+ before(:each) do
283
+ @poefy = Poefy::Poem.new(file_db, { proper: false })
284
+ end
285
+ after(:each) do
286
+ @poefy.close
287
+ end
288
+
289
+ it "initialised object not nil" do
290
+ expect(@poefy).to_not be_nil
291
+ end
292
+
293
+ describe "#make_database( '#{@root}/data/#{file_txt}', true )" do
294
+ it "should make the database '#{@root}/data/#{file_db}" do
295
+ input = `sed '/[a-z]/!d' #{@root}/data/#{file_txt}`
296
+ @poefy.make_database! input
297
+ expect(@poefy.corpus.exists?).to be true
298
+ end
299
+ end
300
+
301
+ describe "using acrostic option" do
302
+ describe "should return correct number of lines" do
303
+ it "({ form: :sonnet, acrostic: 'pauldpthompson' })" do
304
+ poem = @poefy.poem ({ form: :sonnet,
305
+ acrostic: 'pauldpthompson' })
306
+ expect(poem.count).to be 14
307
+ end
308
+ end
309
+ describe "should fail to be created" do
310
+ it "({ form: :sonnet, acrostic: 'qqqqqqqqqqqqqq' })" do
311
+ poem = @poefy.poem ({ form: :sonnet,
312
+ acrostic: 'qqqqqqqqqqqqqq' })
313
+ expect(poem).to be_nil
314
+ end
315
+ end
316
+ end
317
+
318
+ describe "using form string" do
319
+ describe "should return correct number of lines" do
320
+
321
+ # Make sure each form's lines match the expected output.
322
+ # Generate a few to be sure.
323
+ forms_pass.each do |form|
324
+ it "({ form: #{form} })" do
325
+ 10.times do
326
+ poem = @poefy.poem ({ form: form })
327
+ expect(poem.count).to satisfy do |c|
328
+ [*forms[form][:rhyme]].map do |r|
329
+ r.gsub(/[0-9]/,'').length
330
+ end.include?(c)
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ describe "should fail to be created" do
338
+ forms_fail.each do |form|
339
+ it "({ form: #{form} })" do
340
+ 4.times do
341
+ poem = @poefy.poem ({ form: form })
342
+ expect(poem).to be_nil
343
+ end
344
+ end
345
+ end
346
+ end
347
+ end
348
+
349
+ describe "make sonnets" do
350
+ sonnet_options = [
351
+ { rhyme: 'ababcdcdefefgg' },
352
+ { rhyme: 'abab cdcd efef gg', indent: '0101 0101 0011 01' },
353
+ { form: 'sonnet' },
354
+ { form: :sonnet, syllable: 0 },
355
+ { form: :sonnet, syllable: 10 },
356
+ { form: :sonnet, regex: /^[A-Z].*$/ },
357
+ { form: :sonnet, regex: '^[A-Z].*$' },
358
+ { form: :sonnet, acrostic: 'pauldpthompson' },
359
+ { form: 'sonnet', indent: '01010101001101' },
360
+ { form: 'sonnet', proper: false }
361
+ ]
362
+ sonnet_options.each do |option|
363
+ it "#{option}" do
364
+ 4.times do
365
+ poem = @poefy.poem(option)
366
+ expect(poem).to_not be_nil
367
+ end
368
+ end
369
+ end
370
+ end
371
+ end
372
+
373
+ ##############################################################################
374
+
375
+ describe "using dataset 'spec_whitman'" do
376
+
377
+ file_txt = "whitman_leaves.txt"
378
+ file_db = "spec_whitman"
379
+
380
+ # There's a good mix of syllable count, so all forms should pass.
381
+ forms = Poefy::PoeticForms::POETIC_FORMS
382
+ forms_pass = forms.keys
383
+
384
+ before(:each) do
385
+ @poefy = Poefy::Poem.new(file_db, { proper: false })
386
+ end
387
+ after(:each) do
388
+ @poefy.close
389
+ end
390
+
391
+ it "initialised object not nil" do
392
+ expect(@poefy).to_not be_nil
393
+ end
394
+
395
+ describe "#make_database( '#{@root}/data/#{file_txt}', true )" do
396
+ it "should make the database '#{@root}/data/#{file_db}" do
397
+ input = `sed '/[a-z]/!d' #{@root}/data/#{file_txt}`
398
+ @poefy.make_database! input
399
+ expect(@poefy.corpus.exists?).to be true
400
+ end
401
+ end
402
+
403
+ describe "using form string" do
404
+ describe "should return correct number of lines" do
405
+
406
+ # Make sure each form's lines match the expected output.
407
+ # Generate a few to be sure.
408
+ forms_pass.each do |form|
409
+ it "({ form: #{form} })" do
410
+ 10.times do
411
+ poem = @poefy.poem ({ form: form })
412
+ expect(poem.count).to satisfy do |c|
413
+ [*forms[form][:rhyme]].map do |r|
414
+ r.gsub(/[0-9]/,'').length
415
+ end.include?(c)
416
+ end
417
+ end
418
+ end
419
+ end
420
+ end
421
+ end
422
+
423
+ describe "make sonnets" do
424
+ sonnet_options = [
425
+ { rhyme: 'ababcdcdefefgg' },
426
+ { rhyme: 'abab cdcd efef gg', indent: '0101 0101 0011 01' },
427
+ { form: 'sonnet' },
428
+ { form: :sonnet, syllable: 0 },
429
+ { form: :sonnet, syllable: 10 },
430
+ { form: :sonnet, regex: /^[A-Z].*$/ },
431
+ { form: :sonnet, regex: '^[A-Z].*$' },
432
+ { form: :sonnet, acrostic: 'pauldpthompson' },
433
+ { form: 'sonnet', indent: '01010101001101' },
434
+ { form: 'sonnet', proper: false }
435
+ ]
436
+ sonnet_options.each do |option|
437
+ it "#{option}" do
438
+ 4.times do
439
+ poem = @poefy.poem(option)
440
+ expect(poem).to_not be_nil
441
+ end
442
+ end
443
+ end
444
+ end
445
+
446
+ describe "using syllable string" do
447
+
448
+ it "({ rhyme: 'abcb defe', syllable: '[8,6,8,6,0,8,6,8,6]' })" do
449
+ options = {
450
+ rhyme: 'abcb defe',
451
+ syllable: '[8,6,8,6,0,8,6,8,6]'
452
+ }
453
+ poem = @poefy.poem (options)
454
+ expect(poem.count).to be options[:rhyme].length
455
+ end
456
+
457
+ it "({ rhyme: 'abcb defe', syllable: '[8,6,8,6,8,6,8,6]' })" do
458
+ options = {
459
+ rhyme: 'abcb defe',
460
+ syllable: '[8,6,8,6,8,6,8,6]'
461
+ }
462
+ poem = @poefy.poem (options)
463
+ expect(poem.count).to be options[:rhyme].length
464
+ end
465
+ end
466
+ end
467
+
468
+ ##############################################################################
469
+
470
+ describe "reusing the same Poem instance" do
471
+ it "should correctly merge the option hashes" do
472
+
473
+ # Default to use rondeau poetic form, and proper sentence validation
474
+ poefy = Poefy::Poem.new(
475
+ 'spec_shakespeare',
476
+ { form: 'rondeau', proper: true }
477
+ )
478
+
479
+ # Generate a properly sentenced rondeau
480
+ poem = poefy.poem
481
+ expect(poem.count).to be 17
482
+
483
+ # Generate a rondeau without proper validation
484
+ poem = poefy.poem ({ proper: false })
485
+ expect(poem.count).to be 17
486
+
487
+ # Generate a proper rondeau with a certain indentation
488
+ poem = poefy.poem ({ indent: '01012 0012 010112' })
489
+ expect(poem.count).to be 17
490
+
491
+ # Generate other forms
492
+ poem = poefy.poem ({ rhyme: 'abbaabbacdecde' })
493
+ expect(poem.count).to be 14
494
+ poem = poefy.poem ({ form: 'sonnet' })
495
+ expect(poem.count).to be 14
496
+ poem = poefy.poem ({ form: 'ballade' })
497
+ expect(poem.count).to be 31
498
+
499
+ # Generate a default rondeau again
500
+ poem = poefy.poem
501
+ expect(poem.count).to be 17
502
+
503
+ poefy.close
504
+ end
505
+ end
506
+
507
+ ##############################################################################
508
+
509
+ describe "using the transform option" do
510
+
511
+ it "should correctly transform the output 1" do
512
+ poefy = Poefy::Poem.new :spec_shakespeare
513
+ transform_hash = {
514
+ 4 => proc { |line, num, poem| line.upcase },
515
+ 12 => proc { |line, num, poem| line.upcase }
516
+ }
517
+ poem = poefy.poem({ form: :sonnet, transform: transform_hash })
518
+ expect(poem.count).to be 14
519
+ expect(poem[3]).to eq poem[3].upcase
520
+ expect(poem[11]).to eq poem[11].upcase
521
+ poefy.close
522
+ end
523
+
524
+ it "should correctly transform the output 2" do
525
+ poefy = Poefy::Poem.new :spec_shakespeare
526
+ transform_hash = {
527
+ 4 => proc { |line, num, poem| poem.count },
528
+ -3 => proc { |line, num, poem| poem.count },
529
+ 7 => proc { |line, num, poem| 'test string' }
530
+ }
531
+ poem = poefy.poem({ form: :sonnet, transform: transform_hash })
532
+ expect(poem.count).to be 14
533
+ expect(poem[3]).to eq '14'
534
+ expect(poem[11]).to eq '14'
535
+ expect(poem[6]).to eq 'test string'
536
+ poefy.close
537
+ end
538
+
539
+ it "should correctly transform the output 3" do
540
+ poefy = Poefy::Poem.new :spec_shakespeare
541
+ transform_proc = proc { |line, num, poem| line.downcase }
542
+ poem = poefy.poem({ form: :sonnet, transform: transform_proc })
543
+ expect(poem.count).to be 14
544
+ poem.each do |i|
545
+ expect(i).to eq i.downcase
546
+ end
547
+ poefy.close
548
+ end
549
+
550
+ it "should correctly transform the output 4" do
551
+ poefy = Poefy::Poem.new :spec_shakespeare
552
+ transform_proc = proc { |line, num, poem| "#{num} #{line.downcase}" }
553
+ poem = poefy.poem({ form: :sonnet, transform: transform_proc })
554
+ expect(poem.count).to be 14
555
+ poem.each.with_index do |line, index|
556
+ expect(line).to eq line.downcase
557
+ first_word = line.split(' ').first
558
+ expect(first_word).to eq (index + 1).to_s
559
+ end
560
+ poefy.close
561
+ end
562
+ end
563
+
564
+ ##############################################################################
565
+
566
+ describe "using the form_from_text option" do
567
+ before(:all) do
568
+ @text = <<-TEXT
569
+ [Chorus 1]
570
+ Oh yeah, I'll tell you something
571
+ I think you'll understand
572
+ When I'll say that something
573
+ I want to hold your hand
574
+ I want to hold your hand
575
+ I want to hold your hand
576
+
577
+ [Verse 1]
578
+ Oh please, say to me
579
+ You'll let me be your man
580
+ And please, say to me
581
+ You'll let me hold your hand
582
+ I'll let me hold your hand
583
+ I want to hold your hand
584
+ TEXT
585
+ @line_count = @text.split("\n").count
586
+ end
587
+
588
+ it "should use the exact poetic form 1" do
589
+ poefy = Poefy::Poem.new(:spec_whitman, {
590
+ form_from_text: @text
591
+ })
592
+ poem = poefy.poem
593
+ poem.map!(&:strip!)
594
+ expect(poem.count).to be @line_count
595
+ expect(poem[0]).to eq "[Chorus 1]"
596
+ expect(poem[8]).to eq "[Verse 1]"
597
+ expect(poem[5]).to eq poem[4]
598
+ expect(poem[6]).to eq poem[4]
599
+ poefy.close
600
+ end
601
+
602
+ it "should use the exact poetic form 2" do
603
+ poefy = Poefy::Poem.new :spec_whitman
604
+ poem = poefy.poem({
605
+ form_from_text: @text
606
+ })
607
+ poem.map!(&:strip!)
608
+ expect(poem.count).to be @line_count
609
+ expect(poem[0]).to eq "[Chorus 1]"
610
+ expect(poem[8]).to eq "[Verse 1]"
611
+ expect(poem[5]).to eq poem[4]
612
+ expect(poem[6]).to eq poem[4]
613
+ poefy.close
614
+ end
615
+
616
+ it "should correctly modify the poetic form 1" do
617
+ poefy = Poefy::Poem.new(:spec_whitman, {
618
+ form_from_text: @text,
619
+ syllable: 6
620
+ })
621
+ poem = poefy.poem
622
+ poem.map!(&:strip!)
623
+ expect(poem.count).to be @line_count
624
+ expect(poem[0]).to eq "[Chorus 1]"
625
+ expect(poem[8]).to eq "[Verse 1]"
626
+ expect(poem[5]).to eq poem[4]
627
+ expect(poem[6]).to eq poem[4]
628
+ poefy.close
629
+ end
630
+
631
+ it "should correctly modify the poetic form 2" do
632
+ poefy = Poefy::Poem.new :spec_whitman
633
+ poem = poefy.poem({
634
+ form_from_text: @text,
635
+ syllable: 6
636
+ })
637
+ poem.map!(&:strip!)
638
+ expect(poem.count).to be @line_count
639
+ expect(poem[0]).to eq "[Chorus 1]"
640
+ expect(poem[8]).to eq "[Verse 1]"
641
+ expect(poem[5]).to eq poem[4]
642
+ expect(poem[6]).to eq poem[4]
643
+ poefy.close
644
+ end
645
+
646
+ it "should correctly modify the poetic form 3" do
647
+ poefy = Poefy::Poem.new(:spec_whitman, {
648
+ form_from_text: @text
649
+ })
650
+ poem = poefy.poem({
651
+ syllable: 6
652
+ })
653
+ poem.map!(&:strip!)
654
+ expect(poem.count).to be @line_count
655
+ expect(poem[0]).to eq "[Chorus 1]"
656
+ expect(poem[8]).to eq "[Verse 1]"
657
+ expect(poem[5]).to eq poem[4]
658
+ expect(poem[6]).to eq poem[4]
659
+ poefy.close
660
+ end
661
+
662
+ it "should correctly replace the poetic form" do
663
+ poefy = Poefy::Poem.new(:spec_whitman, {
664
+ syllable: 6
665
+ })
666
+ poem = poefy.poem({
667
+ form_from_text: @text
668
+ })
669
+ poem.map!(&:strip!)
670
+ expect(poem.count).to be @line_count
671
+ expect(poem[0]).to eq "[Chorus 1]"
672
+ expect(poem[8]).to eq "[Verse 1]"
673
+ expect(poem[5]).to eq poem[4]
674
+ expect(poem[6]).to eq poem[4]
675
+ poefy.close
676
+ end
677
+
678
+ end
679
+
680
+ end
681
+
682
+ ################################################################################
@@ -0,0 +1,11 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'tempfile'
5
+ require 'poefy'
6
+
7
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
8
+
9
+ RSpec.configure do |config|
10
+ # some (optional) config here
11
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: poefy-pg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Thompson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby_rhymes
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: poefy
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 1.0.0
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '1.0'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 1.0.0
89
+ - !ruby/object:Gem::Dependency
90
+ name: pg
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.21'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 0.21.0
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '0.21'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 0.21.0
109
+ description: PostgreSQL interface for the 'poefy' gem
110
+ email:
111
+ - nossidge@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - ".gitignore"
117
+ - ".rspec"
118
+ - Gemfile
119
+ - LICENSE
120
+ - README.md
121
+ - Rakefile
122
+ - lib/poefy/pg.rb
123
+ - lib/poefy/pg/version.rb
124
+ - poefy-pg.gemspec
125
+ - spec/poefy_pg_spec.rb
126
+ - spec/spec_helper.rb
127
+ homepage: https://github.com/nossidge/poefy-pg
128
+ licenses:
129
+ - GPL-3.0
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.5.2
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: PostgreSQL interface for the 'poefy' gem
151
+ test_files: []