Class: R2::CLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/r2/cli.rb

Overview

Command line interface of the project.

Acts as a minimal orchestrator: interprets the user's input, delegates the execution to the responsible components and presents the results.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(configuration: nil, storage: nil, logger: nil) ⇒ CLI

Initializes the CLI with its dependencies.

Dependencies are loaded lazily: they are only created when the first command actually uses them. This allows help and command listing to work without a configured environment.

Parameters:

  • configuration (Configuration) (defaults to: nil) —

    application configuration

  • storage (Storage) (defaults to: nil) —

    storage used in object operations

  • logger (#debug, nil) (defaults to: nil) —

    logger used for diagnostics



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/r2/cli.rb', line 27

def initialize(
    *,
    configuration: nil,
    storage: nil,
    logger: nil
)
    super(*)
    @configuration = configuration
    @storage = storage
    @injected_logger = logger
    @logger = nil
end

Class Method Details

.exit_on_failure? ⇒ Boolean

Ensures that Thor exits with a non-zero status code when an operation fails.

Returns:

  • (Boolean)


42
43
44
# File 'lib/r2/cli.rb', line 42

def self.exit_on_failure?
    true
end

.start(*args) ⇒ Object

Starts the CLI and clearly presents domain errors not handled by the commands.

Errors raised before a command runs (such as missing configuration) are caught here, shown on the error output and terminate the CLI with status code 1, avoiding stack traces.



52
53
54
55
56
57
# File 'lib/r2/cli.rb', line 52

def self.start(*args)
    super
rescue Errors::Error => e
    warn "Error: #{e.message}"
    exit 1
end

Instance Method Details

#delete(file) ⇒ Object

Deletes a file from the configured Cloudflare R2 bucket.

The operation is considered successful when the storage completes the request without errors.

Parameters:

  • file (String) —

    name of the file to delete



152
153
154
155
156
157
158
159
160
161
# File 'lib/r2/cli.rb', line 152

def delete(file)
    logger.debug("Starting delete: #{file.inspect}.")
    confirm_deletion(file)
    storage.delete(key: file)
    puts "Deleted successfully: #{file}"
    logger.debug("Finished delete: #{file.inspect}.")
rescue Errors::Error => e
    warn "Error: #{e.message}"
    exit 1
end

#download(key) ⇒ Object

Downloads a file from the configured Cloudflare R2 bucket.

Parameters:

  • key (String) —

    object key in the bucket



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/r2/cli.rb', line 114

def download(key)
    destination = options[:output] || File.basename(key)
    logger.debug("Starting download: #{key.inspect} to #{destination.inspect}.")
    ensure_destination_writable(destination)
    storage.download(key: key, destination: destination)
    puts "Downloaded successfully: #{destination}"
    logger.debug("Finished download: #{key.inspect}.")
rescue Errors::Error => e
    warn "Error: #{e.message}"
    exit 1
end

#exists(key) ⇒ Object

Checks whether an object exists in the configured Cloudflare R2 bucket.

Parameters:

  • key (String) —

    object key in the bucket



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/r2/cli.rb', line 212

def exists(key)
    logger.debug("Starting existence check: #{key.inspect}.")

    if storage.exists?(key: key)
        puts "Object exists: #{key}"
        logger.debug("Finished existence check: #{key.inspect} exists.")
    else
        puts "Object not found: #{key}"
        logger.debug("Finished existence check: #{key.inspect} does not exist.")
        exit 1
    end
rescue Errors::Error => e
    warn "Error: #{e.message}"
    exit 1
end

#list ⇒ Object

Lists the files stored in the configured Cloudflare R2 bucket.



183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/r2/cli.rb', line 183

def list
    logger.debug("Starting list: prefix=#{options[:prefix].inspect}.")
    files = storage.list(prefix: options[:prefix])

    files.each do |file|
        puts file
    end
    logger.debug("Finished list: #{files.size} object(s).")
rescue Errors::Error => e
    warn "Error: #{e.message}"
    exit 1
end

#upload(file) ⇒ Object

Uploads a file to the configured Cloudflare R2 bucket.

Parameters:

  • file (String) —

    path of the file to upload



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/r2/cli.rb', line 79

def upload(file)
    key = options[:key] || File.basename(file)
    logger.debug("Starting upload: #{file.inspect} as #{key.inspect}.")
    body = open_file(file)
    storage.upload(key: key, body: body)
    puts "Uploaded successfully: #{key}"
    logger.debug("Finished upload: #{key.inspect}.")
rescue Errors::Error => e
    warn "Error: #{e.message}"
    exit 1
ensure
    body&.close
end