perquackey 0.7.3 → 0.8.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.
@@ -1,3 +1,10 @@
1
+ == Git
2
+
3
+ == 0.8.0 -- 2011-12-23
4
+
5
+ * JSON api: GET /rstlne.json
6
+ * Use optparse-defaults for command line options.
7
+
1
8
  == 0.7.3 -- 2010-04-29
2
9
  * Document C extension.
3
10
 
@@ -1,18 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- begin
4
- require 'perquackey'
5
- rescue LoadError
6
- raise unless $!.message =~ /\bperquackey$/
7
-
8
- lib = File.expand_path('../../lib', __FILE__)
9
- if !$:.include?(lib)
10
- warn "WARN: #{$!}.\n Trying again with #{lib} on the $LOAD_PATH..."
11
- $:.unshift(lib)
12
- retry
13
- end
14
-
15
- raise
16
- end
17
-
3
+ require 'perquackey'
18
4
  Perquackey::Application.run!(ARGV)
@@ -1,31 +1,95 @@
1
- <form action="/" method="post">
2
- <input name="letters"
3
- value="<%= letters %>"
4
- autocomplete="on"
5
- autosave="perquackey"
6
- autofocus="on"
7
- maxlength="13"
8
- pattern="[a-z]{10}([a-z]{3})?"
9
- placeholder="Type your letters here."
10
- required="on"
11
- results="12"
12
- title="Perquackey rolls are 10 or 13 letters."
13
- type="search"
14
- />
15
- </form>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Perquackey</title>
6
+ <style>
7
+ body {
8
+ margin: 0 auto 5em;
9
+ width: 45em;
10
+ }
11
+ section {
12
+ margin-bottom: 1em;
13
+ }
14
+ table {
15
+ border-collapse: collapse;
16
+ }
17
+ tbody tr:nth-child(5) td {
18
+ border-bottom: 1px dashed #ccc;
19
+ }
20
+ td, th {
21
+ padding-right: 1em;
22
+ }
23
+ footer {
24
+ background-color: #eee;
25
+ padding: 1em;
26
+ }
27
+ </style>
28
+ </head>
16
29
 
17
- <table>
18
- <tr>
19
- <% words.headers.each do |header| %>
20
- <th><%= header %></th>
21
- <% end %>
22
- </tr>
30
+ <body>
31
+ <header>
32
+ <h1>Perquackey</h1>
33
+ </header>
23
34
 
24
- <% words.each do |row| %>
25
- <tr>
26
- <% row.each do |word| %>
27
- <td><%= word %></td>
28
- <% end %>
29
- </tr>
30
- <% end %>
31
- </table>
35
+ <section>
36
+ <form action="/" method="post">
37
+ <input name="letters"
38
+ autocomplete="off"
39
+ autofocus="on"
40
+ maxlength="13"
41
+ pattern="[a-z]{10}([a-z]{3})?"
42
+ placeholder="Enter your letters here."
43
+ required="on"
44
+ size="30"
45
+ title="Perquackey rolls are 10 or 13 letters."
46
+ value="<%= letters %>"
47
+ />
48
+ </form>
49
+ </section>
50
+
51
+ <section>
52
+ <table>
53
+ <thead>
54
+ <tr>
55
+ <% words.headers.each do |header| %>
56
+ <th><%= header %></th>
57
+ <% end %>
58
+ </tr>
59
+ </thead>
60
+
61
+ <tbody>
62
+ <% words.each do |row| %>
63
+ <tr>
64
+ <% row.each do |word| %>
65
+ <td><%= word %></td>
66
+ <% end %>
67
+ </tr>
68
+ <% end %>
69
+ </tbody>
70
+ </table>
71
+ </section>
72
+
73
+ <footer>
74
+ <p>
75
+ Enter the letters you rolled to see what you could have spelled.
76
+ </p>
77
+ <p>
78
+ Examples:
79
+ <a href="/eehhhijjoopqt">eehhhijjoopqt</a>,
80
+ <a href="/aaddeginoprsw">aaddeginoprsw</a>
81
+ </p>
82
+ <p>
83
+ These words are taken from
84
+ <a href="http://en.wikipedia.org/wiki/Tournament_Word_List">TWL06, the official word list</a>
85
+ for Scrabble tournaments in North America. (You can
86
+ <a href="http://www.isc.ro/en/commands/lists.html">download this list</a>
87
+ from the Internet Scrabble Club.)
88
+ </p>
89
+ <p>
90
+ <a href="https://github.com/matthewtodd/perquackey">This code is on GitHub</a>.
91
+ Patches welcome!
92
+ </p>
93
+ </footer>
94
+ </body>
95
+ </html>
@@ -1,5 +1,5 @@
1
1
  #include "ruby.h"
