Module: GemManagement

Includes:
ColorfulMessages
Included in:
Merb::RakeHelper
Defined in:
lib/merb-core/tasks/gem_management.rb

Instance Method Summary collapse

Methods included from ColorfulMessages

#error, #info, #note, #success, #warning

Instance Method Details

#clobber(source_dir) ⇒ Object



199
200
201
202
203
# File 'lib/merb-core/tasks/gem_management.rb', line 199

def clobber(source_dir)
  Dir.chdir(source_dir) do 
    system "#{Gem.ruby} -S rake -s clobber" unless File.exists?('Thorfile')
  end
end

#ensure_bin_wrapper_for(gem_dir, bin_dir, *gems) ⇒ Object

Create a modified executable wrapper in the specified bin directory.



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/merb-core/tasks/gem_management.rb', line 280

def ensure_bin_wrapper_for(gem_dir, bin_dir, *gems)
  options = gems.last.is_a?(Hash) ? gems.last : {}
  options[:no_minigems] ||= []
  if bin_dir && File.directory?(bin_dir)
    gems.each do |gem|
      if gemspec_path = Dir[File.join(gem_dir, 'specifications', "#{gem}-*.gemspec")].last
        spec = Gem::Specification.load(gemspec_path)
        enable_minigems = !options[:no_minigems].include?(spec.name)
        spec.executables.each do |exec|
          executable = File.join(bin_dir, exec)
          message "Writing executable wrapper #{executable}"
          File.open(executable, 'w', 0755) do |f|
            f.write(executable_wrapper(spec, exec, enable_minigems))
          end
        end
      end
    end
  end
end

#ensure_bin_wrapper_for_installed_gems(gemspecs, options) ⇒ Object



300
301
302
303
304
305
# File 'lib/merb-core/tasks/gem_management.rb', line 300

def ensure_bin_wrapper_for_installed_gems(gemspecs, options)
  if options[:install_dir] && options[:bin_dir]
    gems = gemspecs.map { |spec| spec.name }
    ensure_bin_wrapper_for(options[:install_dir], options[:bin_dir], *gems)
  end
end

#install_gem(gem, options = {}) ⇒ Object

Install a gem - looks remotely and local gem cache; won’t process rdoc or ri options.



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
# File 'lib/merb-core/tasks/gem_management.rb', line 46

def install_gem(gem, options = {})
  refresh = options.delete(:refresh) || []
  from_cache = (options.key?(:cache) && options.delete(:cache))
  if from_cache
    install_gem_from_cache(gem, options)
  else
    version = options.delete(:version)
    Gem.configuration.update_sources = false

    # Limit source index to install dir
    update_source_index(options[:install_dir]) if options[:install_dir]

    installer = Gem::DependencyInstaller.new(options.merge(:user_install => false))
    
    # Force-refresh certain gems by excluding them from the current index
    if !options[:ignore_dependencies] && refresh.respond_to?(:include?) && !refresh.empty?
      source_index = installer.instance_variable_get(:@source_index)
      source_index.gems.each do |name, spec| 
        source_index.gems.delete(name) if refresh.include?(spec.name)
      end
    end
    
    exception = nil
    begin
      installer.install gem, version
    rescue Gem::InstallError => e
      exception = e
    rescue Gem::GemNotFoundException => e
      if from_cache && gem_file = find_gem_in_cache(gem, version)
        puts "Located #{gem} in gem cache..."
        installer.install gem_file
      else
        exception = e
      end
    rescue => e
      exception = e
    end
    if installer.installed_gems.empty? && exception
      error "Failed to install gem '#{gem} (#{version || 'any version'})' (#{exception.message})"
    end
    ensure_bin_wrapper_for_installed_gems(installer.installed_gems, options)
    installer.installed_gems.each do |spec|
      success "Successfully installed #{spec.full_name}"
    end
    return !installer.installed_gems.empty?
  end
end

#install_gem_from_cache(gem, options = {}) ⇒ Object

Install a gem - looks in the system’s gem cache instead of remotely; won’t process rdoc or ri options.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/merb-core/tasks/gem_management.rb', line 96

def install_gem_from_cache(gem, options = {})
  version = options.delete(:version)
  Gem.configuration.update_sources = false
  installer = Gem::DependencyInstaller.new(options.merge(:user_install => false))
  exception = nil
  begin
    if gem_file = find_gem_in_cache(gem, version)
      puts "Located #{gem} in gem cache..."
      installer.install gem_file
    else
      raise Gem::InstallError, "Unknown gem #{gem}"
    end
  rescue Gem::InstallError => e
    exception = e
  end
  if installer.installed_gems.empty? && exception
    error "Failed to install gem '#{gem}' (#{e.message})"
  end
  ensure_bin_wrapper_for_installed_gems(installer.installed_gems, options)
  installer.installed_gems.each do |spec|
    success "Successfully installed #{spec.full_name}"
  end
end

#install_gem_from_source(source_dir, *args) ⇒ Object

Install a gem from source - builds and packages it first then installs.

Examples: install_gem_from_source(source_dir, :install_dir => …) install_gem_from_source(source_dir, gem_name) install_gem_from_source(source_dir, :skip => […])



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/merb-core/tasks/gem_management.rb', line 126

