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.
@@ -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