Class: RedisCopy::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/redis-copy/cli.rb

Constant Summary collapse

REDIS_URI =
(/\A(?:redis:\/\/)?(\w*:\w+@)?([a-z0-9\-.]+)(:[0-9]{1,5})?(\/(?:(?:1[0-5])|[0-9]))?\z/i).freeze
DEFAULTS =
{
  ui:             :command_line,
  verify:         0,
  pipeline:       :true,
  fail_fast:      false,
  prompt:         true,
  trace:          false,
  debug:          false,
  allow_nonempty: false,
  pattern:        '*',
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(argv = ARGV) ⇒ CLI

Returns a new instance of CLI.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/redis-copy/cli.rb', line 21

def initialize(argv = ARGV)
  argv = argv.dup
  options = {}

  OptionParser.new do |opts|
    opts.version = RedisCopy::VERSION
    opts.banner = "#{opts.program_name} v#{opts.version} (with redis-rb #{Redis::VERSION})\n" +
                  "Usage: #{opts.program_name} [options] <source> <destination>"

    indent_desc = proc do |desc|
      desc.split("\n").join("\n#{opts.summary_indent}#{' '*opts.summary_width} ")
    end

    opts.separator "    <source> and <destination> must be redis connection uris"
    opts.separator "    like [redis://][<username>:<password>@]<hostname>[:<port>][/<db>]"
    opts.separator ''
    opts.separator "Specific options:"

    opts.on('-p', '--pattern PATTERN', indent_desc[
      "Only transfer matching keys (default #{DEFAULTS[:pattern]})\n" +
      "See http://redis.io/commands/keys for more info."
    ]) do |pattern|
      options[:pattern] = pattern
    end

    opts.on('-v', '--[no-]verify [PERCENT]',
      "Verify percentage of transfers -- VERY SLOW (default #{DEFAULTS[:verify]})"
    ) do |verify|
      options[:verify] = case verify
                         when /\A1?[0-9]{2}\z/
                           verify.to_i
                         when false, 'false', 'none'
                           0
                         else
                           100
                         end
    end

    opts.on('-n', '--[no-]allow-nonempty', "Allow non-empty destination (default #{DEFAULTS[:allow_nonempty]})") do |allow_nonempty|
      options[:allow_nonempty] = allow_nonempty
    end

    opts.on('-f', '--[no-]fail-fast', "Abort on first failure (default #{DEFAULTS[:fail_fast]})") do |ff|
      options[:fail_fast] = ff
    end

    opts.on('--[no-]pipeline',
      "Pipeline redis commands where available (default #{DEFAULTS[:pipeline]})"
    ) do |pipeline|
      options[:pipeline] = pipeline
    end

    opts.on('-r', '--require FILENAME', indent_desc.(
      "Require a script; useful for loading third-party\n" +
      "implementations of key-emitter or copy strategies.\n" +
      "Relative paths *must* begin with `../' or `./'.")
    ) do |script|
      begin
        script = File.expand_path(script) if script[/\A..?\//]
        require script
      rescue LoadError => e
        $stderr.puts e.message
        exit 1
      end
    end

    opts.on('-d', '--[no-]debug', "Write debug output (default #{DEFAULTS[:debug]})") do |debug|
      options[:debug] = debug
    end

    opts.on('-t', '--[no-]trace', "Enable backtrace on failure (default #{DEFAULTS[:trace]})") do |trace|
      options[:trace] = trace
    end

    opts.on('--[no-]prompt', "Prompt for confirmation (default #{DEFAULTS[:prompt]})") do |prompt|
      options[:prompt] = prompt
    end

    opts.on('--[no-]dry-run', 'Output configuration and exit') do |d|
      options[:dry_run] = true
    end

    begin
      opts.parse!(argv)
      unless argv.size == 2
        opts.abort "Source and Destination must be specified\n\n" +
                          opts.help
      end
      @source = argv.shift
      @destination = argv.shift

      opts.abort "source is not valid URI" unless @source =~ REDIS_URI
      opts.abort "destination is not valid URI" unless @destination =~ REDIS_URI
    rescue OptionParser::ParseError => error
      $stderr.puts error
      exit 1
    end
  end

  @config = DEFAULTS.merge(options)
end

Instance Method Details

#inspectObject



133
134
135
136
137
138
# File 'lib/redis-copy/cli.rb', line 133

def inspect
  "<#{self.class}\n" +
  "  source: #{@source}\n" +
  "  destination: #{@destination}\n" +
  "  #{@config.map{|k,v| [k,v.inspect].join(': ')}.join("\n  ")}\n/>"
end

#run!Object



123
124
125
126
127
128
129
130
131
# File 'lib/redis-copy/cli.rb', line 123

def run!
  (puts self.inspect; exit 1) if @config.delete(:dry_run)

  RedisCopy::copy(@source, @destination, @config)
rescue => exception
  $stderr.puts exception.message
  $stderr.puts exception.backtrace if @config[:trace]
  exit 1
end