def install_gem_from_source(source_dir, *args)
  installed_gems = []
  opts = args.last.is_a?(Hash) ? args.pop : {}
  Dir.chdir(source_dir) do      
    gem_name     = args[0] || File.basename(source_dir)
    gem_pkg_dir  = File.join(source_dir, 'pkg')
    gem_pkg_glob = File.join(gem_pkg_dir, "#{gem_name}-*.gem")
    skip_gems    = opts.delete(:skip) || []

    # Cleanup what's already there
    clobber(source_dir)
    FileUtils.mkdir_p(gem_pkg_dir) unless File.directory?(gem_pkg_dir)

    # Recursively process all gem packages within the source dir
    skip_gems << gem_name
    packages = package_all(source_dir, skip_gems)
    
    if packages.length == 1
      # The are no subpackages for the main package
      refresh = [gem_name]
    else
      # Gather all packages into the top-level pkg directory
      packages.each do |pkg|
        FileUtils.copy_entry(pkg, File.join(gem_pkg_dir, File.basename(pkg)))
      end
      
      # Finally package the main gem - without clobbering the already copied pkgs
      package(source_dir, false)
      
      # Gather subgems to refresh during installation of the main gem
      refresh = packages.map do |pkg|
        File.basename(pkg, '.gem')[/^(.*?)-([\d\.]+)$/, 1] rescue nil
      end.compact
      
      # Install subgems explicitly even if ignore_dependencies is set
      if opts[:ignore_dependencies]
        refresh.each do |name| 
          gem_pkg = Dir[File.join(gem_pkg_dir, "#{name}-*.gem")][0]
          install_pkg(gem_pkg, opts)
        end
      end
    end
    
    ensure_bin_wrapper_for(opts[:install_dir], opts[:bin_dir], *installed_gems)
    
    # Finally install the main gem
    if install_pkg(Dir[gem_pkg_glob][0], opts.merge(:refresh => refresh))
      installed_gems = refresh
    else
      installed_gems = []
    end
  end
  installed_gems
end

#install_pkg(gem_pkg, opts = {}) ⇒ Object



181
182
183
184
185
186
187
188
# File 'lib/merb-core/tasks/gem_management.rb', line 181

def install_pkg(gem_pkg, opts = {})
  if (gem_pkg && File.exists?(gem_pkg))
    # Needs to be executed from the directory that contains all packages
    Dir.chdir(File.dirname(gem_pkg)) { install_gem(gem_pkg, opts) }
  else
    false
  end
end

#package(source_dir, clobber = true) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/merb-core/tasks/gem_management.rb', line 205

def package(source_dir, clobber = true)
  Dir.chdir(source_dir) do 
    if File.exists?('Thorfile')
      thor ":package"
    elsif File.exists?('Rakefile')
      rake "clobber" if clobber
      rake "package"
    end
  end
  Dir[File.join(source_dir, 'pkg/*.gem')]
end

#package_all(source_dir, skip = [], packages = []) ⇒ Object



217
218
219
220
221
222
223
224
225
226
# File 'lib/merb-core/tasks/gem_management.rb', line 217

def package_all(source_dir, skip = [], packages = [])
  if Dir[File.join(source_dir, '{Rakefile,Thorfile}')][0]
    name = File.basename(source_dir)
    Dir[File.join(source_dir, '*', '{Rakefile,Thorfile}')].each do |taskfile|
      package_all(File.dirname(taskfile), skip, packages)
    end
    packages.push(*package(source_dir)) unless skip.include?(name)
  end
  packages.uniq
end

#partition_dependencies(dependencies, gem_dir) ⇒ Object

Partition gems into system, local and missing gems



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/merb-core/tasks/gem_management.rb', line 248

def partition_dependencies(dependencies, gem_dir)
  system_specs, local_specs, missing_deps = [], [], []
  if gem_dir && File.directory?(gem_dir)
    gem_dir = File.expand_path(gem_dir)
    ::Gem.clear_paths; ::Gem.path.unshift(gem_dir)
    ::Gem.source_index.refresh!
    dependencies.each do |dep|
      gemspecs = ::Gem.source_index.search(dep)
      local = gemspecs.reverse.find { |s| s.loaded_from.index(gem_dir) == 0 }
      if local
        local_specs << local
      elsif gemspecs.last
        system_specs << gemspecs.last
      else
        missing_deps << dep
      end
    end
    ::Gem.clear_paths
  else
    dependencies.each do |dep|
      gemspecs = ::Gem.source_index.search(dep)
      if gemspecs.last
        system_specs << gemspecs.last
      else
        missing_deps << dep
      end
    end
  end
  [system_specs, local_specs, missing_deps]
end

#rake(cmd) ⇒ Object



228
229
230
231
# File 'lib/merb-core/tasks/gem_management.rb', line 228

def rake(cmd)
  cmd << " >/dev/null" if $SILENT && !Gem.win_platform?
  system "#{Gem.ruby} -S #{which('rake')} -s #{cmd} >/dev/null"
end

#thor(cmd) ⇒ Object



233
234
235
236
# File 'lib/merb-core/tasks/gem_management.rb', line 233

def thor(cmd)
  cmd << " >/dev/null" if $SILENT && !Gem.win_platform?
  system "#{Gem.ruby} -S #{which('thor')} #{cmd}"
end

#uninstall_gem(gem, options = {}) ⇒ Object

Uninstall a gem.



191
192
193
194
195
196
197
# File 'lib/merb-core/tasks/gem_management.rb', line 191

def uninstall_gem(gem, options = {})
  if options[:version] && !options[:version].is_a?(Gem::Requirement)
    options[:version] = Gem::Requirement.new ["= #{options[:version]}"]
  end
  update_source_index(options[:install_dir]) if options[:install_dir]
  Gem::Uninstaller.new(gem, options).uninstall rescue nil
end

#which(executable) ⇒ Object

Use the local bin/* executables if available.



239
240
241
242
243
244
245
# File 'lib/merb-core/tasks/gem_management.rb', line 239

def which(executable)
  if File.executable?(exec = File.join(Dir.pwd, 'bin', executable))
    exec
  else
    executable
  end
end