package com.aptana.radrails.cloud.shell; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.radrails.rails.ui.console.RailsShellCommandProvider; import org.rubypeople.rdt.launching.IRubyLaunchConfigurationConstants; import org.rubypeople.rdt.launching.ITerminal; import com.aptana.ide.server.cloud.services.model.studio.SiteUtils; import com.aptana.ide.server.cloud.services.model.studio.StudioSite; import com.aptana.radrails.cloud.internal.CloudUtil; public class AptanaCloudCommandProvider extends RailsShellCommandProvider { static final String LIST_TASKS_SWITCH = "-T"; public static final String APCLOUD = "apcloud"; public static final String APCLOUDIFY = "apcloudify"; public static final String STAGING = "staging"; public static final String PUBLIC = "public"; public static final String CLOUD_SETUP = "cloud:setup"; public static final String APTANA_DEPLOY = "cloud:deploy"; @Override public Set<String> commandsHandled() { Set<String> commands = new HashSet<String>(); commands.add(APCLOUD); commands.add(APCLOUDIFY); return commands; } @Override public List<ICompletionProposal> getCompletionProposals(String prefix, List<String> tokens, int offset) { List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); String token = getLastToken(prefix, tokens); // nothing, or first command only if (tokens.isEmpty() || (tokens.size() == 1 && !prefix.endsWith(" "))) { proposals.add(createProposal(APCLOUDIFY, "Runs a script to set your rails project up to deploy to the Aptana Cloud", offset, token)); proposals.add(createProposal(APCLOUD, "Runs commands to deploy your rails project to the Aptana Cloud", offset, token)); return proposals; } // second or later command // TODO Grab the list of tasks and offer them up, just like we do with Rake if (tokens.contains(APCLOUD)) { Map<String, String> tasks = new ApCloud().getTasks(getProject(), false); if (!containsArg(tokens, tasks.keySet()) && !tokens.contains(LIST_TASKS_SWITCH)) { if (!tokens.contains(PUBLIC) && !tokens.contains(STAGING)) { // Only add non-deploy tasks if user hasn't specified public/staging for (Map.Entry<String, String> entry : tasks.entrySet()) { if (!entry.getKey().contains("deploy")) { proposals.add(createProposal(entry.getKey(), entry.getValue(), offset, token)); } } proposals.add(createProposal(LIST_TASKS_SWITCH, "List all the available tasks", offset, token)); } else { // Add tasks containing "deploy" after user adds "public/staging" for (Map.Entry<String, String> entry : tasks.entrySet()) { // Add tasks with "deploy" in their names if (entry.getKey().contains("deploy")) { proposals.add(createProposal(entry.getKey(), entry.getValue(), offset, token)); } } } } } return proposals; } private boolean containsArg(List<String> tokens, Set<String> tasks) { for (String token : tokens) { for (String task : tasks) { if (task.equalsIgnoreCase(PUBLIC) || task.equalsIgnoreCase(STAGING)) continue; if (token.equals(task)) return true; } } return false; } @Override public void run(ITerminal shell, String command) { if (!command.startsWith(APCLOUDIFY) && !command.startsWith(APCLOUD)) { return; } // Check if they have the necessary gem installed! IStatus result = installCloudGemIfNecessary(); // if we failed to determine, or failed to install, the gem then don't try to run the command. if (!result.isOK()) return; if (command.startsWith(APCLOUDIFY)) { launchApCloudify(shell, command); } else if (command.startsWith(APCLOUD)) { launchInsideShell(shell, command, getEnvMap()); } } protected IStatus installCloudGemIfNecessary() { return CloudUtil.installCloudGemIfNecessary(null); } private void launchApCloudify(ITerminal shell, String command) { // They gave a path, it looks like... if (command.trim().length() > APCLOUDIFY.length()) { String path = command.trim().substring(APCLOUDIFY.length()).trim(); if (path.equals(".")) { // Assume active project IProject project = getProject(); launchInsideShell(shell, APCLOUDIFY + " " + getProjectArg(project), getEnvMap(), refreshAttrMap()); return; } // Hope it's a project name, then use full path to project and switch shell's active project to it. IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path); if (project != null && project.exists()) { launchInsideShell(shell, APCLOUDIFY + " " + getProjectArg(project), getEnvMap(), refreshAttrMap()); shell.setProject(project); return; } // Hmm, just try and interpret command as it is. launchInsideShell(shell, command, getEnvMap()); return; } // No path, assume they want it on active project IProject project = getProject(); launchInsideShell(shell, APCLOUDIFY + " " + getProjectArg(project), getEnvMap(), refreshAttrMap()); } private String getProjectArg(IProject project) { String result = project.getLocation().toOSString(); // Wrap in quotes if there's any whitespace! if (result.indexOf(' ') == -1) return result; return '"' + result + '"'; } private Map<String, Object> refreshAttrMap() { Map<String, Object> attrMap = new HashMap<String, Object>(); attrMap.put(IRubyLaunchConfigurationConstants.ATTR_REQUIRES_REFRESH, true); return attrMap; } private Map<String, String> getEnvMap() { IProject project = getProject(); if (project == null) return null; StudioSite site = SiteUtils.getSite(project); return CloudUtil.getEnvMap(site); } }