ruboto 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/README.md +13 -49
  2. data/Rakefile +26 -26
  3. data/assets/rakelib/ruboto.rake +29 -9
  4. data/assets/res/drawable-hdpi/{icon.png → ic_launcher.png} +0 -0
  5. data/assets/res/drawable-ldpi/{icon.png → ic_launcher.png} +0 -0
  6. data/assets/res/drawable-mdpi/{icon.png → ic_launcher.png} +0 -0
  7. data/assets/samples/sample_activity.rb +19 -9
  8. data/assets/samples/sample_broadcast_receiver.rb +3 -1
  9. data/assets/samples/sample_service.rb +9 -9
  10. data/assets/src/InheritingActivity.java +1 -1
  11. data/assets/src/InheritingBroadcastReceiver.java +4 -4
  12. data/assets/src/InheritingClass.java +1 -1
  13. data/assets/src/InheritingService.java +2 -1
  14. data/assets/src/RubotoActivity.java +26 -17
  15. data/assets/src/RubotoBroadcastReceiver.java +32 -11
  16. data/assets/src/RubotoService.java +23 -13
  17. data/assets/src/org/ruboto/EntryPointActivity.java +18 -26
  18. data/assets/src/org/ruboto/JRubyAdapter.java +468 -0
  19. data/assets/src/org/ruboto/Log.java +22 -0
  20. data/assets/src/org/ruboto/Script.java +113 -587
  21. data/assets/src/org/ruboto/test/ActivityTest.java +7 -7
  22. data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +27 -12
  23. data/assets/src/ruboto.rb +1 -1
  24. data/assets/src/ruboto/activity.rb +10 -1
  25. data/assets/src/ruboto/base.rb +0 -12
  26. data/assets/src/ruboto/broadcast_receiver.rb +12 -0
  27. data/assets/src/ruboto/menu.rb +0 -1
  28. data/assets/src/ruboto/package.rb +11 -0
  29. data/assets/src/ruboto/service.rb +9 -0
  30. data/lib/ruboto/commands/base.rb +1 -1
  31. data/lib/ruboto/util/build.rb +1 -1
  32. data/lib/ruboto/util/update.rb +35 -27
  33. data/lib/ruboto/util/xml_element.rb +25 -12
  34. data/lib/ruboto/version.rb +2 -2
  35. data/test/activity/image_button_activity.rb +14 -11
  36. data/test/activity/image_button_activity_test.rb +2 -6
  37. data/test/activity/image_button_and_button_activity.rb +15 -17
  38. data/test/activity/image_button_and_button_activity_test.rb +4 -8
  39. data/test/activity/option_menu_activity.rb +17 -12
  40. data/test/activity/option_menu_activity_test.rb +1 -4
  41. data/test/activity/psych_activity.rb +20 -13
  42. data/test/activity/psych_activity_test.rb +3 -1
  43. data/test/activity/stack_activity.rb +17 -14
  44. data/test/activity/stack_activity_test.rb +13 -12
  45. data/test/app_test_methods.rb +25 -19
  46. data/test/block_def_activity/image_button_activity.rb +23 -0
  47. data/test/block_def_activity/image_button_activity_test.rb +21 -0
  48. data/test/block_def_activity/image_button_and_button_activity.rb +20 -0
  49. data/test/block_def_activity/image_button_and_button_activity_test.rb +27 -0
  50. data/test/block_def_activity/option_menu_activity.rb +26 -0
  51. data/test/block_def_activity/option_menu_activity_test.rb +18 -0
  52. data/test/block_def_activity/psych_activity.rb +35 -0
  53. data/test/block_def_activity/psych_activity_test.rb +16 -0
  54. data/test/block_def_activity/stack_activity.rb +25 -0
  55. data/test/block_def_activity/stack_activity_test.rb +31 -0
  56. data/test/broadcast_receiver_test.rb +2 -2
  57. data/test/handle_activity/image_button_activity.rb +21 -0
  58. data/test/handle_activity/image_button_activity_test.rb +21 -0
  59. data/test/handle_activity/image_button_and_button_activity.rb +24 -0
  60. data/test/handle_activity/image_button_and_button_activity_test.rb +27 -0
  61. data/test/handle_activity/option_menu_activity.rb +21 -0
  62. data/test/handle_activity/option_menu_activity_test.rb +20 -0
  63. data/test/handle_activity/psych_activity.rb +31 -0
  64. data/test/handle_activity/psych_activity_test.rb +16 -0
  65. data/test/handle_activity/stack_activity.rb +21 -0
  66. data/test/handle_activity/stack_activity_test.rb +32 -0
  67. data/test/minimal_app_test.rb +4 -4
  68. data/test/rake_test.rb +15 -1
  69. data/test/ruboto_gen_test.rb +7 -4
  70. data/test/service_test.rb +110 -21
  71. data/test/test_helper.rb +17 -14
  72. data/test/updated_example_test_methods.rb +5 -14
  73. metadata +30 -7
