hipku 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Hipku
2
+ VERSION = '1.1.0'
3
+ end
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
@@ -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.0.0
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-15 00:00:00.000000000 Z
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. A port of http://gabrielmartin.net/projects/hipku/
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