2
- #include "rubyio.h"
2
+ #include "ruby/io.h"
3
3
 
4
4
  #define MAX_LENGTH 40
5
5
  #define XOUT '@'
@@ -34,23 +34,6 @@ static int anagram_p(char *letters, char *word) {
34
34
  return TRUE;
35
35
  }
36
36
 
37
- /*
38
- * Document-class: Anagrams
39
- * Document-method: anagrams
40
- *
41
- * call-seq:
42
- * anagrams(letters) -> array
43
- *
44
- */
45
- static VALUE Anagrams_anagrams(VALUE self, VALUE letters) {
46
- return rb_apply(
47
- rb_apply(self,
48
- rb_intern("enum_for"),
49
- rb_ary_new3(2, rb_str_new2("each_anagram"), letters)),
50
- rb_intern("to_a"),
51
- rb_ary_new());
52
- }
53
-
54
37
  /*
55
38
  * Document-class: Anagrams
56
39
  * Document-method: each_anagram
@@ -62,11 +45,11 @@ static VALUE Anagrams_anagrams(VALUE self, VALUE letters) {
62
45
  static VALUE Anagrams_each_anagram(VALUE self, VALUE letters_value) {
63
46
  rb_io_t *stream;
64
47
  char word[MAX_LENGTH];
65
- char *letters = RSTRING(letters_value)->ptr;
48
+ char *letters = RSTRING_PTR(letters_value);
66
49
 
67
50
  GetOpenFile(self, stream);
68
51
 
69
- while (fgets(word, sizeof(word), stream->f) != NULL) {
52
+ while (fgets(word, sizeof(word), rb_io_stdio_file(stream)) != NULL) {
70
53
  chop(word);
71
54
 
72
55
  if (anagram_p(letters, word)) {
@@ -92,6 +75,5 @@ static VALUE Anagrams_each_anagram(VALUE self, VALUE letters_value) {
92
75
  void Init_anagrams() {
93
76
  mPerquackey = rb_define_module("Perquackey");
94
77
  mAnagrams = rb_define_module_under(mPerquackey, "Anagrams");
95
- rb_define_method(mAnagrams, "anagrams", Anagrams_anagrams, 1);
96
78
  rb_define_method(mAnagrams, "each_anagram", Anagrams_each_anagram, 1);
97
79
  }
@@ -1,26 +1,18 @@
1
+ require 'perquackey/version'
2
+
1
3
  require 'pathname'
2
- require 'rbconfig'
3
- require 'rbconfig/datadir'
4
+ require 'rubygems'
4
5
 
5
6
  module Perquackey
6
- VERSION = '0.7.3'
7
-
8
7
  autoload :Anagrams, 'perquackey/anagrams'
9
8
  autoload :Application,'perquackey/application'
10
9
  autoload :Console, 'perquackey/console'
11
10
  autoload :Dictionary, 'perquackey/dictionary'
12
11
  autoload :Game, 'perquackey/game'
13
12
  autoload :Server, 'perquackey/server'
14
- autoload :WordTable, 'perquackey/word_table'
13
+ autoload :Table, 'perquackey/table'
15
14
 
16
15
  def self.datadir
17
- @@datadir ||= begin
18
- datadir = RbConfig.datadir('perquackey')
19
- if !File.exist?(datadir)
20
- warn "WARN: #{datadir} does not exist.\n Trying again with relative data directory..."
21
- datadir = File.expand_path('../../data/perquackey', __FILE__)
22
- end
23
- Pathname.new(datadir)
24
- end
16
+ Pathname.new Gem.datadir('perquackey')
25
17
  end
26
18
  end
@@ -1,4 +1,4 @@
1
- require 'optparse'
1
+ require 'optparse/defaults'
2
2
 
3
3
  module Perquackey
4
4
  class Application
@@ -7,11 +7,10 @@ module Perquackey
7
7
  end
8
8
 
9
9
  def initialize
10
- @defaults = %w(--console)
11
-
12
- @options = OptionParser.new do |opts|
13
- opts.banner = "Usage: #{File.basename($0)} [-c|-s]"
14
- opts.version = Perquackey::VERSION
10
+ @options = OptionParser.with_defaults do |opts|
11
+ opts.banner = "Usage: #{File.basename($0)} [-c|-s]"
12
+ opts.defaults = %w(--console)
13
+ opts.version = Perquackey::VERSION
15
14
  opts.separator ''
16
15
 
17
16
  opts.on('-c', '--console', 'Run an interactive console.') do
@@ -22,19 +21,11 @@ module Perquackey
22
21
  @mode = :server
23
22
  @port = port || 3000
24
23
  end
25
-
26
- opts.separator ''
27
- opts.separator 'Defaults:'
28
- opts.separator "#{opts.summary_indent}#{@defaults.join(' ')}"
29
24
  end
30
25
  end
31
26
 
32
27
  def run!(argv)
33
- begin
34
- @options.parse(@defaults.dup.concat(argv))
35
- rescue OptionParser::ParseError
36
- @options.abort($!)
37
- end
28
+ @options.order(argv)
38
29
 
39
30
  case @mode
40
31
  when :console
@@ -6,37 +6,21 @@ module Perquackey
6
6
  new.run
7
7
  end
8
8
 
9
- def initialize
10
- @game = Game.new
11
- end
12
-
13
9
  def run
14
10
  puts 'Type ctrl-d or quit to exit.'
15
11
 
16
12
  loop do
17
- letters = prompt_for_letters
18
-
19
- case letters
13
+ case letters = Readline::readline('> ')
20
14
  when nil, 'quit'
21
15
  puts 'Goodbye.'
22
16
  exit
23
17
  when /^[a-z]+$/
24
18
  Readline::HISTORY.push(letters)
25
- puts words(letters)
19
+ puts Table.new(Dictionary.words(letters))
26
20
  else
27
21
  next
28
22
  end
29
23
  end
30
24
  end
31
-
32
- private
33
-
34
- def prompt_for_letters
35
- Readline::readline('> ')
36
- end
37
-
38
- def words(letters)
39
- @game.words(letters).map { |row| row.join(' ') }
40
- end
41
25
  end
42
26
  end
@@ -1,15 +1,21 @@
1
1
  module Perquackey
2
2
  class Dictionary
3
- TOURNAMENT_WORD_LIST = Perquackey.datadir.join('dict', 'TWL06.txt')
3
+ def self.words(letters)
4
+ new.words(letters)
5
+ end
4
6
 
5
7
  def initialize
6
- @word_list = TOURNAMENT_WORD_LIST
8
+ @word_list = Perquackey.datadir.join('dict', 'TWL06.txt')
7
9
  end
8
10
 
9
11
  def words(letters)
10
- @word_list.open do |stream|
11
- stream.extend(Anagrams).anagrams(letters)
12
+ anagrams = @word_list.open do |stream|
13
+ stream.extend(Anagrams)
14
+ stream.enum_for(:each_anagram, letters).to_a
12
15
  end
16
+
17
+ minimum = (letters.length <= 10) ? 3 : 4
18
+ anagrams.reject { |anagram| anagram.length < minimum }
13
19
  end
14
20
  end
15
21
  end
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
  require 'sinatra/base'
2
3
 
3
4
  module Perquackey
@@ -5,7 +6,15 @@ module Perquackey
5
6
  set :views, Perquackey.datadir.join('templates')
6
7
 
7
8
  get %r{^/([a-z]{0,13})$} do |letters|
8
- erb :index, :locals => { :letters => letters, :words => Game.new.words(letters) }
9
+ erb :index, :locals => {
10
+ :letters => letters,
11
+ :words => Table.new(Dictionary.words(letters))
12
+ }
13
+ end
14
+
15
+ get %r{^/([a-z]{0,13})\.json$} do |letters|
16
+ content_type :json
17
+ Dictionary.words(letters).to_json
9
18
  end
10
19
 
11
20
  post '/' do
@@ -1,5 +1,5 @@
1
1
  module Perquackey
2
- class WordTable
2
+ class Table
3
3
  include Enumerable
4
4
 
5
5
  def initialize(list=[])
@@ -27,5 +27,9 @@ module Perquackey
27
27
  yield headers.collect { |length| @table[length][row_number] or ' ' * length}
28
28
  end
29
29
  end
30
+
31
+ def to_s
32
+ map { |row| row.join(' ') }.join("\n")
33
+ end
30
34
  end
31
35
  end
@@ -0,0 +1,3 @@
1
+ module Perquackey
2
+ VERSION = '0.8.0'
3
+ end
@@ -1,7 +1,7 @@
1
- require 'test/helper'
1
+ require 'helper'
2
2
 
3
3
  class DictionaryTest < Test::Unit::TestCase
4
4
  def test_simple_search
5
- assert_equal ['ab', 'ba', 'cab'], Perquackey::Dictionary.new.words('abc')
5
+ assert_equal ['cab'], Perquackey::Dictionary.new.words('abc')
6
6
  end
7
7
  end
@@ -1,9 +1,2 @@
1
1
  require 'test/unit'
2
2
  require 'perquackey'
3
-
4
- begin
5
- require 'redgreen' if $stdout.tty?
6
- rescue LoadError
7
- # It's nice not to have hard dependencies on any gems for testing, so that
8
- # it's super-easy for your users to run `gem check --test perquackey.`
9
- end
@@ -0,0 +1,34 @@
1
+ require 'helper'
2
+
3
+ class TableTest < Test::Unit::TestCase
4
+ def test_handles_empty_list
5
+ empty_table = Perquackey::Table.new %w()
6
+
7
+ assert_equal [], empty_table.headers.to_a
8
+ assert_equal [], empty_table.to_a
9
+ end
10
+
11
+ def test_handles_some_list
12
+ table = Perquackey::Table.new %w(ape bear cat dog eagle fox goose hippo ibis)
13
+
14
+ assert_equal [3, 4, 5], table.headers.to_a
15
+
16
+ assert_equal [
17
+ ['ape', 'bear', 'eagle'],
18
+ ['cat', 'ibis', 'goose'],
19
+ ['dog', ' ', 'hippo'],
20
+ ['fox', ' ', ' ']
21
+ ], table.to_a
22
+ end
23
+
24
+ def test_to_s
25
+ table = Perquackey::Table.new %w(ape bear cat dog eagle fox goose hippo ibis)
26
+
27
+ assert_equal <<-END.gsub(/^ */, '').strip, table.to_s.strip
28
+ ape bear eagle
29
+ cat ibis goose
30
+ dog hippo
31
+ fox
32
+ END
33
+ end
34
+ end
metadata CHANGED
@@ -1,139 +1,137 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: perquackey
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 7
8
- - 3
9
- version: 0.7.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Matthew Todd
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2010-04-29 00:00:00 +03:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2011-12-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &8436690 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.6.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *8436690
25
+ - !ruby/object:Gem::Dependency
21
26
  name: sinatra
