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