require 'fileutils'
require 'yaml'
module Transender
# :stopdoc:
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
V = YAML.load_file(File.join(File.dirname(__FILE__), %w[.. VERSION.yml]))
VERSION = "#{V[:major]}.#{V[:minor]}.#{V[:patch]}"
# :startdoc:
# Returns the version string for the library.
#
def self.version
VERSION
end
# Returns the library path for the module. If any arguments are given,
# they will be joined to the end of the libray path using
# File.join.
#
def self.libpath( *args )
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
end
# Returns the lpath for the module. If any arguments are given,
# they will be joined to the end of the path using
# File.join.
#
def self.path( *args )
args.empty? ? PATH : ::File.join(PATH, args.flatten)
end
# Utility method used to require all files ending in .rb that lie in the
# directory below this file that has the same name as the filename passed
# in. Optionally, a specific _directory_ name can be passed in such that
# the _filename_ does not have to be equivalent to the directory.
#
def self.require_all_libs_relative_to( fname, dir = nil )
dir ||= ::File.basename(fname, '.*')
search_me = ::File.expand_path(
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
Dir.glob(search_me).sort.each {|rb| require rb}
end
#replaces all 'old' strings with 'fresh' strings
def self.replace_in_file( in_file, old, fresh, out_file)
`sed 's/#{old}/#{fresh}/g' <'#{in_file}' >'#{out_file}'`
end
def self.replace_strings_in_file(old, fresh, filename)
temp = "#{filename}.temp"
FileUtils.mv filename, temp
Transender.replace_in_file(temp, old, fresh, filename)
FileUtils.rm temp
end
def self.zip_dir(dir, out_file)
`tar cvfz '#{out_file}' '#{dir}'`
out_file
end
def self.extract_name(url)
url.split('/').last.split('.').first
end
#Transender - Ji
class Ji
attr_accessor :options, :app_title, :app_path, :transform, :transform_title, :ji_path, :abowl_path, :abowl_yml, :id
# Supply a Hash of options containing:
# options[:app_title]:: your fresh apps title
# options[:transform]:: git repository of the base project - app_title becomes a clone of it
# options[:ji_path]:: output dir
def initialize(options)
raise ArgumentError unless options.class == Hash && options[:app_title] && options[:transform] #&& options[:ji_path]
@options = options
@ji_path = @options[:ji_path] || Dir.getwd
@ji_path = Dir.getwd if @ji_path.empty?
puts "Ji path is: #{@ji_path}"
unless File.exists? @ji_path
begin
FileUtils.mkdir @ji_path
rescue
puts "Output path could not be created."
raise ArgumentError
end
end
@app_title = @options[:app_title]
@app_path = File.join(@ji_path, @app_title)
@abowl_path = File.join(@ji_path, "abowl")
@abowl_yml = File.join(@abowl_path, "abowl.yml")
@transform = @options[:transform]
@transform_title = Transender.extract_name(@transform)
@id = Time.now.strftime("%Y-%m-%d-%s")
end
#read abowl, transend that information into existing project
#deleting, copying files in this proces
def transend
if read_abowl
# handle classes&views files
if @abowl['files']
dirz = @abowl['files'].keys
dirz.each do |type|
dir_string = "#{app_path}/Classes/#{type}/*.{m,h}"
dir_string = "#{app_path}/Views/*.{xib, nib}" if type == "Views"
filenames = @abowl['files'][type]
if filenames&&filenames.size>0
unless filenames.size == 1 && filenames[0] == "all"
Dir[dir_string].each do |file|
name = Transender.extract_name(file)
FileUtils.rm file unless filenames.member? name
end
end
else
FileUtils.rm_rf(Dir.glob(dir_string))
end
end
end
#handle app delegate defines
defines = @abowl['app']
appDelegate = "#{app_path}/Classes/Application/#{app_title}AppDelegate.h"
appDelegate_temp = "#{app_path}/Classes/Application/#{app_title}AppDelegate.h.temp"
File.open(appDelegate_temp, "w") do |outfile|
File.open(appDelegate, "r") do |infile|
while (line = infile.gets)
if line =~ /#define/
key = line.split[1]
if defines.has_key? key
value = defines[key]
if value.to_s =~ /^(\d)*\.(\d)*$/ #is numerical
outfile << (line.gsub /^#define #{key} .*$/, "#define #{key} #{value}")
puts "#define #{key} #{value}"
else
outfile << (line.gsub /^#define #{key} .*$/, "#define #{key} @\"#{value}\"")
puts "#define #{key} #{value}"
end
end
else
outfile << line
end
end
end
end
FileUtils.mv appDelegate_temp, appDelegate
puts "Transended #{@abowl['app']['APP_TITLE']}."
else #try to make a bowl then
make_abowl
end
end
#backdrop and dropback are used inconjunction, to form a form of mending the code back into the project, while maintaining transendability with the mothers source
#saves Clasess, Artwork, Transends and Views to abowl
#this will later be copied back to the project
def backdrop
#make sure there is abowl
make_abowl unless read_abowl
#if the app is already there
if File.exists?(app_path)
# handle artwork if any
files = Dir["#{app_path}/Artwork/*"]
if files&&files.size>0
#remove original artwork from abowl
FileUtils.rm_rf "#{abowl_path}/Artwork"
#copy this apps artworks to abowl
FileUtils.cp_r "#{app_path}/Artwork", "#{abowl_path}/"
end
# handle transends if any
files = Dir["#{app_path}/Transends/*"]
if files&&files.size>0
#remove original transends from abowl
FileUtils.rm_rf "#{abowl_path}/Transends"
#copy this bowls transends
FileUtils.cp_r "#{app_path}/Transends", "#{abowl_path}/"
end
# handle classes&views file
#FileUtils.rm_rf "#{abowl_path}/Classes"
#FileUtils.rm_rf "#{abowl_path}/Views"
if @abowl['files']
dirz = @abowl['files'].keys
dirz.each do |type|
FileUtils.cp_r "#{app_path}/Classes/#{type}", "#{abowl_path}/Classes/" unless type == "Views"
end
end
FileUtils.cp_r "#{app_path}/Views", "#{abowl_path}/"
end
#else do nothinf
end
#drops the diff between saved abowl files back into the app_path
#this way, you can write code without having to worry about .git and stuff
#the mother source can be your template
#you just write the difference
#if you transend with a new release of the mother source, just add existing files in XCode after transending
def dropback
#the abowl has Classes, and the app has classes
#get all abowl classes and copy them over if they do not exist in the app_path
#do the same for views...
# handle artwork if any
files = Dir["#{abowl_path}/Artwork/*"]
if files&&files.size>0
#remove original artwork
FileUtils.rm_rf "#{app_path}/Artwork"
#copy this bowls artworks
FileUtils.cp_r "#{abowl_path}/Artwork", "#{app_path}/"
end
# handle transends if any
files = Dir["#{abowl_path}/Transends/*"]
if files&&files.size>0
#remove original transends
FileUtils.rm_rf "#{app_path}/Transends"
#copy this bowls transends
FileUtils.cp_r "#{abowl_path}/Transends", "#{app_path}/"
end
#copy from abowl.classes to app.classes if abowl.classes.file not in app.classes
`cp -rn '#{abowl_path}/Classes' '#{app_path}/';cp -rn '#{abowl_path}/Views' '#{app_path}/'`
end
#copies from transform and renames
def copy_and_rename
#prepare destination without any warning
FileUtils.rm_rf @app_path #`rm -rf #{@app_path}`
`cp -r #{@transform} #{@app_path}`
rename
end
#clones from transform then removes git
def clone_and_remove_git
#prepare destination without any warning
FileUtils.rm_rf @app_path #`rm -rf #{@app_path}`
if Object.const_defined? 'RSPEC'
#todo
#this does not create a .git inside the cloned project
`git --work-tree=#{@app_path} --git-dir=#{@app_path}/.git clone --no-hardlinks #{@transform} #{@app_path}`
else
`git clone --no-hardlinks #{@transform} #{@app_path}`
end
#remove any past life remains from the fresh project
FileUtils.rm_rf File.join(@app_path, 'build') #`rm -rf #{File.join(@app_path, 'build')}`
FileUtils.rm_rf File.join(@app_path, '.git') #`rm -rf #{File.join(@app_path, '.git')}`
#cleanup Transends and Artwork, so it is empty
FileUtils.rm_rf Dir.glob("#{@app_path}/Artwork/**/*")
FileUtils.rm_rf Dir.glob("#{@app_path}/Transends/**/*")
puts "Cloned from #{@transform} into #{@app_path}."
end
#renames within project app_title
def rename
todo = ""
if rename_files
if replace_strings_in_files
else
todo << 'TODO: You need to replace some strings in some files manually. Thanks.\n'
end
else
todo << 'TODO: You need to rename some files manually. Thanks.\n'
end
todo = todo.empty? ? "Renamed/replaced occurences of #{transform_title} to #{app_title}." : todo
puts todo
end
def zip
z = File.join(ji_path, "#{app_title}.zip")
FileUtils.rm_rf z #`rm -rf #{z}` #remove any previous zips without any warnings
#cd into and zip
`cd #{ji_path}; tar cvfz #{app_title}.zip #{app_title}/`
#return path to zip
puts "Zipped #{app_title} into #{z}" if File.exists?(z)
z
end
def transformize
clone_and_remove_git
rename
zip
end
def transendize
backdrop
clone_and_remove_git
rename
transend
dropback
zip
end
#Use maybe like this: Transender::Ji.xcode_rename(ahash)
def self.xcode_rename(t={})
Ji.new(t).copy_and_rename
end
#Use maybe like this: Transender::Ji.transform_and_zip(ahash) {|zip| render :text => zip}
def self.transform_and_zip(t={}, &block)
zip = Ji.new(t).transformize
yield zip if block
end
#Use maybe like this: Transender::Ji.transend_and_zip(ahash) {|zip| render :text => zip}
def self.transend_and_zip(t={}, &block)
zip = Ji.new(t).transendize
yield zip if block
end
#Transender::Ji.transend
#if there is abowl, it will be used for transending
def self.transend(t={})
ji = Ji.new(t)
ji.backdrop
ji.clone_and_remove_git
ji.rename
ji.transend
ji.dropback
end
private
def rename_files
FileUtils.mv "#{app_path}/#{transform_title}.xcodeproj", "#{app_path}/#{app_title}.xcodeproj"
Dir["#{app_path}/*.{m,h,pch,plist}"].each do |filename|
if filename =~ /#{transform_title}/
FileUtils.mv filename, filename.gsub(/#{transform_title}/, app_title)
end
end
Dir["#{app_path}/Classes/**/*.{m,h}"].each do |filename|
if filename =~ /#{transform_title}/
FileUtils.mv filename, filename.gsub(/#{transform_title}/, app_title)
end
end
true
rescue
puts $!
false
end
def replace_strings_in_files
Dir["#{app_path}/*.{m,h,pch,xib}"].each do |filename|
Transender.replace_strings_in_file(transform_title, app_title, filename)
end
Dir["#{app_path}/Views/*.{m,h,pch,xib}"].each do |filename|
Transender.replace_strings_in_file(transform_title, app_title, filename)
end
Dir["#{app_path}/Classes/**/*.{m,h}"].each do |filename|
Transender.replace_strings_in_file(transform_title, app_title, filename)
end
Dir["#{app_path}/#{app_title}.xcodeproj/*.*"].each do |filename|
Transender.replace_strings_in_file(transform_title, app_title, filename)
end
true
rescue
puts $!
false
end
private
def has_abowl?
File.exists? @abowl_yml
end
def make_abowl
unless File.exists? @abowl_yml #do not overwrite
FileUtils.cp_r File.join(LIBPATH, "abowl"), @ji_path
Transender.replace_strings_in_file('XappX', @app_title, @abowl_yml)
read_abowl
end
rescue
puts "Could not make abowl. #{$!}"
end
def read_abowl
@abowl = YAML.load_file @abowl_yml
rescue
puts "Could not read abowl. #{$!}" if File.exists? @app_path
nil
end
end # class Ji
end # module Transender
Transender.require_all_libs_relative_to(__FILE__)
# EOF