27
+ requirement: &8436330 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
+ type: :runtime
22
34
  prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- segments:
28
- - 0
29
- version: "0"
35
+ version_requirements: *8436330
36
+ - !ruby/object:Gem::Dependency
37
+ name: optparse-defaults
38
+ requirement: &8435520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.1.0
30
44
  type: :runtime
31
- version_requirements: *id001
32
- - !ruby/object:Gem::Dependency
33
- name: redgreen
34
45
  prerelease: false
35
- requirement: &id002 !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- segments:
40
- - 0
41
- version: "0"
42
- type: :development
43
- version_requirements: *id002
44
- - !ruby/object:Gem::Dependency
46
+ version_requirements: *8435520
47
+ - !ruby/object:Gem::Dependency
45
48
  name: ronn
46
- prerelease: false
47
- requirement: &id003 !ruby/object:Gem::Requirement
48
- requirements:
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- segments:
52
- - 0
53
- version: "0"
49
+ requirement: &8434880 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.5'
54
55
  type: :development
55
- version_requirements: *id003
56
- - !ruby/object:Gem::Dependency
57
- name: shoe
58
56
  prerelease: false
59
- requirement: &id004 !ruby/object:Gem::Requirement
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- segments:
64
- - 0
65
- version: "0"
57
+ version_requirements: *8434880
58
+ - !ruby/object:Gem::Dependency
59
+ name: shoe
60
+ requirement: &8434180 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 0.8.0
66
66
  type: :development
