Files
RubyMotion/lib/motion/project/template.rb
2014-03-05 12:20:20 +01:00

185 lines
6.0 KiB
Ruby

# 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