hipku 1.0.0 → 1.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/.gitignore +1 -0
- data/Gemfile +4 -0
- data/README.md +23 -0
- data/hipku-1.0.0.gem +0 -0
- data/hipku.gemspec +22 -0
- data/lib/hipku/dictionary.rb +932 -0
- data/lib/hipku/version.rb +3 -0
- data/lib/hipku.rb +351 -0
- data/spec/hipku_spec.rb +50 -0
- metadata +16 -5
data/lib/hipku.rb
ADDED
@@ -0,0 +1,351 @@
|
|
1
|
+
require 'hipku/dictionary'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
module Hipku
|
5
|
+
IPV4_OCTET_COUNT = 4
|
6
|
+
IPV6_OCTET_COUNT = 8
|
7
|
+
IPV6_PADDING_OCTECT = '0'.freeze
|
8
|
+
IPV4_DIVISOR = 16
|
9
|
+
IPV6_DIVISOR = 256
|
10
|
+
DIVISORS = {
|
11
|
+
ipv4: IPV4_DIVISOR,
|
12
|
+
ipv6: IPV6_DIVISOR,
|
13
|
+
}.freeze
|
14
|
+
PLACEHOLDER_OCTET = 'OCTET'
|
15
|
+
|
16
|
+
NEW_LINE = "\n".freeze
|
17
|
+
PERIOD = '.'.freeze
|
18
|
+
SPACE = ' '.freeze
|
19
|
+
|
20
|
+
FILLER_WORDS = %w[in the and].freeze
|
21
|
+
|
22
|
+
NON_WORDS = [NEW_LINE, PERIOD, SPACE].freeze
|
23
|
+
|
24
|
+
IPV4_HAIKU_STRUCTURE = [
|
25
|
+
Dictionary::ANIMAL_ADJECTIVES,
|
26
|
+
Dictionary::ANIMAL_COLORS,
|
27
|
+
Dictionary::ANIMAL_NOUNS,
|
28
|
+
Dictionary::ANIMAL_VERBS,
|
29
|
+
Dictionary::NATURE_ADJECTIVES,
|
30
|
+
Dictionary::NATURE_NOUNS,
|
31
|
+
Dictionary::PLANT_NOUNS,
|
32
|
+
Dictionary::PLANT_VERBS,
|
33
|
+
].freeze
|
34
|
+
|
35
|
+
IPV6_HAIKU_STRUCTURE = [
|
36
|
+
Dictionary::ADJECTIVES,
|
37
|
+
Dictionary::NOUNS,
|
38
|
+
Dictionary::ADJECTIVES,
|
39
|
+
Dictionary::NOUNS,
|
40
|
+
Dictionary::VERBS,
|
41
|
+
Dictionary::ADJECTIVES,
|
42
|
+
Dictionary::ADJECTIVES,
|
43
|
+
Dictionary::ADJECTIVES,
|
44
|
+
Dictionary::ADJECTIVES,
|
45
|
+
Dictionary::ADJECTIVES,
|
46
|
+
Dictionary::NOUNS,
|
47
|
+
Dictionary::ADJECTIVES,
|
48
|
+
Dictionary::NOUNS,
|
49
|
+
Dictionary::VERBS,
|
50
|
+
Dictionary::ADJECTIVES,
|
51
|
+
Dictionary::NOUNS,
|
52
|
+
].freeze
|
53
|
+
|
54
|
+
HAIKU_STRUCTURE = {
|
55
|
+
ipv4: IPV4_HAIKU_STRUCTURE,
|
56
|
+
ipv6: IPV6_HAIKU_STRUCTURE,
|
57
|
+
}.freeze
|
58
|
+
|
59
|
+
IPV4_SCHEMA = [
|
60
|
+
'The',
|
61
|
+
PLACEHOLDER_OCTET,
|
62
|
+
PLACEHOLDER_OCTET,
|
63
|
+
PLACEHOLDER_OCTET,
|
64
|
+
NEW_LINE,
|
65
|
+
PLACEHOLDER_OCTET,
|
66
|
+
'in the',
|
67
|
+
PLACEHOLDER_OCTET,
|
68
|
+
PLACEHOLDER_OCTET,
|
69
|
+
PERIOD,
|
70
|
+
NEW_LINE,
|
71
|
+
PLACEHOLDER_OCTET,
|
72
|
+
PLACEHOLDER_OCTET,
|
73
|
+
PERIOD,
|
74
|
+
NEW_LINE,
|
75
|
+
].freeze
|
76
|
+
|
77
|
+
IPV6_SCHEMA = [
|
78
|
+
PLACEHOLDER_OCTET,
|
79
|
+
PLACEHOLDER_OCTET,
|
80
|
+
'and',
|
81
|
+
PLACEHOLDER_OCTET,
|
82
|
+
PLACEHOLDER_OCTET,
|
83
|
+
NEW_LINE,
|
84
|
+
PLACEHOLDER_OCTET,
|
85
|
+
PLACEHOLDER_OCTET,
|
86
|
+
PLACEHOLDER_OCTET,
|
87
|
+
PLACEHOLDER_OCTET,
|
88
|
+
PLACEHOLDER_OCTET,
|
89
|
+
PLACEHOLDER_OCTET,
|
90
|
+
PLACEHOLDER_OCTET,
|
91
|
+
PERIOD,
|
92
|
+
NEW_LINE,
|
93
|
+
PLACEHOLDER_OCTET,
|
94
|
+
PLACEHOLDER_OCTET,
|
95
|
+
PLACEHOLDER_OCTET,
|
96
|
+
PLACEHOLDER_OCTET,
|
97
|
+
PLACEHOLDER_OCTET,
|
98
|
+
PERIOD,
|
99
|
+
NEW_LINE,
|
100
|
+
].freeze
|
101
|
+
|
102
|
+
SCHEMA = {
|
103
|
+
ipv4: IPV4_SCHEMA,
|
104
|
+
ipv6: IPV6_SCHEMA,
|
105
|
+
}.freeze
|
106
|
+
|
107
|
+
class << self
|
108
|
+
|
109
|
+
def encode(ip)
|
110
|
+
@version = ip_version(ip)
|
111
|
+
ip = ip.gsub(/[[:space:]]/, '')
|
112
|
+
if @version == :ipv6
|
113
|
+
octets = split_ipv6(ip)
|
114
|
+
# convert from hexidecimal to decimal octets
|
115
|
+
octets.map! {|octet| base_convert(octet, 16, 10)}
|
116
|
+
elsif @version == :ipv4
|
117
|
+
octets = split_ipv4(ip)
|
118
|
+
end
|
119
|
+
|
120
|
+
factored_octets = factor_octets(octets, DIVISORS[@version])
|
121
|
+
|
122
|
+
words = encode_words(factored_octets, HAIKU_STRUCTURE[@version])
|
123
|
+
|
124
|
+
write_haiku(words)
|
125
|
+
end
|
126
|
+
|
127
|
+
def decode(haiku)
|
128
|
+
words = to_word_array(haiku)
|
129
|
+
@version = haiku_version(words)
|
130
|
+
factors = to_factors(words)
|
131
|
+
|
132
|
+
if @version == :ipv6
|
133
|
+
octets = factors
|
134
|
+
# convert from decimal to hexidecimal octets
|
135
|
+
octets.map! {|octet| base_convert(octet, 10, 16)}
|
136
|
+
octets.map! {|octet| octet.size < 2 ? octet.prepend('0') : octet}
|
137
|
+
ip = join_ipv6(factors)
|
138
|
+
elsif @version == :ipv4
|
139
|
+
octets = to_octets(factors)
|
140
|
+
ip = join_ipv4(octets)
|
141
|
+
end
|
142
|
+
|
143
|
+
ip
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def ip_version(ip)
|
149
|
+
case ip
|
150
|
+
when /:/
|
151
|
+
:ipv6
|
152
|
+
when /\./
|
153
|
+
:ipv4
|
154
|
+
else
|
155
|
+
raise "Formatting error in IP Address input (#{ip}). Contains neither ':' or '.'"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def haiku_version(haiku)
|
160
|
+
haiku.each do |word|
|
161
|
+
if IPV4_HAIKU_STRUCTURE[0].include?(word)
|
162
|
+
return :ipv4
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
:ipv6
|
167
|
+
end
|
168
|
+
|
169
|
+
def join_ipv4(octets)
|
170
|
+
octets.join('.')
|
171
|
+
end
|
172
|
+
|
173
|
+
def join_ipv6(octets)
|
174
|
+
joined_octets = []
|
175
|
+
octets_to_join = []
|
176
|
+
octets.each_with_index do |octet, i|
|
177
|
+
next if joined_octets.include?(i)
|
178
|
+
octets_to_join << [octet, octets[i + 1]].join
|
179
|
+
joined_octets << (i + 1)
|
180
|
+
end
|
181
|
+
octets_to_join.join(':')
|
182
|
+
end
|
183
|
+
|
184
|
+
def split_ipv4(ip)
|
185
|
+
octets = ip.split('.')
|
186
|
+
if octets.size < IPV4_OCTET_COUNT
|
187
|
+
raise "Formatting error in IP Address input (#{ip}). IPv4 address has fewer than #{IPV4_OCTET_COUNT} octets."
|
188
|
+
end
|
189
|
+
|
190
|
+
octets
|
191
|
+
end
|
192
|
+
|
193
|
+
def split_ipv6(ip)
|
194
|
+
octets = ip.split(':')
|
195
|
+
|
196
|
+
if octets.size < IPV6_OCTET_COUNT
|
197
|
+
pad_octets(octets)
|
198
|
+
end
|
199
|
+
|
200
|
+
octets
|
201
|
+
end
|
202
|
+
|
203
|
+
def pad_octets(octets)
|
204
|
+
if octets.size < IPV6_OCTET_COUNT
|
205
|
+
# Double :: at the start, fronr padding
|
206
|
+
if octets[0] == '' && octets[1] == ''
|
207
|
+
while octets.size < IPV6_OCTET_COUNT
|
208
|
+
octets.unshift('')
|
209
|
+
end
|
210
|
+
else
|
211
|
+
while octets.size < IPV6_OCTET_COUNT
|
212
|
+
octets.push('')
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
if octets.first == ''
|
218
|
+
octets[0] = IPV6_PADDING_OCTECT
|
219
|
+
end
|
220
|
+
|
221
|
+
if octets.last == ''
|
222
|
+
octets[-1] = IPV6_PADDING_OCTECT
|
223
|
+
end
|
224
|
+
|
225
|
+
octets.each_with_index do |octet, i|
|
226
|
+
if octet == ''
|
227
|
+
octets[i] = IPV6_PADDING_OCTECT
|
228
|
+
end
|
229
|
+
end
|
230
|
+
octets
|
231
|
+
end
|
232
|
+
|
233
|
+
def base_convert(octet, from, to)
|
234
|
+
octet.to_s.to_i(from).to_s(to)
|
235
|
+
end
|
236
|
+
|
237
|
+
def factor_octets(octets, divisor)
|
238
|
+
octets.map do |octet|
|
239
|
+
octet = octet.to_i
|
240
|
+
divisor = divisor.to_i
|
241
|
+
factor1 = octet / divisor
|
242
|
+
factor2 = octet % divisor
|
243
|
+
[factor1, factor2]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def encode_words(factored_octets, structure)
|
248
|
+
words = []
|
249
|
+
|
250
|
+
factored_octets.flatten.each_with_index do |octet, i|
|
251
|
+
dictionary = structure[i]
|
252
|
+
words[i] = dictionary[octet.to_i]
|
253
|
+
end
|
254
|
+
|
255
|
+
words
|
256
|
+
end
|
257
|
+
|
258
|
+
def write_haiku(word_array)
|
259
|
+
placeholder_octet = 'OCTET'
|
260
|
+
schema = SCHEMA[@version].dup
|
261
|
+
spaces_added = 0
|
262
|
+
schema.dup.each_with_index do |schema_part, i|
|
263
|
+
if add_space?(schema_part) && schema[i + spaces_added - 1] != NEW_LINE
|
264
|
+
schema.insert(i + spaces_added, SPACE);
|
265
|
+
spaces_added += 1
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
placeholder_octet_counter = 0
|
270
|
+
haiku_array = schema.map do |schema_part|
|
271
|
+
if schema_part == PLACEHOLDER_OCTET
|
272
|
+
word_array[placeholder_octet_counter].tap { placeholder_octet_counter +=1 }
|
273
|
+
else
|
274
|
+
schema_part
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
capitalize_words(haiku_array).join
|
279
|
+
end
|
280
|
+
|
281
|
+
def add_space?(candidate)
|
282
|
+
!NON_WORDS.include?(candidate)
|
283
|
+
end
|
284
|
+
|
285
|
+
def to_word_array(haiku)
|
286
|
+
haiku = haiku.downcase
|
287
|
+
haiku.gsub!(/\n/, ' ')
|
288
|
+
haiku.gsub!(/[^a-z\ -]/, '');
|
289
|
+
word_array = haiku.split(' ');
|
290
|
+
word_array.reject!{|word| word == '' || FILLER_WORDS.include?(word)}
|
291
|
+
word_array
|
292
|
+
end
|
293
|
+
|
294
|
+
def to_factors(words)
|
295
|
+
factors = []
|
296
|
+
skipped_indexes = []
|
297
|
+
words_passed = 0
|
298
|
+
|
299
|
+
words.each_with_index do |word, i|
|
300
|
+
next if skipped_indexes.include?(i)
|
301
|
+
|
302
|
+
dictionary = HAIKU_STRUCTURE[@version][words_passed]
|
303
|
+
|
304
|
+
dictionary.each do |dictionary_word|
|
305
|
+
extra_words = dictionary_word.split(' ').count
|
306
|
+
word_to_check = words.slice(i, extra_words).join(' ')
|
307
|
+
if dictionary.index(word_to_check)
|
308
|
+
factors << dictionary.index(word_to_check)
|
309
|
+
words_passed += 1
|
310
|
+
|
311
|
+
if extra_words > 1
|
312
|
+
while extra_words > 1 do
|
313
|
+
skipped_indexes << i + extra_words -=1
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
break
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
factors
|
323
|
+
end
|
324
|
+
|
325
|
+
def to_octets(factors)
|
326
|
+
octets = []
|
327
|
+
ignored_factors = []
|
328
|
+
|
329
|
+
factors.each_with_index do |factor, i|
|
330
|
+
next if ignored_factors.include?(i)
|
331
|
+
octets << (DIVISORS[@version] * factor) + factors[(i+1)]
|
332
|
+
ignored_factors << (i + 1)
|
333
|
+
end
|
334
|
+
|
335
|
+
octets
|
336
|
+
end
|
337
|
+
|
338
|
+
def capitalize_words(haiku_array)
|
339
|
+
|
340
|
+
haiku_array[0] = haiku_array.first.capitalize
|
341
|
+
|
342
|
+
haiku_array.each_with_index do |haiku_part, i|
|
343
|
+
if haiku_part == PERIOD
|
344
|
+
if haiku_array[i + 2] && !NON_WORDS.include?(haiku_array[i + 2])
|
345
|
+
haiku_array[i + 2] = haiku_array[i + 2].capitalize
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
data/spec/hipku_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'hipku'
|
2
|
+
describe Hipku do
|
3
|
+
context '.encode' do
|
4
|
+
it 'correctly encodes an IPv4 address' do
|
5
|
+
expect(described_class.encode('127.0.0.1')).to eq("The hungry white ape\naches in the ancient canyon.\nAutumn colors crunch.\n")
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'correctly encodes a fully qualified IPv6 address' do
|
9
|
+
expect(described_class.encode('da13:13a8:4776:d331:b07d:c53f:6714:d193')).to eq("Strong boats and brash quail\ndent lush steep dead shaped mint skunks.\nFar goats boot stary moths.\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'correctly encodes an IPv6 address with padding' do
|
13
|
+
expect(described_class.encode('::1')).to eq("Ace ants and ace ants\naid ace ace ace ace ace ants.\nAce ants aid ace apes.\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'correctly encodes an IPv6 address with padding' do
|
17
|
+
expect(described_class.encode('2c8f:27aa:61fd:56ec')).to eq("Cursed mobs and crazed queens\nfeel wrong gruff tired ace ace ants.\nAce ants aid ace ants.\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context '.decode' do
|
22
|
+
it 'correctly decodes an IPv4 address' do
|
23
|
+
expect(described_class.decode("The hungry white ape\naches in the ancient canyon.\nAutumn colors crunch.\n")).to eq('127.0.0.1')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'correctly decodes an IPv4 address with weird spacing' do
|
27
|
+
expect(described_class.decode("The hungry white ape\n aches in the ancient canyon. \n Autumn colors crunch.\n")).to eq('127.0.0.1')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'correctly decodes an IPv6 address' do
|
31
|
+
expect(described_class.decode("Strong boats and brash quail\ndent lush steep dead shaped mint skunks.\nFar goats boot stary moths.\n")).to eq('da13:13a8:4776:d331:b07d:c53f:6714:d193')
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'correctly decodes an IPv6 address with words in two dictionaries' do
|
35
|
+
expect(described_class.decode("Moist wraiths and mad guards\ndrown grey nude chilled black masked tools.\nShaped frogs blame drab ruffs.")).to eq('7efd:776c:5754:8420:0879:e9b0:5e0c:37b4')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'correctly decodes an IPv6 address with weird spacing' do
|
39
|
+
expect(described_class.decode("Strong boats and brash quail \ndent lush steep dead shaped mint skunks.\n Far goats boot stary moths.\n")).to eq('da13:13a8:4776:d331:b07d:c53f:6714:d193')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'correctly decodes an IPv6 address with front padding' do
|
43
|
+
expect(described_class.decode("Ace ants and ace ants\naid ace ace ace ace ace ants.\nAce ants aid ace apes.\n")).to eq('0000:0000:0000:0000:0000:0000:0000:0001')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'correctly decodes an IPv6 address with back padding' do
|
47
|
+
expect(described_class.decode("Cursed mobs and crazed queens\nfeel wrong gruff tired ace ace ants.\nAce ants aid ace ants.\n")).to eq('2c8f:27aa:61fd:56ec:0000:0000:0000:0000')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hipku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Sunderland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -38,12 +38,22 @@ dependencies:
|
|
38
38
|
- - ">"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description: A simple gem to encode IPv4 and IPv6 addresses as haiku.
|
41
|
+
description: A simple gem to encode/decode IPv4 and IPv6 addresses as/from haiku.
|
42
|
+
A port of http://gabrielmartin.net/projects/hipku/
|
42
43
|
email: agentantelope+hipku@gmail.com
|
43
44
|
executables: []
|
44
45
|
extensions: []
|
45
46
|
extra_rdoc_files: []
|
46
|
-
files:
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- README.md
|
51
|
+
- hipku-1.0.0.gem
|
52
|
+
- hipku.gemspec
|
53
|
+
- lib/hipku.rb
|
54
|
+
- lib/hipku/dictionary.rb
|
55
|
+
- lib/hipku/version.rb
|
56
|
+
- spec/hipku_spec.rb
|
47
57
|
homepage: http://rubygems.org/gems/hipku
|
48
58
|
licenses:
|
49
59
|
- MIT
|
@@ -68,4 +78,5 @@ rubygems_version: 2.4.3
|
|
68
78
|
signing_key:
|
69
79
|
specification_version: 4
|
70
80
|
summary: Encode any IP address as a haiku
|
71
|
-
test_files:
|
81
|
+
test_files:
|
82
|
+
- spec/hipku_spec.rb
|