[sikuli] 209/385: my first test with JRuby

Gilles Filippini pini at moszumanska.debian.org
Sun Jun 29 19:26:14 UTC 2014


This is an automated email from the git hooks/post-receive script.

pini pushed a commit to tag upstream/1.1.0_beta1
in repository sikuli.

commit a979c5392c3e993ff7a4cb33e4a7708f6312bcac
Author: Raimund Hocke <info at its-me-raiman.de>
Date:   Thu Feb 13 16:33:30 2014 +0100

    my first test with JRuby
---
 JRuby/pom.xml                                      |   8 +-
 .../org/sikuli/scriptrunner/JRubyScriptRunner.java | 996 ++++++++++-----------
 JRuby/src/main/resources/rukuli.rb                 |  10 +
 JRuby/src/main/resources/rukuli/app.rb             |  33 +
 JRuby/src/main/resources/rukuli/clickable.rb       | 278 ++++++
 JRuby/src/main/resources/rukuli/config.rb          |  67 ++
 JRuby/src/main/resources/rukuli/exception.rb       |  14 +
 JRuby/src/main/resources/rukuli/key_code.rb        |  21 +
 JRuby/src/main/resources/rukuli/platform.rb        |  12 +
 JRuby/src/main/resources/rukuli/region.rb          |  88 ++
 JRuby/src/main/resources/rukuli/screen.rb          |  20 +
 JRuby/src/main/resources/rukuli/searchable.rb      | 134 +++
 JRuby/src/main/resources/rukuli/typeable.rb        |  32 +
 JRuby/src/main/resources/rukuli/version.rb         |   3 +
 JRuby/src/main/resources/sikulix.rb                |  16 +
 .../sikuli/scriptrunner/JythonScriptRunner.java    |  11 +-
 pom.xml                                            |   2 +-
 17 files changed, 1231 insertions(+), 514 deletions(-)

diff --git a/JRuby/pom.xml b/JRuby/pom.xml
index 9ca8612..fea6a4b 100755
--- a/JRuby/pom.xml
+++ b/JRuby/pom.xml
@@ -6,7 +6,7 @@
 	* Copyright 2010-2014, Sikuli.org
 	* Released under the MIT License.
 	*
-	* xxx, RaiMan 2014
+	* Roman S Samarev, RaiMan 2014
 	-->
 
 	<parent>
@@ -25,9 +25,9 @@
 
 	<developers>
 		<developer>
-			<id>xxx</id>
-			<name>yyy</name>
-			<email>zzz at web.de</email>
+			<id>RomanSSamarev</id>
+			<name>Roman S Samarev</name>
+			<email>samarev at acm.org</email>
 		</developer>
 	</developers>
 
diff --git a/JRuby/src/main/java/org/sikuli/scriptrunner/JRubyScriptRunner.java b/JRuby/src/main/java/org/sikuli/scriptrunner/JRubyScriptRunner.java
index 9597694..5cf5730 100644
--- a/JRuby/src/main/java/org/sikuli/scriptrunner/JRubyScriptRunner.java
+++ b/JRuby/src/main/java/org/sikuli/scriptrunner/JRubyScriptRunner.java
@@ -2,7 +2,7 @@
  * Copyright 2010-2014, Sikuli.org
  * Released under the MIT License.
  *
- * WhoIsWho 2014
+ * Roman S Samarev 2014
  */
 package org.sikuli.scriptrunner;
 
@@ -33,511 +33,507 @@ import org.jruby.javasupport.JavaEmbedUtils.EvalUnit;
 import org.jruby.CompatVersion;
 import org.jruby.embed.LocalContextScope;
 import org.jruby.RubyInstanceConfig.CompileMode;