67
- version_requirements: *id004
68
- description: Provides console session and simple webapp for looking up Perquackey word matches
67
+ prerelease: false
68
+ version_requirements: *8434180
69
+ description: Provides console session and simple webapp for looking up Perquackey
70
+ word matches
69
71
  email: matthew.todd@gmail.com
70
- executables:
72
+ executables:
71
73
  - perquackey
72
- extensions:
74
+ extensions:
73
75
  - ext/perquackey/extconf.rb
74
- extra_rdoc_files:
76
+ extra_rdoc_files:
75
77
  - CHANGELOG.rdoc
76
78
  - README.rdoc
77
79
  - TODO.rdoc
78
80
  - ext/perquackey/anagrams.c
79
- files:
81
+ files:
80
82
  - CHANGELOG.rdoc
81
83
  - README.rdoc
82
84
  - TODO.rdoc
83
85
  - bin/perquackey
84
86
  - data/perquackey/dict/TWL06.txt
85
87
  - data/perquackey/templates/index.erb
86
- - data/perquackey/templates/layout.erb
87
88
  - ext/perquackey/extconf.rb
88
89
  - ext/perquackey/anagrams.c
89
90
  - lib/perquackey/application.rb
90
91
  - lib/perquackey/console.rb
91
92
  - lib/perquackey/dictionary.rb
