improvise 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 +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +115 -0
- data/Rakefile +129 -0
- data/bin/improvise +79 -0
- data/improvise.gemspec +65 -0
- data/lib/improvise/dictionary.rb +29 -0
- data/lib/improvise/dictionarytree.rb +70 -0
- data/lib/improvise/forwarddictionary.rb +35 -0
- data/lib/improvise/io/dictionaryreader.rb +38 -0
- data/lib/improvise/io/dictionarywriter.rb +38 -0
- data/lib/improvise/io/io.rb +2 -0
- data/lib/improvise/version.rb +3 -0
- data/lib/improvise.rb +3 -0
- data/spec/improvise/dictionary_spec.rb +16 -0
- data/spec/improvise/dictionarytree_spec.rb +37 -0
- data/spec/improvise/forwarddictionary_spec.rb +31 -0
- data/spec/improvise/io/dictionaryreader_spec.rb +62 -0
- data/spec/improvise/io/dictionarywriter_spec.rb +65 -0
- data/spec/spec_helper.rb +12 -0
- metadata +193 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4d23a1bbb23c53484630f7e37c9eaf98aefffef7
|
4
|
+
data.tar.gz: 8017aec4d0fa52f0dcda9a65fc8ea17e1dbb573d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b9e9f8471cd08b819667c12dcfe45ef24ca94d2ba9f1ec9180cca1d8e3d8f4c83e6071333c2ca01c3a1b9eeaeb368d8a24a864eef9cf566c11c0bdb08121b88f
|
7
|
+
data.tar.gz: b8d199c5a1f6465e713ae5fadc6ec813cf0527ba7027c13758ce00ae6241c9631f42357b59b6aaec652543beae552a99a57cc7dd47ccd89a02acfd27dbc491db
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Sander Kleykens
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# Improvise
|
2
|
+
Improvise generates random words by learning from existing text.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```
|
8
|
+
gem 'improvise'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
```
|
14
|
+
$ bundle
|
15
|
+
```
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
```
|
19
|
+
$ gem install improvise
|
20
|
+
```
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
Require the 'improvise' gem:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'improvise'
|
27
|
+
```
|
28
|
+
|
29
|
+
### Learn
|
30
|
+
Create a new dictionary with depth 3 and learn from a list of existing words:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
dict = Improvise::ForwardDictionary.new(3)
|
34
|
+
dict.learn!([
|
35
|
+
'ruby',
|
36
|
+
'sapphire',
|
37
|
+
'pearl',
|
38
|
+
'diamond',
|
39
|
+
'amethyst',
|
40
|
+
'topaz',
|
41
|
+
'emerald'
|
42
|
+
])
|
43
|
+
```
|
44
|
+
|
45
|
+
### Generate
|
46
|
+
Generate a 6-letter word:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
dict.generate_word(6)
|
50
|
+
```
|
51
|
+
|
52
|
+
Generate a 7-letter word, starting with 'top':
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
dict.generate_word(7, 'top')
|
56
|
+
```
|
57
|
+
|
58
|
+
### Store
|
59
|
+
Write a dictionary in Ruby's Marshal format to a file called 'dict.bin':
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
dict_file = File.open('dict.bin', 'w')
|
63
|
+
Improvise::IO::DictionaryWriter.write(dict_file, dict)
|
64
|
+
```
|
65
|
+
|
66
|
+
Write a dictionary in Ruby's Marshal format, gzipped, to a file called 'dict.bin.gz':
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
dict_file = File.open('dict.bin.gz', 'w')
|
70
|
+
Improvise::IO::DictionaryWriter.write(dict_file, dict, gzip: true)
|
71
|
+
```
|
72
|
+
|
73
|
+
Write a dictionary in json format, gzipped, to a file called 'dict.json.gz':
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
dict_file = File.open('dict.json.gz', 'w')
|
77
|
+
Improvise::IO::DictionaryWriter.write(dict_file, dict, format: :json, gzip: true)
|
78
|
+
```
|
79
|
+
|
80
|
+
*Note: writing a dictionary closes the passed IO object.*
|
81
|
+
|
82
|
+
### Load
|
83
|
+
Restore a dictionary from a file called 'dict.bin' in Ruby's Marshal format:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
dict_file = File.open('dict.bin', 'r')
|
87
|
+
dict = Improvise::IO::DictionaryReader.read(dict_file)
|
88
|
+
```
|
89
|
+
|
90
|
+
Restore a dictionary from a file called 'dict.bin.gz' in Ruby's Marshal format, gzipped:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
dict_file = File.open('dict.bin.gz', 'r')
|
94
|
+
dict = Improvise::IO::DictionaryReader.read(dict_file, gzip: true)
|
95
|
+
```
|
96
|
+
|
97
|
+
Restore a dictionary from a file called 'dict.json.gz' in json format, gzipped:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
dict_file = File.open('dict.json.gz', 'r')
|
101
|
+
dict = Improvise::IO::DictionaryReader.read(dict_file, format: :json, gzip: true)
|
102
|
+
```
|
103
|
+
|
104
|
+
*Note: reading a dictionary closes the passed IO object.*
|
105
|
+
|
106
|
+
## Command-line tool
|
107
|
+
A command-line interface to the library has been provided under the name 'improvise'.
|
108
|
+
Run `improvise --help` to get a list of available commands.
|
109
|
+
|
110
|
+
## Contributing
|
111
|
+
1. Fork
|
112
|
+
2. Branch (`git checkout -b my-new-feature`)
|
113
|
+
3. Commit (`git commit -am 'Added some feature'`)
|
114
|
+
4. Push (`git push origin my-new-feature`)
|
115
|
+
5. Pull request
|
data/Rakefile
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}/version.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def gemspec_file
|
25
|
+
"#{name}.gemspec"
|
26
|
+
end
|
27
|
+
|
28
|
+
def gem_file
|
29
|
+
"#{name}-#{version}.gem"
|
30
|
+
end
|
31
|
+
|
32
|
+
def replace_header(head, header_name)
|
33
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
34
|
+
end
|
35
|
+
|
36
|
+
#############################################################################
|
37
|
+
#
|
38
|
+
# Standard tasks
|
39
|
+
#
|
40
|
+
#############################################################################
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
require 'rspec/core/rake_task'
|
45
|
+
desc 'Tests'
|
46
|
+
RSpec::Core::RakeTask.new(:spec) do |test|
|
47
|
+
test.pattern = 'spec/**/*_spec.rb'
|
48
|
+
end
|
49
|
+
|
50
|
+
RSpec::Core::RakeTask.new(:coverage) do |test|
|
51
|
+
ENV['COVERAGE'] = 'true'
|
52
|
+
Rake::Task[:spec].execute
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'yard'
|
56
|
+
YARD::Rake::YardocTask.new do |yard|
|
57
|
+
yard.files = ['README*', 'lib/**/*.rb']
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Open an irb session preloaded with this library"
|
61
|
+
task :console do
|
62
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
63
|
+
end
|
64
|
+
|
65
|
+
#############################################################################
|
66
|
+
#
|
67
|
+
# Packaging tasks
|
68
|
+
#
|
69
|
+
#############################################################################
|
70
|
+
|
71
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
72
|
+
task :release => :build do
|
73
|
+
unless `git branch` =~ /^\* master$/
|
74
|
+
puts "You must be on the master branch to release!"
|
75
|
+
exit!
|
76
|
+
end
|
77
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
78
|
+
sh "git tag v#{version}"
|
79
|
+
sh "git push origin master"
|
80
|
+
sh "git push origin v#{version}"
|
81
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
82
|
+
end
|
83
|
+
|
84
|
+
desc "Build #{gem_file} into the pkg directory"
|
85
|
+
task :build => :gemspec do
|
86
|
+
sh "mkdir -p pkg"
|
87
|
+
sh "gem build #{gemspec_file}"
|
88
|
+
sh "mv #{gem_file} pkg"
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "Generate #{gemspec_file}"
|
92
|
+
task :gemspec => :validate do
|
93
|
+
# read spec file and split out manifest section
|
94
|
+
spec = File.read(gemspec_file)
|
95
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
96
|
+
|
97
|
+
# replace name version and date
|
98
|
+
replace_header(head, :name)
|
99
|
+
replace_header(head, :version)
|
100
|
+
replace_header(head, :date)
|
101
|
+
|
102
|
+
# determine file list from git ls-files
|
103
|
+
files = `git ls-files`.
|
104
|
+
split("\n").
|
105
|
+
sort.
|
106
|
+
reject { |file| file =~ /^\./ }.
|
107
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
108
|
+
map { |file| " #{file}" }.
|
109
|
+
join("\n")
|
110
|
+
|
111
|
+
# piece file back together and write
|
112
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
113
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
114
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
115
|
+
puts "Updated #{gemspec_file}"
|
116
|
+
end
|
117
|
+
|
118
|
+
desc "Validate #{gemspec_file}"
|
119
|
+
task :validate do
|
120
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
121
|
+
unless libfiles.empty?
|
122
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
123
|
+
exit!
|
124
|
+
end
|
125
|
+
unless Dir['VERSION*'].empty?
|
126
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
127
|
+
exit!
|
128
|
+
end
|
129
|
+
end
|
data/bin/improvise
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'gli'
|
4
|
+
require 'json'
|
5
|
+
require 'zlib'
|
6
|
+
require 'improvise'
|
7
|
+
|
8
|
+
include GLI::App
|
9
|
+
|
10
|
+
program_desc 'Improvise generates random words by learning from existing text.'
|
11
|
+
version Improvise::VERSION
|
12
|
+
|
13
|
+
flag [:f, :format], :desc => 'The format of the dictionary (marshal or json)', :default_value => :marshal, :must_match => [ :marshal, :json ]
|
14
|
+
switch [:g, :gzip], :desc => 'Indicates if the dictionary is in gzip format', :negatable => false
|
15
|
+
|
16
|
+
desc 'Create or update a dictionary based on an input file'
|
17
|
+
arg_name 'input dictionary'
|
18
|
+
command :learn do |c|
|
19
|
+
c.flag [:d, :depth], :desc => 'The depth to use when building the dictionary', :default_value => 2, :type => Integer
|
20
|
+
|
21
|
+
c.action do |global_options, options, args|
|
22
|
+
if args.length < 1
|
23
|
+
help_now!('Input argument missing.')
|
24
|
+
elsif args.length < 2
|
25
|
+
help_now!('Dictionary argument missing.')
|
26
|
+
end
|
27
|
+
|
28
|
+
input_filename = args[0]
|
29
|
+
dictionary_filename = args[1]
|
30
|
+
depth = options[:depth]
|
31
|
+
format = global_options[:format]
|
32
|
+
gzip = global_options[:gzip]
|
33
|
+
|
34
|
+
input_file = File.open(input_filename, 'r')
|
35
|
+
words = input_file.read.split(/\W+/)
|
36
|
+
input_file.close()
|
37
|
+
|
38
|
+
if File.exists?(dictionary_filename)
|
39
|
+
dictionary = Improvise::IO::DictionaryReader.read(File.open(dictionary_filename, 'r'), format: format, gzip: gzip)
|
40
|
+
else
|
41
|
+
dictionary = Improvise::ForwardDictionary.new(depth)
|
42
|
+
end
|
43
|
+
|
44
|
+
dictionary.learn!(words)
|
45
|
+
Improvise::IO::DictionaryWriter.write(File.open(dictionary_filename, 'w'), dictionary, format: format, gzip: gzip)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'Generate random words based on a dictionary'
|
50
|
+
arg_name 'dictionary length'
|
51
|
+
command :generate do |c|
|
52
|
+
c.flag [:w, :words], :desc => 'The number of words to generate', :default_value => 1, :type => Integer
|
53
|
+
c.flag [:r, :root], :desc => 'Root of the generated word', :type => String
|
54
|
+
|
55
|
+
c.action do |global_options, options, args|
|
56
|
+
if args.length < 1
|
57
|
+
help_now!('Dictionary argument missing.')
|
58
|
+
elsif args.length < 2
|
59
|
+
help_now!('Length argument missing.')
|
60
|
+
end
|
61
|
+
|
62
|
+
dictionary_filename = args[0]
|
63
|
+
words = options[:words]
|
64
|
+
root = options[:root]
|
65
|
+
format = global_options[:format]
|
66
|
+
gzip = global_options[:gzip]
|
67
|
+
|
68
|
+
begin
|
69
|
+
length = Integer(args[1])
|
70
|
+
rescue ArgumentError
|
71
|
+
exit_now!('Length argument is not an integer.')
|
72
|
+
end
|
73
|
+
|
74
|
+
dictionary = Improvise::IO::DictionaryReader.read(File.open(dictionary_filename, 'r'), format: format, gzip: gzip)
|
75
|
+
words.times { puts dictionary.generate_word(length, root) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
exit run(ARGV)
|
data/improvise.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'improvise/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.rubygems_version = '1.3.5'
|
7
|
+
|
8
|
+
s.name = 'improvise'
|
9
|
+
s.version = Improvise::VERSION
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.date = '2014-05-20'
|
12
|
+
s.authors = ['Sander Kleykens']
|
13
|
+
s.email = ['sander@kleykens.com']
|
14
|
+
s.homepage = 'http://github.com/SanderKleykens/improvise'
|
15
|
+
s.license = 'MIT'
|
16
|
+
s.summary = 'Generate random words.'
|
17
|
+
s.description = 'Improvise generates random words by learning from existing text.'
|
18
|
+
|
19
|
+
s.require_paths = %w[lib]
|
20
|
+
|
21
|
+
s.executables = ['improvise']
|
22
|
+
|
23
|
+
s.rdoc_options = ['--charset=UTF-8']
|
24
|
+
s.extra_rdoc_files = ['LICENSE', 'README.md']
|
25
|
+
|
26
|
+
s.add_dependency('gli', '~> 2.10')
|
27
|
+
s.add_dependency('json', '~> 1.8')
|
28
|
+
s.add_dependency('pickup', '>= 0.0.9', '< 1.0')
|
29
|
+
s.add_dependency('rubytree', '>= 0.9.3', '< 1.0')
|
30
|
+
|
31
|
+
s.add_development_dependency('rake', '~> 10.3')
|
32
|
+
s.add_development_dependency('rspec', '~> 2.0')
|
33
|
+
s.add_development_dependency('simplecov', '~> 0.8')
|
34
|
+
s.add_development_dependency('yard', '~> 0.8')
|
35
|
+
|
36
|
+
## Leave this section as-is. It is automatically generated from the
|
37
|
+
## contents of the Git repository via the gemspec task. DO NOT REMOVE
|
38
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
39
|
+
# = MANIFEST =
|
40
|
+
s.files = %w[
|
41
|
+
Gemfile
|
42
|
+
LICENSE
|
43
|
+
README.md
|
44
|
+
Rakefile
|
45
|
+
bin/improvise
|
46
|
+
improvise.gemspec
|
47
|
+
lib/improvise.rb
|
48
|
+
lib/improvise/dictionary.rb
|
49
|
+
lib/improvise/dictionarytree.rb
|
50
|
+
lib/improvise/forwarddictionary.rb
|
51
|
+
lib/improvise/io/dictionaryreader.rb
|
52
|
+
lib/improvise/io/dictionarywriter.rb
|
53
|
+
lib/improvise/io/io.rb
|
54
|
+
lib/improvise/version.rb
|
55
|
+
spec/improvise/dictionary_spec.rb
|
56
|
+
spec/improvise/dictionarytree_spec.rb
|
57
|
+
spec/improvise/forwarddictionary_spec.rb
|
58
|
+
spec/improvise/io/dictionaryreader_spec.rb
|
59
|
+
spec/improvise/io/dictionarywriter_spec.rb
|
60
|
+
spec/spec_helper.rb
|
61
|
+
]
|
62
|
+
# = MANIFEST =
|
63
|
+
|
64
|
+
s.test_files = s.files.select { |path| path =~ /^spec\/*_spec.*\.rb/ }
|
65
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'improvise/dictionarytree'
|
3
|
+
|
4
|
+
module Improvise
|
5
|
+
class Dictionary
|
6
|
+
attr_reader :depth
|
7
|
+
|
8
|
+
def initialize(depth=2, tree=nil)
|
9
|
+
@depth = depth
|
10
|
+
@tree = tree || DictionaryTree.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json(opts = {})
|
14
|
+
{
|
15
|
+
JSON.create_id => self.class.name,
|
16
|
+
'depth' => @depth,
|
17
|
+
'tree' => @tree.as_json
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json(*a)
|
22
|
+
as_json.to_json(*a)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.json_create(json_hash)
|
26
|
+
new(json_hash['depth'], json_hash['tree'])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'tree'
|
3
|
+
require 'pickup'
|
4
|
+
|
5
|
+
module Improvise
|
6
|
+
class DictionaryTree
|
7
|
+
def initialize(root=nil)
|
8
|
+
@root = root || Tree::TreeNode.new('root')
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_entry!(prefix, suffix)
|
12
|
+
prefix_node = DictionaryTree.add_node!(@root, prefix)
|
13
|
+
prefix_node.content += 1
|
14
|
+
|
15
|
+
suffix_node = DictionaryTree.add_node!(prefix_node, suffix)
|
16
|
+
suffix_node.content += 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def random_prefix
|
20
|
+
DictionaryTree.new_pickup(@root).pick
|
21
|
+
end
|
22
|
+
|
23
|
+
def random_suffix(prefix)
|
24
|
+
if prefix.nil?
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
node = @root[prefix]
|
29
|
+
|
30
|
+
if node.nil?
|
31
|
+
return nil
|
32
|
+
else
|
33
|
+
return DictionaryTree.new_pickup(node).pick
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_json(opts = {})
|
38
|
+
{
|
39
|
+
JSON.create_id => self.class.name,
|
40
|
+
'root' => @root.as_json
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_json(*a)
|
45
|
+
as_json.to_json(*a)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.json_create(json_hash)
|
49
|
+
new(json_hash['root'])
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def self.add_node!(root, key)
|
54
|
+
node = root[key]
|
55
|
+
|
56
|
+
if node.nil?
|
57
|
+
node = Tree::TreeNode.new(key, 0)
|
58
|
+
root << node
|
59
|
+
end
|
60
|
+
|
61
|
+
node
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.new_pickup(node)
|
65
|
+
key_func = Proc.new { |item| item.name }
|
66
|
+
weight_func = Proc.new { |item| item.content }
|
67
|
+
Pickup.new(node.children, key_func: key_func, weight_func: weight_func)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'improvise/dictionary'
|
2
|
+
|
3
|
+
module Improvise
|
4
|
+
class ForwardDictionary < Dictionary
|
5
|
+
def initialize(depth=2, tree=nil)
|
6
|
+
super(depth, tree)
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_word(length, root=nil)
|
10
|
+
word = root || @tree.random_prefix
|
11
|
+
word = word.slice(0..(length - 1))
|
12
|
+
|
13
|
+
while word.length < length do
|
14
|
+
suffix = @tree.random_suffix(word[[-@depth, 0].max..-1])
|
15
|
+
word += suffix || @tree.random_prefix
|
16
|
+
word = word.slice(0..(length - 1))
|
17
|
+
end
|
18
|
+
|
19
|
+
word
|
20
|
+
end
|
21
|
+
|
22
|
+
def learn!(words)
|
23
|
+
[*words].each do |word|
|
24
|
+
@tree.add_entry!('', word.slice(0..(@depth - 1)))
|
25
|
+
|
26
|
+
word.split('').each_cons(@depth + 1) do |characters|
|
27
|
+
prefix = characters[0..-2].join('')
|
28
|
+
suffix = characters[-1]
|
29
|
+
|
30
|
+
@tree.add_entry!(prefix, suffix)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Improvise
|
5
|
+
module IO
|
6
|
+
class DictionaryReader
|
7
|
+
attr_accessor :format, :gzip
|
8
|
+
|
9
|
+
def initialize(opts={})
|
10
|
+
@format = opts[:format] || :marshal
|
11
|
+
@gzip = opts[:gzip] || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def read(io)
|
15
|
+
input = nil
|
16
|
+
|
17
|
+
if @gzip
|
18
|
+
gz = Zlib::GzipReader.new(io)
|
19
|
+
input = gz.read
|
20
|
+
gz.close
|
21
|
+
else
|
22
|
+
input = io.read
|
23
|
+
io.close
|
24
|
+
end
|
25
|
+
|
26
|
+
if @format == :json
|
27
|
+
return JSON.load(input)
|
28
|
+
else
|
29
|
+
return Marshal.load(input)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.read(io, opts={})
|
34
|
+
DictionaryReader.new(opts).read(io)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Improvise
|
5
|
+
module IO
|
6
|
+
class DictionaryWriter
|
7
|
+
attr_accessor :format, :gzip
|
8
|
+
|
9
|
+
def initialize(opts={})
|
10
|
+
@format = opts[:format] || :marshal
|
11
|
+
@gzip = opts[:gzip] || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(io, dictionary)
|
15
|
+
dictionary_serialized = nil
|
16
|
+
|
17
|
+
if @format == :json
|
18
|
+
dictionary_serialized = dictionary.to_json
|
19
|
+
else
|
20
|
+
dictionary_serialized = Marshal.dump(dictionary)
|
21
|
+
end
|
22
|
+
|
23
|
+
if @gzip
|
24
|
+
gz = Zlib::GzipWriter.new(io)
|
25
|
+
gz.write(dictionary_serialized)
|
26
|
+
gz.close
|
27
|
+
else
|
28
|
+
io.write(dictionary_serialized)
|
29
|
+
io.close
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.write(io, dictionary, opts={})
|
34
|
+
DictionaryWriter.new(opts).write(io, dictionary)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/improvise.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Improvise::Dictionary do
|
4
|
+
before do
|
5
|
+
@dict1 = Improvise::Dictionary.new(1)
|
6
|
+
@dict2 = Improvise::Dictionary.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have depth 1" do
|
10
|
+
expect(@dict1.depth).to eq(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have depth 2" do
|
14
|
+
expect(@dict2.depth).to eq(2)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Improvise::DictionaryTree do
|
4
|
+
PREFIX = 'te'
|
5
|
+
SUFFIX = 's'
|
6
|
+
|
7
|
+
before do
|
8
|
+
@empty_tree = Improvise::DictionaryTree.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return nil as random prefix if the tree is empty" do
|
12
|
+
expect(@empty_tree.random_prefix).to eq(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return nil as random suffix if the prefix hasn't been found" do
|
16
|
+
expect(@empty_tree.random_suffix('test')).to eq(nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return nil as random suffix if the prefix is nil" do
|
20
|
+
expect(@empty_tree.random_suffix(nil)).to eq(nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return '#{PREFIX}'" do
|
24
|
+
tree = Improvise::DictionaryTree.new
|
25
|
+
tree.add_entry!(PREFIX, SUFFIX)
|
26
|
+
|
27
|
+
expect(tree.random_prefix).to eq(PREFIX)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return '#{SUFFIX}'" do
|
31
|
+
tree = Improvise::DictionaryTree.new
|
32
|
+
tree.add_entry!(PREFIX, SUFFIX)
|
33
|
+
tree.add_entry!('aa', 's')
|
34
|
+
|
35
|
+
expect(tree.random_suffix(PREFIX)).to eq(SUFFIX)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Improvise::ForwardDictionary do
|
4
|
+
before do
|
5
|
+
@dict1 = Improvise::ForwardDictionary.new(1)
|
6
|
+
@dict2 = Improvise::ForwardDictionary.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have depth 1" do
|
10
|
+
expect(@dict1.depth).to eq(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have depth 2" do
|
14
|
+
expect(@dict2.depth).to eq(2)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should generate 'te' using depth 1" do
|
18
|
+
@dict1.learn!('te')
|
19
|
+
expect(@dict1.generate_word(2, 't')).to eq('te')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should generate 'te' using depth 2" do
|
23
|
+
@dict2.learn!('te')
|
24
|
+
expect(@dict2.generate_word(2)).to eq('te')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should generate 'tes' using depth 2" do
|
28
|
+
@dict2.learn!('tes')
|
29
|
+
expect(@dict2.generate_word(3, 'te')).to eq('tes')
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe Improvise::IO::DictionaryReader do
|
5
|
+
before do
|
6
|
+
json = '{
|
7
|
+
"json_class": "Improvise::ForwardDictionary",
|
8
|
+
"depth": 2,
|
9
|
+
"tree": {
|
10
|
+
"json_class": "Improvise::DictionaryTree",
|
11
|
+
"root": {
|
12
|
+
"name": "root",
|
13
|
+
"content": null,
|
14
|
+
"json_class": "Tree::TreeNode",
|
15
|
+
"children": [
|
16
|
+
{
|
17
|
+
"name": "",
|
18
|
+
"content": 1,
|
19
|
+
"json_class": "Tree::TreeNode",
|
20
|
+
"children": [
|
21
|
+
{
|
22
|
+
"name": "te",
|
23
|
+
"content": 1,
|
24
|
+
"json_class": "Tree::TreeNode"
|
25
|
+
}
|
26
|
+
]
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"name": "te",
|
30
|
+
"content": 1,
|
31
|
+
"json_class": "Tree::TreeNode",
|
32
|
+
"children": [
|
33
|
+
{
|
34
|
+
"name": "s",
|
35
|
+
"content": 1,
|
36
|
+
"json_class": "Tree::TreeNode"
|
37
|
+
}
|
38
|
+
]
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}'
|
44
|
+
|
45
|
+
@json_buffer = StringIO.new(json)
|
46
|
+
@json_reader = Improvise::IO::DictionaryReader.new(format: :json)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should read json input correctly" do
|
50
|
+
dict = @json_reader.read(@json_buffer)
|
51
|
+
expect(dict).to be_a(Improvise::ForwardDictionary)
|
52
|
+
expect(dict.depth).to eq(2)
|
53
|
+
expect(dict.generate_word(3)).to eq('tes')
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should read json input correctly (class method)" do
|
57
|
+
dict = Improvise::IO::DictionaryReader.read(@json_buffer, format: :json)
|
58
|
+
expect(dict).to be_a(Improvise::ForwardDictionary)
|
59
|
+
expect(dict.depth).to eq(2)
|
60
|
+
expect(dict.generate_word(3)).to eq('tes')
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe Improvise::IO::DictionaryWriter do
|
5
|
+
before do
|
6
|
+
@dict = Improvise::ForwardDictionary.new(1)
|
7
|
+
@dict.learn!('tes')
|
8
|
+
|
9
|
+
@JSON = '{"json_class":"Improvise::ForwardDictionary","depth":1,"tree":{"json_class":"Improvise::DictionaryTree","root":{"name":"root","content":null,"json_class":"Tree::TreeNode","children":[{"name":"","content":1,"json_class":"Tree::TreeNode","children":[{"name":"t","content":1,"json_class":"Tree::TreeNode"}]},{"name":"t","content":1,"json_class":"Tree::TreeNode","children":[{"name":"e","content":1,"json_class":"Tree::TreeNode"}]},{"name":"e","content":1,"json_class":"Tree::TreeNode","children":[{"name":"s","content":1,"json_class":"Tree::TreeNode"}]}]}}}'
|
10
|
+
@buffer = StringIO.new
|
11
|
+
|
12
|
+
@json_writer = Improvise::IO::DictionaryWriter.new(format: :json)
|
13
|
+
@json_gzip_writer = Improvise::IO::DictionaryWriter.new(format: :json, gzip: true)
|
14
|
+
@marshal_writer = Improvise::IO::DictionaryWriter.new
|
15
|
+
@marshal_gzip_writer = Improvise::IO::DictionaryWriter.new(gzip: true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should write in json format correctly" do
|
19
|
+
@json_writer.write(@buffer, @dict)
|
20
|
+
expect(@buffer.string).to eq(@JSON)
|
21
|
+
expect(@buffer.closed?).to be true
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should write in json format correctly (class method)" do
|
25
|
+
Improvise::IO::DictionaryWriter.write(@buffer, @dict, format: :json)
|
26
|
+
expect(@buffer.string).to eq(@JSON)
|
27
|
+
expect(@buffer.closed?).to be true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should write in marshal format" do
|
31
|
+
@marshal_writer.write(@buffer, @dict)
|
32
|
+
expect(@buffer.size).to be > 0
|
33
|
+
expect(@buffer.closed?).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should write in marshal format (class method)" do
|
37
|
+
Improvise::IO::DictionaryWriter.write(@buffer, @dict)
|
38
|
+
expect(@buffer.size).to be > 0
|
39
|
+
expect(@buffer.closed?).to be true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should write in gzipped json format" do
|
43
|
+
@json_gzip_writer.write(@buffer, @dict)
|
44
|
+
expect(@buffer.size).to be > 0
|
45
|
+
expect(@buffer.closed?).to be true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should write in gzipped json format (class method)" do
|
49
|
+
Improvise::IO::DictionaryWriter.write(@buffer, @dict, format: :json, gzip: true)
|
50
|
+
expect(@buffer.size).to be > 0
|
51
|
+
expect(@buffer.closed?).to be true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should write in gzipped marshal format" do
|
55
|
+
@marshal_gzip_writer.write(@buffer, @dict)
|
56
|
+
expect(@buffer.size).to be > 0
|
57
|
+
expect(@buffer.closed?).to be true
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should write in gzipped marshal format (class method)" do
|
61
|
+
Improvise::IO::DictionaryWriter.write(@buffer, @dict, gzip: true)
|
62
|
+
expect(@buffer.size).to be > 0
|
63
|
+
expect(@buffer.closed?).to be true
|
64
|
+
end
|
65
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: improvise
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sander Kleykens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gli
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pickup
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.9
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '1.0'
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 0.0.9
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rubytree
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.9.3
|
68
|
+
- - "<"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '1.0'
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.9.3
|
78
|
+
- - "<"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '1.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rake
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '10.3'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '10.3'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rspec
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '2.0'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: simplecov
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0.8'
|
116
|
+
type: :development
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0.8'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: yard
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0.8'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0.8'
|
137
|
+
description: Improvise generates random words by learning from existing text.
|
138
|
+
email:
|
139
|
+
- sander@kleykens.com
|
140
|
+
executables:
|
141
|
+
- improvise
|
142
|
+
extensions: []
|
143
|
+
extra_rdoc_files:
|
144
|
+
- LICENSE
|
145
|
+
- README.md
|
146
|
+
files:
|
147
|
+
- Gemfile
|
148
|
+
- LICENSE
|
149
|
+
- README.md
|
150
|
+
- Rakefile
|
151
|
+
- bin/improvise
|
152
|
+
- improvise.gemspec
|
153
|
+
- lib/improvise.rb
|
154
|
+
- lib/improvise/dictionary.rb
|
155
|
+
- lib/improvise/dictionarytree.rb
|
156
|
+
- lib/improvise/forwarddictionary.rb
|
157
|
+
- lib/improvise/io/dictionaryreader.rb
|
158
|
+
- lib/improvise/io/dictionarywriter.rb
|
159
|
+
- lib/improvise/io/io.rb
|
160
|
+
- lib/improvise/version.rb
|
161
|
+
- spec/improvise/dictionary_spec.rb
|
162
|
+
- spec/improvise/dictionarytree_spec.rb
|
163
|
+
- spec/improvise/forwarddictionary_spec.rb
|
164
|
+
- spec/improvise/io/dictionaryreader_spec.rb
|
165
|
+
- spec/improvise/io/dictionarywriter_spec.rb
|
166
|
+
- spec/spec_helper.rb
|
167
|
+
homepage: http://github.com/SanderKleykens/improvise
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options:
|
173
|
+
- "--charset=UTF-8"
|
174
|
+
require_paths:
|
175
|
+
- lib
|
176
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
requirements: []
|
187
|
+
rubyforge_project:
|
188
|
+
rubygems_version: 2.2.2
|
189
|
+
signing_key:
|
190
|
+
specification_version: 2
|
191
|
+
summary: Generate random words.
|
192
|
+
test_files: []
|
193
|
+
has_rdoc:
|