-import org.jruby.embed.PathType;
 
 public class JRubyScriptRunner implements IScriptRunner {
 
-    //<editor-fold defaultstate="collapsed" desc="new logging concept">
-    private static final String me = "JRubyScriptRunner: ";
-    private int lvl = 3;
-
-    private void log(int level, String message, Object... args) {
-        Debug.logx(level, level < 0 ? "error" : "debug",
-                me + message, args);
-    }
-      //</editor-fold>
-
-    /**
-     * The ScriptingContainer instance
-     */
-    private static ScriptingContainer interpreter = null;
-    private static int savedpathlen = 0;
-    private static final String COMPILE_ONLY = "# COMPILE ONLY";
-    /**
-     * sys.argv for the jruby script
-     */
-    private static ArrayList<String> sysargv = null;
-    /**
-     * The header commands, that are executed before every script
-     */
-    private final static String SCRIPT_HEADER
-            = "# coding: utf-8\n"
-            + "require 'java'\n"
-            + "require 'rukuli'\n"
-            + "require 'sikulix'\n"
-            + "Rukuli::Config.run do |config|\n"
-            + "  config.image_path = SIKULI_IMAGE_PATH + '/'\n"
-            + "  config.logging = true\n"
-            + "end\n";
-
-    private static ArrayList<String> codeBefore = null;
-    private static ArrayList<String> codeAfter = null;
-    /**
-     * CommandLine args
-     */
-    private int errorLine;
-    private int errorColumn;
-    private String errorType;
-    private String errorText;
-    private int errorClass;
-    private String errorTrace;
-
-    private static final int PY_SYNTAX = 0;
-    private static final int PY_RUNTIME = 1;
-    private static final int PY_JAVA = 2;
-    private static final int PY_UNKNOWN = -1;
-
-    private static String sikuliLibPath;
-
-    private static String timestampBuilt;
-    private static final String tsb = "##--##Fri Jan  28 13:06:44 MSD 2014##--##";
-
-    @Override
-    public void init(String[] args) {
-        //TODO classpath and other path handlings
-        sikuliLibPath = new File(SikuliX.getJarPath(), "Lib").getAbsolutePath();
-        if (!SikuliX.isRunningFromJar()
-                || !sikuliLibPath.contains("sikuli-ide")
-                || !sikuliLibPath.contains("sikuli-script")) {
-            if (System.getProperty("ruby.path") == null) {
-                System.setProperty("ruby.path", sikuliLibPath);
-                log(lvl, "init: python.path hack: \n" + System.getProperty("ruby.path"));
-            } else {
-                String currentPath = System.getProperty("ruby.path");
-                if (!FileManager.pathEquals(currentPath, sikuliLibPath)) {
-                    log(-1, "init: Not running from jar and Ruby path not empty: Sikuli might not work!\n"
-                            + "Current python.path: " + currentPath);
-                }
-            }
-        }
-    }
-
-    @Override
-    public int runScript(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE) {
-        if (null == scriptfile) {
-            //run the Ruby statements from argv (special for setup functional test)
-            fillSysArgv(null, null);
-            createScriptingContainer();
-            interpreter.put("SIKULI_IMAGE_PATH",
-                    imagedirectory.getAbsolutePath());
-            executeScriptHeader(new String[0]);
-            SikuliX.displaySplash(null);
-            return runRuby(null, scriptArgs, null);
-        }
-        scriptfile = new File(scriptfile.getAbsolutePath());
-        fillSysArgv(scriptfile, scriptArgs);
-        createScriptingContainer();
-        interpreter.put("SIKULI_IMAGE_PATH",
-                imagedirectory.getAbsolutePath());
-        if (forIDE == null) {
-            executeScriptHeader(new String[]{
-                scriptfile.getParentFile().getAbsolutePath(),
-                scriptfile.getParentFile().getParentFile().getAbsolutePath()});
-        } else {
-            executeScriptHeader(new String[]{
-                forIDE[0]});
-        }
-        int exitCode = 0;
-        SikuliX.displaySplashFirstTime(null);
-        SikuliX.displaySplash(null);
-        if (forIDE == null) {
-            exitCode = runRuby(scriptfile, null,
-                    new String[]{scriptfile.getParentFile().getAbsolutePath()});
-        } else {
-            exitCode = runRuby(scriptfile, null, forIDE);
-        }
-        log(lvl + 1, "runScript: at exit: path:");
-        for (Object p : interpreter.getLoadPaths()) {
-            log(lvl + 1, "runScript: " + p.toString());
-        }
-        log(lvl + 1, "runScript: at exit: --- end ---");
-        return exitCode;
-    }
-
-    @Override
-    public int runTest(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE) {
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-    }
-
-    @Override
-    public int runInteractive(String[] scriptArgs) {
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-    }
-
-    @Override
-    public String getCommandLineHelp() {
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-    }
-
-    @Override
-    public String getInteractiveHelp() {
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-    }
-
-    @Override
-    public String getName() {
-        return "jruby";
-    }
-
-    @Override
-    public String[] getFileEndings() {
-        return new String[]{"rb"};
-    }
-
-    @Override
-    public String hasFileEnding(String ending) {
-        for (String suf : getFileEndings()) {
-            if (suf.equals(ending.toLowerCase())) {
-                return suf;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void close() {
-        if (interpreter != null) {
-            interpreter.clear();
-        }
-    }
-
-    @Override
-    public boolean doSomethingSpecial(String action, Object[] args) {
-        if ("redirect".equals(action)) {
-            doRedirect((PipedInputStream[]) args);
-            return true;
-        } else if ("convertSrcToHtml".equals(action)) {
-            //convertSrcToHtml((String) args[0]);
-            return true;
-        } else if ("cleanBundle".equals(action)) {
-            //cleanBundle((String) args[0]);
-            return true;
-        } else if ("createRegionForWith".equals(action)) {
-            //args[0] = createRegionForWith(args[0]);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public void execBefore(String[] stmts) {
-        if (stmts == null) {
-            codeBefore = null;
-            return;
-        }
-        if (codeBefore == null) {
-            codeBefore = new ArrayList<String>();
-        }
-        codeBefore.addAll(Arrays.asList(stmts));
-    }
-
-    @Override
-    public void execAfter(String[] stmts) {
-        if (stmts == null) {
-            codeAfter = null;
-            return;
-        }
-        if (codeAfter == null) {
-            codeAfter = new ArrayList<String>();
-        }
-        codeAfter.addAll(Arrays.asList(stmts));
-    }
-
-    private int runRuby(File ruFile, String[] stmts, String[] scriptPaths) {
-        int exitCode = 0;
-        String stmt = "";
-        boolean fromIDE = false;
-        String filename = "<script>";
-        try {
-            if (null == ruFile) {
-                log(lvl, "runRuby: running statements");
-                StringBuilder buffer = new StringBuilder();
-                for (String e : stmts) {
-                    buffer.append(e);
-                }
-                interpreter.setScriptFilename(filename);
-                interpreter.runScriptlet(buffer.toString());
-            } else {
-                filename = ruFile.getAbsolutePath();
-                if (scriptPaths != null) {
-                    FileReader script = new FileReader(ruFile.getAbsolutePath());
+	//<editor-fold defaultstate="collapsed" desc="new logging concept">
+	private static final String me = "JRubyScriptRunner: ";
+	private int lvl = 3;
+
+	private void log(int level, String message, Object... args) {
+		Debug.logx(level, level < 0 ? "error" : "debug",
+						me + message, args);
+	}
+	//</editor-fold>
+
+	/**
+	 * The ScriptingContainer instance
+	 */
+	private static ScriptingContainer interpreter = null;
+	private static int savedpathlen = 0;
+	private static final String COMPILE_ONLY = "# COMPILE ONLY";
+	/**
+	 * sys.argv for the jruby script
+	 */
+	private static ArrayList<String> sysargv = null;
+	/**
+	 * The header commands, that are executed before every script
+	 */
+	private final static String SCRIPT_HEADER
+					= "# coding: utf-8\n"
+					+ "require 'java'\n"
+					+ "require 'rukuli'\n"
+					+ "require 'sikulix'\n"
+					+ "Rukuli::Config.run do |config|\n"
+					+ "  config.image_path = SIKULI_IMAGE_PATH + '/'\n"
+					+ "  config.logging = true\n"
+					+ "end\n";
+
+	private static ArrayList<String> codeBefore = null;
+	private static ArrayList<String> codeAfter = null;
+	/**
+	 * CommandLine args
+	 */
+	private int errorLine;
+	private int errorColumn;
+	private String errorType;
+	private String errorText;
+	private int errorClass;
+	private String errorTrace;
+
+	private static final int PY_SYNTAX = 0;
+	private static final int PY_RUNTIME = 1;
+	private static final int PY_JAVA = 2;
+	private static final int PY_UNKNOWN = -1;
+
+	private static String sikuliLibPath;
+
+	@Override
+	public void init(String[] args) {
+		//TODO classpath and other path handlings
+		sikuliLibPath = new File(SikuliX.getJarPath(), "Lib").getAbsolutePath();
+		if (!SikuliX.isRunningFromJar()
+						|| !sikuliLibPath.contains("sikuli-ide")
+						|| !sikuliLibPath.contains("sikuli-script")) {
+			if (System.getProperty("ruby.path") == null) {
+				System.setProperty("ruby.path", sikuliLibPath);
+				log(lvl, "init: python.path hack: \n" + System.getProperty("ruby.path"));
+			} else {
+				String currentPath = System.getProperty("ruby.path");
+				if (!FileManager.pathEquals(currentPath, sikuliLibPath)) {
+					log(-1, "init: Not running from jar and Ruby path not empty: Sikuli might not work!\n"
+									+ "Current python.path: " + currentPath);
+				}
+			}
+		}
+	}
+
+	@Override
+	public int runScript(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE) {
+		if (null == scriptfile) {
+			//run the Ruby statements from argv (special for setup functional test)
+			fillSysArgv(null, null);
+			createScriptingContainer();
+			interpreter.put("SIKULI_IMAGE_PATH",
+							imagedirectory.getAbsolutePath());
+			executeScriptHeader(new String[0]);
+			SikuliX.displaySplash(null);
+			return runRuby(null, scriptArgs, null);
+		}
+		scriptfile = new File(scriptfile.getAbsolutePath());
+		fillSysArgv(scriptfile, scriptArgs);
+		createScriptingContainer();
+		interpreter.put("SIKULI_IMAGE_PATH",
+						imagedirectory.getAbsolutePath());
+		if (forIDE == null) {
+			executeScriptHeader(new String[]{
+				scriptfile.getParentFile().getAbsolutePath(),
+				scriptfile.getParentFile().getParentFile().getAbsolutePath()});
+		} else {
+			executeScriptHeader(new String[]{
+				forIDE[0]});
+		}
+		int exitCode = 0;
+		SikuliX.displaySplashFirstTime(null);
+		SikuliX.displaySplash(null);
+		if (forIDE == null) {
+			exitCode = runRuby(scriptfile, null,
+							new String[]{scriptfile.getParentFile().getAbsolutePath()});
+		} else {
+			exitCode = runRuby(scriptfile, null, forIDE);
+		}
+		log(lvl + 1, "runScript: at exit: path:");
+		for (Object p : interpreter.getLoadPaths()) {
+			log(lvl + 1, "runScript: " + p.toString());
+		}
+		log(lvl + 1, "runScript: at exit: --- end ---");
+		return exitCode;
+	}
+
+	@Override
+	public int runTest(File scriptfile, File imagedirectory, String[] scriptArgs, String[] forIDE) {
+		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+	}
+
+	@Override
+	public int runInteractive(String[] scriptArgs) {
+		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+	}
+
+	@Override
+	public String getCommandLineHelp() {
+		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+	}
+
+	@Override
+	public String getInteractiveHelp() {
+		throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+	}
+
+	@Override
+	public String getName() {
+		return "jruby";
+	}
+
+	@Override
+	public String[] getFileEndings() {
+		return new String[]{"rb"};
+	}
+
+	@Override
+	public String hasFileEnding(String ending) {
+		for (String suf : getFileEndings()) {
+			if (suf.equals(ending.toLowerCase())) {
+				return suf;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public void close() {
+		if (interpreter != null) {
+			interpreter.clear();
+		}
+	}
+
+	@Override
+	public boolean doSomethingSpecial(String action, Object[] args) {
+		if ("redirect".equals(action)) {
+			doRedirect((PipedInputStream[]) args);
+			return true;
+		} else if ("convertSrcToHtml".equals(action)) {
+			//convertSrcToHtml((String) args[0]);
+			return true;
+		} else if ("cleanBundle".equals(action)) {
+			//cleanBundle((String) args[0]);
+			return true;
+		} else if ("createRegionForWith".equals(action)) {
+			//args[0] = createRegionForWith(args[0]);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	@Override
+	public void execBefore(String[] stmts) {
+		if (stmts == null) {
+			codeBefore = null;
+			return;
+		}
+		if (codeBefore == null) {
+			codeBefore = new ArrayList<String>();
+		}
+		codeBefore.addAll(Arrays.asList(stmts));
+	}
+
+	@Override
+	public void execAfter(String[] stmts) {
+		if (stmts == null) {
+			codeAfter = null;
+			return;
+		}
+		if (codeAfter == null) {
+			codeAfter = new ArrayList<String>();
+		}
+		codeAfter.addAll(Arrays.asList(stmts));
+	}
+
+	private int runRuby(File ruFile, String[] stmts, String[] scriptPaths) {
+		int exitCode = 0;
+		String stmt = "";
+		boolean fromIDE = false;
+		String filename = "<script>";
+		try {
+			if (null == ruFile) {
+				log(lvl, "runRuby: running statements");
+				StringBuilder buffer = new StringBuilder();
+				for (String e : stmts) {
+					buffer.append(e);
+				}
+				interpreter.setScriptFilename(filename);
+				interpreter.runScriptlet(buffer.toString());
+			} else {
+				filename = ruFile.getAbsolutePath();
+				if (scriptPaths != null) {
+					FileReader script = new FileReader(ruFile.getAbsolutePath());
 // TODO implement compile only !!!
-                    if (scriptPaths[0].toUpperCase().equals(COMPILE_ONLY)) {
-                        log(lvl, "runRuby: running COMPILE_ONLY");
-                        EvalUnit unit = interpreter.parse(script, filename);
-                        //unit.run();
-                    } else {
-                        if (scriptPaths.length > 1) {
-                            filename = FileManager.slashify(scriptPaths[0], true) + 
-                                    scriptPaths[1] + ".sikuli";
-                            log(lvl, "runRuby: running script from IDE: \n" + filename);
-                            fromIDE = true;
-                        } else {
-                            filename = scriptPaths[0];
-                            log(lvl, "runRuby: running script: \n" + filename);
-                        }                        
-                        interpreter.runScriptlet(script, filename);
-                    }
-                } else {
-                    log(-1, "runRuby: invalid arguments");
-                    exitCode = -1;
-                }
-            }
-        } catch (Exception e) {
-            java.util.regex.Pattern p
-                    = java.util.regex.Pattern.compile("SystemExit: ([0-9]+)");
-            Matcher matcher = p.matcher(e.toString());
+					if (scriptPaths[0].toUpperCase().equals(COMPILE_ONLY)) {
+						log(lvl, "runRuby: running COMPILE_ONLY");
+						EvalUnit unit = interpreter.parse(script, filename);
+						//unit.run();
+					} else {
+						if (scriptPaths.length > 1) {
+							filename = FileManager.slashify(scriptPaths[0], true)
+											+ scriptPaths[1] + ".sikuli";
+							log(lvl, "runRuby: running script from IDE: \n" + filename);
+							fromIDE = true;
+						} else {
+							filename = scriptPaths[0];
+							log(lvl, "runRuby: running script: \n" + filename);
+						}
+						interpreter.runScriptlet(script, filename);
+					}
+				} else {
+					log(-1, "runRuby: invalid arguments");
+					exitCode = -1;
+				}
+			}
+		} catch (Exception e) {
+			java.util.regex.Pattern p
+							= java.util.regex.Pattern.compile("SystemExit: ([0-9]+)");
+			Matcher matcher = p.matcher(e.toString());
 //TODO error stop I18N
-            if (matcher.find()) {
-                exitCode = Integer.parseInt(matcher.group(1));
-                Debug.info("Exit code: " + exitCode);
-            } else {
-                //log(-1,_I("msgStopped"));
-                if (null != ruFile) {
-                    exitCode = findErrorSource(e, filename, scriptPaths);
-                } else {
-                    Debug.error("runRuby: Ruby exception: %s with %s", e.getMessage(), stmt);
-                }
-                if (fromIDE) {
-                    exitCode *= -1;
-                } else {
-                    exitCode = 1;
-                }
-            }
-        }
-        return exitCode;
-    }
-
-    private int findErrorSource(Throwable thr, String filename, String[] forIDE) {
-        String err = thr.getMessage();
-
-        errorLine = -1;
-        errorColumn = -1;
-        errorClass = PY_UNKNOWN;
-        errorType = "--UnKnown--";
-        errorText = "--UnKnown--";
-
-        String msg;
-
-        if (err.startsWith("(SyntaxError)")) {
+			if (matcher.find()) {
+				exitCode = Integer.parseInt(matcher.group(1));
+				Debug.info("Exit code: " + exitCode);
+			} else {
+				//log(-1,_I("msgStopped"));
+				if (null != ruFile) {
+					exitCode = findErrorSource(e, filename, scriptPaths);
+				} else {
+					Debug.error("runRuby: Ruby exception: %s with %s", e.getMessage(), stmt);
+				}
+				if (fromIDE) {
+					exitCode *= -1;
+				} else {
+					exitCode = 1;
+				}
+			}
+		}
+		return exitCode;
+	}
+
+	private int findErrorSource(Throwable thr, String filename, String[] forIDE) {
+		String err = thr.getMessage();
+
+		errorLine = -1;
+		errorColumn = -1;
+		errorClass = PY_UNKNOWN;
+		errorType = "--UnKnown--";
+		errorText = "--UnKnown--";
+
+		String msg;
+
+		if (err.startsWith("(SyntaxError)")) {
             //org.jruby.parser.ParserSyntaxException
-            //(SyntaxError) /tmp/sikuli-3213678404470696048.rb:2: syntax error, unexpected tRCURLY
-
-            Pattern pLineS = Pattern.compile("(?<=:)(.*):(.*)");
-            java.util.regex.Matcher mLine = pLineS.matcher(err);
-            if (mLine.find()) {
-                log(lvl + 2, "SyntaxError error line: " + mLine.group(1));
-                errorText = mLine.group(2) == null ? errorText : mLine.group(2);
-                log(lvl + 2, "SyntaxError: " + errorText);
-                errorLine = Integer.parseInt(mLine.group(1));
-                errorColumn = -1;
-                errorClass = PY_SYNTAX;
-                errorType = "SyntaxError";
-            }
-        } else {
+			//(SyntaxError) /tmp/sikuli-3213678404470696048.rb:2: syntax error, unexpected tRCURLY
+
+			Pattern pLineS = Pattern.compile("(?<=:)(.*):(.*)");
+			java.util.regex.Matcher mLine = pLineS.matcher(err);
+			if (mLine.find()) {
+				log(lvl + 2, "SyntaxError error line: " + mLine.group(1));
+				errorText = mLine.group(2) == null ? errorText : mLine.group(2);
+				log(lvl + 2, "SyntaxError: " + errorText);
+				errorLine = Integer.parseInt(mLine.group(1));
+				errorColumn = -1;
+				errorClass = PY_SYNTAX;
+				errorType = "SyntaxError";
+			}
+		} else {
             //if (err.startsWith("(NameError)")) {
-            // org.jruby.embed.EvalFailedException
-            //(NameError) undefined local variable or method `asdf' for main:Object
-
-            Pattern type = Pattern.compile("(?<=\\()(\\w*)");
-            java.util.regex.Matcher mLine = type.matcher(err);
-            if (mLine.find()) {
-                errorType = mLine.group(1);
-            }
-            Throwable cause = thr.getCause();
-            //cause.printStackTrace();
-            for (StackTraceElement line : cause.getStackTrace()) {
-                if (line.getFileName().equals(filename)) {
-                    errorText = cause.getMessage();
-                    errorColumn = -1;
-                    errorLine = line.getLineNumber();
-                    errorClass = PY_RUNTIME;
-                    this.errorText = thr.getMessage();
-
-                    if (errorType.equals("Rukuli::ImageNotFound")) {
-                        errorType = "FindFailed";
-                    } else if (errorType.equals("RuntimeError")) {
-                        errorClass = PY_JAVA;
-                    }
-                    //errorType = "NameError";
-                    break;
-                }
-            }
-        }
-
-        msg = "script";
-        if (forIDE != null) {
-            msg += " [ " + forIDE[1] + " ]";
-        }
-        if (errorLine != -1) {
-            //log(-1,_I("msgErrorLine", srcLine));
-            msg += " stopped with error in line " + errorLine;
-            if (errorColumn != -1) {
-                msg += " at column " + errorColumn;
-            }
-        } else {
-            msg += "] stopped with error at line --unknown--";
-        }
-
-        if (errorClass == PY_RUNTIME || errorClass == PY_SYNTAX) {
-            Debug.error(msg);
-            Debug.error(errorType + " ( " + errorText + " )");
-            if (errorClass == PY_RUNTIME) {
-                Throwable cause = thr.getCause();
-                //cause.printStackTrace();
-                StackTraceElement[] stack = cause.getStackTrace();
-                /*StringWriter writer = new StringWriter();
-                 PrintWriter out = new PrintWriter(writer);                    
-                 cause.printStackTrace(out);
-                 errorTrace = writer.toString();*/
-                StringBuilder builder = new StringBuilder();
-                for (StackTraceElement line : stack) {
-                    builder.append(line.getLineNumber());
-                    builder.append(":\t");
-                    builder.append(line.getClassName());
-                    builder.append(" ( ");
-                    builder.append(line.getMethodName());
-                    builder.append(" )\t");
-                    builder.append(line.getFileName());
-                    builder.append('\n');
-                }
-                errorTrace = builder.toString();
-                if (errorTrace.length() > 0) {
-                    Debug.error("--- Traceback --- error source first\n"
-                            + "line: class ( method ) file \n" + errorTrace
-                            + "[error] --- Traceback --- end --------------");
-                    log(lvl + 2, "--- Traceback --- error source first\n"
-                            + "line: class ( method ) file \n" + errorTrace
-                            + "[error] --- Traceback --- end --------------");
-                }
-            }
-        } else if (errorClass == PY_JAVA) {
-        } else {
-            Debug.error(msg);
-            Debug.error("Could not evaluate error source nor reason. Analyze StackTrace!");
-            Debug.error(err);
-        }
-        return errorLine;
-    }
-
-    /**
-     * Initializes the ScriptingContainer and creates an instance.
-     */
-    private void createScriptingContainer() {
+			// org.jruby.embed.EvalFailedException
+			//(NameError) undefined local variable or method `asdf' for main:Object
+
+			Pattern type = Pattern.compile("(?<=\\()(\\w*)");
+			java.util.regex.Matcher mLine = type.matcher(err);
+			if (mLine.find()) {
+				errorType = mLine.group(1);
+			}
+			Throwable cause = thr.getCause();
+			//cause.printStackTrace();
+			for (StackTraceElement line : cause.getStackTrace()) {
+				if (line.getFileName().equals(filename)) {
+					errorText = cause.getMessage();
+					errorColumn = -1;
+					errorLine = line.getLineNumber();
+					errorClass = PY_RUNTIME;
+					this.errorText = thr.getMessage();
+
+					if (errorType.equals("Rukuli::ImageNotFound")) {
+						errorType = "FindFailed";
+					} else if (errorType.equals("RuntimeError")) {
+						errorClass = PY_JAVA;
+					}
+					//errorType = "NameError";
+					break;
+				}
+			}
+		}
+
+		msg = "script";
+		if (forIDE != null) {
+			msg += " [ " + forIDE[1] + " ]";
+		}
+		if (errorLine != -1) {
+			//log(-1,_I("msgErrorLine", srcLine));
+			msg += " stopped with error in line " + errorLine;
+			if (errorColumn != -1) {
+				msg += " at column " + errorColumn;
+			}
+		} else {
+			msg += "] stopped with error at line --unknown--";
+		}
+
+		if (errorClass == PY_RUNTIME || errorClass == PY_SYNTAX) {
+			Debug.error(msg);
+			Debug.error(errorType + " ( " + errorText + " )");
+			if (errorClass == PY_RUNTIME) {
+				Throwable cause = thr.getCause();
+				//cause.printStackTrace();
+				StackTraceElement[] stack = cause.getStackTrace();
+				/*StringWriter writer = new StringWriter();
+				 PrintWriter out = new PrintWriter(writer);
+				 cause.printStackTrace(out);
+				 errorTrace = writer.toString();*/
+				StringBuilder builder = new StringBuilder();
+				for (StackTraceElement line : stack) {
+					builder.append(line.getLineNumber());
+					builder.append(":\t");
+					builder.append(line.getClassName());
+					builder.append(" ( ");
+					builder.append(line.getMethodName());
+					builder.append(" )\t");
+					builder.append(line.getFileName());
+					builder.append('\n');
+				}
+				errorTrace = builder.toString();
+				if (errorTrace.length() > 0) {
+					Debug.error("--- Traceback --- error source first\n"
+									+ "line: class ( method ) file \n" + errorTrace
+									+ "[error] --- Traceback --- end --------------");
+					log(lvl + 2, "--- Traceback --- error source first\n"
+									+ "line: class ( method ) file \n" + errorTrace
+									+ "[error] --- Traceback --- end --------------");
+				}
+			}
+		} else if (errorClass == PY_JAVA) {
+		} else {
+			Debug.error(msg);
+			Debug.error("Could not evaluate error source nor reason. Analyze StackTrace!");
+			Debug.error(err);
+		}
+		return errorLine;
+	}
+
+	/**
+	 * Initializes the ScriptingContainer and creates an instance.
+	 */
+	private void createScriptingContainer() {
 //TODO create a specific RubyPath (sys.path)
-        if (interpreter == null) {
-            //ScriptingContainer.initialize(System.getProperties(), null, sysargv.toArray(new String[0]));
-
-            interpreter = new ScriptingContainer(
-                    LocalContextScope.THREADSAFE);
-            interpreter.setCompatVersion(CompatVersion.RUBY2_0);
-            interpreter.setCompileMode(CompileMode.JIT);
-        }
-    }
-
-    public ScriptingContainer getScriptingContainer() {
-        if (interpreter == null) {
-            sysargv = new ArrayList<String>();
-            sysargv.add("--???--");
-            sysargv.addAll(Arrays.asList(Settings.getArgs()));
-            createScriptingContainer();
-        }
-        return interpreter;
-    }
-
-    /**
-     * Executes the defined header for the jython script.
-     *
-     * @param syspaths List of all syspath entries
-     */
-    private void executeScriptHeader(String[] syspaths) {
+		if (interpreter == null) {
+			//ScriptingContainer.initialize(System.getProperties(), null, sysargv.toArray(new String[0]));
+
+			interpreter = new ScriptingContainer(
+							LocalContextScope.THREADSAFE);
+			interpreter.setCompatVersion(CompatVersion.RUBY2_0);
+			interpreter.setCompileMode(CompileMode.JIT);
+		}
+	}
+
+	public ScriptingContainer getScriptingContainer() {
+		if (interpreter == null) {
+			sysargv = new ArrayList<String>();
+			sysargv.add("--???--");
+			sysargv.addAll(Arrays.asList(Settings.getArgs()));
+			createScriptingContainer();
+		}
+		return interpreter;
+	}
+
+	/**
+	 * Executes the defined header for the jython script.
+	 *
+	 * @param syspaths List of all syspath entries
+	 */
+	private void executeScriptHeader(String[] syspaths) {
 // TODO implement compile only
-        if (syspaths.length > 0 && syspaths[0].toUpperCase().equals(COMPILE_ONLY)) {
-            return;
-        }
-        List<String> jypath = interpreter.getLoadPaths();
-        if (!FileManager.pathEquals((String) jypath.get(0), sikuliLibPath)) {
-            log(lvl, "executeScriptHeader: adding SikuliX Lib path to sys.path\n" + sikuliLibPath);
-            int jypathLength = jypath.size();
-            String[] jypathNew = new String[jypathLength + 1];
-            jypathNew[0] = sikuliLibPath;
-            for (int i = 0; i < jypathLength; i++) {
-                log(lvl + 1, "executeScriptHeader: before: %d: %s", i, jypath.get(i));
-                jypathNew[i + 1] = (String) jypath.get(i);
-            }
-            for (int i = 0; i < jypathLength; i++) {
-                jypath.set(i, jypathNew[i]);
-            }
-            jypath.add(jypathNew[jypathNew.length - 1]);
-            for (int i = 0; i < jypathNew.length; i++) {
-                log(lvl + 1, "executeScriptHeader: after: %d: %s", i, jypath.get(i));
-            }
-        }
-        if (savedpathlen == 0) {
-            savedpathlen = interpreter.getLoadPaths().size();
-            log(lvl + 1, "executeScriptHeader: saved sys.path: %d", savedpathlen);
-        }
-        while (interpreter.getLoadPaths().size() > savedpathlen) {
-            interpreter.getLoadPaths().remove(savedpathlen);
-        }
-        log(lvl + 1, "executeScriptHeader: at entry: path:");
-        for (Object p : interpreter.getLoadPaths()) {
-            log(lvl + 1, p.toString());
-        }
-        log(lvl + 1, "executeScriptHeader: at entry: --- end ---");
-        for (String syspath : syspaths) {
-            jypath.add(FileManager.slashify(syspath, false));
-        }
-
-        interpreter.runScriptlet(SCRIPT_HEADER);
-
-        if (codeBefore != null) {
-            StringBuilder buffer = new StringBuilder();
-            for (String line : codeBefore) {
-                buffer.append(line);
-            }
-            interpreter.runScriptlet(buffer.toString());
-        }
-    }
-
-    private boolean doRedirect(PipedInputStream[] pin) {
-        ScriptingContainer interpreter = getScriptingContainer();
-        try {
-            PipedOutputStream pout = new PipedOutputStream(pin[0]);
-            PrintStream ps = new PrintStream(pout, true);
-            System.setOut(ps);
-            interpreter.setOutput(ps);
-        } catch (Exception e) {
-            log(-1, "doRedirect: Couldn't redirect STDOUT\n%s", e.getMessage());
-            return false;
-        }
-        try {
-            PipedOutputStream pout = new PipedOutputStream(pin[1]);
-            PrintStream ps = new PrintStream(pout, true);
-            System.setErr(ps);
-            interpreter.setError(ps);
-        } catch (Exception e) {
-            log(-1, "doRedirect: Couldn't redirect STDERR\n%s", e.getMessage());
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Fills the sysargv list for the Ruby script
-     *
-     * @param filename The file containing the script: Has to be passed as first
-     * parameter in Ruby
-     * @param argv The parameters passed to Sikuli with --args
-     */
-    private void fillSysArgv(File filename, String[] argv) {
-        sysargv = new ArrayList<String>();
-        if (filename != null) {
-            sysargv.add(filename.getAbsolutePath());
-        }
-        if (argv != null) {
-            sysargv.addAll(Arrays.asList(argv));
-        }
-    }
-}
\ No newline at end of file
+		if (syspaths.length > 0 && syspaths[0].toUpperCase().equals(COMPILE_ONLY)) {
+			return;
+		}
+		List<String> jypath = interpreter.getLoadPaths();
+		if (!FileManager.pathEquals((String) jypath.get(0), sikuliLibPath)) {
+			log(lvl, "executeScriptHeader: adding SikuliX Lib path to sys.path\n" + sikuliLibPath);
+			int jypathLength = jypath.size();
+			String[] jypathNew = new String[jypathLength + 1];
+			jypathNew[0] = sikuliLibPath;
+			for (int i = 0; i < jypathLength; i++) {
+				log(lvl + 1, "executeScriptHeader: before: %d: %s", i, jypath.get(i));
+				jypathNew[i + 1] = (String) jypath.get(i);
+			}
+			for (int i = 0; i < jypathLength; i++) {
+				jypath.set(i, jypathNew[i]);
+			}
+			jypath.add(jypathNew[jypathNew.length - 1]);
+			for (int i = 0; i < jypathNew.length; i++) {
+				log(lvl + 1, "executeScriptHeader: after: %d: %s", i, jypath.get(i));
+			}
+		}
+		if (savedpathlen == 0) {
+			savedpathlen = interpreter.getLoadPaths().size();
+			log(lvl + 1, "executeScriptHeader: saved sys.path: %d", savedpathlen);
+		}
+		while (interpreter.getLoadPaths().size() > savedpathlen) {
+			interpreter.getLoadPaths().remove(savedpathlen);
+		}
+		log(lvl + 1, "executeScriptHeader: at entry: path:");
+		for (Object p : interpreter.getLoadPaths()) {
+			log(lvl + 1, p.toString());
+		}
+		log(lvl + 1, "executeScriptHeader: at entry: --- end ---");
+		for (String syspath : syspaths) {
+			jypath.add(FileManager.slashify(syspath, false));
+		}
+
+		interpreter.runScriptlet(SCRIPT_HEADER);
+
+		if (codeBefore != null) {
+			StringBuilder buffer = new StringBuilder();
+			for (String line : codeBefore) {
+				buffer.append(line);
+			}
+			interpreter.runScriptlet(buffer.toString());
+		}
+	}
+
+	private boolean doRedirect(PipedInputStream[] pin) {
+		ScriptingContainer interpreter = getScriptingContainer();
+		try {
+			PipedOutputStream pout = new PipedOutputStream(pin[0]);
+			PrintStream ps = new PrintStream(pout, true);
+			System.setOut(ps);
+			interpreter.setOutput(ps);
+		} catch (Exception e) {
+			log(-1, "doRedirect: Couldn't redirect STDOUT\n%s", e.getMessage());
+			return false;
+		}
+		try {
+			PipedOutputStream pout = new PipedOutputStream(pin[1]);
+			PrintStream ps = new PrintStream(pout, true);
+			System.setErr(ps);
+			interpreter.setError(ps);
+		} catch (Exception e) {
+			log(-1, "doRedirect: Couldn't redirect STDERR\n%s", e.getMessage());
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Fills the sysargv list for the Ruby script
+	 *
+	 * @param filename The file containing the script: Has to be passed as first
+	 * parameter in Ruby
+	 * @param argv The parameters passed to Sikuli with --args
+	 */
+	private void fillSysArgv(File filename, String[] argv) {
+		sysargv = new ArrayList<String>();
+		if (filename != null) {
+			sysargv.add(filename.getAbsolutePath());
+		}
+		if (argv != null) {
+			sysargv.addAll(Arrays.asList(argv));
+		}
+	}
+}
diff --git a/JRuby/src/main/resources/rukuli.rb b/JRuby/src/main/resources/rukuli.rb
new file mode 100755
index 0000000..e0dbf97
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli.rb
@@ -0,0 +1,10 @@
+require "rukuli/platform"
+#require Rukuli::Platform.sikulix_path
+require "rukuli/version"
+
+require "rukuli/app"
+require "rukuli/exception"
+require "rukuli/region"
+require "rukuli/screen"
+require "rukuli/key_code"
+require "rukuli/config"
diff --git a/JRuby/src/main/resources/rukuli/app.rb b/JRuby/src/main/resources/rukuli/app.rb
new file mode 100755
index 0000000..d45b268
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/app.rb
@@ -0,0 +1,33 @@
+# An App object represents a running app on the system.
+#
+module Rukuli
+  class App
+
+    # Public: creates a new App instance
+    #
+    # app_name - String name of the app
+    #
+    # Examples
+    #
+    #   App.new("TextEdit")
+    #
+    # Returns the newly initialized App
+    def initialize(app_name)
+      @java_obj = org.sikuli.script::App.new(app_name)
+    end
+
+    # Public: brings the App to focus
+    #
+    # Returns nothing
+    def focus
+      @java_obj.focus()
+    end
+
+    # Public: the Region instance representing the app's window
+    #
+    # Returns the newly initialized Region
+    def window
+      Region.new(@java_obj.window())
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/clickable.rb b/JRuby/src/main/resources/rukuli/clickable.rb
new file mode 100755
index 0000000..72c7d0e
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/clickable.rb
@@ -0,0 +1,278 @@
+# The Clickable module defines interaction with the mouse. It is included in
+# the Region class.
+#
+module Rukuli
+  module Clickable
+
+    # Public: Performs a single click on an image match or point (x, y)
+    #
+    # args - String representing filename of image to find and click
+    # args - Fixnum, Fixnum representing x and y coordinates within
+    # a Region (0,0) is the top left
+    #
+    # Examples
+    #
+    #   region.click('smile.png')
+    #   region.click(123, 432)
+    #
+    # Returns nothing
+    def click(*args)
+      case args.length
+        when 1 then click_image(args[0])
+        when 2 then click_point(args[0], args[1])
+        else raise ArgumentError
+      end
+    end
+
+    # Public: Performs a double click on an image match or point (x, y)
+    #
+    # args - String representing filename of image to find and click
+    # args - Fixnum, Fixnum representing x and y coordinates within
+    # a Region (0,0) is the top left
+    #
+    # Examples
+    #
+    #   region.double_click('smile.png')
+    #   region.double_click(123, 432)
+    #
+    # Returns nothing
+    def double_click(*args)
+      case args.length
+        when 1 then click_image(args[0], true)
+        when 2 then click_point(args[0], args[1], true)
+        else raise ArgumentError
+      end
+    end
+
+    # Public: Performs a click and hold on an image match or point (x, y)
+    #
+    # args - String representing filename of image to find and click
+    # args - Fixnum, Fixnum representing x and y coordinates within
+    # a Region (0,0) is the top left
+    # seconds - Fixnum representing the number of seconds to hold down
+    # before releasing
+    #
+    # Examples
+    #
+    #   region.click_and_hold('smile.png', 2)
+    #   region.click_and_hold(123, 432, 2)
+    #
+    # Returns nothing
+    def click_and_hold(seconds = 1, *args)
+      case args.length
+        when 1 then click_image_and_hold(args[0], seconds)
+        when 2 then click_point_and_hold(args[0], args[1], seconds)
+        else raise ArgumentError
+      end
+    end
+
+    # Public: Performs a mouse down, drag, and mouse up
+    #
+    # start_x - Fixnum representing the x of the mouse down
+    # start_y - Fixnum representing the y of the mouse down
+    # end_x   - Fixnum representing the x of the mouse up
+    # end_y   - Fixnum representing the y of the mouse up
+    #
+    # Examples
+    #
+    #   region.drag_drop(20, 12, 23, 44)
+    #
+    # Returns nothing
+    def drag_drop(start_x, start_y, end_x, end_y)
+      @java_obj.dragDrop(
+        offset_location(start_x, start_y),
+        offset_location(end_x, end_y)
+      )
+    end
+
+    # Public: Simulates turning of the mouse wheel up
+    #
+    # steps - Fixnum representing the number of steps to turn the mouse wheel
+    #
+    # Examples
+    #
+    #   region.wheel_up(10)
+    #
+    # Returns nothing
+    def wheel_up(steps = 1)
+      @java_obj.wheel(-1, steps)
+    end
+
+    # Public: Simulates turning of the mouse wheel down
+    #
+    # steps - Fixnum representing the number of steps to turn the mouse wheel
+    #
+    # Examples
+    #
+    #   region.wheel_down(10)
+    #
+    # Returns nothing
+    def wheel_down(steps = 1)
+      @java_obj.wheel(1, steps)
+    end
+
+    # Public: Performs a hover on an image match or point (x, y)
+    #
+    # args - String representing filename of image to find and hover
+    # args - Fixnum, Fixnum representing x and y coordinates within
+    # a Region (0,0) is the top left
+    #
+    # Examples
+    #
+    #   region.hover('smile.png')
+    #   region.hover(123, 432)
+    #
+    # Returns nothing
+    def hover(*args)
+      case args.length
+        when 1 then hover_image(args[0])
+        when 2 then hover_point(args[0], args[1])
+        else raise ArgumentError
+      end
+    end
+
+    private
+
+    # Private: turns the mouse wheel
+    #
+    # direction - Fixnum represeting direction to turn wheel
+    # steps - the number of steps to turn the mouse wheel
+    #
+    # Returns nothing
+    def wheel(direction, steps)
+      @java_obj.wheel(direction, steps)
+    end
+
+    # Private: clicks on a matched Region based on an image based search
+    #
+    # filename - A String representation of the filename of the region to
+    # match against
+    # seconds  - The length in seconds to hold the mouse
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def click_image_and_hold(filename, seconds)
+      begin
+        pattern = org.sikuli.script::Pattern.new(filename).similar(0.9)
+        @java_obj.hover(pattern)
+        @java_obj.mouseDown(java.awt.event.InputEvent::BUTTON1_MASK)
+        sleep(seconds.to_i)
+        @java_obj.mouseUp(0)
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    # Private: clicks on a point within the region
+    #
+    # filename - A String representation of the filename of the region to
+    # match against
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def click_point_and_hold(x, y, seconds)
+      begin
+        @java_obj.hover(location(x, y))
+        @java_obj.mouseDown(java.awt.event.InputEvent::BUTTON1_MASK)
+        sleep(seconds.to_i)
+        @java_obj.mouseUp(0)
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    # Private: clicks on a matched Region based on an image based search
+    #
+    # filename - A String representation of the filename of the region to
+    # match against
+    # is_double - (optional) Boolean determining if should be a double click
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def click_image(filename, is_double = false, and_hold = false)
+      begin
+        if is_double
+          @java_obj.doubleClick(filename, 0)
+        else
+          @java_obj.click(filename, 0)
+        end
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    # Private: clicks on a point relative to a Region's top left corner
+    #
+    # x         - a Fixnum representing the x component of the point to click
+    # y         - a Fixnum representing the y component of the point to click
+    # is_double - (optional) Boolean determining if should be a double click
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def click_point(x, y, is_double = false)
+      if is_double
+        @java_obj.doubleClick(offset_location(x, y))
+      else
+        @java_obj.click(offset_location(x, y))
+      end
+    end
+
+    # Private: hovers on a matched Region based on an image based search
+    #
+    # filename - A String representation of the filename of the region to
+    # match against
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def hover_image(filename)
+      begin
+        @java_obj.hover(filename)
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    # Private: hovers on a point relative to a Region's top left corner
+    #
+    # x         - a Fixnum representing the x component of the point to hover
+    # y         - a Fixnum representing the y component of the point to hover
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def hover_point(x, y)
+      @java_obj.hover(offset_location(x, y))
+    end
+
+    # Private: create a new instance of Location
+    #
+    # x         - a Fixnum representing the x component of the point to hover
+    # y         - a Fixnum representing the y component of the point to hover
+    #
+    # Return location class instance
+    def location(x, y)
+      org.sikuli.script::Location.new(x, y)
+    end
+
+    # Private: location with offset
+    #
+    # x         - a Fixnum representing the x component of the point to hover
+    # y         - a Fixnum representing the y component of the point to hover
+    #
+    # Return new location
+    def offset_location(x, y)
+      location(x, y).offset(x(), y())
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/config.rb b/JRuby/src/main/resources/rukuli/config.rb
new file mode 100755
index 0000000..16e6cdc
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/config.rb
@@ -0,0 +1,67 @@
+# Config variables for the Sikuli driver
+#
+module Rukuli
+  class Config
+    class << self
+
+      # Public: the Boolean representing whether or not to perform a 1 second
+      # highlight when an image is matched through Searchable#find,
+      # Searchable#find_all. Defaults to false.
+      attr_accessor :highlight_on_find
+
+      # Public: the absolute file path where Sikuli will look for images when
+      # a just a filename is passed to a search or click method
+      #
+      # Returns the String representation of the path
+      def image_path
+        java.lang.System.getProperty("SIKULI_IMAGE_PATH")
+      end
+
+      # Public: the setter for the absolute file path where Sikuli will search
+      # for images with given a filename as an image
+      #
+      # Examples
+      #
+      #  Rukuli::Config.image_path = "/Users/andreanastacio/rukuli/images/"
+      #
+      # Returns nothing
+      def image_path=(path)
+        java.lang.System.setProperty("SIKULI_IMAGE_PATH", path)
+      end
+
+      # Public: turns stdout logging on and off for the Sikuli java classes.
+      # Defaults to true.
+      #
+      # Examples
+      #
+      #  Rukuli::Config.logging = false
+      #
+      # Returns nothing
+      def logging=(boolean)
+        return unless [TrueClass, FalseClass].include? boolean.class
+        org.sikuli.basics::Settings.InfoLogs   = boolean
+        org.sikuli.basics::Settings.ActionLogs = boolean
+        org.sikuli.basics::Settings.DebugLogs  = boolean
+      end
+
+      # Public: convienence method for grouping the setting of config
+      # variables
+      #
+      # Examples
+      #
+      #   Rukuli::Config.run do |config|
+      #     config.logging = true
+      #     config.image_path = "/User/andreanastacio/images"
+      #     config.highlight_on_find = true
+      #   end
+      #
+      # Returns nothing
+      def run(*args)
+        if block_given?
+          yield self
+        end
+      end
+
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/exception.rb b/JRuby/src/main/resources/rukuli/exception.rb
new file mode 100755
index 0000000..c6e1524
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/exception.rb
@@ -0,0 +1,14 @@
+# Exception classes for Sikuli image searching and matching
+#
+module Rukuli
+
+  # Thrown when Sikuli is unable to find a match within the region for the
+  # file given.
+  #
+  class ImageNotFound < StandardError; end
+
+  # Thrown when a filename is given that is not found on disk in the image
+  # path. Image path can be configured using Rukuli::Config.image_path
+  #
+  class FileDoesNotExist < StandardError; end
+end
diff --git a/JRuby/src/main/resources/rukuli/key_code.rb b/JRuby/src/main/resources/rukuli/key_code.rb
new file mode 100755
index 0000000..e882ca9
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/key_code.rb
@@ -0,0 +1,21 @@
+require 'java'
+java_import 'org.sikuli.script.Key'
+java_import 'org.sikuli.script.KeyModifier'
+
+#
+# These constants represent keyboard codes for interacting with the keyboard.
+# Keyboard interaction is defined in the Rukuli::Typeable module.
+#
+module Rukuli
+  KEY_CMD   = KeyModifier::META
+  KEY_SHIFT = KeyModifier::SHIFT
+  KEY_CTRL  = KeyModifier::CTRL
+  KEY_ALT   = KeyModifier::ALT
+  
+  KEY_BACKSPACE = Key::BACKSPACE
+  KEY_RETURN    = Key::ENTER
+  LEFT_ARROW    = Key::LEFT
+  RIGHT_ARROW   = Key::RIGHT
+  UP_ARROW      = Key::UP
+  DOWN_ARROW    = Key::DOWN
+end
diff --git a/JRuby/src/main/resources/rukuli/platform.rb b/JRuby/src/main/resources/rukuli/platform.rb
new file mode 100755
index 0000000..7a33055
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/platform.rb
@@ -0,0 +1,12 @@
+module Rukuli
+  class Platform
+
+    def self.sikulix_path
+      path = "#{ENV['SIKULIX_HOME']}"
+      if ENV['SIKULIX_HOME'].nil?
+        raise LoadError, "Failed to load 'sikuli-java.jar'\nMake sure SIKULIX_HOME is set!"
+      end
+      path
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/region.rb b/JRuby/src/main/resources/rukuli/region.rb
new file mode 100755
index 0000000..f84df94
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/region.rb
@@ -0,0 +1,88 @@
+# A Region represents a rectangle on screen. Regions are the main point of
+# interaction for Sikuli actions. Regions can receive actions from the mouse,
+# keyboard, and image search.
+#
+require "rukuli/clickable"
+require "rukuli/typeable"
+require "rukuli/searchable"
+
+module Rukuli
+  class Region
+    include Clickable
+    include Typeable
+    include Searchable
+
+    # Public: creates a new Region object
+    #
+    # args - Array representing x (left bound), y (top), width, height
+    #        4 Fixnums left, top, width, height
+    #        An instance of an org.sikuli.script::Region
+    #
+    #   Examples
+    #
+    #     Region.new([10, 10, 200, 300])
+    #     Region.new(10, 10, 200, 300)
+    #     Region.new(another_region)
+    #
+    # Returns the newly initialized object
+    def initialize(*args)
+      @java_obj = org.sikuli.script::Region.new(*args)
+    end
+
+    # Public: highlight the region with a ~ 5 pixel red border
+    #
+    # seconds - Fixnum length of time to show border
+    #
+    # Returns nothing
+    def highlight(seconds = 1)
+      @java_obj.java_send(:highlight, [Java::int], seconds)
+    end
+
+    # Public: the x component of the top, left corner of the Region
+    def x
+      @java_obj.x()
+    end
+
+    # Public: the y component of the top, left corner of the Region
+    def y
+      @java_obj.y()
+    end
+
+    # Public: the width in pixels of the Region
+    def width
+      @java_obj.w()
+    end
+
+    # Public: the height in pixels of the Region
+    def height
+      @java_obj.h()
+    end
+
+    # Public: provide access to all region methods provided by the SikuliScript API
+    # See http://sikuli.org/doc/java/edu/mit/csail/uid/Region.html
+    def method_missing method_name, *args, &block
+      @java_obj.send method_name, *args, &block
+    end
+
+    private
+
+    # Private: interpret a java NativeException and raises a more descriptive
+    # exception
+    #
+    # exception - The original java exception thrown by the sikuli java_obj
+    # filename  - A string representing the filename to include in the
+    # exception message
+    #
+    # Returns nothing
+    def raise_exception(exception, filename)
+      message = exception.message
+      if message.start_with? "java.lang."
+        raise exception.message
+      elsif message.start_with? "org.sikuli.script.FindFailed"
+        raise Rukuli::FileDoesNotExist, "The file '#{filename}' does not exist."
+      else
+        raise Rukuli::ImageNotFound, "The image '#{filename}' did not match in this region."
+      end
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/screen.rb b/JRuby/src/main/resources/rukuli/screen.rb
new file mode 100755
index 0000000..a783800
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/screen.rb
@@ -0,0 +1,20 @@
+# A Screen object defines a special type of Rukuli::Region that represents
+# the entire screen.
+#
+# TODO: Test the Screen object with multiple monitors attached.
+#
+module Rukuli
+  class Screen < Region
+
+    # Public: creates a new Screen object
+    #
+    # Examples
+    #
+    #   screen = Rukuli::Screen.new
+    #
+    # Returns the newly initialized Screen object
+    def initialize
+      @java_obj = org.sikuli.script::Screen.new()
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/searchable.rb b/JRuby/src/main/resources/rukuli/searchable.rb
new file mode 100755
index 0000000..11254fc
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/searchable.rb
@@ -0,0 +1,134 @@
+# The Rukuli::Searchable module is the heart of Sikuli. It defines the
+# wrapper around Sikuli's on screen image searching and matching capability
+# It is implemented by the Region class.
+#
+module Rukuli
+  module Searchable
+
+    # Public: search for an image within a Region
+    #
+    # filename   - A String representation of the filename to match against
+    # similarity - A Float between 0 and 1 representing the threshold for
+    # matching an image. Passing 1 corresponds to a 100% pixel for pixel
+    # match. Defaults to 0.9 (90% match)
+    #
+    # Examples
+    #
+    #   region.find('needle.png')
+    #   region.find('needle.png', 0.5)
+    #
+    # Returns an instance of Region representing the best match
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def find(filename, similarity = 0.9)
+      begin
+        pattern = build_pattern(filename, similarity)
+        match = Region.new(@java_obj.find(pattern))
+        match.highlight if Rukuli::Config.highlight_on_find
+        match
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    # Public: search for an image within a region (does not raise ImageNotFound exceptions)
+    #
+    # filename   - A String representation of the filename to match against
+    # similarity - A Float between 0 and 1 representing the threshold for
+    # matching an image. Passing 1 corresponds to a 100% pixel for pixel
+    # match. Defaults to 0.9 (90% match)
+    #
+    # Examples
+    #
+    #   region.find!('needle.png')
+    #   region.find!('needle.png', 0.5)
+    #
+    # Returns the match or nil if no match is found
+    def find!(filename, similarity = 0.9)
+      begin
+        find(filename, similarity)
+      rescue Rukuli::ImageNotFound => e
+        nil
+      end
+    end
+
+    # Public: search for an image within a Region and return all matches
+    #
+    # TODO: Sort return results so they are always returned in the same order
+    # (top left to bottom right)
+    #
+    # filename   - A String representation of the filename to match against
+    # similarity - A Float between 0 and 1 representing the threshold for
+    # matching an image. Passing 1 corresponds to a 100% pixel for pixel
+    # match. Defaults to 0.9 (90% match)
+    #
+    # Examples
+    #
+    #   region.find_all('needle.png')
+    #   region.find_all('needle.png', 0.5)
+    #
+    # Returns an array of Region objects that match the given file and
+    # threshold
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def find_all(filename, similarity = 0.9)
+      begin
+        pattern = build_pattern(filename, similarity)
+        matches = @java_obj.findAll(pattern)
+        regions = matches.collect do |r|
+          match = Region.new(r)
+          match.highlight if Rukuli::Config.highlight_on_find
+          match
+        end
+        regions
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    # Public: wait for a match to appear within a region
+    #
+    # filename   - A String representation of the filename to match against
+    # time       - A Fixnum representing the amount of time to wait defaults
+    # to 2 seconds
+    # similarity - A Float between 0 and 1 representing the threshold for
+    # matching an image. Passing 1 corresponds to a 100% pixel for pixel
+    # match. Defaults to 0.9 (90% match)
+    #
+    # Examples
+    #
+    #    region.wait('needle.png') # wait for needle.png to appear for up to 1 second
+    #    region.wait('needle.png', 10) # wait for needle.png to appear for 10 seconds
+    #
+    # Returns nothing
+    #
+    # Throws Rukuli::FileNotFound if the file could not be found on the system
+    # Throws Rukuli::ImageNotMatched if no matches are found within the region
+    def wait(filename, time = 2, similarity = 0.9)
+      begin
+        pattern = build_pattern(filename, similarity)
+        match = Region.new(@java_obj.wait(pattern, time))
+        match.highlight if Rukuli::Config.highlight_on_find
+        match
+      rescue NativeException => e
+        raise_exception e, filename
+      end
+    end
+
+    private
+
+    # Private: builds a java Pattern to check
+    #
+    # filename   - A String representation of the filename to match against
+    # similarity - A Float between 0 and 1 representing the threshold for
+    # matching an image. Passing 1 corresponds to a 100% pixel for pixel
+    # match. Defaults to 0.9 (90% match)
+    #
+    # Returns a org.sikuli.script::Pattern object to match against
+    def build_pattern(filename, similarity)
+      org.sikuli.script::Pattern.new(filename).similar(similarity)
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/typeable.rb b/JRuby/src/main/resources/rukuli/typeable.rb
new file mode 100755
index 0000000..02b7e6e
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/typeable.rb
@@ -0,0 +1,32 @@
+# Defines interactions with the keyboard. Implemented in the Region class.
+#
+module Rukuli
+  module Typeable
+
+    # Public: Types text as if it was being typed on the keyboard with an
+    # optional key modifier
+    #
+    # text - String representing text to be typed on keyboard
+    # modifier - (optional) Sikilu constant (defined in key_code.rb)
+    # representing key to hold while typing text
+    #
+    # Examples
+    #
+    #   region.type("Hello World")
+    #   region.type("s", Rukuli::KEY_CMD) # saves a file
+    #
+    # Returns nothing
+    def type(text, modifier = 0)
+      @java_obj.type(nil, text, modifier)
+    end
+
+    # Public: Types text then presses the return/enter key on the keyboard
+    #
+    # text - String
+    #
+    # Returns nothing
+    def enter(text)
+      @java_obj.type(text + Sikuli::KEY_RETURN)
+    end
+  end
+end
diff --git a/JRuby/src/main/resources/rukuli/version.rb b/JRuby/src/main/resources/rukuli/version.rb
new file mode 100755
index 0000000..17b5ca4
--- /dev/null
+++ b/JRuby/src/main/resources/rukuli/version.rb
@@ -0,0 +1,3 @@
+module Rukuli
+  VERSION = "1.0.0"
+end
diff --git a/JRuby/src/main/resources/sikulix.rb b/JRuby/src/main/resources/sikulix.rb
new file mode 100755
index 0000000..62820af
--- /dev/null
+++ b/JRuby/src/main/resources/sikulix.rb
@@ -0,0 +1,16 @@
+require_relative 'rukuli'
+include Rukuli
+
+$screen = Screen.new
+
+clickable  = [:click, :double_click, :click_and_hold, :drag_drop,
+              :hover, :wheel_down, :wheel_up ]
+
+typeable   = [:enter, :type]
+
+searchable = [:find, :find!, :find_all, :wait]
+
+(clickable + typeable + searchable).each do |name|
+  method = $screen.method (name)
+  Object.send(:define_method, name){ |*args| method.call *args }
+end
diff --git a/Jython/src/main/java/org/sikuli/scriptrunner/JythonScriptRunner.java b/Jython/src/main/java/org/sikuli/scriptrunner/JythonScriptRunner.java
index 8ff21bb..3251ca6 100644
--- a/Jython/src/main/java/org/sikuli/scriptrunner/JythonScriptRunner.java
+++ b/Jython/src/main/java/org/sikuli/scriptrunner/JythonScriptRunner.java
@@ -93,20 +93,13 @@ public class JythonScriptRunner implements IScriptRunner {
           = FileManager.convertStreamToString(SikuliBundleCleaner);
   private static String sikuliLibPath;
 
-  private static String timestampBuilt;
-  private static final String tsb = "##--##Tue Jan 21 15:53:09 CET 2014##--##";
-  static {
-    Debug.log(3, "SikuliX Jython Support Build: %s %s", Settings.getVersionShort(), 
-            SikuliX.makeTimestamp(tsb));
-  }
-
   /**
    * {@inheritDoc}
    */
   @Override
   public void init(String[] param) {
     sikuliLibPath = new File(SikuliX.getJarPath(), "Lib").getAbsolutePath();
-    if (!SikuliX.isRunningFromJar() 
+    if (!SikuliX.isRunningFromJar()
           || !sikuliLibPath.contains("sikuli-ide")
           || !sikuliLibPath.contains("sikuli-script")
         ) {
@@ -195,7 +188,7 @@ public class JythonScriptRunner implements IScriptRunner {
                       + scr + "\"");
             } else {
               log(lvl, "runPython: running script: \n" + scriptPaths[0]);
-              interpreter.exec("sys.argv[0] = \"" + scriptPaths[0] + "\"");              
+              interpreter.exec("sys.argv[0] = \"" + scriptPaths[0] + "\"");
             }
             interpreter.execfile(pyFile.getAbsolutePath());
           }
diff --git a/pom.xml b/pom.xml
index f7e0fb9..3d63283 100755
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
 	<developers>
 		<developer>
 			<id>RaiMan</id>
-			<name>RaiMan</name>
+			<name>Raimund Hocke</name>
 			<email>rmhdevelop at me.com</email>
 		</developer>
 	</developers>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/sikuli.git



More information about the pkg-java-commits mailing list