@@ -4,7 +4,7 @@ import java.io.IOException;
4
4
 
5
5
  public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
6
6
  private String scriptName = null;
7
- private boolean initialized = false;
7
+ private Object rubyInstance;
8
8
 
9
9
  public void setCallbackProc(int id, Object obj) {
10
10
  // Error: no callbacks
@@ -25,17 +25,33 @@ public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
25
25
  if (name != null) {
26
26
  setScriptName(name);
27
27
 
28
- if (Script.isInitialized()) {
28
+ if (JRubyAdapter.isInitialized()) {
29
29
  loadScript();
30
30
  }
31
31
  }
32
32
  }
33
33
 
34
34
  protected void loadScript() {
35
- Script.put("$broadcast_receiver", this);
35
+
36
+ // TODO(uwe): Only needed for non-class-based definitions
37
+ // Can be removed if we stop supporting non-class-based definitions
38
+ JRubyAdapter.put("$broadcast_receiver", this);
39
+ // TODO end
40
+
36
41
  if (scriptName != null) {
37
42
  try {
38
- new Script(scriptName).execute();
43
+ String rubyClassName = Script.toCamelCase(scriptName);
44
+ System.out.println("Looking for Ruby class: " + rubyClassName);
45
+ Object rubyClass = JRubyAdapter.get(rubyClassName);
46
+ if (rubyClass == null) {
47
+ System.out.println("Loading script: " + scriptName);
48
+ JRubyAdapter.exec(new Script(scriptName).getContents());
49
+ rubyClass = JRubyAdapter.get(rubyClassName);
50
+ }
51
+ if (rubyClass != null) {
52
+ System.out.println("Instanciating Ruby class: " + rubyClassName);
53
+ rubyInstance = JRubyAdapter.callMethod(rubyClass, "new", this, Object.class);
54
+ }
39
55
  } catch(IOException e) {
40
56
  throw new RuntimeException("IOException loading broadcast receiver script", e);
41
57
  }
@@ -43,16 +59,21 @@ public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
43
59
  }
44
60
 
45
61
  public void onReceive(android.content.Context context, android.content.Intent intent) {
46
- Script.put("$context", context);
47
- Script.put("$broadcast_receiver", this);
48
- Script.put("$intent", intent);
49
-
50
62
  try {
51
- Script.execute("$broadcast_receiver.on_receive($context, $intent)");
63
+ System.out.println("onReceive: " + rubyInstance);
64
+ if (rubyInstance != null) {
65
+ JRubyAdapter.callMethod(rubyInstance, "on_receive", new Object[]{context, intent});
66
+ } else {
67
+ // TODO(uwe): Only needed for non-class-based definitions
68
+ // Can be removed if we stop supporting non-class-based definitions
69
+ JRubyAdapter.put("$context", context);
70
+ JRubyAdapter.put("$broadcast_receiver", this);
71
+ JRubyAdapter.put("$intent", intent);
72
+ JRubyAdapter.execute("$broadcast_receiver.on_receive($context, $intent)");
73
+ // TODO end
74
+ }
52
75
  } catch(Exception e) {
53
76
  e.printStackTrace();
54
77
  }
55
78
  }
56
79
  }
57
-
58
-
@@ -2,12 +2,11 @@ package THE_PACKAGE;
2
2
 
3
3
  import org.ruboto.Script;
4
4
  import java.io.IOException;
5
- import android.app.ProgressDialog;
6
5
 
7
6
  public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
8
7
  private String scriptName;
9
- private String remoteVariable = "";
10
8
  public Object[] args;
9
+ private Object rubyInstance;
11
10
 
12
11
  THE_CONSTANTS
13
12
 
@@ -17,11 +16,6 @@ THE_CONSTANTS
17
16
  callbackProcs[id] = obj;
18
17
  }
19
18
 
20
- public THE_RUBOTO_CLASS setRemoteVariable(String var) {
21
- remoteVariable = ((var == null) ? "" : (var + "."));
22
- return this;
23
- }
24
-
25
19
  public void setScriptName(String name){
26
20
  scriptName = name;
27
21
  }
@@ -33,20 +27,36 @@ THE_CONSTANTS
33
27
 
34
28
  @Override
