bin/oak in oak-0.0.3 vs bin/oak in oak-0.4.1

- old
+ new

@@ -1,6 +1,245 @@ #!/usr/bin/env ruby +# +# oak.rb: cli driver for encoding strings in the OAK format. +# +# author: jhw@prosperworks.com +# incept: 2016-03-05 +# -$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib') -require 'oak' +require_relative '../lib/oak.rb' +require 'optimist' -Oak.start +OLD_ARGV = ARGV.dup # ARGV is consumed by Optimist but we use later. +OPTS = Optimist.options do + banner "#{$0} cli driver for OAK" + banner <<-OPTIMIST_EXAMPLES +Examples: + $ echo hello | bin/oak.rb + oak_3CNB_1944283675_15_RjFTVTVfaGVsbG8_ok + $ (echo hello ; echo world) | bin/oak.rb + oak_3CNB_1944283675_15_RjFTVTVfaGVsbG8_ok + oak_3CNB_2139413982_15_RjFTVTVfd29ybGQ_ok + $ (echo hello ; echo world) | bin/oak.rb --compression zlib --force + oak_3CZB_1944283675_26_eJxzMwwONY3PSM3JyQcAFF4DyA_ok + oak_3CZB_2139413982_26_eJxzMwwONY0vzy_KSQEAFNgD3A_ok + $ (echo hello ; echo world) | bin/oak.rb --format none + oak_3CNN_1944283675_11_F1SU5_hello_ok + oak_3CNN_2139413982_11_F1SU5_world_ok + $ (echo hello ; echo world) | bin/oak.rb | bin/oak.rb --mode decode-lines + hello + world +OPTIMIST_EXAMPLES + banner "Options:" + version "#{$0} #{OAK::VERSION}" + opt :redundancy, 'redundancy', :default => 'crc32' + opt :format, 'format', :default => 'base64' + opt :compression, 'compression', :default => 'none' + opt :force, 'compress even if bigger', :default => false + opt :mode, 'mode', :default => 'encode-lines' + opt :key_chain, 'key chain env name', :type => :string + opt :key, 'encrypt key name', :type => :string + opt :key_check, 'check available keys', :default => false + opt :key_generate, 'generate new key', :default => false + opt :force_oak_4, 'force OAK_4 even unencrypted', :default => false + opt :eigen, 'calc eigenratio', :type => :int + opt :self_test, 'self-test only', :default => false + opt :help, 'show this help' +end +Optimist::die :eigen, "must be non-negative" if OPTS[:eigen] && OPTS[:eigen] < 0 + +oak_opts = {} +oak_opts[:redundancy] = OPTS[:redundancy] +oak_opts[:compression] = OPTS[:compression] +oak_opts[:force] = OPTS[:force] +oak_opts[:format] = OPTS[:format] +oak_opts[:key_chain] = OAK.parse_env_chain(ENV,OPTS[:key_chain]) +oak_opts[:key] = OPTS[:key] +oak_opts[:force_oak_4] = OPTS[:force_oak_4] + +if !OAK::REDUNDANCY_2_CODE.keys.include?(oak_opts[:redundancy]) + Optimist::die :redundancy, "bogus #{OPTS[:redundancy]}" +end +if !OAK::COMPRESSION_2_CODE.keys.include?(oak_opts[:compression]) + Optimist::die :compression, "bogus #{OPTS[:compression]}" +end +cool_formats = OAK::FORMAT_2_CODE.keys +if !cool_formats.include?(oak_opts[:format]) + Optimist::die :format, "bogus #{OPTS[:format]} not in #{cool_formats}" +end + +=begin + +doctest: simple transcoding +>> OAK::decode(OAK::encode([1,"2",3.000001])) +=> [1,"2",3.000001] +>> OAK::decode(OAK::encode({foo: "bar"})) +=> {foo: "bar"} +>> OAK::decode(OAK::encode({foo: :bar})) +=> {foo: :bar} +>> OAK::decode(OAK::encode("Hello, World!")) +=> "Hello, World!" +>> OAK::decode(OAK::encode("Hello, World!", format: :none, redundancy: :none)) +=> "Hello, World!" + +doctest: stability of encoding +>> OAK::decode("oak_3NNB_0_30_RjNIMV8xXzJZQTNfZm9vU1UzX2Jhcg_ok") +=> {:foo=>"bar"} +>> OAK::encode(1, format: :base64, redundancy: :none) +=> "oak_3NNB_0_6_RjFJMQ_ok" +>> OAK::encode(1, format: :base64, redundancy: :crc32) +=> "oak_3CNB_3405226796_6_RjFJMQ_ok" +>> OAK::encode(1, format: :none, redundancy: :crc32) +=> "oak_3CNN_3405226796_4_F1I1_ok" +>> hello_utf8 = "Hello, World!".force_encoding('UTF-8') +=> "Hello, World!" +>> OAK::encode(hello_utf8, format: :base64, redundancy: :none) +=> "oak_3NNB_0_27_RjFTVTEzX0hlbGxvLCBXb3JsZCE_ok" +>> OAK::encode(hello_utf8, format: :none, redundancy: :crc32) +=> "oak_3CNN_2351984628_20_F1SU13_Hello, World!_ok" + +Note above I used force_encoding('UTF-8') after discovering that with +Ruby 2.1.6 on Mac I get Encoding.default_encoding is UTF-8, but with +Ruby 2.1.6 on Linux I get Encoding.default_encoding is US-ASCII! + +=end + +if __FILE__ == $0 + if OPTS[:self_test] + require 'rubydoctest' + exit RubyDocTest::Runner.new(File.read(__FILE__), __FILE__).run ? 0 : 1 + end + if OPTS[:key_check] + if !OPTS[:key_chain] + puts "no --key-chain specified" + else + keys = oak_opts[:key_chain].keys.keys + if 0 == keys.size + puts "#{OPTS[:key_chain]}: no keys found" + else + puts "#{OPTS[:key_chain]}: found keys: #{keys.join(' ')}" + end + end + end + if OPTS[:key_generate] + STDOUT.puts OAK.encode(OAK.random_key) + exit 0 + end + if !$stdin.tty? + if OPTS[:eigen] + prev = STDIN.read + puts "input: %d" % prev.size + OPTS[:eigen].times do |i| + oak = OAK.encode(prev,oak_opts) + psize = prev.size + wsize = oak.size + ratio = 1.0 * wsize / psize + puts " iter %3d: %4d => %4d ratio %.2f" % [i,psize,wsize,ratio] + prev = oak + end + exit 0 + end + unhappiness = 0 + case OPTS[:mode] + when 'cat' + ARGF.each_line.map(&:strip).each do |line| + puts line + end + when 'encode-lines' + ARGF.each_line.map(&:strip).each do |line| + puts OAK.encode(line,oak_opts) + end + when 'decode-lines' + ARGF.each_line.map(&:strip).each do |line| + puts OAK.decode(line,oak_opts) + end + when 'encode-file' + puts OAK.encode(STDIN.read,oak_opts) + when 'decode-file' + STDOUT.write OAK.decode(STDIN.read.strip,oak_opts) + when 'recode-file' + puts OAK.encode(OAK.decode(STDIN.read,oak_opts),oak_opts) + when 'crazy' + # + # --mode crazy prints out a sample of OAK strings for various + # challenging cases. + # + cycle_a = ['cycle_a','TBD'] + cycle_b = ['cycle_b',cycle_a] + cycle_a[1] = cycle_b + dag_c = ['dag_c'] + dag_b = ['dag_b',dag_c] + dag_a = ['dag_a',dag_b,dag_c] + [ + 'hello', + ['hello'] + ['hello',:hello] * 2, + {1=>'a','b'=>2,[]=>3,''=>4,{}=>5,nil=>6}, + ['x','x','x','x','x','x','x','x','x','x','x','x','x'], + ['x'] * 13, + cycle_a, + dag_a, + [1,-123,0.12,-0.123,Float::NAN,-Float::INFINITY,3.14159265358979], + ].each do |obj| + oak = OAK.encode( + obj, + redundancy: :crc32, + format: :none, + compression: :none, + ) + puts "" + puts "obj: #{obj}" + puts " oak: #{oak}" + begin + dec = OAK.decode(oak,oak_opts) + if dec != obj + if !dec.is_a?(Float) && !enc.is_a?(Float) && !dec.nan? && !enc.nan? + unhappiness += 1 + puts " BAD: #{dec}" + end + end + rescue OAK::CantTouchThisStringError => ex + puts " BAD: #{ex.message}: #{ex.backtrace_locations[0]}" + unhappiness += 1 + end + end + when 'tests' + [ + [1,2,3], + {:foo=>'foo','foo'=>['x']*10}, + -1, + Float::NAN, + nil, + ].each do |obj| + puts " #{obj} => [" + key_chain = OAK::KeyChain.new( + { 'l0ng3r' => OAK::Key.new('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') } + ) + [ + {redundancy: :none, format: :none, compression: :none }, + {redundancy: :none, format: :base64,compression: :lz4, force: true}, + {redundancy: :crc32,format: :base64,compression: :zlib, force: true}, + {redundancy: :crc32,format: :base64,compression: :bzip2,force: true}, + {redundancy: :sha1, format: :base64,compression: :lzma, force: true}, + {key_chain: key_chain,force_oak_4: true,format: :none, }, + {key_chain: key_chain,force_oak_4: true, }, + {key_chain: key_chain,key: 'l0ng3r', }, + ].each do |opts| + oak = OAK.encode(obj,opts) + puts " '#{oak}'," + dec = OAK.decode(oak,opts) + if dec != obj + if !dec.is_a?(Float) && !enc.is_a?(Float) && !dec.nan? && !enc.nan? + unhappiness += 1 + end + end + end + puts " ]," + end + else + Optimist::die :mode, "bogus mode #{OPTS[:mode]}" + end + if unhappiness > 0 + puts "unhappiness: #{unhappiness}" + end + exit unhappiness + end +end