92
- - lib/perquackey/game.rb
93
93
  - lib/perquackey/server.rb
94
- - lib/perquackey/word_table.rb
94
+ - lib/perquackey/table.rb
95
+ - lib/perquackey/version.rb
95
96
  - lib/perquackey.rb
96
97
  - man/perquackey.1
97
98
  - man/perquackey.1.ronn
98
99
  - test/dictionary_test.rb
99
100
  - test/helper.rb
100
- - test/word_table_test.rb
101
- has_rdoc: true
101
+ - test/table_test.rb
102
102
  homepage: http://github.com/matthewtodd/perquackey
103
103
  licenses: []
104
-
105
104
  post_install_message:
106
- rdoc_options:
105
+ rdoc_options:
107
106
  - --main
108
107
  - README.rdoc
109
108
  - --title
110
- - perquackey-0.7.3
109
+ - perquackey-0.8.0
111
110
  - --inline-source
112
- require_paths:
111
+ require_paths:
113
112
  - lib
114
- required_ruby_version: !ruby/object:Gem::Requirement
115
- requirements:
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- segments:
119
- - 1
120
- - 8
121
- - 7
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
122
118
  version: 1.8.7
123
- required_rubygems_version: !ruby/object:Gem::Requirement
124
- requirements:
125
- - - ">="
126
- - !ruby/object:Gem::Version
127
- segments:
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ segments:
128
126
  - 0
129
- version: "0"
127
+ hash: -138864973
130
128
  requirements: []
131
-
132
129
  rubyforge_project:
133
- rubygems_version: 1.3.6
130
+ rubygems_version: 1.8.11
134
131
  signing_key:
135
132
  specification_version: 3
136
- summary: Provides console session and simple webapp for looking up Perquackey word matches
137
- test_files:
133
+ summary: Provides console session and simple webapp for looking up Perquackey word
134
+ matches
135
+ test_files:
138
136
  - test/dictionary_test.rb
139
- - test/word_table_test.rb
137
+ - test/table_test.rb
@@ -1,24 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <title>Perquackey</title>
6
- </head>
7
-
8
- <body>
9
- <header>
10
- <h1>Perquackey</h1>
11
- </header>
12
-
13
- <%= yield %>
14
-
15
- <footer>
16
- <p>
17
- Now using the
18
- <a href="http://en.wikipedia.org/wiki/Tournament_Word_List">TWL06 word list</a>
19
- courtesy of the
20
- <a href="http://www.isc.ro/en/commands/lists.html">Internet Scrabble Club</a>.
21
- </p>
22
- </footer>
23
- </body>
24
- </html>
@@ -1,17 +0,0 @@
1
- module Perquackey
2
- class Game
3
- def initialize
4
- @dictionary = Dictionary.new
5
- end
6
-
7
- def words(letters)
8
- WordTable.new(@dictionary.words(letters).reject { |word| word.length < minimum_length(letters) })
9
- end
10
-
11
- private
12
-
13
- def minimum_length(letters)
14
- letters.length > 10 ? 4 : 3
15
- end
16
- end
17
- end
@@ -1,23 +0,0 @@
1
- require 'test/helper'
2
-
3
- class WordTableTest < Test::Unit::TestCase
4
- def test_handles_empty_list
5
- empty_table = Perquackey::WordTable.new %w()
6
-
7
- assert_equal [], empty_table.headers.to_a
8
- assert_equal [], empty_table.to_a
9
- end
10
-
11
- def test_handles_some_list
12
- table = Perquackey::WordTable.new %w(ape bear cat dog eagle fox goose hippo ibis)
13
-
14
- assert_equal [3, 4, 5], table.headers.to_a
15
-
16
- assert_equal [
17
- 'ape bear eagle',
18
- 'cat ibis goose',
19
- 'dog hippo',
20
- 'fox '
21
- ], table.collect { |row| row.join(' ') }
22
- end
23
- end