35
29
  public void onCreate() {
30
+ System.out.println("RubotoService.onCreate()");
36
31
  args = new Object[0];
37
32
 
38
33
  super.onCreate();
39
34
 
40
- if (Script.setUpJRuby(this)) {
41
- Script.defineGlobalVariable("$context", this);
42
- Script.defineGlobalVariable("$service", this);
35
+ if (JRubyAdapter.setUpJRuby(this)) {
36
+ // TODO(uwe): Only needed for non-class-based definitions
37
+ // Can be removed if we stop supporting non-class-based definitions
38
+ JRubyAdapter.defineGlobalVariable("$context", this);
39
+ JRubyAdapter.defineGlobalVariable("$service", this);
40
+ // TODO end
43
41
 
44
42
  try {
45
43
  if (scriptName != null) {
46
- new Script(scriptName).execute();
44
+ String rubyClassName = Script.toCamelCase(scriptName);
45
+ System.out.println("Looking for Ruby class: " + rubyClassName);
46
+ Object rubyClass = JRubyAdapter.get(rubyClassName);
47
+ if (rubyClass == null) {
48
+ System.out.println("Loading script: " + scriptName);
49
+ JRubyAdapter.exec(new Script(scriptName).getContents());
50
+ rubyClass = JRubyAdapter.get(rubyClassName);
51
+ }
52
+ if (rubyClass != null) {
53
+ System.out.println("Instanciating Ruby class: " + rubyClassName);
54
+ rubyInstance = JRubyAdapter.callMethod(rubyClass, "new", this, Object.class);
55
+ JRubyAdapter.callMethod(rubyInstance, "on_create");
56
+ }
47
57
  } else {
48
- Script.execute("$service.initialize_ruboto");
49
- Script.execute("$service.on_create");
58
+ JRubyAdapter.execute("$service.initialize_ruboto");
59
+ JRubyAdapter.execute("$service.on_create");
50
60
  }
51
61
  } catch(IOException e) {
52
62
  e.printStackTrace();
@@ -1,10 +1,5 @@
1
1
  package org.ruboto;
2
2
 
3
- import java.io.File;
4
- import java.io.IOException;
5
-
6
- import org.ruboto.Script;
7
-
8
3
  import android.app.ProgressDialog;
9
4
  import android.content.BroadcastReceiver;
10
5
  import android.content.Context;
@@ -14,8 +9,6 @@ import android.content.Intent;
14
9
  import android.content.IntentFilter;
15
10
  import android.net.Uri;
16
11
  import android.os.Bundle;
17
- import android.os.Handler;
18
- import android.util.Log;
19
12
  import android.view.View;
20
13
  import android.widget.TextView;
21
14
  import android.widget.Toast;
@@ -28,7 +21,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
28
21
  protected boolean appStarted = false;
29
22
 
30
23
  public void onCreate(Bundle bundle) {
31
- Log.d("RUBOTO", "onCreate: ");
24
+ Log.d("onCreate: ");
32
25
 
33
26
  try {
34
27
  splash = Class.forName(getPackageName() + ".R$layout").getField("splash").getInt(null);
@@ -36,32 +29,32 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
36
29
  splash = -1;
37
30
  }
38
31
 
39
- if (Script.isInitialized()) {
32
+ if (JRubyAdapter.isInitialized()) {
40
33
  appStarted = true;
41
34
  }
42
35
  super.onCreate(bundle);
43
36
  }
44
37
 
45
38
  public void onResume() {
46
- Log.d("RUBOTO", "onResume: ");
39
+ Log.d("onResume: ");
47
40
 
48
41
  if(appStarted) {
49
- Log.d("RUBOTO", "onResume: App already started!");
42
+ Log.d("onResume: App already started!");
50
43
  super.onResume();
51
44
  return;
52
45
  }
53
46
 
54
- Log.d("RUBOTO", "onResume: Checking JRuby");
55
- if (Script.isInitialized()) {
56
- Log.d("RUBOTO", "Already initialized");
47
+ Log.d("onResume: Checking JRuby");
48
+ if (JRubyAdapter.isInitialized()) {
49
+ Log.d("Already initialized");
57
50
  fireRubotoActivity();
58
51
  } else {
59
- Log.d("RUBOTO", "Not initialized");
52
+ Log.d("Not initialized");
60
53
  showProgress();
61
54
  receiver = new BroadcastReceiver(){
62
55
  public void onReceive(Context context, Intent intent) {
63
- Log.i("RUBOTO", "received broadcast: " + intent);
64
- Log.i("RUBOTO", "URI: " + intent.getData());
56
+ Log.i("received broadcast: " + intent);
57
+ Log.i("URI: " + intent.getData());
65
58
  if (intent.getData().toString().equals("package:org.ruboto.core")) {
66
59
  Toast.makeText(context,"Ruboto Core is now installed.",Toast.LENGTH_SHORT).show();
67
60
  if (receiver != null) {
@@ -82,7 +75,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
82
75
  }
83
76
 
84
77
  public void onPause() {
85
- Log.d("RUBOTO", "onPause: ");
78
+ Log.d("onPause: ");
86
79
 
87
80
  if (receiver != null) {
88
81
  unregisterReceiver(receiver);
@@ -92,7 +85,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
92
85
  }
93
86
 
94
87
  public void onDestroy() {
95
- Log.d("RUBOTO", "onDestroy: ");
88
+ Log.d("onDestroy: ");
96
89
 
97
90
  super.onDestroy();
98
91
  if (dialogCancelled) {
@@ -104,9 +97,9 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
104
97
  private void initJRuby(final boolean firstTime) {
105
98
  new Thread(new Runnable() {
106
99
  public void run() {
107
- final boolean jrubyOk = Script.setUpJRuby(EntryPointActivity.this);
100
+ final boolean jrubyOk = JRubyAdapter.setUpJRuby(EntryPointActivity.this);
108
101
  if (jrubyOk) {
109
- Log.d("RUBOTO", "onResume: JRuby OK");
102
+ Log.d("onResume: JRuby OK");
110
103
  prepareJRuby();
111
104
  runOnUiThread(new Runnable() {
112
105
  public void run() {
@@ -117,7 +110,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
117
110
  runOnUiThread(new Runnable() {
118
111
  public void run() {
119
112
  if (firstTime) {
120
- Log.d("RUBOTO", "onResume: Checking JRuby - IN UI thread");
113
+ Log.d("onResume: Checking JRuby - IN UI thread");
121
114
  try {
122
115
  setContentView(Class.forName(getPackageName() + ".R$layout").getField("get_ruboto_core").getInt(null));
123
116
  } catch (Exception e) {
@@ -147,7 +140,6 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
147
140
  startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=org.ruboto.core")));
148
141
  } catch (android.content.ActivityNotFoundException anfe) {
149
142
  try {
150
- TextView textView = (TextView) findViewById(Class.forName(getPackageName() + ".R$id").getField("text").getInt(null));
151
143
  Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(RUBOTO_URL));
152
144
  startActivity(intent);
153
145
  } catch (Exception e) {}
@@ -157,7 +149,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
157
149
  protected void fireRubotoActivity() {
158
150
  if(appStarted) return;
159
151
  appStarted = true;
160
- Log.i("RUBOTO", "Starting activity");
152
+ Log.i("Starting activity");
161
153
  loadScript();
162
154
  onStart();
163
155
  super.onResume();
@@ -166,7 +158,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
166
158
 
167
159
  private void showProgress() {
168
160
  if (loadingDialog == null) {
169
- Log.i("RUBOTO", "Showing progress");
161
+ Log.i("Showing progress");
170
162
  if (splash > 0) {
171
163
  requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
172
164
  setContentView(splash);
@@ -185,7 +177,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
185
177
 
186
178
  private void hideProgress() {
187
179
  if (loadingDialog != null) {
188
- Log.d("RUBOTO", "Hide progress");
180
+ Log.d("Hide progress");
189
181
  loadingDialog.dismiss();
190
182
  loadingDialog = null;
191
183
  }
@@ -0,0 +1,468 @@
1
+ package org.ruboto;
2
+
3
+ import java.io.File;
4
+ import java.io.FilenameFilter;
5
+ import java.io.PrintStream;
6
+ import java.lang.reflect.InvocationTargetException;
7
+ import java.lang.reflect.Method;
8
+
9
+ import android.content.Context;
10
+ import android.content.pm.ApplicationInfo;
11
+ import android.content.pm.PackageInfo;
12
+ import android.content.pm.PackageManager;
13
+ import android.content.pm.PackageManager.NameNotFoundException;
14
+ import android.os.Environment;
15
+ import dalvik.system.PathClassLoader;
16
+
17
+ public class JRubyAdapter {
18
+ private static Object ruby;
19
+ private static boolean isDebugBuild = false;
20
+ private static PrintStream output = null;
21
+ private static boolean initialized = false;
22
+ private static String localContextScope = "SINGLETON";
23
+ private static String localVariableBehavior = "TRANSIENT";
24
+ private static String RUBOTO_CORE_VERSION_NAME;
25
+
26
+ public static void callMethod(Object receiver, String methodName, Object[] args) {
27
+ try {
28
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object[].class);
29
+ callMethodMethod.invoke(ruby, receiver, methodName, args);
30
+ } catch (NoSuchMethodException nsme) {
31
+ throw new RuntimeException(nsme);
32
+ } catch (IllegalAccessException iae) {
33
+ throw new RuntimeException(iae);
34
+ } catch (java.lang.reflect.InvocationTargetException ite) {
35
+ printStackTrace(ite);
36
+ if (isDebugBuild) {
37
+ throw new RuntimeException(ite);
38
+ }
39
+ }
40
+ }
41
+
42
+ public static void callMethod(Object object, String methodName, Object arg) {
43
+ callMethod(object, methodName, new Object[] { arg });
44
+ }
45
+
46
+ public static void callMethod(Object object, String methodName) {
47
+ callMethod(object, methodName, new Object[] {});
48
+ }
49
+
50
+ @SuppressWarnings("unchecked")
51
+ public static <T> T callMethod(Object receiver, String methodName, Object[] args, Class<T> returnType) {
52
+ try {
53
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object[].class, Class.class);
54
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, args, returnType);
55
+ } catch (NoSuchMethodException nsme) {
56
+ throw new RuntimeException(nsme);
57
+ } catch (IllegalAccessException iae) {
58
+ throw new RuntimeException(iae);
59
+ } catch (java.lang.reflect.InvocationTargetException ite) {
60
+ printStackTrace(ite);
61
+ }
62
+ return null;
63
+ }
64
+
65
+ @SuppressWarnings("unchecked")
66
+ public static <T> T callMethod(Object receiver, String methodName, Object arg, Class<T> returnType) {
67
+ try {
68
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object.class, Class.class);
69
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, arg, returnType);
70
+ } catch (NoSuchMethodException nsme) {
71
+ throw new RuntimeException(nsme);
72
+ } catch (IllegalAccessException iae) {
73
+ throw new RuntimeException(iae);
74
+ } catch (java.lang.reflect.InvocationTargetException ite) {
75
+ printStackTrace(ite);
76
+ }
77
+ return null;
78
+ }
79
+
80
+ @SuppressWarnings("unchecked")
81
+ public static <T> T callMethod(Object receiver, String methodName, Class<T> returnType) {
82
+ try {
83
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Class.class);
84
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, returnType);
85
+ } catch (NoSuchMethodException nsme) {
86
+ throw new RuntimeException(nsme);
87
+ } catch (IllegalAccessException iae) {
88
+ throw new RuntimeException(iae);
89
+ } catch (java.lang.reflect.InvocationTargetException ite) {
90
+ printStackTrace(ite);
91
+ }
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #put(String name, Object object)}
97
+ */
98
+ @Deprecated public static void defineGlobalConstant(String name, Object object) {
99
+ put(name, object);
100
+ }
101
+
102
+ /**
103
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #put(String name, Object object)}
104
+ */
105
+ @Deprecated public static void defineGlobalVariable(String name, Object object) {
106
+ put(name, object);
107
+ }
108
+
109
+ /**
110
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #runScriptlet(String code)}
111
+ */
112
+ @Deprecated public static Object exec(String code) {
113
+ try {
114
+ Method runScriptletMethod = ruby.getClass().getMethod("runScriptlet", String.class);
115
+ return runScriptletMethod.invoke(ruby, code);
116
+ } catch (NoSuchMethodException nsme) {
117
+ throw new RuntimeException(nsme);
118
+ } catch (IllegalAccessException iae) {
119
+ throw new RuntimeException(iae);
120
+ } catch (java.lang.reflect.InvocationTargetException ite) {
121
+ if (isDebugBuild) {
122
+ throw ((RuntimeException) ite.getCause());
123
+ } else {
124
+ return null;
125
+ }
126
+ }
127
+ }
128
+
129
+ /**
130
+ * @deprecated As of Ruboto 0.7.0, replaced by {@link #runScriptlet(String code)}
131
+ */
132
+ @Deprecated public static String execute(String code) {
133
+ Object result = exec(code);
134
+ return result != null ? result.toString() : "nil";
135
+ // TODO: Why is callMethod returning "main"?
136
+ // return result != null ? callMethod(result, "inspect", String.class) : "null";
137
+ }
138
+
139
+ public static Object get(String name) {
140
+ try {
141
+ Method getMethod = ruby.getClass().getMethod("get", String.class);
142
+ return getMethod.invoke(ruby, name);
143
+ } catch (NoSuchMethodException nsme) {
144
+ throw new RuntimeException(nsme);
145
+ } catch (IllegalAccessException iae) {
146
+ throw new RuntimeException(iae);
147
+ } catch (java.lang.reflect.InvocationTargetException ite) {
148
+ throw new RuntimeException(ite);
149
+ }
150
+ }
151
+
152
+ public static String getPlatformVersionName() {
153
+ return RUBOTO_CORE_VERSION_NAME;
154
+ }
155
+
156
+ public static String getScriptFilename() {
157
+ return callScriptingContainerMethod(String.class, "getScriptFilename");
158
+ }
159
+
160
+ public static boolean isDebugBuild() {
161
+ return isDebugBuild;
162
+ }
163
+
164
+ public static synchronized boolean isInitialized() {
165
+ return initialized;
166
+ }
167
+
168
+ public static void put(String name, Object object) {
169
+ try {
170
+ Method putMethod = ruby.getClass().getMethod("put", String.class, Object.class);
171
+ putMethod.invoke(ruby, name, object);
172
+ } catch (NoSuchMethodException nsme) {
173
+ throw new RuntimeException(nsme);
174
+ } catch (IllegalAccessException iae) {
175
+ throw new RuntimeException(iae);
176
+ } catch (java.lang.reflect.InvocationTargetException ite) {
177
+ throw new RuntimeException(ite);
178
+ }
179
+ }
180
+
181
+ public static Object runScriptlet(String code) {
182
+ try {
183
+ Method runScriptletMethod = ruby.getClass().getMethod("runScriptlet", String.class);
184
+ return runScriptletMethod.invoke(ruby, code);
185
+ } catch (NoSuchMethodException nsme) {
186
+ throw new RuntimeException(nsme);
187
+ } catch (IllegalAccessException iae) {
188
+ throw new RuntimeException(iae);
189
+ } catch (java.lang.reflect.InvocationTargetException ite) {
190
+ if (isDebugBuild) {
191
+ throw ((RuntimeException) ite.getCause());
192
+ } else {
193
+ return null;
194
+ }
195
+ }
196
+ }
197
+
198
+ public static synchronized boolean setUpJRuby(Context appContext) {
199
+ return setUpJRuby(appContext, output == null ? System.out : output);
200
+ }
201
+
202
+ @SuppressWarnings({ "unchecked", "rawtypes" })
203
+ public static synchronized boolean setUpJRuby(Context appContext, PrintStream out) {
204
+ if (!initialized) {
205
+ // BEGIN Ruboto HeapAlloc
206
+ @SuppressWarnings("unused")
207
+ byte[] arrayForHeapAllocation = new byte[13 * 1024 * 1024];
208
+ arrayForHeapAllocation = null;
209
+ // END Ruboto HeapAlloc
210
+ setDebugBuild(appContext);
211
+ Log.d("Setting up JRuby runtime (" + (isDebugBuild ? "DEBUG" : "RELEASE") + ")");
212
+ System.setProperty("jruby.bytecode.version", "1.6");
213
+ System.setProperty("jruby.interfaces.useProxy", "true");
214
+ System.setProperty("jruby.management.enabled", "false");
215
+ System.setProperty("jruby.objectspace.enabled", "false");
216
+ System.setProperty("jruby.thread.pooling", "true");
217
+ System.setProperty("jruby.native.enabled", "false");
218
+ // System.setProperty("jruby.compat.version", "RUBY1_8"); // RUBY1_9 is the default
219
+
220
+ // Uncomment these to debug Ruby source loading
221
+ // System.setProperty("jruby.debug.loadService", "true");
222
+ // System.setProperty("jruby.debug.loadService.timing", "true");
223
+
224
+
225
+ ClassLoader classLoader;
226
+ Class<?> scriptingContainerClass;
227
+ String apkName = null;
228
+
229
+ try {
230
+ scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer");
231
+ System.out.println("Found JRuby in this APK");
232
+ classLoader = JRubyAdapter.class.getClassLoader();
233
+ try {
234
+ apkName = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0).sourceDir;
235
+ } catch (NameNotFoundException e) {}
236
+ } catch (ClassNotFoundException e1) {
237
+ String packageName = "org.ruboto.core";
238
+ try {
239
+ PackageInfo pkgInfo = appContext.getPackageManager().getPackageInfo(packageName, 0);
240
+ apkName = pkgInfo.applicationInfo.sourceDir;
241
+ RUBOTO_CORE_VERSION_NAME = pkgInfo.versionName;
242
+ } catch (PackageManager.NameNotFoundException e2) {
243
+ out.println("JRuby not found in local APK:");
244
+ e1.printStackTrace(out);
245
+ out.println("JRuby not found in platform APK:");
246
+ e2.printStackTrace(out);
247
+ return false;
248
+ }
249
+
250
+ System.out.println("Found JRuby in platform APK");
251
+ classLoader = new PathClassLoader(apkName, JRubyAdapter.class.getClassLoader());
252
+
253
+ try {
254
+ scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
255
+ } catch (ClassNotFoundException e) {
256
+ // FIXME(uwe): ScriptingContainer not found in the platform APK...
257
+ e.printStackTrace();
258
+ return false;
259
+ }
260
+ }
261
+
262
+ try {
263
+ Class scopeClass = Class.forName("org.jruby.embed.LocalContextScope", true, scriptingContainerClass.getClassLoader());
264
+ Class behaviorClass = Class.forName("org.jruby.embed.LocalVariableBehavior", true, scriptingContainerClass.getClassLoader());
265
+
266
+ ruby = scriptingContainerClass
267
+ .getConstructor(scopeClass, behaviorClass)
268
+ .newInstance(Enum.valueOf(scopeClass, localContextScope),
269
+ Enum.valueOf(behaviorClass, localVariableBehavior));
270
+
271
+ Class compileModeClass = Class.forName("org.jruby.RubyInstanceConfig$CompileMode", true, classLoader);
272
+ callScriptingContainerMethod(Void.class, "setCompileMode", Enum.valueOf(compileModeClass, "OFF"));
273
+
274
+ // Class traceTypeClass = Class.forName("org.jruby.runtime.backtrace.TraceType", true, classLoader);
275
+ // Method traceTypeForMethod = traceTypeClass.getMethod("traceTypeFor", String.class);
276
+ // Object traceTypeRaw = traceTypeForMethod.invoke(null, "raw");
277
+ // callScriptingContainerMethod(Void.class, "setTraceType", traceTypeRaw);
278
+
279
+ // FIXME(uwe): Write tutorial on profiling.
280
+ // container.getProvider().getRubyInstanceConfig().setProfilingMode(mode);
281
+
282
+ // callScriptingContainerMethod(Void.class, "setClassLoader", classLoader);
283
+ Method setClassLoaderMethod = ruby.getClass().getMethod("setClassLoader", ClassLoader.class);
284
+ setClassLoaderMethod.invoke(ruby, classLoader);
285
+
286
+ Thread.currentThread().setContextClassLoader(classLoader);
287
+
288
+ String defaultCurrentDir = appContext.getFilesDir().getPath();
289
+ Log.d("Setting JRuby current directory to " + defaultCurrentDir);
290
+ callScriptingContainerMethod(Void.class, "setCurrentDirectory", defaultCurrentDir);
291
+
292
+ if (out != null) {
293
+ output = out;
294
+ setOutputStream(out);
295
+ } else if (output != null) {
296
+ setOutputStream(output);
297
+ }
298
+
299
+ String jrubyHome = "file:" + apkName + "!";
300
+ Log.i("Setting JRUBY_HOME: " + jrubyHome);
301
+ System.setProperty("jruby.home", jrubyHome);
302
+
303
+ addLoadPath(scriptsDirName(appContext));
304
+
305
+ initialized = true;
306
+ } catch (ClassNotFoundException e) {
307
+ handleInitException(e);
308
+ } catch (IllegalArgumentException e) {
309
+ handleInitException(e);
310
+ } catch (SecurityException e) {
311
+ handleInitException(e);
312
+ } catch (InstantiationException e) {
313
+ handleInitException(e);
314
+ } catch (IllegalAccessException e) {
315
+ handleInitException(e);
316
+ } catch (InvocationTargetException e) {
317
+ handleInitException(e);
318
+ } catch (NoSuchMethodException e) {
319
+ handleInitException(e);
320
+ }
321
+ }
322
+ return initialized;
323
+ }
324
+
325
+ public static void setScriptFilename(String name) {
326
+ callScriptingContainerMethod(Void.class, "setScriptFilename", name);
327
+ }
328
+
329
+ public static boolean usesPlatformApk() {
330
+ return RUBOTO_CORE_VERSION_NAME != null;
331
+ }
332
+
333
+ // Private methods
334
+
335
+ private static Boolean addLoadPath(String scriptsDir) {
336
+ if (new File(scriptsDir).exists()) {
337
+ Log.i("Added directory to load path: " + scriptsDir);
338
+ Script.addDir(scriptsDir);
339
+ runScriptlet("$:.unshift '" + scriptsDir + "' ; $:.uniq!");
340
+ Log.d("Changing JRuby current directory to " + scriptsDir);
341
+ callScriptingContainerMethod(Void.class, "setCurrentDirectory", scriptsDir);
342
+ return true;
343
+ } else {
344
+ Log.i("Extra scripts dir not present: " + scriptsDir);
345
+ return false;
346
+ }
347
+ }
348
+
349
+ @SuppressWarnings("unchecked")
350
+ private static <T> T callScriptingContainerMethod(Class<T> returnType, String methodName, Object... args) {
351
+ Class<?>[] argClasses = new Class[args.length];
352
+ for (int i = 0; i < argClasses.length; i++) {
353
+ argClasses[i] = args[i].getClass();
354
+ }
355
+ try {
356
+ Method method = ruby.getClass().getMethod(methodName, argClasses);
357
+ T result = (T) method.invoke(ruby, args);
358
+ return result;
359
+ } catch (RuntimeException re) {
360
+ re.printStackTrace();
361
+ } catch (IllegalAccessException e) {
362
+ e.printStackTrace();
363
+ } catch (InvocationTargetException e) {
364
+ printStackTrace(e);
365
+ } catch (NoSuchMethodException e) {
366
+ e.printStackTrace();
367
+ }
368
+ return null;
369
+ }
370
+
371
+ private static void handleInitException(Exception e) {
372
+ Log.e("Exception starting JRuby");
373
+ Log.e(e.getMessage() != null ? e.getMessage() : e.getClass().getName());
374
+ e.printStackTrace();
375
+ ruby = null;
376
+ }
377
+
378
+ static void printStackTrace(Throwable t) {
379
+ // TODO(uwe): Simplify this when Issue #144 is resolved
380
+ try {
381
+ t.printStackTrace(output);
382
+ } catch (NullPointerException npe) {
383
+ // TODO(uwe): printStackTrace should not fail
384
+ for (java.lang.StackTraceElement ste : t.getStackTrace()) {
385
+ output.append(ste.toString() + "\n");
386
+ }
387
+ }
388
+ }
389
+
390
+ private static String scriptsDirName(Context context) {
391
+ File storageDir = null;
392
+ if (JRubyAdapter.isDebugBuild()) {
393
+
394
+ // FIXME(uwe): Simplify this as soon as we drop support for android-7
395
+ if (android.os.Build.VERSION.SDK_INT >= 8) {
396
+ try {
397
+ Method method = context.getClass().getMethod("getExternalFilesDir", String.class);
398
+ storageDir = (File) method.invoke(context, (Object) null);
399
+ } catch (SecurityException e) {
400
+ JRubyAdapter.printStackTrace(e);
401
+ } catch (NoSuchMethodException e) {
402
+ JRubyAdapter.printStackTrace(e);
403
+ } catch (IllegalArgumentException e) {
404
+ JRubyAdapter.printStackTrace(e);
405
+ } catch (IllegalAccessException e) {
406
+ JRubyAdapter.printStackTrace(e);
407
+ } catch (InvocationTargetException e) {
408
+ JRubyAdapter.printStackTrace(e);
409
+ }
410
+ } else {
411
+ storageDir = new File(Environment.getExternalStorageDirectory(), "Android/data/" + context.getPackageName() + "/files");
412
+ Log.e("Calculated path to sdcard the old way: " + storageDir);
413
+ }
414
+ // FIXME end
415
+
416
+ if (storageDir == null || (!storageDir.exists() && !storageDir.mkdirs())) {
417
+ Log.e("Development mode active, but sdcard is not available. Make sure you have added\n<uses-permission android:name='android.permission.WRITE_EXTERNAL_STORAGE' />\nto your AndroidManifest.xml file.");
418
+ storageDir = context.getFilesDir();
419
+ }
420
+ } else {
421
+ storageDir = context.getFilesDir();
422
+ }
423
+ return storageDir.getAbsolutePath() + "/scripts";
424
+ }
425
+
426
+ private static void setDebugBuild(Context context) {
427
+ PackageManager pm = context.getPackageManager();
428
+ PackageInfo pi;
429
+ try {
430
+ pi = pm.getPackageInfo(context.getPackageName(), 0);
431
+ isDebugBuild = ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
432
+ } catch (NameNotFoundException e) {
433
+ isDebugBuild = false;
434
+ }
435
+ }
436
+
437
+ private static void setLocalContextScope(String val) {
438
+ localContextScope = val;
439
+ }
440
+
441
+ private static void setLocalVariableBehavior(String val) {
442
+ localVariableBehavior = val;
443
+ }
444
+
445
+ private static void setOutputStream(PrintStream out) {
446
+ if (ruby == null) {
447
+ output = out;
448
+ } else {
449
+ try {
450
+ Method setOutputMethod = ruby.getClass().getMethod("setOutput", PrintStream.class);
451
+ setOutputMethod.invoke(ruby, out);
452
+ Method setErrorMethod = ruby.getClass().getMethod("setError", PrintStream.class);
453
+ setErrorMethod.invoke(ruby, out);
454
+ } catch (IllegalArgumentException e) {
455
+ handleInitException(e);
456
+ } catch (SecurityException e) {
457
+ handleInitException(e);
458
+ } catch (IllegalAccessException e) {
459
+ handleInitException(e);
460
+ } catch (InvocationTargetException e) {
461
+ handleInitException(e);
462
+ } catch (NoSuchMethodException e) {
463
+ handleInitException(e);
464
+ }
465
+ }
466
+ }
467
+
468
+ }