random-words 1.0.5 → 1.0.7

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.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec_status +166 -39
  3. data/.rubocop.yml +4 -0
  4. data/.rubocop_todo.yml +313 -0
  5. data/CHANGELOG.md +67 -0
  6. data/Gemfile +15 -12
  7. data/Gemfile.lock +46 -26
  8. data/README.md +208 -15
  9. data/bin/randw +258 -92
  10. data/lib/random-words/array.rb +71 -0
  11. data/lib/random-words/config.rb +254 -0
  12. data/lib/random-words/generator.rb +257 -94
  13. data/lib/random-words/hash.rb +16 -0
  14. data/lib/random-words/html2markdown.rb +205 -0
  15. data/lib/random-words/lorem-markdown.rb +397 -0
  16. data/lib/random-words/number-to-word.rb +139 -0
  17. data/lib/random-words/source.rb +106 -0
  18. data/lib/random-words/string.rb +224 -45
  19. data/lib/random-words/table-cleanup.rb +210 -0
  20. data/lib/random-words/version.rb +1 -1
  21. data/lib/random-words/words/1984/adjectives.txt +103 -0
  22. data/lib/random-words/words/1984/adverbs.txt +92 -0
  23. data/lib/random-words/words/1984/articles-plural.txt +10 -0
  24. data/lib/random-words/words/1984/articles-singular.txt +10 -0
  25. data/lib/random-words/words/1984/clauses.txt +79 -0
  26. data/lib/random-words/words/1984/config.yml +4 -0
  27. data/lib/random-words/words/1984/conjunctions-coordinating.txt +20 -0
  28. data/lib/random-words/words/1984/conjunctions-subordinate.txt +29 -0
  29. data/lib/random-words/words/1984/names.txt +12 -0
  30. data/lib/random-words/words/1984/nouns-plural.txt +89 -0
  31. data/lib/random-words/words/1984/nouns-singular.txt +74 -0
  32. data/lib/random-words/words/1984/numbers.yml +5 -0
  33. data/lib/random-words/words/1984/phrases.txt +16 -0
  34. data/lib/random-words/words/1984/prepositions.txt +89 -0
  35. data/lib/random-words/words/1984/terminators.txt +16 -0
  36. data/lib/random-words/words/1984/verbs-passive.txt +83 -0
  37. data/lib/random-words/words/1984/verbs-plural.txt +91 -0
  38. data/lib/random-words/words/1984/verbs-singular.txt +110 -0
  39. data/lib/random-words/words/alice/adjectives.txt +80 -0
  40. data/lib/random-words/words/alice/adverbs.txt +87 -0
  41. data/lib/random-words/words/alice/articles-plural.txt +10 -0
  42. data/lib/random-words/words/alice/articles-singular.txt +10 -0
  43. data/lib/random-words/words/alice/clauses.txt +81 -0
  44. data/lib/random-words/words/alice/config.yml +4 -0
  45. data/lib/random-words/words/alice/conjunctions-coordinating.txt +20 -0
  46. data/lib/random-words/words/alice/conjunctions-subordinate.txt +29 -0
  47. data/lib/random-words/words/alice/names.txt +10 -0
  48. data/lib/random-words/words/alice/nouns-plural.txt +95 -0
  49. data/lib/random-words/words/alice/nouns-singular.txt +82 -0
  50. data/lib/random-words/words/alice/numbers.yml +5 -0
  51. data/lib/random-words/words/alice/phrases.txt +16 -0
  52. data/lib/random-words/words/alice/prepositions.txt +45 -0
  53. data/lib/random-words/words/alice/terminators.txt +16 -0
  54. data/lib/random-words/words/alice/verbs-passive.txt +495 -0
  55. data/lib/random-words/words/alice/verbs-plural.txt +115 -0
  56. data/lib/random-words/words/alice/verbs-singular.txt +97 -0
  57. data/lib/random-words/words/bacon/clauses.txt +592 -592
  58. data/lib/random-words/words/bacon/config.yml +4 -0
  59. data/lib/random-words/words/bacon/conjunctions-coordinating.txt +20 -0
  60. data/lib/random-words/words/bacon/names.txt +54 -0
  61. data/lib/random-words/words/bacon/numbers.yml +5 -0
  62. data/lib/random-words/words/bacon/phrases.txt +20 -0
  63. data/lib/random-words/words/bacon/prepositions.txt +45 -0
  64. data/lib/random-words/words/bacon/terminators.txt +16 -0
  65. data/lib/random-words/words/corporate/config.yml +4 -0
  66. data/lib/random-words/words/corporate/conjunctions-coordinating.txt +20 -0
  67. data/lib/random-words/words/corporate/names.txt +74 -0
  68. data/lib/random-words/words/corporate/numbers.yml +5 -0
  69. data/lib/random-words/words/corporate/phrases.txt +29 -0
  70. data/lib/random-words/words/corporate/prepositions.txt +45 -0
  71. data/lib/random-words/words/corporate/terminators.txt +16 -0
  72. data/lib/random-words/words/doctor/adjectives.txt +92 -0
  73. data/lib/random-words/words/doctor/adverbs.txt +92 -0
  74. data/lib/random-words/words/doctor/articles-plural.txt +10 -0
  75. data/lib/random-words/words/doctor/articles-singular.txt +10 -0
  76. data/lib/random-words/words/doctor/clauses.txt +83 -0
  77. data/lib/random-words/words/doctor/config.yml +10 -0
  78. data/lib/random-words/words/doctor/conjunctions-coordinating.txt +20 -0
  79. data/lib/random-words/words/doctor/conjunctions-subordinate.txt +29 -0
  80. data/lib/random-words/words/doctor/names.txt +74 -0
  81. data/lib/random-words/words/doctor/nouns-plural.txt +84 -0
  82. data/lib/random-words/words/doctor/nouns-singular.txt +84 -0
  83. data/lib/random-words/words/doctor/numbers.yml +5 -0
  84. data/lib/random-words/words/doctor/phrases.txt +29 -0
  85. data/lib/random-words/words/doctor/prepositions.txt +45 -0
  86. data/lib/random-words/words/doctor/terminators.txt +16 -0
  87. data/lib/random-words/words/doctor/verbs-passive.txt +83 -0
  88. data/lib/random-words/words/doctor/verbs-plural.txt +88 -0
  89. data/lib/random-words/words/doctor/verbs-singular.txt +83 -0
  90. data/lib/random-words/words/english/clauses.txt +592 -592
  91. data/lib/random-words/words/english/config.yml +4 -0
  92. data/lib/random-words/words/english/conjunctions-coordinating.txt +20 -0
  93. data/lib/random-words/words/english/names.txt +74 -0
  94. data/lib/random-words/words/english/numbers.yml +5 -0
  95. data/lib/random-words/words/english/phrases.txt +29 -0
  96. data/lib/random-words/words/english/prepositions.txt +45 -0
  97. data/lib/random-words/words/english/terminators.txt +16 -0
  98. data/lib/random-words/words/english/verbs-plural.txt +1 -0
  99. data/lib/random-words/words/english/verbs-singular.txt +1 -0
  100. data/lib/random-words/words/foulmouth/adjectives.txt +89 -0
  101. data/lib/random-words/words/foulmouth/adverbs.txt +97 -0
  102. data/lib/random-words/words/foulmouth/articles-plural.txt +12 -0
  103. data/lib/random-words/words/foulmouth/articles-singular.txt +11 -0
  104. data/lib/random-words/words/foulmouth/clauses.txt +74 -0
  105. data/lib/random-words/words/foulmouth/config.yml +4 -0
  106. data/lib/random-words/words/foulmouth/conjunctions-coordinating.txt +20 -0
  107. data/lib/random-words/words/foulmouth/conjunctions-subordinate.txt +29 -0
  108. data/lib/random-words/words/foulmouth/names.txt +81 -0
  109. data/lib/random-words/words/foulmouth/nouns-plural.txt +96 -0
  110. data/lib/random-words/words/foulmouth/nouns-singular.txt +99 -0
  111. data/lib/random-words/words/foulmouth/numbers.yml +5 -0
  112. data/lib/random-words/words/foulmouth/phrases.txt +30 -0
  113. data/lib/random-words/words/foulmouth/prepositions.txt +35 -0
  114. data/lib/random-words/words/foulmouth/terminators.txt +16 -0
  115. data/lib/random-words/words/foulmouth/verbs-passive.txt +143 -0
  116. data/lib/random-words/words/foulmouth/verbs-plural.txt +91 -0
  117. data/lib/random-words/words/foulmouth/verbs-singular.txt +104 -0
  118. data/lib/random-words/words/hipster/adjectives.txt +100 -0
  119. data/lib/random-words/words/hipster/adverbs.txt +89 -0
  120. data/lib/random-words/words/hipster/articles-plural.txt +10 -0
  121. data/lib/random-words/words/hipster/articles-singular.txt +10 -0
  122. data/lib/random-words/words/hipster/clauses.txt +180 -0
  123. data/lib/random-words/words/hipster/config.yml +4 -0
  124. data/lib/random-words/words/hipster/conjunctions-coordinating.txt +20 -0
  125. data/lib/random-words/words/hipster/conjunctions-subordinate.txt +29 -0
  126. data/lib/random-words/words/hipster/names.txt +73 -0
  127. data/lib/random-words/words/hipster/nouns-plural.txt +96 -0
  128. data/lib/random-words/words/hipster/nouns-singular.txt +100 -0
  129. data/lib/random-words/words/hipster/numbers.yml +5 -0
  130. data/lib/random-words/words/hipster/phrases.txt +21 -0
  131. data/lib/random-words/words/hipster/prepositions.txt +45 -0
  132. data/lib/random-words/words/hipster/terminators.txt +16 -0
  133. data/lib/random-words/words/hipster/verbs-passive.txt +88 -0
  134. data/lib/random-words/words/hipster/verbs-plural.txt +106 -0
  135. data/lib/random-words/words/hipster/verbs-singular.txt +114 -0
  136. data/lib/random-words/words/latin/config.yml +4 -0
  137. data/lib/random-words/words/latin/conjunctions-coordinating.txt +15 -0
  138. data/lib/random-words/words/latin/names.txt +93 -0
  139. data/lib/random-words/words/latin/numbers.yml +5 -0
  140. data/lib/random-words/words/latin/phrases.txt +16 -0
  141. data/lib/random-words/words/latin/prepositions.txt +18 -0
  142. data/lib/random-words/words/latin/terminators.txt +16 -0
  143. data/lib/random-words/words/spanish/adjectives.txt +81 -0
  144. data/lib/random-words/words/spanish/adverbs.txt +87 -0
  145. data/lib/random-words/words/spanish/articles-plural.txt +4 -0
  146. data/lib/random-words/words/spanish/articles-singular.txt +4 -0
  147. data/lib/random-words/words/spanish/clauses.txt +74 -0
  148. data/lib/random-words/words/spanish/config.yml +4 -0
  149. data/lib/random-words/words/spanish/conjunctions-coordinating.txt +8 -0
  150. data/lib/random-words/words/spanish/conjunctions-subordinate.txt +16 -0
  151. data/lib/random-words/words/spanish/names.txt +61 -0
  152. data/lib/random-words/words/spanish/nouns-plural.txt +92 -0
  153. data/lib/random-words/words/spanish/nouns-singular.txt +125 -0
  154. data/lib/random-words/words/spanish/numbers.yml +5 -0
  155. data/lib/random-words/words/spanish/phrases.txt +31 -0
  156. data/lib/random-words/words/spanish/prepositions.txt +31 -0
  157. data/lib/random-words/words/spanish/terminators.txt +17 -0
  158. data/lib/random-words/words/spanish/verbs-passive.txt +495 -0
  159. data/lib/random-words/words/spanish/verbs-plural.txt +326 -0
  160. data/lib/random-words/words/spanish/verbs-singular.txt +351 -0
  161. data/lib/random-words/words/veggie/adjectives.txt +111 -0
  162. data/lib/random-words/words/veggie/adverbs.txt +81 -0
  163. data/lib/random-words/words/veggie/articles-plural.txt +10 -0
  164. data/lib/random-words/words/veggie/articles-singular.txt +10 -0
  165. data/lib/random-words/words/veggie/clauses.txt +57 -0
  166. data/lib/random-words/words/veggie/config.yml +4 -0
  167. data/lib/random-words/words/veggie/conjunctions-coordinating.txt +20 -0
  168. data/lib/random-words/words/veggie/conjunctions-subordinate.txt +29 -0
  169. data/lib/random-words/words/veggie/names.txt +93 -0
  170. data/lib/random-words/words/veggie/nouns-plural.txt +78 -0
  171. data/lib/random-words/words/veggie/nouns-singular.txt +105 -0
  172. data/lib/random-words/words/veggie/numbers.yml +5 -0
  173. data/lib/random-words/words/veggie/phrases.txt +20 -0
  174. data/lib/random-words/words/veggie/prepositions.txt +45 -0
  175. data/lib/random-words/words/veggie/terminators.txt +16 -0
  176. data/lib/random-words/words/veggie/verbs-passive.txt +56 -0
  177. data/lib/random-words/words/veggie/verbs-plural.txt +79 -0
  178. data/lib/random-words/words/veggie/verbs-singular.txt +79 -0
  179. data/lib/random-words.rb +28 -0
  180. data/random-words.gemspec +3 -1
  181. data/src/_README.md +208 -15
  182. metadata +181 -9
  183. data/lib/random-words/words/bacon/numbers.txt +0 -21
  184. data/lib/random-words/words/corporate/numbers.txt +0 -21
  185. data/lib/random-words/words/english/numbers.txt +0 -21
  186. data/lib/random-words/words/latin/numbers.txt +0 -21
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'nokogiri'
5
+ require_relative 'table-cleanup'
6
+
7
+ module RandomWords
8
+ # Convert HTML to Markdown
9
+ class HTML2Markdown
10
+ attr_reader :markdown
11
+
12
+ # Initialize the HTML2Markdown converter
13
+ # @param str [String] The HTML string to convert
14
+ # @param baseurl [String] The base URL for resolving relative links
15
+ def initialize(str, baseurl = nil)
16
+ @links = []
17
+ @baseuri = (baseurl ? URI.parse(baseurl) : nil)
18
+ @section_level = 0
19
+ @encoding = str.encoding
20
+ @markdown = output_for(Nokogiri::HTML(str, baseurl).root).gsub(/\n\n\n+/, "\n\n\n")
21
+ end
22
+
23
+ # Convert the HTML to Markdown
24
+ # @return [String] The converted Markdown string
25
+ # @example
26
+ # converter = HTML2Markdown.new('<p>Hello world</p>')
27
+ # puts converter.to_s
28
+ def to_s
29
+ i = 0
30
+ @markdown = TableCleanup.new(@markdown).clean
31
+ "#{@markdown}\n\n" + @links.map { |link|
32
+ i += 1
33
+ "[#{i}]: #{link[:href]}" + (link[:title] ? %( "#{link[:title]}") : '')
34
+ }.join("\n")
35
+ end
36
+
37
+ # Recursively convert child nodes
38
+ # @param node [Nokogiri::XML::Node] The parent node
39
+ def output_for_children(node)
40
+ node.children.map do |el|
41
+ output_for(el)
42
+ end.join
43
+ end
44
+
45
+ # Add a link to the list of links
46
+ # @param link [Hash] The link to add
47
+ def add_link(link)
48
+ if @baseuri
49
+ begin
50
+ link[:href] = URI.parse(link[:href])
51
+ rescue Exception
52
+ link[:href] = URI.parse('')
53
+ end
54
+ link[:href].scheme = @baseuri.scheme unless link[:href].scheme
55
+ unless link[:href].opaque
56
+ link[:href].host = @baseuri.host unless link[:href].host
57
+ link[:href].path = @baseuri.path.to_s + '/' + link[:href].path.to_s if link[:href].path.to_s[0] != '/'
58
+ end
59
+ link[:href] = link[:href].to_s
60
+ end
61
+ @links.each_with_index do |l, i|
62
+ return i + 1 if l[:href] == link[:href]
63
+ end
64
+ @links << link
65
+ @links.length
66
+ end
67
+
68
+ # Wrap a string to 74 characters
69
+ # @param str [String] The string to wrap
70
+ # @return [String] The wrapped string
71
+ def wrap(str)
72
+ return str if str =~ /\n/
73
+
74
+ out = []
75
+ line = []
76
+ str.split(/[ \t]+/).each do |word|
77
+ line << word
78
+ if line.join(' ').length >= 74
79
+ out << line.join(' ')
80
+ line = []
81
+ end
82
+ end
83
+ out << (line.join(' ') + (str[-1..-1] =~ /[ \t\n]/ ? str[-1..-1] : ''))
84
+ out.join("\n")
85
+ end
86
+
87
+ # Convert a node to Markdown
88
+ # @param node [Nokogiri::XML::Node] The node to convert
89
+ # @return [String] The converted Markdown string
90
+ def output_for(node)
91
+ case node.name
92
+ when 'head', 'style', 'script'
93
+ ''
94
+ when 'br'
95
+ " \n"
96
+ when 'p', 'div'
97
+ "\n\n#{output_for_children(node)}\n\n"
98
+ when 'section', 'article'
99
+ @section_level += 1
100
+ o = "\n\n----\n\n#{output_for_children(node)}\n\n"
101
+ @section_level -= 1
102
+ o
103
+ when /h(\d+)/
104
+ "\n\n" + (('#' * (::Regexp.last_match(1).to_i + @section_level)) + ' ' + output_for_children(node)) + "\n\n"
105
+ when /mark/
106
+ "==#{output_for_children(node)}=="
107
+ when 'blockquote'
108
+ @section_level += 1
109
+ o = "\n\n> #{output_for_children(node).lstrip.gsub("\n", "\n> ")}\n\n".gsub(/> \n(> \n)+/, "> \n")
110
+ @section_level -= 1
111
+ o
112
+ when 'cite'
113
+ "*#{output_for_children(node)}*"
114
+ when 'ul'
115
+ "\n\n" + node.children.map { |el|
116
+ next if el.name == 'text'
117
+
118
+ "* #{output_for_children(el).gsub(/^(\t)|( )/, "\t\t").gsub(/^>/, "\t>")}\n"
119
+ }.join + "\n\n"
120
+ when 'ol'
121
+ i = 0
122
+ "\n\n" + node.children.map { |el|
123
+ next if el.name == 'text'
124
+
125
+ i += 1
126
+ "#{i}. #{output_for_children(el).gsub(/^(\t)|( )/, "\t\t").gsub(/^>/, "\t>")}\n"
127
+ }.join + "\n\n"
128
+ when 'code'
129
+ if @in_pre
130
+ @in_pre = false
131
+ node.text.strip
132
+ else
133
+ "`#{output_for_children(node).gsub("\n", ' ')}`"
134
+ end
135
+ when 'pre'
136
+ @in_pre = true
137
+ lang = node['lang'] || node['language']
138
+ lang = '' if lang.nil? || lang.empty?
139
+ "\n\n```#{lang}\n" + output_for_children(node).lstrip + "\n```\n\n"
140
+ when 'hr'
141
+ "\n\n----\n\n"
142
+ when 'a', 'link'
143
+ link = { href: node['href'], title: node['title'] }
144
+ "[#{output_for_children(node).gsub("\n", ' ')}][#{add_link(link)}]"
145
+ when 'img'
146
+ link = { href: node['src'], title: node['title'] }
147
+ "![#{node['alt']}][#{add_link(link)}]"
148
+ when 'video', 'audio', 'embed'
149
+ link = { href: node['src'], title: node['title'] }
150
+ "[#{output_for_children(node).gsub("\n", ' ')}][#{add_link(link)}]"
151
+ when 'object'
152
+ link = { href: node['data'], title: node['title'] }
153
+ "[#{output_for_children(node).gsub("\n", ' ')}][#{add_link(link)}]"
154
+ when 'i', 'em', 'u'
155
+ "_#{output_for_children(node)}_"
156
+ when 'b', 'strong'
157
+ "**#{output_for_children(node)}**"
158
+ when 'dl'
159
+ "\n\n" + node.children.map do |el|
160
+ case el.name
161
+ when 'dt'
162
+ "#{output_for_children(el)}\n"
163
+ when 'dd'
164
+ ": #{output_for_children(el)}\n\n"
165
+ end
166
+ end.join + "\n\n"
167
+ # Tables are not part of Markdown, so we output WikiCreole
168
+ when 'table'
169
+ @table_header = true
170
+ node.children.select do |c|
171
+ %w[tr tbody thead].include?(c.name)
172
+ end.map do |c|
173
+ output_for(c)
174
+ end.join
175
+ when 'tr'
176
+ header = "\n"
177
+ if @table_header
178
+ @table_header = false
179
+ cells = node.children.select do |c|
180
+ %w[th td].include?(c.name)
181
+ end.count
182
+ header = "\n|#{cells.times.map { '-------' }.join('|')}|\n"
183
+ end
184
+ node.children.select do |c|
185
+ %w[th td].include?(c.name)
186
+ end.map do |c|
187
+ output_for(c)
188
+ end.join.gsub('||', '|') + header
189
+ when 'th', 'td'
190
+ "| #{output_for_children(node)} |"
191
+ when 'text'
192
+ # Sometimes Nokogiri lies. Force the encoding back to what we know it is
193
+ if (c = node.content.force_encoding(@encoding)) =~ /\S/
194
+ c.gsub!(/\n\n+/, '<$PreserveDouble$>')
195
+ c.gsub!(/\s+/, ' ')
196
+ c.gsub('<$PreserveDouble$>', "\n\n")
197
+ else
198
+ c
199
+ end
200
+ else
201
+ output_for_children(node)
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,397 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RandomWords
4
+ # Generates random Lorem Ipsum text in Markdown format.
5
+ class LoremMarkdown
6
+ # Stores the output
7
+ attr_accessor :output
8
+
9
+ # Stores the RandomWords::Generator
10
+ attr_accessor :generator
11
+
12
+ # Generates random Lorem Ipsum text.
13
+ # @param options [Hash] Options for generating Lorem Ipsum text.
14
+ # @return [String] A string of random Lorem Ipsum text.
15
+ def initialize(options = {})
16
+ @added = {
17
+ em: false,
18
+ strong: false,
19
+ link: false,
20
+ code: false,
21
+ mark: false
22
+ }
23
+
24
+ defaults = {
25
+ source: :latin,
26
+ grafs: 10,
27
+ sentences: 5,
28
+ length: :medium,
29
+ decorate: true,
30
+ link: false,
31
+ ul: false,
32
+ ol: false,
33
+ dl: false,
34
+ bq: false,
35
+ code: false,
36
+ mark: false,
37
+ headers: false,
38
+ table: false
39
+ }
40
+
41
+ @options = defaults.merge(options)
42
+
43
+ @generator = RandomWords::Generator.new(@options[:source], {
44
+ sentence_length: @options[:length],
45
+ paragraph_length: @options[:sentences]
46
+ })
47
+
48
+ @output = ''
49
+ strong = @options[:decorate]
50
+ em = @options[:decorate]
51
+ links = @options[:link]
52
+ code = @options[:code]
53
+ mark = @options[:mark]
54
+ force = []
55
+ # Generate the specified number of paragraphs.
56
+ @options[:grafs].times.with_index do |_, i|
57
+ @output += paragraph(1, em: em, strong: strong, links: links, code: code, mark: mark, force: force)
58
+ em = Random.rand(0..3).zero? && @options[:decorate]
59
+ strong = Random.rand(0..3).zero? && @options[:decorate]
60
+ links = Random.rand(0..3).zero? && @options[:link]
61
+ code = Random.rand(0..3).zero? && @options[:code]
62
+
63
+ if i == @options[:grafs] - 2
64
+ force << :code if !@added[:code] && @options[:code]
65
+ force << :link if !@added[:link] && @options[:link]
66
+ force << :em if !@added[:em] && @options[:decorate]
67
+ force << :strong if !@added[:strong] && @options[:decorate]
68
+ end
69
+ @output += "\n\n"
70
+ end
71
+
72
+ generate
73
+ end
74
+
75
+ # Use the RandomWords class to generate the Lorem Ipsum text.
76
+ def generate
77
+ compress_newlines
78
+
79
+ items = { short: 4, medium: 8, long: 10, very_long: 12 }[@options[:length]]
80
+
81
+ if @options[:ul] && @options[:ol]
82
+ # If both unordered and ordered lists are specified, add them both.
83
+ inject_block(1, -> { list(items, :ul) })
84
+ inject_block(1, -> { list(items, :ol) })
85
+ elsif @options[:ul]
86
+ # If only unordered list is specified, add it.
87
+ inject_block(2, -> { list(items, :ul) })
88
+ elsif @options[:ol]
89
+ # If only ordered list is specified, add it.
90
+ inject_block(2, -> { list(items, :ol) })
91
+ end
92
+
93
+ # Add definition list if specified.
94
+ inject_block(1, -> { list(items, :dl) }) if @options[:dl]
95
+
96
+ # Add blockquote if specified.
97
+ inject_block(1, -> { blockquote(items / 2) }) if @options[:bq]
98
+
99
+ # Add headers if specified.
100
+ inject_headers if @options[:headers]
101
+
102
+ # Add code block if specified.
103
+ inject_block(1, -> { code }) if @options[:code]
104
+
105
+ # Add table if specified.
106
+ inject_block(1, -> { table }) if @options[:table]
107
+
108
+ ensure_block_newlines
109
+ compress_newlines
110
+ end
111
+
112
+ private
113
+
114
+ # Rolls for zero with the specified odds.
115
+ def roll(odds)
116
+ Random.rand(0..odds).zero?
117
+ end
118
+
119
+ # Generates a list of items.
120
+ # @param [Integer] count The number of items to generate.
121
+ # @param [Symbol] type The type of list to generate (:ul, :ol, :dl).
122
+ # @return [String] The generated list.
123
+ def list(count, type)
124
+ ul = "\n\n<#{type}>\n"
125
+
126
+ count.times do
127
+ links = roll(4) && @options[:link]
128
+ em = roll(2) && @options[:decorate]
129
+ strong = roll(2) && @options[:decorate]
130
+ code = roll(4) && @options[:code]
131
+ mark = roll(6) && @options[:mark]
132
+ frag = fragment(1, 4).cap_first
133
+ long_frag = fragment(4, 8).cap_first
134
+ long_frag = inject_inline(long_frag, -> { link }) if links
135
+ long_frag = inject_inline(long_frag, -> { emphasis(:em) }) if em
136
+ long_frag = inject_inline(long_frag, -> { emphasis(:strong) }) if strong
137
+ long_frag = inject_inline(long_frag, -> { code_span }, 1) if code
138
+ long_frag = inject_inline(long_frag, -> { emphasis(:mark) }, 1) if mark
139
+ long_frag = long_frag.restore_spaces if links
140
+ if type == :dl
141
+ ul += "\t<dt>#{frag}</dt>\n"
142
+ ul += "\t<dd>#{long_frag}</dd>\n"
143
+ else
144
+ ul += "\t<li>#{long_frag}</li>\n"
145
+ end
146
+ end
147
+ ul + "</#{type}>\n\n"
148
+ end
149
+
150
+ # Generates a blockquote.
151
+ # @param [Integer] count The number of items to generate.
152
+ # @param [Boolean] nested Whether the blockquote is nested.
153
+ # @return [String] The generated blockquote.
154
+ def blockquote(count, nested = false)
155
+ if count > 1 && !nested
156
+ level1 = Random.rand(1..count - 1).to_i
157
+ level2 = count - level1
158
+ else
159
+ level1 = 1
160
+ level2 = 0
161
+ end
162
+ cite = "\n<cite>— #{@generator.name}</cite>\n" unless nested
163
+ newline = nested ? '' : "\n\n"
164
+ quote = "#{newline}<blockquote>\n"
165
+ quote += paragraph(level1,
166
+ em: @options[:decorate],
167
+ strong: @options[:decorate],
168
+ links: @options[:link],
169
+ code: @options[:code],
170
+ mark: @options[:mark]).gsub(
171
+ /\n+/, "\n"
172
+ )
173
+ quote += blockquote(level2, true).strip if level2.positive?
174
+ "#{quote}#{cite}</blockquote>#{newline}"
175
+ end
176
+
177
+ # Generates a header.
178
+ # @param [Integer] level The level of the header (1-6).
179
+ # @return [String] The generated header.
180
+ def header(level)
181
+ level = 6 if level > 6
182
+ pre = level > 1 ? "\n\n" : ''
183
+ h = "#{pre}<h#{level}>"
184
+ h += fragment(2, 8).cap_first
185
+ h + "</h#{level}>\n\n"
186
+ end
187
+
188
+ # Injects headers in the output. Number injected depends on overall length
189
+ # of the input.
190
+ # @return [String] The output with injected headers.
191
+ def inject_headers
192
+ count = if @options[:grafs] == 1
193
+ 1
194
+ elsif @options[:grafs] <= 3
195
+ 2
196
+ elsif @options[:grafs] <= 5
197
+ 4
198
+ elsif @options[:grafs] == 6
199
+ 5
200
+ else
201
+ 6
202
+ end
203
+ grafs = @output.split("\n\n").reverse
204
+
205
+ @output = header(1)
206
+ @output += "#{grafs.slice!(-1)}\n\n"
207
+
208
+ if count == 1
209
+ grafs = grafs.reverse
210
+ @output += grafs.join("\n\n")
211
+ return
212
+ end
213
+
214
+ level = 2
215
+
216
+ c = (grafs.length / count).to_i
217
+
218
+ while grafs.any? && level <= count
219
+ @output += header(level)
220
+ @output += grafs.slice!(grafs.length - c, c).join("\n\n")
221
+ level += 1
222
+ end
223
+ @output += grafs.join("\n\n")
224
+ compress_newlines
225
+ end
226
+
227
+ # Generates a random table. Length is determined by @options[:length]
228
+ # @return [String] The generated table.
229
+ def table
230
+ items = { short: 2, medium: 2, long: 4, very_long: 6 }[@options[:length]]
231
+ # Generates a random table.
232
+ table = "\n\n<table>\n"
233
+ table += "\t<tr>\n"
234
+ items.times do
235
+ table += "\t\t<th>#{fragment(1, 2).cap_first}</th>\n"
236
+ end
237
+ table += "\t</tr>\n"
238
+ items.times do
239
+ table += "\t<tr>\n"
240
+ items.times do
241
+ table += "\t\t<td>#{Random.rand(10_000)}</td>\n"
242
+ end
243
+ table += "\t</tr>\n"
244
+ end
245
+ table += "</table>\n\n"
246
+ table
247
+ end
248
+
249
+ # Generates a random code block
250
+ # @return [String] The generated code block.
251
+ def code
252
+ lang = @generator.code_lang
253
+ code = "\n\n<pre lang=\"#{lang}\"><code>\n"
254
+ code += "#{@generator.code_snippet(lang)}\n"
255
+ "#{code}</code></pre>\n\n"
256
+ end
257
+
258
+ # Generates a random code span
259
+ def code_span
260
+ @added[:code] = true
261
+ code = '<code>'
262
+ code += "#{@generator.words(Random.rand(1..5))} "
263
+ "#{code.strip}</code>"
264
+ end
265
+
266
+ # Generates a random emphasis tag
267
+ # @param [String] tag The tag to use for emphasis.
268
+ def emphasis(tag)
269
+ @added[tag] = true
270
+ "<#{tag}>#{fragment(1, 4)}</#{tag}>"
271
+ end
272
+
273
+ # Generates a random link
274
+ # @return [String] The generated link.
275
+ def link
276
+ @added[:link] = true
277
+ path = "/#{path_string(4, 8)}/#{path_string(4, 8)}"
278
+ title = fragment(4, 8).cap_first
279
+ "<a%%href=\"https://example.com#{path}\"%%title=\"#{title.preserve_spaces}\">#{fragment(1,
280
+ 8).cap_first}</a>"
281
+ end
282
+
283
+ # Injects a block of text into the output.
284
+ # @param [Integer] count The number of blocks to inject.
285
+ # @param [Proc] block The block whose result to inject.
286
+ def inject_block(count, block)
287
+ grafs = @output.split("\n\n")
288
+
289
+ len = if grafs.length < 2
290
+ 1
291
+ else
292
+ Random.rand(1..grafs.length / 2).to_i
293
+ end
294
+
295
+ @output = "#{grafs.slice!(0, len).join("\n\n")}\n\n"
296
+
297
+ # count = { short: 4, medium: 8, long: 10, very_long: 12 }[@options[:length]]
298
+ @output += block.call
299
+ added = 1
300
+ while grafs.any?
301
+ len = Random.rand(1..grafs.length)
302
+
303
+ @output += "#{grafs.slice!(0, len).join("\n\n")}\n\n"
304
+ if added < count
305
+ @output += block.call
306
+ added += 1
307
+ end
308
+ end
309
+
310
+ compress_newlines
311
+ end
312
+
313
+ # Injects inline text into the output.
314
+ # @param [String] text The text to inject into.
315
+ # @param [Proc] block The block whose result to inject.
316
+ # @param [Integer] max The maximum number of times to inject the block.
317
+ def inject_inline(text, block, max = nil)
318
+ max ||= { short: 1, medium: 2, long: 3, very_long: 4 }[@options[:length]]
319
+
320
+ words = text.split(' ')
321
+ out = []
322
+ count = 0
323
+ while words.any?
324
+ if count >= max
325
+ out << words.slice!(0..-1)
326
+ break
327
+ else
328
+ el = block.call
329
+ out << words.slice!(0..Random.rand(1..words.length))
330
+ out << el if words.any?
331
+ count += 1
332
+ end
333
+ end
334
+ out.join(' ').term
335
+ end
336
+
337
+ # Generates a paragraph of Lorem Ipsum text.
338
+ # The `words` method generates a specified number of words.
339
+ # @param [Integer] count The number of paragraphs to generate.
340
+ # @param [Boolean] em Whether to include emphasis tags.
341
+ # @param [Boolean] strong Whether to include strong tags.
342
+ # @param [Boolean] links Whether to include links.
343
+ # @param [Boolean] code Whether to include code tags.
344
+ # @param [Boolean] mark Whether to include mark tags.
345
+ # @param [Array] force An array of tags to force.
346
+ # @return [String] The generated paragraph.
347
+ def paragraph(count, em: false, strong: false, links: false, code: false, mark: false, force: [])
348
+ output = ''
349
+ s = { short: 2, medium: 4, long: 6, very_long: 8 }[@options[:length]]
350
+ count.times do
351
+ p = @generator.sentences(s).join(' ')
352
+
353
+ should_em = force.include?(:em) || (em && roll(1))
354
+ should_strong = force.include?(:strong) || (strong && roll(1))
355
+ should_code = force.include?(:code) || (code && roll(6))
356
+ should_link = force.include?(:links) || (links && roll(4))
357
+ should_mark = force.include?(:mark) || (mark && roll(8))
358
+ p = inject_inline(p, -> { link }) if should_link
359
+ p = inject_inline(p, -> { emphasis('em') }) if should_em
360
+ p = inject_inline(p, -> { emphasis('strong') }) if should_strong
361
+ p = inject_inline(p, -> { emphasis('mark') }, 1) if should_mark
362
+ p = inject_inline(p, -> { code_span }, 1) if should_code
363
+
364
+ output += "<p>#{p.restore_spaces.cap_first.term('.')}</p>\n\n"
365
+ end
366
+ output
367
+ end
368
+
369
+ # Find block level tags and add newlines around them.
370
+ def ensure_block_newlines
371
+ # Adds newlines around block level tags.
372
+ @output.gsub!(/(<(h\d|div|blockquote|dl|ul|ol)>)/, "\n\n\\1")
373
+ @output.gsub!(%r{(</(h\d|div|blockquote|dl|ul|ol)>)\s*}, "\\1\n\n")
374
+ end
375
+
376
+ # Compresses newlines in the output.
377
+ # This method removes extra newlines from the output.
378
+ def compress_newlines
379
+ @output.compress_newlines!
380
+ end
381
+
382
+ # Generates a random fragment of text.
383
+ # The `words` method generates a specified number of words.
384
+ # @param [Integer] min The minimum number of words to generate.
385
+ # @param [Integer] max The maximum number of words to generate.
386
+ # @return [String] The generated fragment.
387
+ def fragment(min, max)
388
+ @generator.words(Random.rand(min..max)).no_term(generator.terminators).downcase_first
389
+ end
390
+
391
+ # Generates a random string of characters.
392
+ # The `characters` method generates a specified number of characters.
393
+ def path_string(min, max)
394
+ @generator.characters(min, max, whole_words: false, whitespace: false).downcase
395
+ end
396
+ end
397
+ end