aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/gem_dependency.rb
blob: f8d97840c1f7273f7d7b332bf438694e6edea2d9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
module Rails
  class GemDependency
    attr_accessor :name, :requirement, :version, :lib, :source

    def self.unpacked_path
      @unpacked_path ||= File.join(RAILS_ROOT, 'vendor', 'gems')
    end

    def initialize(name, options = {})
      require 'rubygems' unless Object.const_defined?(:Gem)

      if options[:requirement]
        @requirement = options[:requirement]
      elsif options[:version]
        @requirement = Gem::Requirement.create(options[:version])
      end

      @version  = @requirement.instance_variable_get("@requirements").first.last if @requirement
      @name     = name.to_s
      @lib      = options[:lib]
      @source   = options[:source]
      @loaded   = @frozen = @load_paths_added = false
      @unpack_directory = nil
    end

    def unpacked_paths
      Dir[File.join(self.class.unpacked_path, "#{@name}-#{@version || "*"}")]
    end

    def add_load_paths
      return if @loaded || @load_paths_added
      unpacked_paths = self.unpacked_paths
      if unpacked_paths.empty?
        args = [@name]
        args << @requirement.to_s if @requirement
        gem *args
      else
        $LOAD_PATH.unshift File.join(unpacked_paths.first, 'lib')
        ext = File.join(unpacked_paths.first, 'ext')
        $LOAD_PATH.unshift(ext) if File.exist?(ext)
        @frozen = true
      end
      @load_paths_added = true
    rescue Gem::LoadError
    end

    def dependencies
      all_dependencies = specification.dependencies.map do |dependency|
        GemDependency.new(dependency.name, :requirement => dependency.version_requirements)
      end
      all_dependencies += all_dependencies.map(&:dependencies).flatten
      all_dependencies.uniq
    end

    def gem_dir(base_directory)
      File.join(base_directory, specification.full_name)
    end

    def load
      return if @loaded || @load_paths_added == false
      require(@lib || @name)
      @loaded = true
    rescue LoadError
      puts $!.to_s
      $!.backtrace.each { |b| puts b }
    end

    def frozen?
      @frozen
    end

    def loaded?
      @loaded
    end

    def load_paths_added?
      @load_paths_added
    end

    def install
      cmd = "#{gem_command} #{install_command.join(' ')}"
      puts cmd
      puts %x(#{cmd})
    end

    def unpack_to(directory)
      FileUtils.mkdir_p directory
      Dir.chdir directory do
        Gem::GemRunner.new.run(unpack_command)
      end

      # copy the gem's specification into GEMDIR/.specification so that
      # we can access information about the gem on deployment systems
      # without having the gem installed
      spec_filename = File.join(gem_dir(directory), '.specification')
      File.open(spec_filename, 'w') do |file|
        file.puts specification.to_yaml
      end
    end

    def ==(other)
      self.name == other.name && self.requirement == other.requirement
    end

    def specification
      @spec ||= Gem.source_index.search(Gem::Dependency.new(@name, @requirement)).sort_by { |s| s.version }.last
    end

    private
      def gem_command
        RUBY_PLATFORM =~ /win32/ ? 'gem.bat' : 'gem'
      end

      def install_command
        cmd = %w(install) << @name
        cmd << "--version" << %("#{@requirement.to_s}") if @requirement
        cmd << "--source"  << @source  if @source
        cmd
      end

      def unpack_command
        cmd = %w(unpack) << @name
        cmd << "--version" << %("#{@requirement.to_s}") if @requirement
        cmd
      end
  end
end