Class: Cabriolet::Modifier

Inherits:
Object
  • Object
show all
Defined in:
lib/cabriolet/modifier.rb

Overview

Archive modification functionality (add, update, remove files)

Defined Under Namespace

Classes: FileObject

Instance Method Summary collapse

Constructor Details

#initialize(path) ⇒ Modifier

Returns a new instance of Modifier.



6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/cabriolet/modifier.rb', line 6

def initialize(path)
  @path = path
  @format = FormatDetector.detect(path)
  @modifications = []
  @parser_class = FormatDetector.format_to_parser(@format)

  raise UnsupportedFormatError, "Unknown format: #{path}" unless @format

  unless @parser_class
    raise UnsupportedFormatError,
          "No parser for format: #{@format}"
  end
end

Instance Method Details

#add_file(name, source: nil, data: nil, **options) ⇒ self

Add a file to the archive

Examples:

modifier = Cabriolet::Modifier.new('archive.cab')
modifier.add_file('new.txt', source: 'path/to/new.txt')
modifier.add_file('data.bin', data: binary_data)
modifier.save

Parameters:

  • name (String)

    File name in archive

  • source (String, nil) (defaults to: nil)

    Source file path (nil for data parameter)

  • data (String, nil) (defaults to: nil)

    File data (if source not provided)

  • options (Hash)

    File metadata options

Returns:

  • (self)


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/cabriolet/modifier.rb', line 33

def add_file(name, source: nil, data: nil, **options)
  if source.nil? && data.nil?
    raise ArgumentError,
          "Must provide either source or data"
  end

  file_data = source ? File.read(source, mode: "rb") : data

  @modifications << {
    action: :add,
    name: name,
    data: file_data,
    options: options,
  }

  self
end

#previewArray<Hash>

Preview modifications without saving

Returns:

  • (Array<Hash>)

    List of planned modifications



154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/cabriolet/modifier.rb', line 154

def preview
  @modifications.map do |mod|
    case mod[:action]
    when :add
      { action: "ADD", file: mod[:name], size: mod[:data].bytesize }
    when :update
      { action: "UPDATE", file: mod[:name], size: mod[:data].bytesize }
    when :remove
      { action: "REMOVE", file: mod[:name] }
    when :rename
      { action: "RENAME", from: mod[:old_name], to: mod[:new_name] }
    end
  end
end

#remove_file(name) ⇒ self

Remove a file from the archive

Examples:

modifier.remove_file('old.txt')

Parameters:

  • name (String)

    File name to remove

Returns:

  • (self)


85
86
87
88
89
90
91
92
# File 'lib/cabriolet/modifier.rb', line 85

def remove_file(name)
  @modifications << {
    action: :remove,
    name: name,
  }

  self
end

#rename_file(old_name, new_name) ⇒ self

Rename a file in the archive

Examples:

modifier.rename_file('old_name.txt', 'new_name.txt')

Parameters:

  • old_name (String)

    Current file name

  • new_name (String)

    New file name

Returns:

  • (self)


102
103
104
105
106
107
108
109
110
# File 'lib/cabriolet/modifier.rb', line 102

def rename_file(old_name, new_name)
  @modifications << {
    action: :rename,
    old_name: old_name,
    new_name: new_name,
  }

  self
end

#save(output: nil) ⇒ ModificationReport

Save modifications to the archive

Examples:

modifier.save  # Update in-place
modifier.save(output: 'modified.cab')  # Save to new file

Parameters:

  • output (String, nil) (defaults to: nil)

    Output path (nil for in-place update)

Returns:



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/cabriolet/modifier.rb', line 120

def save(output: nil)
  output ||= @path

  # Parse original archive
  archive = @parser_class.new.parse(@path)

  # Apply modifications
  modified_files = apply_modifications(archive)

  # Rebuild archive
  rebuild_archive(modified_files, output)

  ModificationReport.new(
    success: true,
    original: @path,
    output: output,
    modifications: @modifications.count,
    added: @modifications.count { |m| m[:action] == :add },
    updated: @modifications.count { |m| m[:action] == :update },
    removed: @modifications.count { |m| m[:action] == :remove },
    renamed: @modifications.count { |m| m[:action] == :rename },
  )
rescue StandardError => e
  ModificationReport.new(
    success: false,
    original: @path,
    output: output,
    error: e.message,
  )
end

#update_file(name, source: nil, data: nil, **options) ⇒ self

Update an existing file in the archive

Examples:

modifier.update_file('config.xml', data: new_xml_data)

Parameters:

  • name (String)

    File name to update

  • source (String, nil) (defaults to: nil)

    Source file path

  • data (String, nil) (defaults to: nil)

    New file data

Returns:

  • (self)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/cabriolet/modifier.rb', line 60

def update_file(name, source: nil, data: nil, **options)
  if source.nil? && data.nil?
    raise ArgumentError,
          "Must provide either source or data"
  end

  file_data = source ? File.read(source, mode: "rb") : data

  @modifications << {
    action: :update,
    name: name,
    data: file_data,
    options: options,
  }

  self
end