class Gena::Application
Public Class Methods
check_dependencies(plugin_name, dependencies)
click to toggle source
# File lib/cli/cli.rb, line 136 def self.check_dependencies(plugin_name, dependencies) missing_gems = {} dependencies.each do |gem_name, version| begin require gem_name rescue LoadError missing_gems[gem_name] = version end end if missing_gems.count > 0 puts "The following gems required for #{plugin_name.yellow}:\n\t#{missing_gems.keys.join(', ')}" puts "Would you like to install them? (Yn)" answer = STDIN.gets.chomp if answer == 'Y' || answer == 'y' || answer == '' || answer == 'yes' gems = missing_gems.collect { |k, v| "#{k}:#{v}" } command = "gem install #{gems.join(" ")}" puts command result = system(command) if result puts "All dependencies installed successfully! Run your command again.".green else puts "Error occured while installing dependencies. Please install them manually and try again.".red end return false else puts "Unable to run #{plugin_name}. Please install dependencies and try again.".red return false end else return true end end
class_for_command()
click to toggle source
class_for_command
- hash to store custom classes (actually Plugin
subclasses) for each command registered with gena
# File lib/cli/cli.rb, line 11 def class_for_command @class_for_command ||= Hash.new end
class_for_command=(commands)
click to toggle source
# File lib/cli/cli.rb, line 15 def class_for_command=(commands) @class_for_command = commands end
finish()
click to toggle source
# File lib/cli/cli.rb, line 93 def finish XcodeUtils.shared.save_project Application.new.check_gena_version end
help(shell, subcommand = false)
click to toggle source
Override help to forward
# File lib/cli/cli.rb, line 25 def help(shell, subcommand = false) #List plugin commands separately from Gena general commands plugins = [] class_for_command.each do |command, klass| plugins += klass.printable_commands(false) end plugins.uniq! list = printable_commands(true, subcommand) Thor::Util.thor_classes_in(self).each do |klass| list += klass.printable_commands(false) end list -= plugins # Remove this line to disable alphabetical sorting # list.sort! { |a, b| a[0] <=> b[0] } # Add this line to remove the help-command itself from the output # list.reject! {|l| l[0].split[1] == 'help'} if defined?(@package_name) && @package_name shell.say "#{@package_name} commands:" else shell.say "General commands:" end shell.print_table(list, :indent => 2, :truncate => true) shell.say class_options_help(shell) shell.say "Plugins:" shell.print_table(plugins, :indent => 2, :truncate => true) end
plugin_classes()
click to toggle source
# File lib/cli/cli.rb, line 19 def plugin_classes class_for_command.values.uniq end
start(given_args = ARGV, config = {})
click to toggle source
Override start to do custom dispatch (looking for plugin for unknown command)
# File lib/cli/cli.rb, line 65 def start(given_args = ARGV, config = {}) config[:shell] ||= Thor::Base.shell.new command_name = normalize_command_name(retrieve_command_name(given_args.dup)) clazz = command_name ? class_for_command[command_name] : nil if command_name && clazz if check_dependencies(command_name, clazz.dependencies) clazz.dispatch(nil, given_args.dup, nil, config) end else dispatch(nil, given_args.dup, nil, config) end finish rescue Thor::Error => e config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) exit(1) if exit_on_failure? rescue Errno::EPIPE # This happens if a thor command is piped to something like `head`, # which closes the pipe when it's done reading. This will also # mean that if the pipe is closed, further unnecessary # computation will not occur. exit(0) end
Public Instance Methods
ask_with_default(message, default)
click to toggle source
# File lib/cli/init.rb, line 267 def ask_with_default(message, default) value = ask(message) if value.empty? value = default end value end
check()
click to toggle source
# File lib/cli/init.rb, line 107 def check $verbose = (ARGV.include? '-v') if $verbose ARGV.reject! { |n| n == '-v' || n == '--verbose' } end unless Config.exists? if ARGV == ['init'] init ARGV.replace([]) else unless no? "'gena.plist' is not exists. Do you want to create new one? (Y/n)", Color::YELLOW init end end end $config = Gena::Config.new $config.load_plist_config unless $config.data say '\'gena.plist\' is corrupted! Try recreating', Color::RED abort end if !$config.data['plugins_url'] || $config.data['plugins_url'].count == 0 say "'plugins_url' key is missing inside 'gena.plist'", Color::RED abort end download_plugins load_plugins save_plugin_configs end
check_gena_version()
click to toggle source
# File lib/cli/cli.rb, line 104 def check_gena_version # Read cache say "Checking for update.." if $verbose version_path = File.expand_path "#{GENA_HOME}/version.plist" plist = File.exists?(version_path) ? Plist::parse_xml(version_path) : Hash.new data = nil if !plist['timestamp'] || (DateTime.now.to_time.to_i - plist['timestamp'].to_i) > GENA_UPDATE_CHECK_INTERVAL last_release_text = `curl https://api.github.com/repos/alexgarbarev/gena/releases/latest -s` data = JSON.parse(last_release_text) plist['data'] = data plist['timestamp'] = DateTime.now.to_time.to_i File.open(version_path, 'w') { |f| f.write plist.to_plist } else data = plist['data'] end tag_name = data['tag_name'] if tag_name > VERSION say "New update v#{tag_name} is available for gena.\nSee release notes: #{data['url']}", Color::YELLOW if data['body'].length > 0 say "---------------------\n#{data['body']}\n---------------------", Color::YELLOW end say "Please update by:\n#{set_color('gem install gena', Color::GREEN)}", Color::YELLOW end end
common_path_in_target(target, except_match)
click to toggle source
# File lib/cli/init.rb, line 286 def common_path_in_target(target, except_match) common = '' target.source_build_phase.files.each do |file| unless file.file_ref.real_path.to_s[except_match] path_components = file.file_ref.real_path.to_s #.split('/') if common.empty? common = path_components else common = common.path_intersection path_components end end end if File.file? common common = common.delete_last_path_component end if common[-1] == '/' common = common[0..-2] end common end
download_path_for(url)
click to toggle source
# File lib/cli/init.rb, line 194 def download_path_for(url) hash = Digest::MD5.hexdigest url File.expand_path "#{GENA_HOME}/plugins/#{hash}" end
download_plugin_at(url)
click to toggle source
# File lib/cli/init.rb, line 199 def download_plugin_at(url) say "Downloading plugin from '#{url}'..", Color::GREEN, ' ' output_path = File.expand_path(download_path_for(url)) FileUtils.rmtree output_path FileUtils.mkdir_p output_path result = gena_system "git clone --depth 1 #{url} #{output_path}" if result say 'success!', Color::GREEN @downloaded_urls[key_for_url(url)] = { 'hash' => `git ls-remote #{url} refs/heads/master | cut -f 1`.strip, 'timestamp' => DateTime.now.to_time.to_i } else say 'Failed! Run with \'-v\' to debug', Color::RED end end
download_plugins()
click to toggle source
# File lib/cli/init.rb, line 142 def download_plugins load_downloaded_urls $config.data['plugins_url'].each do |plugin_url| if remote_url? plugin_url is_old = old_url?(plugin_url) if !downloaded_url?(plugin_url) || is_old message = is_old ? 'not up to date' : 'not downloaded yet' say "Plugin at '#{plugin_url}' #{message}", Color::YELLOW download_plugin_at plugin_url end end end save_downloaded_urls end
downloaded_url?(url)
click to toggle source
# File lib/cli/init.rb, line 161 def downloaded_url?(url) File.exists? File.expand_path(download_path_for(url)) end
file_exists_in_target?(target, file_name)
click to toggle source
# File lib/cli/init.rb, line 308 def file_exists_in_target?(target, file_name) target.source_build_phase.files.each do |file| if File.basename(file.file_ref.real_path.to_s) == file_name return true end end false end
init()
click to toggle source
# File lib/cli/init.rb, line 28 def init xcode_project_name = `find . -name *.xcodeproj` xcode_project_name.strip! xcode_project_name = ask_with_default("Enter path for #{set_color('project', Color::YELLOW)} or ENTER to continue (#{xcode_project_name}):", xcode_project_name) xcode_project = Xcodeproj::Project.open(xcode_project_name) main_target = nil test_target = nil xcode_project.native_targets.each do |target| if target.product_type == 'com.apple.product-type.application' main_target = target elsif target.product_type == 'com.apple.product-type.bundle.unit-test' test_target = target end end hash = Hash.new hash[:plugins_url] = [ 'https://github.com/alexgarbarev/gena-plugins.git' ] unless main_target say "Can't find application target in your Xcode project. Please create application target and try again", Color::RED abort end default_build_configuration = main_target.build_configuration_list.default_configuration_name || 'Debug' info_plist_value = main_target.build_configuration_list.get_setting('INFOPLIST_FILE')[default_build_configuration] if info_plist_value['$(SRCROOT)/'] info_plist_value['$(SRCROOT)/'] = '' end hash['company'] = xcode_project.root_object.attributes['ORGANIZATIONNAME'].to_s hash['prefix'] = xcode_project.root_object.attributes['CLASSPREFIX'].to_s hash['project_name'] = xcode_project.root_object.name hash['info_plist'] = info_plist_value if main_target sources_path = common_path_in_target(main_target, 'main.m') sources_path = relative_to_current_dir(sources_path) hash['project_target'] = main_target.name hash['sources_dir'] = ask_with_default("Enter path for #{set_color('sources', Color::YELLOW)} or ENTER to continue (#{sources_path}):", sources_path) end if test_target tests_path = common_path_in_target(test_target, "#{sources_path}/") tests_path = relative_to_current_dir(tests_path) hash['test_target'] = test_target.name hash['tests_dir'] = ask_with_default("Enter path for #{set_color('tests', Color::YELLOW)} or ENTER to continue (#{tests_path}):", tests_path) else say "Can't find target for UnitTests. You setup it later using 'test_target' and 'tests_dir' key (similar to sources)", Color::YELLOW hash['test_target'] = '' hash['tests_dir'] = '' end language = file_exists_in_target?(main_target, 'main.m') ? "objc" : "swift" hash['language'] = ask_with_default("Enter main #{set_color('language', Color::YELLOW)} or ENTER to continue (#{language}):", language) say '===============================================================', Color::YELLOW print_table(hash) say '===============================================================', Color::YELLOW File.open('gena.plist', 'w') { |f| f.write hash.to_plist } say "'gena.plist' created..", Color::GREEN end
key_for_url(url)
click to toggle source
# File lib/cli/init.rb, line 190 def key_for_url(url) Digest::MD5.hexdigest url end
load_downloaded_urls()
click to toggle source
# File lib/cli/init.rb, line 177 def load_downloaded_urls plist_path = File.expand_path "#{GENA_HOME}/plugins.plist" if File.exists? plist_path @downloaded_urls = Plist::parse_xml(plist_path) else @downloaded_urls = Hash.new end end
load_plugins()
click to toggle source
# File lib/cli/init.rb, line 217 def load_plugins registered = [] $config.data['plugins_url'].each do |plugin_url| path = remote_url?(plugin_url) ? download_path_for(plugin_url) : plugin_url Dir["#{File.expand_path(path)}/**/*.rb"].each do |file| template_name = file.split(File::SEPARATOR)[-2] unless registered.include? template_name registered << template_name say "Loading '#{file}'..", Color::YELLOW if $verbose require file end end end Gena::Plugin.descendants.each do |clazz| clazz.setup_thor_commands end end
old_url?(url)
click to toggle source
# File lib/cli/init.rb, line 165 def old_url?(url) entity = @downloaded_urls[key_for_url(url)] return true unless entity elapsed_seconds = DateTime.now.to_time.to_i - entity['timestamp'].to_i if elapsed_seconds > GENA_UPDATE_CHECK_INTERVAL current_hash = `git ls-remote #{url} refs/heads/master | cut -f 1`.strip entity['timestamp'] = DateTime.now.to_time.to_i return current_hash != entity['hash'] end false end
reconfigure(command)
click to toggle source
# File lib/cli/init.rb, line 6 def reconfigure(command) $config = Gena::Config.new $config.load_plist_config data = $config.data[GENA_PLUGINS_CONFIG_KEY] || Hash.new Application.plugin_classes.each do |klass| plugin_config_name = klass.plugin_config_name if plugin_config_name == command say "Setting defaults for '#{command}'..", Color::GREEN data[plugin_config_name] = klass.plugin_config_defaults $config.data[GENA_PLUGINS_CONFIG_KEY] = data $config.save! return end end say "Can't find plugin with name '#{command}'", Color::RED end
relative_to_current_dir(path)
click to toggle source
# File lib/cli/init.rb, line 275 def relative_to_current_dir(path) if path.empty? || !path["#{Dir.pwd}/"] path else result = path.dup result["#{Dir.pwd}/"] = '' result end end
remote_url?(url)
click to toggle source
# File lib/cli/init.rb, line 157 def remote_url?(url) url =~ /(.+@)*([\w\d\.]+):(.*)/ end
save_downloaded_urls()
click to toggle source
# File lib/cli/init.rb, line 186 def save_downloaded_urls File.open(File.expand_path("#{GENA_HOME}/plugins.plist"), 'w') { |f| f.write @downloaded_urls.to_plist } end
save_plugin_configs()
click to toggle source
# File lib/cli/init.rb, line 235 def save_plugin_configs data = $config.data[GENA_PLUGINS_CONFIG_KEY] || Hash.new Application.plugin_classes.each do |klass| defaults = klass.plugin_config_defaults if defaults.count > 0 plugin_config_name = klass.plugin_config_name if !data[plugin_config_name] || data[plugin_config_name].count == 0 say "Writing config defaults for plugin '#{plugin_config_name}'..", Color::GREEN data[plugin_config_name] = defaults elsif !data[plugin_config_name].keys.to_set.eql? defaults.keys.to_set missing_keys = defaults.keys - data[plugin_config_name].keys if missing_keys.count > 0 say "Adding missing config keys #{missing_keys} for plugin '#{plugin_config_name}.'", Color::GREEN missing_keys.each { |key| data[plugin_config_name][key] = defaults[key] } end end end end $config.data[GENA_PLUGINS_CONFIG_KEY] = data $config.save! end