spoonerize 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +28 -1
- data/README.md +26 -2
- data/bin/spoonerize-web +5 -0
- data/lib/spoonerize/cli.rb +3 -3
- data/lib/spoonerize/config.rb +2 -0
- data/lib/spoonerize/spoonerism.rb +14 -0
- data/lib/spoonerize/version.rb +1 -1
- data/lib/spoonerize/web/cli.rb +72 -0
- data/lib/spoonerize/web/public/styles.css +178 -0
- data/lib/spoonerize/web/views/index.erb +61 -0
- data/lib/spoonerize/web/views/layout.erb +20 -0
- data/lib/spoonerize/web.rb +121 -0
- data/lib/spoonerize.rb +10 -0
- data/spoonerize.gemspec +4 -0
- metadata +51 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ad82a22fd996a0e5af1653a6ed66f27863da252331b4fa58865980d6a27b063
|
|
4
|
+
data.tar.gz: d206494ce8cc6e9dabbda3ab86389d080e57d1baf0a003800ba6edd3fe3c3b13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8b89f4ea9ca38fb0ee9773ca55e6fb654e94d4135f4b4748abc91c90fe014e3c1bd90e6f0c9c4f9d74f2efcf64b7bfd8b488ebcf3cf92dc40d9c2616089d30e
|
|
7
|
+
data.tar.gz: 1c81200ffd8922cc1384c11b9a8a8766eae3d59f3301c544dbf9a6928617c85d231f931cec768d4ad4c7c8fcbc697ca94d666c182340974c7d406411c11dab34
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
spoonerize (
|
|
4
|
+
spoonerize (2.0.0)
|
|
5
5
|
csv
|
|
6
|
+
puma (~> 8.0)
|
|
7
|
+
rackup (~> 2.3)
|
|
8
|
+
sinatra (~> 4.2)
|
|
6
9
|
|
|
7
10
|
GEM
|
|
8
11
|
remote: https://rubygems.org/
|
|
9
12
|
specs:
|
|
10
13
|
ast (2.4.3)
|
|
14
|
+
base64 (0.3.0)
|
|
11
15
|
csv (3.3.5)
|
|
12
16
|
date (3.5.1)
|
|
13
17
|
erb (6.0.4)
|
|
14
18
|
json (2.20.0)
|
|
15
19
|
language_server-protocol (3.17.0.5)
|
|
16
20
|
lint_roller (1.1.0)
|
|
21
|
+
logger (1.7.0)
|
|
22
|
+
mustermann (3.1.1)
|
|
23
|
+
nio4r (2.7.5)
|
|
17
24
|
parallel (1.28.0)
|
|
18
25
|
parser (3.3.11.1)
|
|
19
26
|
ast (~> 2.4.1)
|
|
@@ -23,7 +30,19 @@ GEM
|
|
|
23
30
|
psych (5.4.0)
|
|
24
31
|
date
|
|
25
32
|
stringio
|
|
33
|
+
puma (8.0.2)
|
|
34
|
+
nio4r (~> 2.0)
|
|
26
35
|
racc (1.8.1)
|
|
36
|
+
rack (3.2.6)
|
|
37
|
+
rack-protection (4.2.1)
|
|
38
|
+
base64 (>= 0.1.0)
|
|
39
|
+
logger (>= 1.6.0)
|
|
40
|
+
rack (>= 3.0.0, < 4)
|
|
41
|
+
rack-session (2.1.2)
|
|
42
|
+
base64 (>= 0.1.0)
|
|
43
|
+
rack (>= 3.0.0)
|
|
44
|
+
rackup (2.3.1)
|
|
45
|
+
rack (>= 3)
|
|
27
46
|
rainbow (3.1.1)
|
|
28
47
|
rake (13.4.2)
|
|
29
48
|
rdoc (7.2.0)
|
|
@@ -50,6 +69,13 @@ GEM
|
|
|
50
69
|
rubocop (>= 1.75.0, < 2.0)
|
|
51
70
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
52
71
|
ruby-progressbar (1.13.0)
|
|
72
|
+
sinatra (4.2.1)
|
|
73
|
+
logger (>= 1.6.0)
|
|
74
|
+
mustermann (~> 3.0)
|
|
75
|
+
rack (>= 3.0.0, < 4)
|
|
76
|
+
rack-protection (= 4.2.1)
|
|
77
|
+
rack-session (>= 2.0.0, < 3)
|
|
78
|
+
tilt (~> 2.0)
|
|
53
79
|
standard (1.54.0)
|
|
54
80
|
language_server-protocol (~> 3.17.0.2)
|
|
55
81
|
lint_roller (~> 1.0)
|
|
@@ -65,6 +91,7 @@ GEM
|
|
|
65
91
|
stringio (3.2.0)
|
|
66
92
|
test-unit (3.7.8)
|
|
67
93
|
power_assert
|
|
94
|
+
tilt (2.7.0)
|
|
68
95
|
tsort (0.2.0)
|
|
69
96
|
unicode-display_width (3.2.0)
|
|
70
97
|
unicode-emoji (~> 4.1)
|
data/README.md
CHANGED
|
@@ -100,7 +100,7 @@ Here is a list of all available options:
|
|
|
100
100
|
|
|
101
101
|
```
|
|
102
102
|
-r, --[no-]reverse Reverse flipping
|
|
103
|
-
-l, --[no-]lazy Skip
|
|
103
|
+
-l, --[no-]lazy Skip common words
|
|
104
104
|
-c, --[no-]consonants-only Only flip consonant-starting words
|
|
105
105
|
-m, --[no-]map Print words mapping
|
|
106
106
|
-p, --[no-]print-log Print all entries in the log
|
|
@@ -108,10 +108,34 @@ Here is a list of all available options:
|
|
|
108
108
|
--exclude=WORD Words to skip
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
## Web Usage
|
|
112
|
+
The gem also installs a small Sinatra app:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
spoonerize-web
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
By default, Sinatra starts on its normal local development address. You can
|
|
119
|
+
choose a host or port when you need to:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
spoonerize-web --host 127.0.0.1 --port 9292
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Open the printed local URL in your browser, enter a phrase, choose any options,
|
|
126
|
+
and submit the form. The page reloads with the spoonerized result and keeps
|
|
127
|
+
your phrase and options in the form. Check "Save result" to write a successful
|
|
128
|
+
result to the configured log file.
|
|
129
|
+
|
|
130
|
+
The web app ships with the main gem for now, so `gem install spoonerize`
|
|
131
|
+
installs both `spoonerize` and `spoonerize-web`.
|
|
132
|
+
|
|
111
133
|
### Config File
|
|
112
134
|
You can create a Ruby config file called `~/.spoonerizerc`. The CLI loads this
|
|
113
135
|
file automatically before it parses command-line options, so options set in the
|
|
114
|
-
file can still be overridden at runtime by executable flags.
|
|
136
|
+
file can still be overridden at runtime by executable flags. The web app loads
|
|
137
|
+
the same file when it starts, and uses those values for the initial form
|
|
138
|
+
defaults.
|
|
115
139
|
|
|
116
140
|
```ruby
|
|
117
141
|
Spoonerize.configure do |config|
|
data/bin/spoonerize-web
ADDED
data/lib/spoonerize/cli.rb
CHANGED
|
@@ -7,10 +7,10 @@ module Spoonerize
|
|
|
7
7
|
# The class for handling the command-line interface.
|
|
8
8
|
class Cli
|
|
9
9
|
##
|
|
10
|
-
# The config file the
|
|
10
|
+
# The config file the CLI loads before parsing runtime options.
|
|
11
11
|
#
|
|
12
12
|
# @return [String]
|
|
13
|
-
CONFIG_FILE =
|
|
13
|
+
CONFIG_FILE = Spoonerize::CONFIG_FILE
|
|
14
14
|
|
|
15
15
|
##
|
|
16
16
|
# Creates an instance of +Spoonerism+ and runs what the user requested.
|
|
@@ -131,7 +131,7 @@ module Spoonerize
|
|
|
131
131
|
o.on("-r", "--[no-]reverse", "Reverse flipping") do |v|
|
|
132
132
|
prefs[:reverse] = v
|
|
133
133
|
end
|
|
134
|
-
o.on("-l", "--[no-]lazy", "Skip
|
|
134
|
+
o.on("-l", "--[no-]lazy", "Skip common words") do |v|
|
|
135
135
|
prefs[:lazy] = v
|
|
136
136
|
end
|
|
137
137
|
o.on("-c", "--[no-]consonants-only", "Only flip consonant-starting words") do |v|
|
data/lib/spoonerize/config.rb
CHANGED
|
@@ -4,8 +4,22 @@ module Spoonerize
|
|
|
4
4
|
##
|
|
5
5
|
# The main word-flipper.
|
|
6
6
|
class Spoonerism
|
|
7
|
+
##
|
|
8
|
+
# Letters that always represent vowel sounds.
|
|
9
|
+
#
|
|
10
|
+
# @return [String]
|
|
7
11
|
VOWEL_LETTERS = "aeio"
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
# Letters that always represent consonant sounds.
|
|
15
|
+
#
|
|
16
|
+
# @return [String]
|
|
8
17
|
CONSONANT_LETTERS = "bcdfghjklmnprstvwxz"
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# Letters that make an initial "y" act like a vowel sound.
|
|
21
|
+
#
|
|
22
|
+
# @return [String]
|
|
9
23
|
Y_FOLLOWING_CONSONANTS = "bcdfghjklmnpqrstvwxz"
|
|
10
24
|
|
|
11
25
|
##
|
data/lib/spoonerize/version.rb
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
|
|
5
|
+
require_relative "../web"
|
|
6
|
+
|
|
7
|
+
module Spoonerize
|
|
8
|
+
class Web
|
|
9
|
+
##
|
|
10
|
+
# Command-line launcher for the web app.
|
|
11
|
+
class Cli
|
|
12
|
+
##
|
|
13
|
+
# Server options used when no command-line overrides are passed.
|
|
14
|
+
#
|
|
15
|
+
# @return [Hash]
|
|
16
|
+
DEFAULT_OPTIONS = {
|
|
17
|
+
host: Web.bind,
|
|
18
|
+
port: Web.port
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Starts the web app from command-line arguments.
|
|
23
|
+
#
|
|
24
|
+
# @param [Array] options
|
|
25
|
+
#
|
|
26
|
+
# @return [nil]
|
|
27
|
+
def self.execute(options = [])
|
|
28
|
+
new(options).execute
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Parsed server options.
|
|
33
|
+
#
|
|
34
|
+
# @return [Hash]
|
|
35
|
+
attr_reader :options
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Create a web CLI launcher.
|
|
39
|
+
#
|
|
40
|
+
# @param [Array] options Command-line arguments.
|
|
41
|
+
#
|
|
42
|
+
# @return [self]
|
|
43
|
+
def initialize(options)
|
|
44
|
+
@options = DEFAULT_OPTIONS.merge(parse(options))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# Starts the Sinatra web app.
|
|
49
|
+
#
|
|
50
|
+
# @return [nil]
|
|
51
|
+
def execute
|
|
52
|
+
Web.run!(bind: options[:host], port: options[:port])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def parse(options)
|
|
58
|
+
{}.tap do |prefs|
|
|
59
|
+
OptionParser.new do |o|
|
|
60
|
+
o.version = ::Spoonerize::Version.to_s
|
|
61
|
+
o.on("--host=HOST", "Host to bind") do |value|
|
|
62
|
+
prefs[:host] = value
|
|
63
|
+
end
|
|
64
|
+
o.on("--port=PORT", Integer, "Port to bind") do |value|
|
|
65
|
+
prefs[:port] = value
|
|
66
|
+
end
|
|
67
|
+
end.parse!(options)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light;
|
|
3
|
+
--bg: #f7f7f4;
|
|
4
|
+
--ink: #24211f;
|
|
5
|
+
--muted: #6c655f;
|
|
6
|
+
--line: #d7d2c9;
|
|
7
|
+
--panel: #fffdfa;
|
|
8
|
+
--accent: #1d6f78;
|
|
9
|
+
--accent-dark: #145059;
|
|
10
|
+
--notice-bg: #fff0dc;
|
|
11
|
+
--notice-line: #e1a853;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
* {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
body {
|
|
19
|
+
margin: 0;
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
background: var(--bg);
|
|
22
|
+
color: var(--ink);
|
|
23
|
+
font-family: ui-serif, Georgia, "Times New Roman", serif;
|
|
24
|
+
line-height: 1.5;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.page {
|
|
28
|
+
min-height: 100vh;
|
|
29
|
+
display: grid;
|
|
30
|
+
place-items: start center;
|
|
31
|
+
padding: 4rem 1rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.shell {
|
|
35
|
+
width: min(100%, 46rem);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.masthead {
|
|
39
|
+
margin-bottom: 2rem;
|
|
40
|
+
border-bottom: 1px solid var(--line);
|
|
41
|
+
padding-bottom: 1.25rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
h1 {
|
|
45
|
+
margin: 0;
|
|
46
|
+
font-size: clamp(2.2rem, 7vw, 4rem);
|
|
47
|
+
line-height: 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.result {
|
|
51
|
+
margin: 0 0 1.75rem;
|
|
52
|
+
font-size: clamp(2rem, 6vw, 3.6rem);
|
|
53
|
+
font-weight: 800;
|
|
54
|
+
line-height: 1.05;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.notice {
|
|
58
|
+
margin: 0 0 1.5rem;
|
|
59
|
+
border: 1px solid var(--notice-line);
|
|
60
|
+
background: var(--notice-bg);
|
|
61
|
+
padding: 0.8rem 1rem;
|
|
62
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
63
|
+
font-weight: 650;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.spoonerize-form {
|
|
67
|
+
display: grid;
|
|
68
|
+
gap: 1rem;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.field,
|
|
72
|
+
fieldset {
|
|
73
|
+
display: grid;
|
|
74
|
+
gap: 0.45rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.field span,
|
|
78
|
+
legend {
|
|
79
|
+
color: var(--muted);
|
|
80
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
81
|
+
font-size: 0.85rem;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
input[type="text"] {
|
|
86
|
+
width: 100%;
|
|
87
|
+
border: 1px solid var(--line);
|
|
88
|
+
border-radius: 4px;
|
|
89
|
+
background: var(--panel);
|
|
90
|
+
color: var(--ink);
|
|
91
|
+
font: inherit;
|
|
92
|
+
font-size: 1.1rem;
|
|
93
|
+
padding: 0.75rem 0.85rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
input[type="text"]:focus {
|
|
97
|
+
border-color: var(--accent);
|
|
98
|
+
outline: 3px solid color-mix(in srgb, var(--accent) 22%, transparent);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fieldset {
|
|
102
|
+
margin: 0;
|
|
103
|
+
border: 1px solid var(--line);
|
|
104
|
+
border-radius: 4px;
|
|
105
|
+
background: var(--panel);
|
|
106
|
+
padding: 0.9rem 1rem 1rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
legend {
|
|
110
|
+
padding: 0 0.35rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.check {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 0.55rem;
|
|
117
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.check input {
|
|
121
|
+
width: 1rem;
|
|
122
|
+
height: 1rem;
|
|
123
|
+
accent-color: var(--accent);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
button {
|
|
127
|
+
width: fit-content;
|
|
128
|
+
border: 0;
|
|
129
|
+
border-radius: 4px;
|
|
130
|
+
background: var(--accent);
|
|
131
|
+
color: #fff;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
134
|
+
font-size: 1rem;
|
|
135
|
+
font-weight: 750;
|
|
136
|
+
padding: 0.72rem 1rem;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
button:hover,
|
|
140
|
+
button:focus {
|
|
141
|
+
background: var(--accent-dark);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.footer {
|
|
145
|
+
width: min(100%, 46rem);
|
|
146
|
+
margin-top: 2.5rem;
|
|
147
|
+
border-top: 1px solid var(--line);
|
|
148
|
+
padding-top: 1rem;
|
|
149
|
+
color: var(--muted);
|
|
150
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
151
|
+
font-size: 0.82rem;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.footer a {
|
|
155
|
+
color: inherit;
|
|
156
|
+
text-decoration-color: color-mix(in srgb, var(--muted) 45%, transparent);
|
|
157
|
+
text-underline-offset: 0.16em;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.footer a:hover,
|
|
161
|
+
.footer a:focus {
|
|
162
|
+
color: var(--accent-dark);
|
|
163
|
+
text-decoration-color: currentcolor;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.footer span {
|
|
167
|
+
margin: 0 0.35rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@media (max-width: 40rem) {
|
|
171
|
+
.page {
|
|
172
|
+
padding-top: 2rem;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
button {
|
|
176
|
+
width: 100%;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<section class="shell">
|
|
2
|
+
<header class="masthead">
|
|
3
|
+
<h1>Flip a phrase</h1>
|
|
4
|
+
</header>
|
|
5
|
+
|
|
6
|
+
<% if @result %>
|
|
7
|
+
<p class="result"><%= h @result %></p>
|
|
8
|
+
<% if @saved %>
|
|
9
|
+
<p class="notice">Saved.</p>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% elsif @error %>
|
|
12
|
+
<p class="notice"><%= h @error %></p>
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<form action="/" method="post" class="spoonerize-form">
|
|
16
|
+
<label class="field">
|
|
17
|
+
<span>Phrase</span>
|
|
18
|
+
<input
|
|
19
|
+
type="text"
|
|
20
|
+
name="phrase"
|
|
21
|
+
value="<%= h @phrase %>"
|
|
22
|
+
placeholder="Enter phrase to spoonerize..."
|
|
23
|
+
autofocus>
|
|
24
|
+
</label>
|
|
25
|
+
|
|
26
|
+
<fieldset>
|
|
27
|
+
<legend>Options</legend>
|
|
28
|
+
|
|
29
|
+
<label class="check">
|
|
30
|
+
<input type="checkbox" name="reverse" value="1" <%= checked?("reverse") %>>
|
|
31
|
+
<span>Reverse flipping</span>
|
|
32
|
+
</label>
|
|
33
|
+
|
|
34
|
+
<label class="check">
|
|
35
|
+
<input type="checkbox" name="lazy" value="1" <%= checked?("lazy") %>>
|
|
36
|
+
<span>Skip common words</span>
|
|
37
|
+
</label>
|
|
38
|
+
|
|
39
|
+
<label class="check">
|
|
40
|
+
<input type="checkbox" name="consonants_only" value="1" <%= checked?("consonants_only") %>>
|
|
41
|
+
<span>Only flip consonant-starting words</span>
|
|
42
|
+
</label>
|
|
43
|
+
|
|
44
|
+
<label class="check">
|
|
45
|
+
<input type="checkbox" name="save" value="1" <%= @save ? "checked" : nil %>>
|
|
46
|
+
<span>Save result</span>
|
|
47
|
+
</label>
|
|
48
|
+
</fieldset>
|
|
49
|
+
|
|
50
|
+
<label class="field">
|
|
51
|
+
<span>Excluded words</span>
|
|
52
|
+
<input
|
|
53
|
+
type="text"
|
|
54
|
+
name="excluded_words"
|
|
55
|
+
value="<%= h @excluded_words_value %>"
|
|
56
|
+
placeholder="word another">
|
|
57
|
+
</label>
|
|
58
|
+
|
|
59
|
+
<button type="submit">Spoonerize</button>
|
|
60
|
+
</form>
|
|
61
|
+
</section>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Spoonerize</title>
|
|
7
|
+
<link rel="stylesheet" href="/styles.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<main class="page">
|
|
11
|
+
<%= yield %>
|
|
12
|
+
|
|
13
|
+
<footer class="footer">
|
|
14
|
+
<a href="https://github.com/evanthegrayt">evanthegrayt</a>
|
|
15
|
+
<span>/</span>
|
|
16
|
+
<a href="https://github.com/evanthegrayt/spoonerize">spoonerize</a>
|
|
17
|
+
</footer>
|
|
18
|
+
</main>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sinatra/base"
|
|
4
|
+
|
|
5
|
+
require_relative "../spoonerize"
|
|
6
|
+
|
|
7
|
+
module Spoonerize
|
|
8
|
+
##
|
|
9
|
+
# Sinatra app for spoonerizing phrases in the browser.
|
|
10
|
+
class Web < Sinatra::Base
|
|
11
|
+
##
|
|
12
|
+
# Boolean Spoonerism options exposed by the web form.
|
|
13
|
+
#
|
|
14
|
+
# @return [Array<String>]
|
|
15
|
+
OPTION_NAMES = %w[reverse lazy consonants_only].freeze
|
|
16
|
+
|
|
17
|
+
set :root, File.expand_path(File.join(__dir__, "..", ".."))
|
|
18
|
+
set :public_folder, File.expand_path(File.join(__dir__, "web", "public"))
|
|
19
|
+
set :views, File.expand_path(File.join(__dir__, "web", "views"))
|
|
20
|
+
set :static, true
|
|
21
|
+
set :show_exceptions, false
|
|
22
|
+
|
|
23
|
+
configure do
|
|
24
|
+
Spoonerize.load_config_file(Spoonerize::CONFIG_FILE) if File.file?(Spoonerize::CONFIG_FILE)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
helpers do
|
|
28
|
+
##
|
|
29
|
+
# HTML checkbox attribute for a truthy option value.
|
|
30
|
+
#
|
|
31
|
+
# @param [String] name The option name.
|
|
32
|
+
#
|
|
33
|
+
# @return [String, nil]
|
|
34
|
+
def checked?(name)
|
|
35
|
+
option_value(name) ? "checked" : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Current boolean value for a web form option.
|
|
40
|
+
#
|
|
41
|
+
# @param [String] name The option name.
|
|
42
|
+
#
|
|
43
|
+
# @return [Boolean]
|
|
44
|
+
def option_value(name)
|
|
45
|
+
@options.fetch(name.to_sym)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Escapes a value for safe HTML output.
|
|
50
|
+
#
|
|
51
|
+
# @param [Object] value The value to escape.
|
|
52
|
+
#
|
|
53
|
+
# @return [String]
|
|
54
|
+
def h(value)
|
|
55
|
+
Rack::Utils.escape_html(value)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
get "/" do
|
|
60
|
+
prepare_request(false)
|
|
61
|
+
erb :index
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
post "/" do
|
|
65
|
+
prepare_request(true)
|
|
66
|
+
@result = spoonerize_phrase if @submitted && !@phrase.strip.empty?
|
|
67
|
+
|
|
68
|
+
erb :index
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def options_from_params
|
|
74
|
+
OPTION_NAMES.to_h do |name|
|
|
75
|
+
value = @submitted ? params.key?(name) : Spoonerize.config.public_send(name)
|
|
76
|
+
[name.to_sym, value]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def excluded_words_from(value)
|
|
81
|
+
value.split(/[,\s]+/).reject(&:empty?)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def excluded_words_from_params
|
|
85
|
+
return excluded_words_from(params["excluded_words"].to_s) if @submitted
|
|
86
|
+
|
|
87
|
+
Spoonerize.config.excluded_words
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def prepare_request(submitted)
|
|
91
|
+
@submitted = submitted
|
|
92
|
+
@phrase = params["phrase"].to_s
|
|
93
|
+
@options = options_from_params
|
|
94
|
+
@excluded_words = excluded_words_from_params
|
|
95
|
+
@excluded_words_value = @excluded_words.join(" ")
|
|
96
|
+
@save = @submitted && params.key?("save")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def spoonerize_phrase
|
|
100
|
+
spoonerism = Spoonerism.new(
|
|
101
|
+
*@phrase.split,
|
|
102
|
+
**@options,
|
|
103
|
+
excluded_words: @excluded_words
|
|
104
|
+
)
|
|
105
|
+
result = spoonerism.to_s
|
|
106
|
+
spoonerism.save if @save
|
|
107
|
+
@saved = @save
|
|
108
|
+
|
|
109
|
+
result
|
|
110
|
+
rescue => error
|
|
111
|
+
@error = friendly_error(error)
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def friendly_error(error)
|
|
116
|
+
return "Not enough words to flip." if error.message == "Not enough words to flip"
|
|
117
|
+
|
|
118
|
+
error.message
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/spoonerize.rb
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
##
|
|
4
|
+
# The main namespace for the gem.
|
|
5
|
+
module Spoonerize
|
|
6
|
+
##
|
|
7
|
+
# The config file the user can create to change default runtime options.
|
|
8
|
+
#
|
|
9
|
+
# @return [String]
|
|
10
|
+
CONFIG_FILE = File.expand_path(File.join(ENV["HOME"], ".spoonerizerc"))
|
|
11
|
+
end
|
|
12
|
+
|
|
3
13
|
require_relative "spoonerize/config"
|
|
4
14
|
require_relative "spoonerize/spoonerism"
|
|
5
15
|
require_relative "spoonerize/bumper"
|
data/spoonerize.gemspec
CHANGED
|
@@ -10,6 +10,7 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
spec.summary = %(Spoonerize phrases from the command line.)
|
|
11
11
|
spec.description = %(Spoonerize phrases from the command line. Comes with an API)
|
|
12
12
|
spec.homepage = "https://evanthegrayt.github.io/spoonerize/"
|
|
13
|
+
spec.required_ruby_version = ">= 3.2"
|
|
13
14
|
|
|
14
15
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
15
16
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
@@ -29,6 +30,9 @@ Gem::Specification.new do |spec|
|
|
|
29
30
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
30
31
|
spec.require_paths = ["lib"]
|
|
31
32
|
spec.add_dependency "csv"
|
|
33
|
+
spec.add_dependency "puma", "~> 8.0"
|
|
34
|
+
spec.add_dependency "rackup", "~> 2.3"
|
|
35
|
+
spec.add_dependency "sinatra", "~> 4.2"
|
|
32
36
|
spec.add_development_dependency "rdoc"
|
|
33
37
|
spec.add_development_dependency "rake", "~> 13.0", ">= 13.0.1"
|
|
34
38
|
spec.add_development_dependency "standard", "= 1.54.0"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spoonerize
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Evan Gray
|
|
@@ -23,6 +23,48 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: puma
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '8.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rackup
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.3'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.3'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: sinatra
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '4.2'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '4.2'
|
|
26
68
|
- !ruby/object:Gem::Dependency
|
|
27
69
|
name: rdoc
|
|
28
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -95,6 +137,7 @@ description: Spoonerize phrases from the command line. Comes with an API
|
|
|
95
137
|
email: evanthegrayt@vivaldi.net
|
|
96
138
|
executables:
|
|
97
139
|
- spoonerize
|
|
140
|
+
- spoonerize-web
|
|
98
141
|
extensions: []
|
|
99
142
|
extra_rdoc_files: []
|
|
100
143
|
files:
|
|
@@ -107,6 +150,7 @@ files:
|
|
|
107
150
|
- Rakefile
|
|
108
151
|
- _config.yml
|
|
109
152
|
- bin/spoonerize
|
|
153
|
+
- bin/spoonerize-web
|
|
110
154
|
- lib/spoonerize.rb
|
|
111
155
|
- lib/spoonerize/bumper.rb
|
|
112
156
|
- lib/spoonerize/cli.rb
|
|
@@ -114,6 +158,11 @@ files:
|
|
|
114
158
|
- lib/spoonerize/log.rb
|
|
115
159
|
- lib/spoonerize/spoonerism.rb
|
|
116
160
|
- lib/spoonerize/version.rb
|
|
161
|
+
- lib/spoonerize/web.rb
|
|
162
|
+
- lib/spoonerize/web/cli.rb
|
|
163
|
+
- lib/spoonerize/web/public/styles.css
|
|
164
|
+
- lib/spoonerize/web/views/index.erb
|
|
165
|
+
- lib/spoonerize/web/views/layout.erb
|
|
117
166
|
- spoonerize.gemspec
|
|
118
167
|
homepage: https://evanthegrayt.github.io/spoonerize/
|
|
119
168
|
licenses:
|
|
@@ -130,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
130
179
|
requirements:
|
|
131
180
|
- - ">="
|
|
132
181
|
- !ruby/object:Gem::Version
|
|
133
|
-
version: '
|
|
182
|
+
version: '3.2'
|
|
134
183
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
184
|
requirements:
|
|
136
185
|
- - ">="
|