# encoding: utf-8 # Copyright (c) 2012, HipByte SPRL and contributors # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. require 'motion/error' require 'erb' require 'fileutils' module Motion; module Project class Template # for ERB attr_reader :name Paths = [ File.expand_path(File.join(__FILE__, '../template')), File.expand_path(File.join(ENV['HOME'], 'Library/RubyMotion/template')) ] # TODO Caching these and making it based on the Paths constant makes it # less simple to register plugin templates, because you cannot add # them to the Paths constant and ensure this method will return those # newly registered templates either. The only nice way atm to register # them is to add them directly to this cached `@all_templates` var. # For instance, from the Joybox plugin: # # require 'motion/project/template' # Dir.glob(File.expand_path('../../template/joybox-*', __FILE__)).each do |template_path| # Motion::Project::Template.all_templates[File.basename(template_path)] = template_path # end # def self.all_templates @all_templates ||= begin h = {} Paths.map { |path| Dir.glob(path + '/*') }.flatten.select { |x| !x.match(/^\./) and File.directory?(x) }.each do |template_path| h[File.basename(template_path)] = template_path end h end end # TODO This seems to be unused. Templates = Paths.map { |path| Dir.glob(path + '/*') }.flatten.select { |x| !x.match(/^\./) and File.directory?(x) }.map { |x| File.basename(x) } def initialize(app_name, template_name) @name = @app_name = app_name @template_name = template_name.to_s repository = Repository.new(@template_name) if repository.exist? repository.clone @template_name = repository.name end @template_directory = self.class.all_templates[@template_name] unless @template_directory raise InformativeError, "Cannot find template `#{@template_name}' in " \ "#{Paths.join(' or ')}. Available templates: " \ "#{self.class.all_templates.keys.join(', ')}" end unless app_name.match(/^[\w\s-]+$/) raise InformativeError, "Invalid project name." end if File.exist?(app_name) raise InformativeError, "Directory `#{app_name}' already exists" end end def generate App.log 'Create', @app_name FileUtils.mkdir(@app_name) Dir.chdir(@app_name) do create_directories() create_files() end end private def template_directory @template_directory end def create_directories template_files = File.join(template_directory, 'files') Dir.glob(File.join(template_files, "**/")).each do |dir| dir.sub!("#{template_files}/", '') dir = replace_file_name(dir) FileUtils.mkdir_p(dir) if dir.length > 0 end end def create_files template_files = File.join(template_directory, 'files') Dir.glob(File.join(template_files, "**/*"), File::FNM_DOTMATCH).each do |src| dest = src.sub("#{template_files}/", '') next if File.directory?(src) next if dest.include?(".DS_Store") dest = replace_file_name(dest) if dest =~ /(.+)\.erb$/ App.log 'Create', "#{@app_name}/#{$1}" File.open($1, "w") { |io| io.print ERB.new(File.read(src)).result(binding) } else App.log 'Create', "#{@app_name}/#{dest}" FileUtils.cp(src, dest) end end end def replace_file_name(file_name) file_name = file_name.gsub("{name}", "#{@name}") file_name end class Repository attr_reader :name def initialize(template) @url = template @name = begin # Extract repo name from HTTP, SSH or Git URLs: case template when /\w+:\/\/.+@*[\w\d\.]+\/.+\/(.+).git/i, /git@.+:.+\/(.+)\.git/i $1 end end end def exist? @name != nil end def clone path = File.expand_path(File.join(ENV['HOME'], 'Library/RubyMotion/template', @name)) App.log 'Template', "Cloning #{@name} template" git_clone(path) end private def git_clone(path) if File.exist?(path) # directory exists just do a pull App.log 'Template', "#{@name} already exists, performing a pull" system("git --work-tree=#{path} --git-dir=#{path}/.git pull origin master") else # no directory exists so clone result = system("git clone #{@url} #{path}") unless result App.log 'Template', "Unable to clone #{@url}" end end end end end end; end