poefy-sqlite3 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 601ccce9d7cf3212b804700e0071548fe4e88669
4
+ data.tar.gz: 6619ec5b781ff1677302925782bd86369dc484e9
5
+ SHA512:
6
+ metadata.gz: 51a75059555a8147f33b70776fe1d17255e487d4b0e8f77e708b61a1cf1e1240a861625a6810a83c72f882022800ada63bbb67ec8c1f52de6e1ae719fac4e5a0
7
+ data.tar.gz: 66644fda8fd56121eeedc553958136a917a0a0e9f7fd2de5952f3258eb6972a1c34dcd89904d4c5ff5ae2ba47e9c33c0b87d3422215b4d3f980503fe4d8cf780
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-sqlite3
2
+
3
+ by Paul Thompson - nossidge@gmail.com
4
+
5
+ SQLite 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,275 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ ################################################################################
5
+ # Extend 'Database' class for connecting to a sqlite3 database.
6
+ # These methods are specific to sqlite3.
7
+ # Other databases should be implemented in separate gems.
8
+ ################################################################################
9
+
10
+ require 'sqlite3'
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! database_name, sql
20
+ path = Database::path database_name
21
+ con = SQLite3::Database.open path
22
+ rs = con.execute sql
23
+ con.close
24
+ rs
25
+ end
26
+
27
+ # List all database files in the directory.
28
+ # Does not include databases used for testing.
29
+ def self.list
30
+ Dir[Poefy.root + '/data/*.db'].map do |i|
31
+ File.basename(i, '.db')
32
+ end.reject do |i|
33
+ i.start_with?('spec_')
34
+ end.sort - ['test']
35
+ end
36
+
37
+ # Get the description of a database.
38
+ def self.desc database_name
39
+ begin
40
+ sql = "SELECT comment FROM comment;"
41
+ Database::single_exec!(database_name, sql).flatten.first
42
+ rescue
43
+ ''
44
+ end
45
+ end
46
+
47
+ # List all database files and their descriptions.
48
+ def self.list_with_desc
49
+ Database::list.map do |i|
50
+ begin
51
+ [i, Database::desc(i)]
52
+ rescue
53
+ [i, '']
54
+ end
55
+ end.to_h
56
+ end
57
+
58
+ # Get the path of a database.
59
+ def self.path database_name
60
+ Poefy.root + '/data/' + File.basename(database_name, '.db') + '.db'
61
+ end
62
+
63
+ ############################################################################
64
+
65
+ # This is the type of database that is being used.
66
+ # It is also used as a signifier that a database has been specified.
67
+ def type
68
+ 'sqlite3'
69
+ end
70
+
71
+ # Get/set the description of the database.
72
+ def desc
73
+ Database::desc @name
74
+ end
75
+ def desc=(description)
76
+ execute! "DELETE FROM comment;"
77
+ execute! "INSERT INTO comment VALUES ( ? );", description.to_s
78
+ end
79
+
80
+ # The number of lines in the table.
81
+ def count
82
+ return 0 if not exists?
83
+ sql = "SELECT COUNT(*) AS num FROM #{table};"
84
+ execute!(sql).first['num'].to_i
85
+ end
86
+
87
+ # See if the database file exists or not.
88
+ def exists?
89
+ File.exists?(db_file)
90
+ end
91
+
92
+ # Get all rhyming lines for the word.
93
+ def rhymes word, key = nil
94
+ return nil if word.nil?
95
+
96
+ sql = <<-SQL
97
+ SELECT rhyme, final_word, syllables, line
98
+ FROM lines
99
+ WHERE rhyme = ?
100
+ ORDER BY rhyme, final_word, syllables, line
101
+ SQL
102
+ output = word.to_phrase.rhymes.keys.map do |rhyme|
103
+ rs = execute!(sql, [rhyme]).to_a
104
+ rs.each{ |a| a.reject!{ |k| k.is_a? Numeric }}
105
+ end.flatten
106
+
107
+ if !key.nil? and %w[rhyme final_word syllables line].include?(key)
108
+ output.map!{ |i| i[key] }
109
+ end
110
+ output
111
+ end
112
+
113
+ private
114
+
115
+ # The name of the table.
116
+ def table
117
+ 'lines'
118
+ end
119
+
120
+ # Create a new database.
121
+ def new_connection
122
+ File.delete(db_file) if File.exists?(db_file)
123
+ @db = SQLite3::Database.new(db_file)
124
+ @db.results_as_hash = true
125
+ end
126
+
127
+ # Open a connection to the database.
128
+ def open_connection
129
+ @db = SQLite3::Database.open(db_file)
130
+ @db.results_as_hash = true
131
+ end
132
+
133
+ # Execute a query.
134
+ def execute! sql, *args
135
+ db.execute sql, *args
136
+ end
137
+
138
+ # Insert an array of poefy-described lines.
139
+ def insert_lines table_name, rows
140
+ sql = "INSERT INTO #{table_name} VALUES ( ?, ?, ?, ? )"
141
+ db.transaction do |db_tr|
142
+ rows.each do |line|
143
+ db_tr.execute sql, line
144
+ end
145
+ end
146
+ end
147
+
148
+ ##########################################################################
149
+
150
+ # Find the correct database file.
151
+ # If local, just use the value.
152
+ # Else, use the database in /data/ directory.
153
+ def db_file
154
+ if @local
155
+ @name
156
+ elsif @db_file
157
+ @db_file
158
+ else
159
+ path = Poefy.root + '/data'
160
+ file = File.basename(@name, '.db')
161
+ @db_file = path + '/' + file + '.db'
162
+ end
163
+ end
164
+
165
+ ##########################################################################
166
+
167
+ # Create the table and the index.
168
+ def create_table table_name, description = nil
169
+ execute! <<-SQL
170
+ CREATE TABLE #{table_name} (
171
+ line TEXT,
172
+ syllables SMALLINT,
173
+ final_word TEXT,
174
+ rhyme TEXT
175
+ );
176
+ SQL
177
+ execute! <<-SQL
178
+ CREATE TABLE comment (
179
+ comment TEXT
180
+ );
181
+ SQL
182
+ execute! <<-SQL
183
+ CREATE INDEX idx ON #{table_name} (
184
+ rhyme, final_word, line
185
+ );
186
+ SQL
187
+ self.desc = description
188
+ end
189
+
190
+ ##########################################################################
191
+
192
+ # Define SQL of the stored procedures.
193
+ def sprocs_sql_hash
194
+ sql = {}
195
+ sql[:rbc] = <<-SQL
196
+ SELECT rhyme, COUNT(rhyme) AS count
197
+ FROM (
198
+ SELECT rhyme, final_word, COUNT(final_word) AS wc
199
+ FROM #{table}
200
+ GROUP BY rhyme, final_word
201
+ )
202
+ GROUP BY rhyme
203
+ HAVING count >= ?
204
+ SQL
205
+ sql[:rbcs] = <<-SQL
206
+ SELECT rhyme, COUNT(rhyme) AS count
207
+ FROM (
208
+ SELECT rhyme, final_word, COUNT(final_word) AS wc
209
+ FROM #{table}
210
+ WHERE syllables BETWEEN ? AND ?
211
+ GROUP BY rhyme, final_word
212
+ )
213
+ GROUP BY rhyme
214
+ HAVING count >= ?
215
+ SQL
216
+ sql[:la] = <<-SQL
217
+ SELECT line, syllables, final_word, rhyme
218
+ FROM #{table} WHERE rhyme = ?
219
+ SQL
220
+ sql[:las] = <<-SQL
221
+ SELECT line, syllables, final_word, rhyme
222
+ FROM #{table} WHERE rhyme = ?
223
+ AND syllables BETWEEN ? AND ?
224
+ SQL
225
+ sql
226
+ end
227
+
228
+ # Create the stored procedures in the database.
229
+ def create_sprocs
230
+ sprocs_sql_hash.each do |key, value|
231
+ @sproc[key] = db.prepare value
232
+ end
233
+ rescue
234
+ handle_error \
235
+ "ERROR: Database table structure is invalid.\n" +
236
+ " Please manually DROP the corrupt table and recreate it."
237
+ end
238
+
239
+ # Find rhymes and counts greater than a certain length.
240
+ def sproc_rhymes_by_count rhyme_count
241
+ @sproc[:rbc].reset!
242
+ @sproc[:rbc].bind_param(1, rhyme_count)
243
+ @sproc[:rbc].execute.to_a
244
+ end
245
+
246
+ # Also adds syllable selection.
247
+ def sproc_rhymes_by_count_syllables rhyme_count, syllable_min_max
248
+ @sproc[:rbcs].reset!
249
+ @sproc[:rbcs].bind_param(1, syllable_min_max[:min])
250
+ @sproc[:rbcs].bind_param(2, syllable_min_max[:max])
251
+ @sproc[:rbcs].bind_param(3, rhyme_count)
252
+ @sproc[:rbcs].execute.to_a
253
+ end
254
+
255
+ # Find all lines for a certain rhyme.
256
+ def sproc_lines_by_rhyme rhyme
257
+ @sproc[:la].reset!
258
+ @sproc[:la].bind_param(1, rhyme)
259
+ @sproc[:la].execute.to_a
260
+ end
261
+
262
+ # Also adds syllable selection.
263
+ def sproc_lines_by_rhyme_syllables rhyme, syllable_min_max
264
+ @sproc[:las].reset!
265
+ @sproc[:las].bind_param(1, rhyme)
266
+ @sproc[:las].bind_param(2, syllable_min_max[:min])
267
+ @sproc[:las].bind_param(3, syllable_min_max[:max])
268
+ @sproc[:las].execute.to_a
269
+ end
270
+
271
+ end
272
+
273
+ end
274
+
275
+ ################################################################################
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ module Poefy
5
+
6
+ module Sqlite3
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
@@ -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/sqlite3/version.rb'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'poefy-sqlite3'
9
+ s.authors = ['Paul Thompson']
10
+ s.email = ['nossidge@gmail.com']
11
+
12
+ s.summary = %q{SQLite interface for the 'poefy' gem}
13
+ s.description = %q{SQLite interface for the 'poefy' gem}
14
+ s.homepage = 'https://github.com/nossidge/poefy-sqlite3'
15
+
16
+ s.version = Poefy::Sqlite3.version_number
17
+ s.date = Poefy::Sqlite3.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('sqlite3', '~> 1.3', '>= 1.3.13')
31
+ end
@@ -0,0 +1,686 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: UTF-8
3
+
4
+ ################################################################################
5
+
6
+ describe Poefy::Poem, "-- SQLite" do
7
+
8
+ before(:all) do
9
+ require 'poefy/sqlite3'
10
+ @root = Poefy.root
11
+ dbs = %w{spec_test_tiny spec_shakespeare spec_whitman}
12
+ dbs.each do |db_name|
13
+ db_file = "#{@root}/data/#{db_name}.db"
14
+ File.delete(db_file) if File.exists?(db_file)
15
+ end
16
+ end
17
+
18
+ after(:all) do
19
+ dbs = %w{spec_test_tiny spec_shakespeare spec_whitman}
20
+ dbs.each do |db_name|
21
+ db_file = "#{@root}/data/#{db_name}.db"
22
+ File.delete(db_file) if File.exists?(db_file)
23
+ end
24
+ end
25
+
26
+ ##############################################################################
27
+
28
+ describe "using tiny dataset 'spec_test_tiny'" do
29
+ corpus = "spec_test_tiny"
30
+ db_file = "#{Poefy.root}/data/#{corpus}.db"
31
+
32
+ # Create a small corpus of a few rhymes.
33
+ text_array = %w{man plan flan can dan fish dish wish bee sea tree flea}
34
+ text_array.map!{ |i| 'a ' + i }
35
+ text_string = text_array.join("\n")
36
+ row_count = text_array.count
37
+
38
+ before(:each) do
39
+ @poefy = Poefy::Poem.new(corpus, { proper: false })
40
+ end
41
+ after(:each) do
42
+ @poefy.close
43
+ end
44
+ it "initialised object not nil" do
45
+ expect(@poefy).to_not be_nil
46
+ end
47
+
48
+ # Create corpora in the three different ways.
49
+ describe "@poefy#make_database!" do
50
+
51
+ it "Use array of strings" do
52
+ @poefy.make_database! text_array
53
+ expect(@poefy.corpus.exists?).to be true
54
+ expect(File.exists?(db_file)).to be true
55
+ expect(@poefy.corpus.count).to be row_count
56
+ poem = @poefy.poem({ rhyme: 'aabb' })
57
+ expect(poem.count).to be 4
58
+ end
59
+
60
+ it "Use one long newline delimited string" do
61
+ @poefy.make_database! text_string
62
+ expect(@poefy.corpus.exists?).to be true
63
+ expect(File.exists?(db_file)).to be true
64
+ expect(@poefy.corpus.count).to be row_count
65
+ poem = @poefy.poem({ rhyme: 'aabb' })
66
+ expect(poem.count).to be 4
67
+ end
68
+
69
+ it "Use text lines from a file" do
70
+
71
+ # Create a temp file.
72
+ tmp = Tempfile.new('spec-', Poefy.root + '/spec')
73
+ text_path = tmp.path
74
+ tmp.write text_string
75
+ tmp.close
76
+
77
+ @poefy.make_database! text_path
78
+ expect(@poefy.corpus.exists?).to be true
79
+ expect(File.exists?(db_file)).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.db"
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
+ db_file = "#{@root}/data/#{file_db}"
296
+ input = `sed '/[a-z]/!d' #{@root}/data/#{file_txt}`
297
+ @poefy.make_database input
298
+ expect(@poefy.corpus.exists?).to be true
299
+ expect(File.exists?(db_file)).to be true
300
+ end
301
+ end
302
+
303
+ describe "using acrostic option" do
304
+ describe "should return correct number of lines" do
305
+ it "({ form: :sonnet, acrostic: 'pauldpthompson' })" do
306
+ poem = @poefy.poem ({ form: :sonnet,
307
+ acrostic: 'pauldpthompson' })
308
+ expect(poem.count).to be 14
309
+ end
310
+ end
311
+ describe "should fail to be created" do
312
+ it "({ form: :sonnet, acrostic: 'qqqqqqqqqqqqqq' })" do
313
+ poem = @poefy.poem ({ form: :sonnet,
314
+ acrostic: 'qqqqqqqqqqqqqq' })
315
+ expect(poem).to be_nil
316
+ end
317
+ end
318
+ end
319
+
320
+ describe "using form string" do
321
+ describe "should return correct number of lines" do
322
+
323
+ # Make sure each form's lines match the expected output.
324
+ # Generate a few to be sure.
325
+ forms_pass.each do |form|
326
+ it "({ form: #{form} })" do
327
+ 10.times do
328
+ poem = @poefy.poem ({ form: form })
329
+ expect(poem.count).to satisfy do |c|
330
+ [*forms[form][:rhyme]].map do |r|
331
+ r.gsub(/[0-9]/,'').length
332
+ end.include?(c)
333
+ end
334
+ end
335
+ end
336
+ end
337
+ end
338
+
339
+ describe "should fail to be created" do
340
+ forms_fail.each do |form|
341
+ it "({ form: #{form} })" do
342
+ 4.times do
343
+ poem = @poefy.poem ({ form: form })
344
+ expect(poem).to be_nil
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ describe "make sonnets" do
352
+ sonnet_options = [
353
+ { rhyme: 'ababcdcdefefgg' },
354
+ { rhyme: 'abab cdcd efef gg', indent: '0101 0101 0011 01' },
355
+ { form: 'sonnet' },
356
+ { form: :sonnet, syllable: 0 },
357
+ { form: :sonnet, syllable: 10 },
358
+ { form: :sonnet, regex: /^[A-Z].*$/ },
359
+ { form: :sonnet, regex: '^[A-Z].*$' },
360
+ { form: :sonnet, acrostic: 'pauldpthompson' },
361
+ { form: 'sonnet', indent: '01010101001101' },
362
+ { form: 'sonnet', proper: false }
363
+ ]
364
+ sonnet_options.each do |option|
365
+ it "#{option}" do
366
+ 4.times do
367
+ poem = @poefy.poem(option)
368
+ expect(poem).to_not be_nil
369
+ end
370
+ end
371
+ end
372
+ end
373
+ end
374
+
375
+ ##############################################################################
376
+
377
+ describe "using dataset 'spec_whitman'" do
378
+
379
+ file_txt = "whitman_leaves.txt"
380
+ file_db = "spec_whitman.db"
381
+
382
+ # There's a good mix of syllable count, so all forms should pass.
383
+ forms = Poefy::PoeticForms::POETIC_FORMS
384
+ forms_pass = forms.keys
385
+
386
+ before(:each) do
387
+ @poefy = Poefy::Poem.new(file_db, { proper: false })
388
+ end
389
+ after(:each) do
390
+ @poefy.close
391
+ end
392
+
393
+ it "initialised object not nil" do
394
+ expect(@poefy).to_not be_nil
395
+ end
396
+
397
+ describe "#make_database( '#{@root}/data/#{file_txt}', true )" do
398
+ it "should make the database '#{@root}/data/#{file_db}" do
399
+ db_file = "#{@root}/data/#{file_db}"
400
+ input = `sed '/[a-z]/!d' #{@root}/data/#{file_txt}`
401
+ @poefy.make_database input
402
+ expect(@poefy.corpus.exists?).to be true
403
+ expect(File.exists?(db_file)).to be true
404
+ end
405
+ end
406
+
407
+ describe "using form string" do
408
+ describe "should return correct number of lines" do
409
+
410
+ # Make sure each form's lines match the expected output.
411
+ # Generate a few to be sure.
412
+ forms_pass.each do |form|
413
+ it "({ form: #{form} })" do
414
+ 10.times do
415
+ poem = @poefy.poem ({ form: form })
416
+ expect(poem.count).to satisfy do |c|
417
+ [*forms[form][:rhyme]].map do |r|
418
+ r.gsub(/[0-9]/,'').length
419
+ end.include?(c)
420
+ end
421
+ end
422
+ end
423
+ end
424
+ end
425
+ end
426
+
427
+ describe "make sonnets" do
428
+ sonnet_options = [
429
+ { rhyme: 'ababcdcdefefgg' },
430
+ { rhyme: 'abab cdcd efef gg', indent: '0101 0101 0011 01' },
431
+ { form: 'sonnet' },
432
+ { form: :sonnet, syllable: 0 },
433
+ { form: :sonnet, syllable: 10 },
434
+ { form: :sonnet, regex: /^[A-Z].*$/ },
435
+ { form: :sonnet, regex: '^[A-Z].*$' },
436
+ { form: :sonnet, acrostic: 'pauldpthompson' },
437
+ { form: 'sonnet', indent: '01010101001101' },
438
+ { form: 'sonnet', proper: false }
439
+ ]
440
+ sonnet_options.each do |option|
441
+ it "#{option}" do
442
+ 4.times do
443
+ poem = @poefy.poem(option)
444
+ expect(poem).to_not be_nil
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ describe "using syllable string" do
451
+
452
+ it "({ rhyme: 'abcb defe', syllable: '[8,6,8,6,0,8,6,8,6]' })" do
453
+ options = {
454
+ rhyme: 'abcb defe',
455
+ syllable: '[8,6,8,6,0,8,6,8,6]'
456
+ }
457
+ poem = @poefy.poem (options)
458
+ expect(poem.count).to be options[:rhyme].length
459
+ end
460
+
461
+ it "({ rhyme: 'abcb defe', syllable: '[8,6,8,6,8,6,8,6]' })" do
462
+ options = {
463
+ rhyme: 'abcb defe',
464
+ syllable: '[8,6,8,6,8,6,8,6]'
465
+ }
466
+ poem = @poefy.poem (options)
467
+ expect(poem.count).to be options[:rhyme].length
468
+ end
469
+ end
470
+ end
471
+
472
+ ##############################################################################
473
+
474
+ describe "reusing the same Poem instance" do
475
+ it "should correctly merge the option hashes" do
476
+
477
+ # Default to use rondeau poetic form, and proper sentence validation
478
+ poefy = Poefy::Poem.new(
479
+ 'spec_shakespeare',
480
+ { form: 'rondeau', proper: true }
481
+ )
482
+
483
+ # Generate a properly sentenced rondeau
484
+ poem = poefy.poem
485
+ expect(poem.count).to be 17
486
+
487
+ # Generate a rondeau without proper validation
488
+ poem = poefy.poem ({ proper: false })
489
+ expect(poem.count).to be 17
490
+
491
+ # Generate a proper rondeau with a certain indentation
492
+ poem = poefy.poem ({ indent: '01012 0012 010112' })
493
+ expect(poem.count).to be 17
494
+
495
+ # Generate other forms
496
+ poem = poefy.poem ({ rhyme: 'abbaabbacdecde' })
497
+ expect(poem.count).to be 14
498
+ poem = poefy.poem ({ form: 'sonnet' })
499
+ expect(poem.count).to be 14
500
+ poem = poefy.poem ({ form: 'ballade' })
501
+ expect(poem.count).to be 31
502
+
503
+ # Generate a default rondeau again
504
+ poem = poefy.poem
505
+ expect(poem.count).to be 17
506
+
507
+ poefy.close
508
+ end
509
+ end
510
+
511
+ ##############################################################################
512
+
513
+ describe "using the transform option" do
514
+
515
+ it "should correctly transform the output 1" do
516
+ poefy = Poefy::Poem.new :spec_shakespeare
517
+ transform_hash = {
518
+ 4 => proc { |line, num, poem| line.upcase },
519
+ 12 => proc { |line, num, poem| line.upcase }
520
+ }
521
+ poem = poefy.poem({ form: :sonnet, transform: transform_hash })
522
+ expect(poem.count).to be 14
523
+ expect(poem[3]).to eq poem[3].upcase
524
+ expect(poem[11]).to eq poem[11].upcase
525
+ poefy.close
526
+ end
527
+
528
+ it "should correctly transform the output 2" do
529
+ poefy = Poefy::Poem.new :spec_shakespeare
530
+ transform_hash = {
531
+ 4 => proc { |line, num, poem| poem.count },
532
+ -3 => proc { |line, num, poem| poem.count },
533
+ 7 => proc { |line, num, poem| 'test string' }
534
+ }
535
+ poem = poefy.poem({ form: :sonnet, transform: transform_hash })
536
+ expect(poem.count).to be 14
537
+ expect(poem[3]).to eq '14'
538
+ expect(poem[11]).to eq '14'
539
+ expect(poem[6]).to eq 'test string'
540
+ poefy.close
541
+ end
542
+
543
+ it "should correctly transform the output 3" do
544
+ poefy = Poefy::Poem.new :spec_shakespeare
545
+ transform_proc = proc { |line, num, poem| line.downcase }
546
+ poem = poefy.poem({ form: :sonnet, transform: transform_proc })
547
+ expect(poem.count).to be 14
548
+ poem.each do |i|
549
+ expect(i).to eq i.downcase
550
+ end
551
+ poefy.close
552
+ end
553
+
554
+ it "should correctly transform the output 4" do
555
+ poefy = Poefy::Poem.new :spec_shakespeare
556
+ transform_proc = proc { |line, num, poem| "#{num} #{line.downcase}" }
557
+ poem = poefy.poem({ form: :sonnet, transform: transform_proc })
558
+ expect(poem.count).to be 14
559
+ poem.each.with_index do |line, index|
560
+ expect(line).to eq line.downcase
561
+ first_word = line.split(' ').first
562
+ expect(first_word).to eq (index + 1).to_s
563
+ end
564
+ poefy.close
565
+ end
566
+ end
567
+
568
+ ##############################################################################
569
+
570
+ describe "using the form_from_text option" do
571
+ before(:all) do
572
+ @text = <<-TEXT
573
+ [Chorus 1]
574
+ Oh yeah, I'll tell you something
575
+ I think you'll understand
576
+ When I'll say that something
577
+ I want to hold your hand
578
+ I want to hold your hand
579
+ I want to hold your hand
580
+
581
+ [Verse 1]
582
+ Oh please, say to me
583
+ You'll let me be your man
584
+ And please, say to me
585
+ You'll let me hold your hand
586
+ I'll let me hold your hand
587
+ I want to hold your hand
588
+ TEXT
589
+ @line_count = @text.split("\n").count
590
+ end
591
+
592
+ it "should use the exact poetic form 1" do
593
+ poefy = Poefy::Poem.new(:spec_whitman, {
594
+ form_from_text: @text
595
+ })
596
+ poem = poefy.poem
597
+ poem.map!(&:strip!)
598
+ expect(poem.count).to be @line_count
599
+ expect(poem[0]).to eq "[Chorus 1]"
600
+ expect(poem[8]).to eq "[Verse 1]"
601
+ expect(poem[5]).to eq poem[4]
602
+ expect(poem[6]).to eq poem[4]
603
+ poefy.close
604
+ end
605
+
606
+ it "should use the exact poetic form 2" do
607
+ poefy = Poefy::Poem.new :spec_whitman
608
+ poem = poefy.poem({
609
+ form_from_text: @text
610
+ })
611
+ poem.map!(&:strip!)
612
+ expect(poem.count).to be @line_count
613
+ expect(poem[0]).to eq "[Chorus 1]"
614
+ expect(poem[8]).to eq "[Verse 1]"
615
+ expect(poem[5]).to eq poem[4]
616
+ expect(poem[6]).to eq poem[4]
617
+ poefy.close
618
+ end
619
+
620
+ it "should correctly modify the poetic form 1" do
621
+ poefy = Poefy::Poem.new(:spec_whitman, {
622
+ form_from_text: @text,
623
+ syllable: 6
624
+ })
625
+ poem = poefy.poem
626
+ poem.map!(&:strip!)
627
+ expect(poem.count).to be @line_count
628
+ expect(poem[0]).to eq "[Chorus 1]"
629
+ expect(poem[8]).to eq "[Verse 1]"
630
+ expect(poem[5]).to eq poem[4]
631
+ expect(poem[6]).to eq poem[4]
632
+ poefy.close
633
+ end
634
+
635
+ it "should correctly modify the poetic form 2" do
636
+ poefy = Poefy::Poem.new :spec_whitman
637
+ poem = poefy.poem({
638
+ form_from_text: @text,
639
+ syllable: 6
640
+ })
641
+ poem.map!(&:strip!)
642
+ expect(poem.count).to be @line_count
643
+ expect(poem[0]).to eq "[Chorus 1]"
644
+ expect(poem[8]).to eq "[Verse 1]"
645
+ expect(poem[5]).to eq poem[4]
646
+ expect(poem[6]).to eq poem[4]
647
+ poefy.close
648
+ end
649
+
650
+ it "should correctly modify the poetic form 3" do
651
+ poefy = Poefy::Poem.new(:spec_whitman, {
652
+ form_from_text: @text
653
+ })
654
+ poem = poefy.poem({
655
+ syllable: 6
656
+ })
657
+ poem.map!(&:strip!)
658
+ expect(poem.count).to be @line_count
659
+ expect(poem[0]).to eq "[Chorus 1]"
660
+ expect(poem[8]).to eq "[Verse 1]"
661
+ expect(poem[5]).to eq poem[4]
662
+ expect(poem[6]).to eq poem[4]
663
+ poefy.close
664
+ end
665
+
666
+ it "should correctly replace the poetic form" do
667
+ poefy = Poefy::Poem.new(:spec_whitman, {
668
+ syllable: 6
669
+ })
670
+ poem = poefy.poem({
671
+ form_from_text: @text
672
+ })
673
+ poem.map!(&:strip!)
674
+ expect(poem.count).to be @line_count
675
+ expect(poem[0]).to eq "[Chorus 1]"
676
+ expect(poem[8]).to eq "[Verse 1]"
677
+ expect(poem[5]).to eq poem[4]
678
+ expect(poem[6]).to eq poem[4]
679
+ poefy.close
680
+ end
681
+
682
+ end
683
+
684
+ end
685
+
686
+ ################################################################################
@@ -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-sqlite3
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: sqlite3
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.3'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 1.3.13
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '1.3'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 1.3.13
109
+ description: SQLite 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/sqlite3.rb
123
+ - lib/poefy/sqlite3/version.rb
124
+ - poefy-sqlite3.gemspec
125
+ - spec/poefy_sqlite3_spec.rb
126
+ - spec/spec_helper.rb
127
+ homepage: https://github.com/nossidge/poefy-sqlite3
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: SQLite interface for the 'poefy' gem
151
+ test_files: []