spoonerize 1.0.0 → 2.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 +4 -4
- data/Gemfile.lock +28 -1
- data/README.md +28 -3
- 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 +2 -2
- data/lib/spoonerize/web/cli.rb +72 -0
- data/lib/spoonerize/web/public/styles.css +336 -0
- data/lib/spoonerize/web/views/index.erb +63 -0
- data/lib/spoonerize/web/views/layout.erb +20 -0
- data/lib/spoonerize/web/views/saved.erb +44 -0
- data/lib/spoonerize/web.rb +146 -0
- data/lib/spoonerize.rb +10 -0
- data/spoonerize.gemspec +4 -0
- metadata +52 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf971b88e97e4e680e1aaa25fdd21238734738ff4b6e230580f93305f5c823a5
|
|
4
|
+
data.tar.gz: da9f97e86eeb122f7c1ecbe057a5f73bc0908a86ab5dc5acbb76898ff4e326cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8a74bb8fa978b4a298ea9c3e5fbb3fb53be59875a9a41749570a1014462d5fe4a8aa13dd02683163ee1177b812cd3a69edff1a880975463b83a1fa143f5b2508
|
|
7
|
+
data.tar.gz: 263a67ba0a8d3bc0d3d89f46a4f2ef9a4f2cb9475684ca0a16309916dab7ffc370c5bd0796224e411f0b22a209aac1779aa21b340bdae96fd88a27836d50bc11
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
spoonerize (1.0
|
|
4
|
+
spoonerize (2.1.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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Welcome to Spoonerize -- a word game.
|
|
2
2
|
[](https://actions-badge.atrox.dev/evanthegrayt/spoonerize/goto?ref=master)
|
|
3
|
-
[](https://rubygems.org/gems/spoonerize)
|
|
4
|
+

|
|
4
5
|
[](https://opensource.org/licenses/MIT)
|
|
5
6
|
|
|
6
7
|
> Spoonerism *[noun]* a verbal error in which a speaker accidentally transposes
|
|
@@ -100,7 +101,7 @@ Here is a list of all available options:
|
|
|
100
101
|
|
|
101
102
|
```
|
|
102
103
|
-r, --[no-]reverse Reverse flipping
|
|
103
|
-
-l, --[no-]lazy Skip
|
|
104
|
+
-l, --[no-]lazy Skip common words
|
|
104
105
|
-c, --[no-]consonants-only Only flip consonant-starting words
|
|
105
106
|
-m, --[no-]map Print words mapping
|
|
106
107
|
-p, --[no-]print-log Print all entries in the log
|
|
@@ -108,10 +109,34 @@ Here is a list of all available options:
|
|
|
108
109
|
--exclude=WORD Words to skip
|
|
109
110
|
```
|
|
110
111
|
|
|
112
|
+
## Web Usage
|
|
113
|
+
The gem also installs a small Sinatra app:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
spoonerize-web
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
By default, Sinatra starts on its normal local development address. You can
|
|
120
|
+
choose a host or port when you need to:
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
spoonerize-web --host 127.0.0.1 --port 9292
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Open the printed local URL in your browser, enter a phrase, choose any options,
|
|
127
|
+
and submit the form. The page reloads with the spoonerized result and keeps
|
|
128
|
+
your phrase and options in the form. Check "Save result" to write a successful
|
|
129
|
+
result to the configured log file.
|
|
130
|
+
|
|
131
|
+
The web app ships with the main gem for now, so `gem install spoonerize`
|
|
132
|
+
installs both `spoonerize` and `spoonerize-web`.
|
|
133
|
+
|
|
111
134
|
### Config File
|
|
112
135
|
You can create a Ruby config file called `~/.spoonerizerc`. The CLI loads this
|
|
113
136
|
file automatically before it parses command-line options, so options set in the
|
|
114
|
-
file can still be overridden at runtime by executable flags.
|
|
137
|
+
file can still be overridden at runtime by executable flags. The web app loads
|
|
138
|
+
the same file when it starts, and uses those values for the initial form
|
|
139
|
+
defaults.
|
|
115
140
|
|
|
116
141
|
```ruby
|
|
117
142
|
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,336 @@
|
|
|
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
|
+
.masthead-with-action {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: flex-end;
|
|
47
|
+
justify-content: space-between;
|
|
48
|
+
gap: 1rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
h1 {
|
|
52
|
+
margin: 0;
|
|
53
|
+
font-size: clamp(1.9rem, 4vw, 2.75rem);
|
|
54
|
+
line-height: 1.08;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.lede {
|
|
58
|
+
margin: 0.5rem 0 0;
|
|
59
|
+
color: var(--muted);
|
|
60
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
61
|
+
font-size: 0.95rem;
|
|
62
|
+
font-weight: 700;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.action-link {
|
|
66
|
+
flex: 0 0 auto;
|
|
67
|
+
color: var(--accent-dark);
|
|
68
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
69
|
+
font-size: 0.95rem;
|
|
70
|
+
font-weight: 750;
|
|
71
|
+
text-decoration-color: color-mix(in srgb, var(--accent-dark) 35%, transparent);
|
|
72
|
+
text-underline-offset: 0.18em;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.action-link:hover,
|
|
76
|
+
.action-link:focus {
|
|
77
|
+
color: var(--accent);
|
|
78
|
+
text-decoration-color: currentcolor;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.result {
|
|
82
|
+
margin: 0 0 1.75rem;
|
|
83
|
+
font-size: clamp(2rem, 6vw, 3.6rem);
|
|
84
|
+
font-weight: 800;
|
|
85
|
+
line-height: 1.05;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.notice {
|
|
89
|
+
margin: 0 0 1.5rem;
|
|
90
|
+
border: 1px solid var(--notice-line);
|
|
91
|
+
background: var(--notice-bg);
|
|
92
|
+
padding: 0.8rem 1rem;
|
|
93
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
94
|
+
font-weight: 650;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.spoonerize-form {
|
|
98
|
+
display: grid;
|
|
99
|
+
gap: 1rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.field,
|
|
103
|
+
fieldset {
|
|
104
|
+
display: grid;
|
|
105
|
+
gap: 0.45rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.field span,
|
|
109
|
+
legend {
|
|
110
|
+
color: var(--muted);
|
|
111
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
112
|
+
font-size: 0.85rem;
|
|
113
|
+
font-weight: 700;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
input[type="text"] {
|
|
117
|
+
width: 100%;
|
|
118
|
+
border: 1px solid var(--line);
|
|
119
|
+
border-radius: 4px;
|
|
120
|
+
background: var(--panel);
|
|
121
|
+
color: var(--ink);
|
|
122
|
+
font: inherit;
|
|
123
|
+
font-size: 1.1rem;
|
|
124
|
+
padding: 0.75rem 0.85rem;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
input[type="text"]:focus {
|
|
128
|
+
border-color: var(--accent);
|
|
129
|
+
outline: 3px solid color-mix(in srgb, var(--accent) 22%, transparent);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fieldset {
|
|
133
|
+
margin: 0;
|
|
134
|
+
border: 1px solid var(--line);
|
|
135
|
+
border-radius: 4px;
|
|
136
|
+
background: var(--panel);
|
|
137
|
+
padding: 0.9rem 1rem 1rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
legend {
|
|
141
|
+
padding: 0 0.35rem;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.check {
|
|
145
|
+
display: flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
gap: 0.55rem;
|
|
148
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.check input {
|
|
152
|
+
width: 1rem;
|
|
153
|
+
height: 1rem;
|
|
154
|
+
accent-color: var(--accent);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
button {
|
|
158
|
+
width: fit-content;
|
|
159
|
+
border: 0;
|
|
160
|
+
border-radius: 4px;
|
|
161
|
+
background: var(--accent);
|
|
162
|
+
color: #fff;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
165
|
+
font-size: 1rem;
|
|
166
|
+
font-weight: 750;
|
|
167
|
+
padding: 0.72rem 1rem;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
button:hover,
|
|
171
|
+
button:focus {
|
|
172
|
+
background: var(--accent-dark);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.table-wrap {
|
|
176
|
+
overflow-x: auto;
|
|
177
|
+
border: 1px solid var(--line);
|
|
178
|
+
border-radius: 4px;
|
|
179
|
+
background: var(--panel);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.saved-table {
|
|
183
|
+
width: 100%;
|
|
184
|
+
border-collapse: collapse;
|
|
185
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
186
|
+
font-size: 0.95rem;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.saved-table th {
|
|
190
|
+
border-bottom: 1px solid var(--line);
|
|
191
|
+
padding: 0.8rem 0.9rem;
|
|
192
|
+
text-align: left;
|
|
193
|
+
vertical-align: top;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.saved-table td {
|
|
197
|
+
border-bottom: 1px solid var(--line);
|
|
198
|
+
padding: 0;
|
|
199
|
+
text-align: left;
|
|
200
|
+
vertical-align: top;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.saved-table th {
|
|
204
|
+
color: var(--muted);
|
|
205
|
+
font-size: 0.78rem;
|
|
206
|
+
letter-spacing: 0;
|
|
207
|
+
text-transform: uppercase;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.saved-table tbody tr:last-child td {
|
|
211
|
+
border-bottom: 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.saved-table td {
|
|
215
|
+
color: var(--ink);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.saved-table .row-link {
|
|
219
|
+
display: block;
|
|
220
|
+
min-height: 100%;
|
|
221
|
+
padding: 0.8rem 0.9rem;
|
|
222
|
+
color: inherit;
|
|
223
|
+
text-decoration: none;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.saved-table tbody tr:hover,
|
|
227
|
+
.saved-table tbody tr:focus-within {
|
|
228
|
+
background: color-mix(in srgb, var(--accent) 7%, var(--panel));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.saved-table .row-link:focus {
|
|
232
|
+
outline: 3px solid color-mix(in srgb, var(--accent) 22%, transparent);
|
|
233
|
+
outline-offset: 2px;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.saved-table td:last-child {
|
|
237
|
+
color: var(--muted);
|
|
238
|
+
white-space: nowrap;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.footer {
|
|
242
|
+
width: min(100%, 46rem);
|
|
243
|
+
margin-top: 2.5rem;
|
|
244
|
+
border-top: 1px solid var(--line);
|
|
245
|
+
padding-top: 1rem;
|
|
246
|
+
color: var(--muted);
|
|
247
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
248
|
+
font-size: 0.82rem;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.footer a {
|
|
252
|
+
color: inherit;
|
|
253
|
+
text-decoration-color: color-mix(in srgb, var(--muted) 45%, transparent);
|
|
254
|
+
text-underline-offset: 0.16em;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.footer a:hover,
|
|
258
|
+
.footer a:focus {
|
|
259
|
+
color: var(--accent-dark);
|
|
260
|
+
text-decoration-color: currentcolor;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.footer span {
|
|
264
|
+
margin: 0 0.35rem;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
@media (max-width: 40rem) {
|
|
268
|
+
.page {
|
|
269
|
+
padding-top: 2rem;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
button {
|
|
273
|
+
width: 100%;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.masthead-with-action {
|
|
277
|
+
display: grid;
|
|
278
|
+
align-items: start;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@media (max-width: 34rem) {
|
|
283
|
+
.table-wrap {
|
|
284
|
+
overflow: visible;
|
|
285
|
+
border: 0;
|
|
286
|
+
background: transparent;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.saved-table,
|
|
290
|
+
.saved-table thead,
|
|
291
|
+
.saved-table tbody,
|
|
292
|
+
.saved-table th,
|
|
293
|
+
.saved-table td,
|
|
294
|
+
.saved-table tr {
|
|
295
|
+
display: block;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.saved-table thead {
|
|
299
|
+
position: absolute;
|
|
300
|
+
width: 1px;
|
|
301
|
+
height: 1px;
|
|
302
|
+
overflow: hidden;
|
|
303
|
+
clip: rect(0 0 0 0);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.saved-table tr {
|
|
307
|
+
margin-bottom: 0.8rem;
|
|
308
|
+
border: 1px solid var(--line);
|
|
309
|
+
border-radius: 4px;
|
|
310
|
+
background: var(--panel);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.saved-table td {
|
|
314
|
+
display: grid;
|
|
315
|
+
grid-template-columns: 7rem 1fr;
|
|
316
|
+
gap: 0.75rem;
|
|
317
|
+
border-bottom: 1px solid var(--line);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.saved-table td::before {
|
|
321
|
+
content: attr(data-label);
|
|
322
|
+
padding: 0.8rem 0 0.8rem 0.9rem;
|
|
323
|
+
color: var(--muted);
|
|
324
|
+
font-size: 0.78rem;
|
|
325
|
+
font-weight: 800;
|
|
326
|
+
text-transform: uppercase;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.saved-table .row-link {
|
|
330
|
+
padding: 0.8rem 0.9rem 0.8rem 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.saved-table td:last-child {
|
|
334
|
+
white-space: normal;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<section class="shell">
|
|
2
|
+
<header class="masthead masthead-with-action">
|
|
3
|
+
<h1>Spoonerize a phrase</h1>
|
|
4
|
+
<a class="action-link" href="/saved/">Saved spoonerisms</a>
|
|
5
|
+
</header>
|
|
6
|
+
|
|
7
|
+
<% if @result %>
|
|
8
|
+
<p class="result"><%= h @result %></p>
|
|
9
|
+
<% if @saved %>
|
|
10
|
+
<p class="notice">Saved.</p>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% elsif @error %>
|
|
13
|
+
<% display_error = @error == "Not enough words to flip." ? "Not enough words to spoonerize." : @error %>
|
|
14
|
+
<p class="notice"><%= h display_error %></p>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<form action="/" method="post" class="spoonerize-form">
|
|
18
|
+
<label class="field">
|
|
19
|
+
<span>Phrase</span>
|
|
20
|
+
<input
|
|
21
|
+
type="text"
|
|
22
|
+
name="phrase"
|
|
23
|
+
value="<%= h @phrase %>"
|
|
24
|
+
placeholder="Enter phrase to spoonerize..."
|
|
25
|
+
autofocus>
|
|
26
|
+
</label>
|
|
27
|
+
|
|
28
|
+
<fieldset>
|
|
29
|
+
<legend>Options</legend>
|
|
30
|
+
|
|
31
|
+
<label class="check">
|
|
32
|
+
<input type="checkbox" name="reverse" value="1" <%= checked?("reverse") %>>
|
|
33
|
+
<span>Reverse spoonerizing</span>
|
|
34
|
+
</label>
|
|
35
|
+
|
|
36
|
+
<label class="check">
|
|
37
|
+
<input type="checkbox" name="lazy" value="1" <%= checked?("lazy") %>>
|
|
38
|
+
<span>Skip common words</span>
|
|
39
|
+
</label>
|
|
40
|
+
|
|
41
|
+
<label class="check">
|
|
42
|
+
<input type="checkbox" name="consonants_only" value="1" <%= checked?("consonants_only") %>>
|
|
43
|
+
<span>Only spoonerize consonant-starting words</span>
|
|
44
|
+
</label>
|
|
45
|
+
|
|
46
|
+
<label class="check">
|
|
47
|
+
<input type="checkbox" name="save" value="1" <%= @save ? "checked" : nil %>>
|
|
48
|
+
<span>Save result</span>
|
|
49
|
+
</label>
|
|
50
|
+
</fieldset>
|
|
51
|
+
|
|
52
|
+
<label class="field">
|
|
53
|
+
<span>Excluded words</span>
|
|
54
|
+
<input
|
|
55
|
+
type="text"
|
|
56
|
+
name="excluded_words"
|
|
57
|
+
value="<%= h @excluded_words_value %>"
|
|
58
|
+
placeholder="word another">
|
|
59
|
+
</label>
|
|
60
|
+
|
|
61
|
+
<button type="submit">Spoonerize</button>
|
|
62
|
+
</form>
|
|
63
|
+
</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,44 @@
|
|
|
1
|
+
<section class="shell">
|
|
2
|
+
<header class="masthead masthead-with-action">
|
|
3
|
+
<div>
|
|
4
|
+
<h1>Saved spoonerisms</h1>
|
|
5
|
+
<p class="lede"><%= @entries.size %> saved <%= @entries.size == 1 ? "spoonerism" : "spoonerisms" %></p>
|
|
6
|
+
</div>
|
|
7
|
+
<a class="action-link" href="/">New spoonerism</a>
|
|
8
|
+
</header>
|
|
9
|
+
|
|
10
|
+
<% if @entries.empty? %>
|
|
11
|
+
<p class="notice">No saved spoonerisms yet.</p>
|
|
12
|
+
<% else %>
|
|
13
|
+
<div class="table-wrap">
|
|
14
|
+
<table class="saved-table">
|
|
15
|
+
<thead>
|
|
16
|
+
<tr>
|
|
17
|
+
<th scope="col">Phrase</th>
|
|
18
|
+
<th scope="col">Result</th>
|
|
19
|
+
<th scope="col">Options</th>
|
|
20
|
+
</tr>
|
|
21
|
+
</thead>
|
|
22
|
+
<tbody>
|
|
23
|
+
<% @entries.each do |entry| %>
|
|
24
|
+
<% phrase = entry[0].to_s %>
|
|
25
|
+
<% result = entry[1].to_s %>
|
|
26
|
+
<% options = entry[2].to_s %>
|
|
27
|
+
<% entry_path = saved_entry_path(phrase, result) %>
|
|
28
|
+
<tr>
|
|
29
|
+
<td data-label="Phrase">
|
|
30
|
+
<a class="row-link" href="<%= h entry_path %>"><%= h phrase %></a>
|
|
31
|
+
</td>
|
|
32
|
+
<td data-label="Result">
|
|
33
|
+
<a class="row-link" href="<%= h entry_path %>"><strong><%= h result %></strong></a>
|
|
34
|
+
</td>
|
|
35
|
+
<td data-label="Options">
|
|
36
|
+
<a class="row-link" href="<%= h entry_path %>"><%= h options %></a>
|
|
37
|
+
</td>
|
|
38
|
+
</tr>
|
|
39
|
+
<% end %>
|
|
40
|
+
</tbody>
|
|
41
|
+
</table>
|
|
42
|
+
</div>
|
|
43
|
+
<% end %>
|
|
44
|
+
</section>
|
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
|
|
58
|
+
##
|
|
59
|
+
# Path for loading a saved spoonerism on the main web form.
|
|
60
|
+
#
|
|
61
|
+
# @param [String] phrase The original phrase.
|
|
62
|
+
# @param [String] result The saved spoonerized result.
|
|
63
|
+
#
|
|
64
|
+
# @return [String]
|
|
65
|
+
def saved_entry_path(phrase, result)
|
|
66
|
+
"/?#{Rack::Utils.build_query("phrase" => phrase, "result" => result)}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
get "/" do
|
|
71
|
+
prepare_request(false)
|
|
72
|
+
@result = params["result"].to_s unless params["result"].to_s.empty?
|
|
73
|
+
erb :index
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
get "/saved" do
|
|
77
|
+
redirect "/saved/"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
get "/saved/" do
|
|
81
|
+
@entries = saved_entries
|
|
82
|
+
erb :saved
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
post "/" do
|
|
86
|
+
prepare_request(true)
|
|
87
|
+
@result = spoonerize_phrase if @submitted && !@phrase.strip.empty?
|
|
88
|
+
|
|
89
|
+
erb :index
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def options_from_params
|
|
95
|
+
OPTION_NAMES.to_h do |name|
|
|
96
|
+
value = @submitted ? params.key?(name) : Spoonerize.config.public_send(name)
|
|
97
|
+
[name.to_sym, value]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def excluded_words_from(value)
|
|
102
|
+
value.split(/[,\s]+/).reject(&:empty?)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def excluded_words_from_params
|
|
106
|
+
return excluded_words_from(params["excluded_words"].to_s) if @submitted
|
|
107
|
+
|
|
108
|
+
Spoonerize.config.excluded_words
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def prepare_request(submitted)
|
|
112
|
+
@submitted = submitted
|
|
113
|
+
@phrase = params["phrase"].to_s
|
|
114
|
+
@options = options_from_params
|
|
115
|
+
@excluded_words = excluded_words_from_params
|
|
116
|
+
@excluded_words_value = @excluded_words.join(" ")
|
|
117
|
+
@save = @submitted && params.key?("save")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def saved_entries
|
|
121
|
+
Spoonerize::Log.new(Spoonerize.config.logfile_name).contents.reverse
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def spoonerize_phrase
|
|
125
|
+
spoonerism = Spoonerism.new(
|
|
126
|
+
*@phrase.split,
|
|
127
|
+
**@options,
|
|
128
|
+
excluded_words: @excluded_words
|
|
129
|
+
)
|
|
130
|
+
result = spoonerism.to_s
|
|
131
|
+
spoonerism.save if @save
|
|
132
|
+
@saved = @save
|
|
133
|
+
|
|
134
|
+
result
|
|
135
|
+
rescue => error
|
|
136
|
+
@error = friendly_error(error)
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def friendly_error(error)
|
|
141
|
+
return "Not enough words to flip." if error.message == "Not enough words to flip"
|
|
142
|
+
|
|
143
|
+
error.message
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
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: 1.0
|
|
4
|
+
version: 2.1.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,12 @@ 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
|
|
166
|
+
- lib/spoonerize/web/views/saved.erb
|
|
117
167
|
- spoonerize.gemspec
|
|
118
168
|
homepage: https://evanthegrayt.github.io/spoonerize/
|
|
119
169
|
licenses:
|
|
@@ -130,7 +180,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
130
180
|
requirements:
|
|
131
181
|
- - ">="
|
|
132
182
|
- !ruby/object:Gem::Version
|
|
133
|
-
version: '
|
|
183
|
+
version: '3.2'
|
|
134
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
185
|
requirements:
|
|
136
186
|
- - ">="
|