gem-patch 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # gem-patch
2
+
3
+ A RubyGems plugin that patches gems.
4
+
5
+ ## Description
6
+
7
+ `gem-patch` is a RubyGems plugin that helps to patch gems without manually opening and rebuilding them. It opens a given .gem file, extracts it, patches it with system `patch` command, clones its spec, updates the file list and builds the patched gem.
8
+
9
+ ## Usage
10
+
11
+ `gem patch [options] name-version.gem PATCH [PATCH ...]`
12
+
13
+ Optionally with `-pNUMBER` or `--strip=NUMBER` option that sets the file name strip count to NUMBER
14
+ (same options as for `patch` command on Linux machines).
15
+
16
+ ## Requirements
17
+
18
+ This version is build for both RubyGems 1.8 and RubyGems 2.0.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = gem-patch
2
+
3
+ A RubyGems plugin that patches gems.
4
+
5
+ == Description
6
+
7
+ `gem-patch` is a RubyGems plugin that helps to patch gems without manually opening and rebuilding them. It opens a given .gem file, extracts it, patches it with system `patch` command, clones its spec, updates the file list and builds the patched gem.
8
+
9
+ == Usage
10
+
11
+ `gem patch [options] name-version.gem PATCH [PATCH ...]`
12
+
13
+ Optionally with `-pNUMBER` or `--strip=NUMBER` option that sets the file name strip count to NUMBER
14
+ (same options as for `patch` command on Linux machines).
15
+
16
+ == Requirements
17
+
18
+ This version is build for RubyGems 2.0.a, a branch for RubyGems 1.8 is also available.
@@ -0,0 +1,54 @@
1
+ require "rubygems/command"
2
+ require "rubygems/patcher"
3
+
4
+ class Gem::Commands::PatchCommand < Gem::Command
5
+ def initialize
6
+ super "patch", "Patches the gem with the given patches and generates patched gem.",
7
+ :output => Dir.pwd, :strip => 0
8
+
9
+ # Same as 'patch -pNUMBER' on Linux machines
10
+ add_option('-pNUMBER', '--strip=NUMBER', 'Set the file name strip count to NUMBER.') do |number, options|
11
+ options[:strip] = number
12
+ end
13
+ end
14
+
15
+ def arguments # :nodoc:
16
+ args = <<-EOF
17
+ GEMFILE path to the gem file to patch
18
+ PATCH [PATCH ...] list of patches to apply
19
+ EOF
20
+ return args.gsub(/^\s+/, '')
21
+ end
22
+
23
+ def description # :nodoc:
24
+ <<-EOF
25
+ `gem-patch` is a RubyGems plugin that helps to patch gems without manually opening and rebuilding them.
26
+ It opens a given .gem file, extracts it, patches it with system "patch" command,
27
+ clones its spec, updates the file list and builds the patched gem.
28
+ EOF
29
+ end
30
+
31
+ def usage # :nodoc:
32
+ "#{program_name} GEMFILE PATCH [PATCH ...]"
33
+ end
34
+
35
+ def execute
36
+ gemfile = options[:args].shift
37
+ patches = options[:args]
38
+
39
+ # No gem
40
+ unless gemfile
41
+ raise Gem::CommandLineError,
42
+ "Please specify a gem file on the command line (e.g. gem patch foo-0.1.0.gem PATCH [PATCH ...])"
43
+ end
44
+
45
+ # No patches
46
+ if patches.empty?
47
+ raise Gem::CommandLineError,
48
+ "Please specify patches to apply (e.g. gem patch foo-0.1.0.gem foo.patch bar.patch ...)"
49
+ end
50
+
51
+ patcher = Gem::Patcher.new(gemfile, options[:output])
52
+ patcher.patch_with(patches, options[:strip])
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ require "rubygems/installer"
2
+ require "rubygems/builder"
3
+
4
+ ##
5
+ # Simulate RubyGems 2.0 behavior to use master branch of gem-patch plugin with RubyGems 1.8
6
+
7
+ module Gem::Package
8
+ def self.new gem
9
+ @gem = gem
10
+ self
11
+ end
12
+
13
+ def self.extract_files dir
14
+ @installer = Gem::Installer.new @gem
15
+ @installer.unpack dir
16
+ @spec = @installer.spec
17
+ end
18
+
19
+ def self.build skip_validation=false
20
+ @builder = Gem::Builder.new @spec
21
+ @builder.build
22
+ end
23
+
24
+ def self.spec=(spec)
25
+ @spec = spec
26
+ end
27
+
28
+ def self.spec
29
+ @spec
30
+ end
31
+ end
@@ -0,0 +1,128 @@
1
+ require "rbconfig"
2
+ require "tmpdir"
3
+ require "rubygems/package"
4
+
5
+ class Gem::Patcher
6
+ include Gem::UserInteraction
7
+
8
+ if Gem::VERSION < '2.0'
9
+ require "rubygems/package-1.8"
10
+ end
11
+
12
+ class PatchCommandMissing < StandardError; end
13
+
14
+ def initialize(gemfile, output_dir)
15
+ @gemfile = gemfile
16
+ @output_dir = output_dir
17
+
18
+ # @target_dir is a temporary directory where the gem files live
19
+ tmpdir = Dir.mktmpdir
20
+ basename = File.basename(gemfile, '.gem')
21
+ @target_dir = File.join(tmpdir, basename)
22
+ end
23
+
24
+ ##
25
+ # Patch the gem, move the new patched gem to the working directory and return the path
26
+
27
+ def patch_with(patches, strip_number)
28
+ check_patch_command_is_installed
29
+ extract_gem
30
+
31
+ # Apply all patches
32
+ patches.each do |patch|
33
+ info 'Applying patch ' + patch
34
+ apply_patch(patch, strip_number)
35
+ end
36
+
37
+ build_patched_gem
38
+
39
+ # Move the newly generated gem to the working directory
40
+ gem_file = IO.read(File.join @target_dir, @package.spec.file_name)
41
+
42
+ File.open(File.join(@output_dir, @package.spec.file_name), 'w') do |f|
43
+ f.write gem_file
44
+ end
45
+
46
+ # Return the path to the patched gem
47
+ File.join @output_dir, "#{@package.spec.file_name}"
48
+ end
49
+
50
+ def apply_patch(patch, strip_number)
51
+ patch_path = File.expand_path(patch)
52
+ info 'Path to the patch to apply: ' + patch_path
53
+
54
+ # Apply the patch by calling 'patch -pNUMBER < patch'
55
+ Dir.chdir @target_dir do
56
+ if system("patch --verbose -p#{strip_number} < #{patch_path}")
57
+ info 'Succesfully patched by ' + patch
58
+ else
59
+ info 'Error: Unable to patch with ' + patch
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def extract_gem
67
+ @package = Gem::Package.new @gemfile
68
+
69
+ # Unpack
70
+ info "Unpacking gem '#{@gemfile}' in " + @target_dir
71
+ @package.extract_files @target_dir
72
+ end
73
+
74
+ def build_patched_gem
75
+ patched_package = Gem::Package.new @package.spec.file_name
76
+ patched_package.spec = @package.spec.clone
77
+ patched_package.spec.files = files_in_gem
78
+
79
+ # Change dir and build the patched gem
80
+ Dir.chdir @target_dir do
81
+ patched_package.build false
82
+ end
83
+ end
84
+
85
+ def info(msg)
86
+ say msg if Gem.configuration.verbose
87
+ end
88
+
89
+ def files_in_gem
90
+ files = []
91
+
92
+ Dir.foreach(@target_dir) do |file|
93
+ if File.directory? File.join @target_dir, file
94
+ files += files_in_dir(file) unless /\./.match(file)
95
+ else
96
+ files << file
97
+ end
98
+ end
99
+
100
+ delete_original_files(files)
101
+ end
102
+
103
+ def files_in_dir(dir)
104
+ files = []
105
+
106
+ Dir.foreach(File.join @target_dir, dir) do |file|
107
+ if File.directory? File.join @target_dir, dir, file
108
+ files += files_in_dir(File.join dir, file) unless /\./.match(file)
109
+ else
110
+ files << File.join(dir, file)
111
+ end
112
+ end
113
+
114
+ files
115
+ end
116
+
117
+ def delete_original_files(files)
118
+ files.each do |file|
119
+ files.delete file if /\.orig/.match(file)
120
+ end
121
+ end
122
+
123
+ def check_patch_command_is_installed
124
+ unless system("patch --version")
125
+ raise PatchCommandMissing, 'Calling `patch` command failed. Do you have it installed?'
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,2 @@
1
+ require "rubygems/command_manager"
2
+ Gem::CommandManager.instance.register_command :patch
data/rakefile.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'rubygems/package_task'
2
+ require 'rake/testtask'
3
+ require 'rdoc/task'
4
+
5
+ gemspec = Gem::Specification.new do |s|
6
+ s.name = "gem-patch"
7
+ s.version = "0.1.1"
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "RubyGems plugin for patching gems."
10
+ s.description = <<-EOF
11
+ `gem-patch` is a RubyGems plugin that helps to patch gems without manually opening and rebuilding them.
12
+ It opens a given .gem file, extracts it, patches it with system `patch` command,
13
+ clones its spec, updates the file list and builds the patched gem.
14
+ EOF
15
+ s.licenses = ["MIT"]
16
+ s.author = "Josef Stribny"
17
+ s.email = "jstribny@redhat.com"
18
+ s.required_ruby_version = ">= 1.8.7"
19
+ s.required_rubygems_version = ">= 1.8.0"
20
+ s.files = FileList["README.md", "README.rdoc", "rakefile.rb",
21
+ "lib/**/*.rb", "test/**/test*.rb"]
22
+ end
23
+
24
+ Gem::PackageTask.new gemspec do |pkg|
25
+ end
26
+
27
+ Rake::RDocTask.new do |rd|
28
+ rd.main = "README.rdoc"
29
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
30
+ end
31
+
32
+ Rake::TestTask.new('test') do |t|
33
+ t.libs << 'test'
34
+ t.pattern = 'test/**/test*.rb'
35
+ t.verbose = true
36
+ end
37
+
38
+ task :default => [:test]
@@ -0,0 +1,34 @@
1
+ require "rubygems/test_case"
2
+ require "rubygems/commands/patch_command"
3
+
4
+ class TestGemCommandsPatchCommand < Gem::TestCase
5
+ def setup
6
+ super
7
+
8
+ @command = Gem::Commands::PatchCommand.new
9
+ end
10
+
11
+ def test_execute_no_gemfile
12
+ @command.options[:args] = []
13
+
14
+ e = assert_raises Gem::CommandLineError do
15
+ use_ui @ui do
16
+ @command.execute
17
+ end
18
+ end
19
+
20
+ assert_match 'Please specify a gem file on the command line (e.g. gem patch foo-0.1.0.gem PATCH [PATCH ...])', e.message
21
+ end
22
+
23
+ def test_execute_no_patch
24
+ @command.options[:args] = ['Gemfile.gem']
25
+
26
+ e = assert_raises Gem::CommandLineError do
27
+ use_ui @ui do
28
+ @command.execute
29
+ end
30
+ end
31
+
32
+ assert_match 'Please specify patches to apply (e.g. gem patch foo-0.1.0.gem foo.patch bar.patch ...)', e.message
33
+ end
34
+ end
@@ -0,0 +1,285 @@
1
+ require "rubygems/test_case"
2
+ require "rubygems/patcher"
3
+
4
+ class TestGemPatch < Gem::TestCase
5
+ def setup
6
+ super
7
+
8
+ @gems_dir = File.join @tempdir, 'gems'
9
+ @lib_dir = File.join @tempdir, 'gems', 'lib'
10
+ FileUtils.mkdir_p @lib_dir
11
+ end
12
+
13
+ ##
14
+ # Test changing a file in a gem with -p1 option
15
+
16
+ def test_change_file_patch
17
+ gemfile = bake_testing_gem
18
+
19
+ patches = []
20
+ patches << bake_change_file_patch
21
+
22
+ # Creates new patched gem in @gems_dir
23
+ patcher = Gem::Patcher.new(gemfile, @gems_dir)
24
+ patched_gem = patcher.patch_with(patches, 1)
25
+
26
+ # Unpack
27
+ package = Gem::Package.new patched_gem
28
+ package.extract_files @gems_dir
29
+
30
+ assert_equal patched_file, file_contents('foo.rb')
31
+ end
32
+
33
+ ##
34
+ # Test adding a file into a gem with -p0 option
35
+
36
+ def test_new_file_patch
37
+ gemfile = bake_testing_gem
38
+
39
+ patches = []
40
+ patches << bake_new_file_patch
41
+
42
+ # Create a new patched gem in @gems_fir
43
+ patcher = Gem::Patcher.new(gemfile, @gems_dir)
44
+ patched_gem = patcher.patch_with(patches, 0)
45
+
46
+ # Unpack
47
+ package = Gem::Package.new patched_gem
48
+ package.extract_files @gems_dir
49
+
50
+ assert_equal original_file, file_contents('bar.rb')
51
+ end
52
+
53
+ ##
54
+ # Test adding and deleting a file in a gem with -p0 option
55
+
56
+ def test_delete_file_patch
57
+ gemfile = bake_testing_gem
58
+
59
+ patches = []
60
+ patches << bake_new_file_patch
61
+ patches << bake_delete_file_patch
62
+
63
+ # Create a new patched gem in @gems_fir
64
+ patcher = Gem::Patcher.new(gemfile, @gems_dir)
65
+ patched_gem = patcher.patch_with(patches, 0)
66
+
67
+ # Unpack
68
+ package = Gem::Package.new patched_gem
69
+ package.extract_files @gems_dir
70
+
71
+ # Only foo.rb should stay in /lib, bar.rb should be gone
72
+ assert_raises(RuntimeError, 'File not found') {
73
+ file_contents(File.join @lib_dir, 'bar.rb')
74
+ }
75
+ end
76
+
77
+ ##
78
+ # Incorrect patch, nothing happens
79
+
80
+ def test_gem_should_not_change
81
+ gemfile = bake_testing_gem
82
+
83
+ patches = []
84
+ patches << bake_incorrect_patch
85
+
86
+ # Create a new patched gem in @gems_fir
87
+ patcher = Gem::Patcher.new(gemfile, @gems_dir)
88
+ patched_gem = patcher.patch_with(patches, 0)
89
+
90
+ # Unpack
91
+ package = Gem::Package.new patched_gem
92
+ package.extract_files @gems_dir
93
+
94
+ assert_equal original_file, file_contents('foo.rb')
95
+ assert_equal original_gemspec, current_gemspec
96
+ end
97
+
98
+ def bake_change_file_patch
99
+ patch_path = File.join(@gems_dir, 'change_file.patch')
100
+
101
+ File.open(patch_path, 'w') do |f|
102
+ f.write change_file_patch
103
+ end
104
+
105
+ patch_path
106
+ end
107
+
108
+ def bake_new_file_patch
109
+ patch_path = File.join(@gems_dir, 'new_file.patch')
110
+
111
+ File.open(patch_path, 'w') do |f|
112
+ f.write new_file_patch
113
+ end
114
+
115
+ patch_path
116
+ end
117
+
118
+ def bake_delete_file_patch
119
+ patch_path = File.join(@gems_dir, 'delete_file.patch')
120
+
121
+ File.open(patch_path, 'w') do |f|
122
+ f.write delete_file_patch
123
+ end
124
+
125
+ patch_path
126
+ end
127
+
128
+ def bake_incorrect_patch
129
+ patch_path = File.join(@gems_dir, 'incorrect.patch')
130
+
131
+ File.open(patch_path, 'w') do |f|
132
+ f.write incorrect_patch
133
+ end
134
+
135
+ patch_path
136
+ end
137
+
138
+ def bake_original_gem_files
139
+ # Create /lib/foo.rb
140
+ file_path = File.join(@lib_dir, 'foo.rb')
141
+
142
+ File.open(file_path, 'w') do |f|
143
+ f.write original_file
144
+ end
145
+
146
+ # Create .gemspec file
147
+ gemspec_path = File.join(@gems_dir, 'foo-0.gemspec')
148
+
149
+ File.open(gemspec_path, 'w') do |f|
150
+ f.write original_gemspec
151
+ end
152
+ end
153
+
154
+ def bake_testing_gem
155
+ bake_original_gem_files
156
+
157
+ test_package = Gem::Package.new 'foo-0.gem'
158
+ test_package.spec = Gem::Specification.load(File.join(@gems_dir, 'foo-0.gemspec'))
159
+
160
+ # Build
161
+ Dir.chdir @gems_dir do
162
+ test_package.build false
163
+ end
164
+
165
+ File.join(@gems_dir, 'foo-0.gem')
166
+ end
167
+
168
+ def current_gemspec
169
+ gemspec_path = File.join(@gems_dir, 'foo-0.gemspec')
170
+
171
+ IO.read(gemspec_path)
172
+ end
173
+
174
+ ##
175
+ # Get the content of the given file in @lib_dir
176
+
177
+ def file_contents(file)
178
+ file_path = File.join(@lib_dir, file)
179
+
180
+ begin
181
+ file_content = IO.read(file_path)
182
+ rescue
183
+ raise RuntimeError, 'File not found'
184
+ end
185
+
186
+ file_content
187
+ end
188
+
189
+ def original_gemspec
190
+ <<-EOF
191
+ Gem::Specification.new do |s|
192
+ s.platform = Gem::Platform::RUBY
193
+ s.name = 'foo'
194
+ s.version = 0
195
+ s.author = 'A User'
196
+ s.email = 'example@example.com'
197
+ s.homepage = 'http://example.com'
198
+ s.summary = "this is a summary"
199
+ s.description = "This is a test description"
200
+ s.files = ['lib/foo.rb']
201
+ end
202
+ EOF
203
+ end
204
+
205
+ def original_file
206
+ <<-EOF
207
+ module Foo
208
+ def bar
209
+ 'Original'
210
+ end
211
+ end
212
+ EOF
213
+ end
214
+
215
+ def patched_file
216
+ <<-EOF
217
+ module Foo
218
+ class Bar
219
+ def foo_bar
220
+ 'Patched'
221
+ end
222
+ end
223
+ end
224
+ EOF
225
+ end
226
+
227
+ def change_file_patch
228
+ <<-EOF
229
+ diff -u a/lib/foo.rb b/lib/foo.rb
230
+ --- a/lib/foo.rb
231
+ +++ b/lib/foo.rb
232
+ @@ -1,6 +1,8 @@
233
+ module Foo
234
+ - def bar
235
+ - 'Original'
236
+ + class Bar
237
+ + def foo_bar
238
+ + 'Patched'
239
+ + end
240
+ end
241
+ end
242
+ EOF
243
+ end
244
+
245
+ def new_file_patch
246
+ <<-EOF
247
+ diff lib/bar.rb lib/bar.rb
248
+ --- /dev/null
249
+ +++ lib/bar.rb
250
+ @@ -0,0 +1,5 @@
251
+ + module Foo
252
+ + def bar
253
+ + 'Original'
254
+ + end
255
+ + end
256
+ EOF
257
+ end
258
+
259
+ def delete_file_patch
260
+ <<-EOF
261
+ diff lib/bar.rb lib/bar.rb
262
+ --- lib/bar.rb
263
+ +++ /dev/null
264
+ @@ -1,5 +0,0 @@
265
+ - module Foo
266
+ - def bar
267
+ - 'Original'
268
+ - end
269
+ - end
270
+ EOF
271
+ end
272
+
273
+ def incorrect_patch
274
+ <<-EOF
275
+ diff lib/foo.rb lib/foo.rb
276
+ --- lib/foo.rb
277
+ +++ /dev/null
278
+ - module Foo
279
+ - def bar
280
+ - 'Original'
281
+ - end
282
+ - end
283
+ EOF
284
+ end
285
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem-patch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josef Stribny
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-02 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! " `gem-patch` is a RubyGems plugin that helps to
15
+ patch gems without manually opening and rebuilding them.\n It
16
+ opens a given .gem file, extracts it, patches it with system `patch` command,\n
17
+ \ clones its spec, updates the file list and builds the patched
18
+ gem.\n"
19
+ email: jstribny@redhat.com
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - README.md
25
+ - README.rdoc
26
+ - rakefile.rb
27
+ - lib/rubygems_plugin.rb
28
+ - lib/rubygems/package-1.8.rb
29
+ - lib/rubygems/patcher.rb
30
+ - lib/rubygems/commands/patch_command.rb
31
+ - test/rubygems/test_gem_patch.rb
32
+ - test/rubygems/test_gem_commands_patch_command.rb
33
+ homepage:
34
+ licenses:
35
+ - MIT
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.8.7
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: 1.8.0
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: RubyGems plugin for patching gems.
58
+ test_files: []