File Coverage

blib/lib/Bundler/MultiGem/Model/Gem.pm
Criterion Covered Total %
statement 55 136 40.4
branch 6 24 25.0
condition 0 3 0.0
subroutine 19 26 73.0
pod 16 17 94.1
total 96 206 46.6


line stmt bran cond sub pod time code
1             package Bundler::MultiGem::Model::Gem {
2 1     1   67931 use 5.006;
  1         14  
3 1     1   4 use strict;
  1         2  
  1         16  
4 1     1   3 use warnings;
  1         2  
  1         21  
5              
6 1     1   5 use Exporter qw(import);
  1         1  
  1         45  
7             our @EXPORT = qw(gem_vname gem_vmodule_name norm_v);
8 1     1   456 use common::sense;
  1         11  
  1         4  
9              
10 1     1   371 use Bundler::MultiGem::Utl::InitConfig qw(ruby_constantize);
  1         3  
  1         61  
11 1     1   461 use File::Spec::Functions qw(catfile catdir);
  1         729  
  1         53  
12 1     1   6 use File::Find;
  1         2  
  1         1751  
13              
14             =head1 NAME
15              
16             Bundler::MultiGem::Model::Gem - The utility to install multiple versions of the same ruby gem
17              
18             =head1 VERSION
19              
20             Version 0.01
21              
22             =head1 SYNOPSIS
23              
24             This module contains utility functions for manipulating gems
25              
26             =head1 SUBROUTINES
27              
28             =head2 new
29             Take config as argument
30             =cut
31             sub new {
32 1     1 1 454 my $class = shift;
33 1         3 my $self = { config => shift };
34 1         298 bless $self, $class;
35 1         6 return $self;
36             }
37              
38             =head2 config
39             config getter
40             =cut
41             sub config {
42 7     7 1 21 my ($self, $key) = @_;
43 7 100       18 if (!defined $key) {
44 1         12 return $self->{config};
45             }
46 6         27 return $self->{config}->{$key}
47             }
48              
49             =head2 name
50             name getter
51             =cut
52             sub name {
53 2     2 1 6 my $self = shift;
54 2         5 return $self->config("name")
55             }
56              
57             =head2 source
58             source getter
59             =cut
60             sub source {
61 1     1 1 3 my $self = shift;
62 1         3 return $self->config("source")
63             }
64              
65             =head2 main_module
66             main_module getter
67             =cut
68             sub main_module {
69 2     2 1 4 my $self = shift;
70 2         6 return $self->config("main_module")
71             }
72              
73             =head2 versions
74             versions getter
75             =cut
76             sub versions {
77 1     1 1 3 my $self = shift;
78 1         3 return $self->config("versions")
79             }
80              
81             =head2 vname
82             vname getter e.g. v010-foo for gem 'foo', '0.1.0'
83             =cut
84              
85             sub vname {
86 2     2 1 48 my ($self, $v) = @_;
87 2 100       7 if (!defined $v) {
88 1         8 die "You need to provide a version to vname method";
89             }
90 1         4 return Bundler::MultiGem::Model::Gem::gem_vname($self->name, $v);
91             }
92              
93             =head2 vmodule_name
94             vmodule_name getter e.g. V010::Foo for gem 'foo', '0.1.0'
95             =cut
96              
97             sub vmodule_name {
98 2     2 1 31 my ($self, $v) = @_;
99 2 100       6 if (!defined $v) {
100 1         9 die "You need to provide a version to vmodule_name method";
101             }
102 1         4 return Bundler::MultiGem::Model::Gem::gem_vmodule_name($self->main_module, $v);
103             }
104              
105             =head1 EXPORTS
106              
107             =head2 gem_vname
108              
109             =cut
110             sub gem_vname {
111 2     2 1 1493 my ($gem_name, $v) = @_;
112 2         9 join('-', (norm_v($v), $gem_name));
113             }
114              
115             =head2 gem_vmodule_name
116             =cut
117             sub gem_vmodule_name {
118 3     3 1 540 my ($gem_module_name, $v) = @_;
119 3         10 ruby_constantize(join('-', (norm_v($v), $gem_module_name)));
120             }
121              
122             =head2 norm_v
123              
124             Normalize version name
125              
126             =cut
127             sub norm_v {
128 7     7 1 2291 my $v = shift;
129 7         14 for ($v) {
130 7         35 s/\.//g;
131             }
132 7         45 "v${v}";
133             }
134              
135             =head2 apply
136             =cut
137              
138             sub apply {
139 0     0 1   my ($self, $dir) = @_;
140 0           my $pkg_dir = $dir->dirs("pkg");
141 0           my $target_dir = $dir->dirs("target");
142 0           my @gemfile_statements = ();
143 0           foreach my $v (@{$self->versions}) {
  0            
144 0           my $normv = Bundler::MultiGem::Model::Gem::norm_v($v);
145 0           my $gem_vname = $self->vname($v);
146 0           my $gem_path = catfile( $pkg_dir, "${gem_vname}.gem" );
147 0           my $extracted_dir = catdir( $target_dir, $gem_vname );
148              
149 0           $self->fetch($gem_path, $v);
150 0           Bundler::MultiGem::Model::Gem::unpack_gem($gem_path, $target_dir);
151              
152 0           $self->process_gemfile($v, $extracted_dir);
153              
154 0           my $lib_dir = catdir( $extracted_dir, 'lib' );
155             # process main gem module
156 0           $self->rename_main_file($v, $lib_dir);
157              
158             # Rename gem name dir in lib directory
159 0 0         rename( catdir( $lib_dir, $self->name ), catdir( $lib_dir, $gem_vname )) ||
160             warn catdir( $lib_dir, $self->name ) . "does not exists: $!";
161              
162             # Process all rb files in $extracted_dir, this should be refined later
163 0           my @ruby_files = ();
164             find(
165             {
166             wanted => sub {
167 0     0     my $F = $File::Find::name;
168 0 0         push @ruby_files, $F if ($F =~ /(rb|rake|Rakefile)$/)
169             },
170 0           no_chdir => 1
171             },
172             $extracted_dir
173             );
174              
175 0           foreach my $f (@ruby_files) {
176 0           $self->process_single_file($v, $f);
177             }
178              
179 0           print $gem_vname . " completed!\n";
180 0           push @gemfile_statements, "gem '$gem_vname', path: '$extracted_dir'";
181             }
182 0           print "Process completed.\n\n";
183 0           print "You can add to your Gemfile something like:\n";
184 0           foreach (@gemfile_statements) { print "$_\n"; }
  0            
185             }
186              
187             =head2 process_gemfile
188             =cut
189              
190             sub process_gemfile {
191 0     0 1   my ($self, $v, $extracted_dir) = @_;
192 0           my ($n, $vn, $mm, $vmn) = (
193             $self->name, $self->vname($v), $self->main_module, $self->vmodule_name($v)
194             );
195 0           my $gemspec = catfile($extracted_dir, $n . ".gemspec");
196 0           my $new_gemspec = catfile($extracted_dir, $vn . ".gemspec");
197              
198             # Process .gemspec
199 0 0         open(GEMSPEC, "<${gemspec}") || die "Can't open ${gemspec}: $!";
200 0 0         open(NEW_GEMSPEC, ">${new_gemspec}") || die "Can't open ${new_gemspec}: $!";
201              
202 0           while( my $line = ){
203 0 0         if ( $line =~ /${n}\/version/ ) { next; }
  0            
204 0           for ($line) {
205             # Replace version reference from file
206 0           s/${mm}::VERSION/'$v'/;
207 0           s/${n}/${vn}/g;
208 0           s/${mm}/${vmn}/g;
209             }
210              
211 0           print NEW_GEMSPEC $line;
212             }
213 0           close(NEW_GEMSPEC);
214 0           close(GEMSPEC);
215              
216 0   0       unlink $gemspec || warn "Could not unlink ${gemspec}: $!";
217             }
218              
219             =head2 rename_main_file
220             =cut
221              
222             sub rename_main_file {
223 0     0 1   my ($self, $v, $lib_dir) = @_;
224              
225 0           my $normv = Bundler::MultiGem::Model::Gem::norm_v($v);
226 0           my $new_main_module = ruby_constantize($normv);
227              
228 0           my $main_module_file = catfile( $lib_dir, $self->name . ".rb" );
229 0           my $new_main_module_file = catfile( $lib_dir, $self->vname($v) . ".rb" );
230              
231 0 0         if ( -e $main_module_file ) {
232 0 0         open(ORIGINAL, "<${main_module_file}") ||
233             die "Can't open ${main_module_file}: $!";
234 0           my @file_content = ;
235 0           close(ORIGINAL);
236              
237 0 0         open(NEW_FILE, ">${new_main_module_file}") ||
238             die "Can't open ${new_main_module_file}: $!";
239 0           print NEW_FILE "module ${new_main_module}; end\n";
240 0           foreach my $line (@file_content) { print NEW_FILE $line; }
  0            
241 0           close(NEW_FILE);
242              
243 0           unlink $main_module_file;
244             }
245             }
246              
247             =head2 process_single_file
248             =cut
249             sub process_single_file {
250 0     0 1   my ($self, $v, $f) = @_;
251              
252 0           my ($n, $vn, $mm, $vmn) = (
253             $self->name, $self->vname($v), $self->main_module, $self->vmodule_name($v)
254             );
255 0           my $bkp = $f . ".bak";
256 0           rename($f, $bkp);
257 0           open(I, "<$bkp");
258 0           open(O, ">$f");
259 0           while(my $line = ) {
260 0           for ($line) {
261 0           s/${n}/${vn}/g;
262 0           s/${mm}/${vmn}/g;
263             }
264 0           print O $line;
265             }
266 0           close(O);
267 0           close(I);
268 0           unlink $bkp;
269             }
270              
271             =head2 unpack_gem
272             =cut
273              
274             sub unpack_gem {
275 0     0 1   my ($gem_filepath, $target_dir) = @_;
276 0           system("gem unpack ${gem_filepath} --target ${target_dir}");
277             }
278              
279             =head2 fetch_gem
280             =cut
281              
282             sub fetch {
283 0     0 0   my ( $self, $fp, $gv ) = (@_);
284 0 0         if (! -f $fp ) {
285 0           my $cmd = "gem fetch " . $self->name . " --version " . $gv .
286             " --source " . $self->source;
287 0           system("$cmd");
288 0           rename( $self->name . "-$gv" . ".gem", $fp );
289             }
290             }
291             };
292             1;