lib/xcodeproj/workspace.rb



require 'fileutils'
require 'rexml/document'

module Xcodeproj

  # The {Workspace} allows to generate, read and serialize Xcode Workspace
  # documents.
  #
  class Workspace

    # @return [Array<String>] the paths of the projects contained in the
    #   workspace.
    #
    attr_reader :projpaths

    # Returns a new workspace initialized with the given `xcodeproj` paths.
    #
    # @param [String] projpaths
    #   one or more `xcodeproj` paths.
    #
    def initialize(*projpaths)
      @projpaths = projpaths
    end

    # Returns a workspace generated by reading the contents of the given path.
    #
    # @param [String] path
    #   the path of the `xcworkspace` file.
    #
    # @return [Workspace] the generated workspace.
    #
    def self.new_from_xcworkspace(path)
      begin
        from_s(File.read(File.join(path, 'contents.xcworkspacedata')))
      rescue Errno::ENOENT
        new
      end
    end

    # Returns a workspace generated by reading the contents of the given
    # XML representation.
    #
    # @param [String] xml
    #   the XML representation of the workspace.
    #
    # @return [Workspace] the generated workspace.
    #
    def self.from_s(xml)
      document = REXML::Document.new(xml)
      projpaths = document.get_elements("/Workspace/FileRef").map do |node|
        node.attribute("location").to_s.sub(/^group:/, '')
      end
      new(*projpaths)
    end

    # Adds a new path to the list of the of projects contained in the
    #   workspace.
    #
    # @param [String] projpath
    #   The path of the project to add.
    #
    # @return [void]
    #
    def <<(projpath)
      @projpaths << projpath
    end

    # Checks if the workspace contains the project with the given path.
    #
    # @param [String] projpath
    #   The path of the project to add.
    #
    # @return [Boolean] whether the project is contained in the workspace.
    #
    def include?(projpath)
      @projpaths.include?(projpath)
    end

    # The template to generate a workspace XML representation.
    #
    TEMPLATE = %q[<?xml version="1.0" encoding="UTF-8"?><Workspace version="1.0"></Workspace>]

    # @return [String] the XML representation of the workspace.
    #
    def to_s
      REXML::Document.new(TEMPLATE).tap do |document|
        @projpaths.each do |projpath|
          document.root << REXML::Element.new("FileRef").tap do |el|
            el.attributes['location'] = "group:#{projpath}"
          end
        end
      end.to_s
    end

    # Saves the workspace at the given `xcworkspace` path.
    #
    # @param [String] path
    #   the path where to save the project.
    #
    # @return [void]
    #
    def save_as(path)
      FileUtils.mkdir_p(path)
      File.open(File.join(path, 'contents.xcworkspacedata'), 'w') do |out|
        out << to_s
      end
    end
  end
end