ruboto-core 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +1 -1
  3. data/README.md +42 -3
  4. data/Rakefile +5 -0
  5. data/assets/Rakefile +72 -7
  6. data/assets/assets/scripts/ruboto.rb +124 -71
  7. data/assets/res/drawable-hdpi/icon.png +0 -0
  8. data/assets/res/drawable-ldpi/icon.png +0 -0
  9. data/assets/res/drawable-mdpi/icon.png +0 -0
  10. data/assets/samples/sample_activity.rb +3 -3
  11. data/assets/samples/sample_activity_test.rb +21 -0
  12. data/assets/samples/sample_broadcast_receiver.rb +1 -1
  13. data/assets/samples/sample_broadcast_receiver_test.rb +1 -0
  14. data/assets/samples/sample_service.rb +2 -1
  15. data/assets/samples/sample_service_test.rb +1 -0
  16. data/assets/src/InheritingActivity.java +6 -4
  17. data/assets/src/RubotoActivity.java +17 -5
  18. data/assets/src/org/ruboto/Script.java +30 -17
  19. data/assets/src/org/ruboto/test/ActivityTest.java +58 -0
  20. data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +102 -0
  21. data/assets/test/assets/scripts/test_helper.rb +9 -0
  22. data/bin/ruboto +3 -920
  23. data/lib/java_class_gen/android_api.xml +1 -1
  24. data/lib/ruboto.rb +15 -0
  25. data/lib/ruboto/api.rb +21 -0
  26. data/lib/ruboto/commands/base.rb +373 -0
  27. data/lib/ruboto/core_ext/array.rb +6 -0
  28. data/lib/ruboto/core_ext/object.rb +10 -0
  29. data/lib/ruboto/util/asset_copier.rb +20 -0
  30. data/lib/ruboto/util/build.rb +183 -0
  31. data/lib/ruboto/util/code_formatting.rb +22 -0
  32. data/lib/ruboto/util/log_action.rb +20 -0
  33. data/lib/ruboto/util/main_fix.rb +13 -0
  34. data/lib/ruboto/util/objectspace.rb +8 -0
  35. data/lib/ruboto/util/scan_in_api.rb +40 -0
  36. data/lib/ruboto/util/update.rb +102 -0
  37. data/lib/ruboto/util/verify.rb +59 -0
  38. data/lib/ruboto/util/xml_element.rb +197 -0
  39. data/test/app_test.rb +44 -0
  40. data/test/test_helper.rb +4 -0
  41. metadata +41 -14
@@ -1,4 +1,4 @@
1
- require 'ruboto.rb'
1
+ require 'ruboto'
2
2
 
3
3
  ruboto_import_widgets :TextView, :LinearLayout, :Button
4
4
 
@@ -7,8 +7,8 @@ $activity.handle_create do |bundle|
7
7
 
8
8
  setup_content do
9
9
  linear_layout :orientation => LinearLayout::VERTICAL do
10
- @text_view = text_view :text => "What hath Matz wrought?"
11
- button :text => "M-x butterfly", :width => :wrap_content
10
+ @text_view = text_view :text => "What hath Matz wrought?", :id => 42
11
+ button :text => "M-x butterfly", :width => :wrap_content, :id => 43
12
12
  end
13
13
  end
14
14
 
@@ -0,0 +1,21 @@
1
+ activity Java::THE_PACKAGE.SampleActivity
2
+
3
+ setup do |activity|
4
+ start = Time.now
5
+ loop do
6
+ @text_view = activity.findViewById(42)
7
+ break if @text_view || (Time.now - start > 60)
8
+ sleep 1
9
+ end
10
+ assert @text_view
11
+ end
12
+
13
+ test('initial setup') do |activity|
14
+ assert_equal "What hath Matz wrought?", @text_view.text
15
+ end
16
+
17
+ test('button changes text') do |activity|
18
+ button = activity.findViewById(43)
19
+ button.performClick
20
+ assert_equal "What hath Matz wrought!", @text_view.text
21
+ end
@@ -1,4 +1,4 @@
1
- require 'ruboto.rb'
1
+ require 'ruboto'
2
2
 
3
3
  # despite the name, the when_launched block will get called whenever
4
4
  # the BroadcastReceiver receives an intent (whenever onReceive is called)
@@ -1,4 +1,5 @@
1
- require 'ruboto.rb'
1
+ require 'ruboto'
2
+
2
3
  $service.handle_create do
3
4
  # define what your service does. Directly put any code that you want
4
5
  # executed when onCreate gets called. Define the rest of the
@@ -0,0 +1 @@
1
+ # TODO
@@ -2,9 +2,11 @@ package THE_PACKAGE;
2
2
 
3
3
  public class InheritingActivity extends org.ruboto.RubotoActivity {
4
4
  public void onCreate(android.os.Bundle arg0) {
5
+ try {
6
+ setSplash(Class.forName("THE_PACKAGE.R$layout").getField("splash").getInt(null));
7
+ } catch (Exception e) {}
5
8
 
6
- setScriptName("start.rb");
7
- super.onCreate(arg0);
8
- }
9
-
9
+ setScriptName("start.rb");
10
+ super.onCreate(arg0);
11
+ }
10
12
  }
@@ -13,6 +13,7 @@ import android.os.Handler;
13
13
  public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
14
14
  private Ruby __ruby__;
15
15
  private String scriptName;
16
+ private int splash = 0;
16
17
  private String remoteVariable = "";
17
18
  public Object[] args;
18
19
  private ProgressDialog loadingDialog;
@@ -40,6 +41,10 @@ THE_CONSTANTS
40
41
  return this;
41
42
  }
42
43
 
44
+ public void setSplash(int a_res){
45
+ splash = a_res;
46
+ }
47
+
43
48
  public void setScriptName(String name){
44
49
  scriptName = name;
45
50
  }
@@ -57,10 +62,16 @@ THE_CONSTANTS
57
62
  super.onCreate(arg0);
58
63
 
59
64
  if (Script.getRuby() != null) {
65
+ backgroundCreate();
60
66
  finishCreate();
61
67
  } else {
68
+ if (splash == 0) {
69
+ loadingDialog = ProgressDialog.show(this, null, "Loading...", true, false);
70
+ } else {
71
+ requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
72
+ setContentView(splash);
73
+ }
62
74
  loadingThread.start();
63
- loadingDialog = ProgressDialog.show(this, null, "Loading...", true, false);
64
75
  }
65
76
  }
66
77
 
@@ -69,27 +80,28 @@ THE_CONSTANTS
69
80
  private final Thread loadingThread = new Thread() {
70
81
  public void run(){
71
82
  Script.setUpJRuby(null);
83
+ backgroundCreate();
72
84
  loadingHandler.post(loadingComplete);
73
85
  }
74
86
  };
75
87
 
76
88
  private final Runnable loadingComplete = new Runnable(){
77
89
  public void run(){
78
- loadingDialog.dismiss();
90
+ if (loadingDialog != null) loadingDialog.dismiss();
79
91
  finishCreate();
80
92
  onStart();
81
93
  onResume();
82
94
  }
83
95
  };
84
96
 
85
- private void finishCreate() {
97
+ private void backgroundCreate() {
86
98
  Script.copyScriptsIfNeeded(getFilesDir().getAbsolutePath() + "/scripts", getAssets());
87
-
88
99
  getRuby();
89
-
90
100
  Script.defineGlobalVariable("$activity", this);
91
101
  Script.defineGlobalVariable("$bundle", args[0]);
102
+ }
92
103
 
104
+ private void finishCreate() {
93
105
  android.os.Bundle configBundle = getIntent().getBundleExtra("RubotoActivity Config");
94
106
 
95
107
  if (configBundle != null) {
@@ -21,6 +21,7 @@ import org.jruby.RubyInstanceConfig;
21
21
  import org.jruby.exceptions.RaiseException;
22
22
  import org.jruby.javasupport.JavaUtil;
23
23
  import org.jruby.parser.EvalStaticScope;
24
+ import org.jruby.runtime.builtin.IRubyObject;
24
25
  import org.jruby.runtime.DynamicScope;
25
26
  import org.jruby.runtime.ThreadContext;
26
27
  import org.jruby.runtime.scope.ManyVarsDynamicScope;
@@ -62,6 +63,7 @@ public class Script {
62
63
 
63
64
  public static synchronized Ruby setUpJRuby(PrintStream out) {
64
65
  if (ruby == null) {
66
+ System.setProperty("jruby.interfaces.useProxy", "true");
65
67
  RubyInstanceConfig config = new RubyInstanceConfig();
66
68
  config.setCompileMode(RubyInstanceConfig.CompileMode.OFF);
67
69
 
@@ -89,13 +91,17 @@ public class Script {
89
91
  public static String execute(String code) {
90
92
  if (!initialized) return null;
91
93
  try {
92
- return ruby.evalScriptlet(code, scope).inspect().asJavaString();
94
+ return exec(code).inspect().asJavaString();
93
95
  } catch (RaiseException re) {
94
96
  re.printStackTrace(ruby.getErrorStream());
95
97
  return null;
96
98
  }
97
99
  }
98
100
 
101
+ public static IRubyObject exec(String code) throws RaiseException {
102
+ return ruby.evalScriptlet(code, scope);
103
+ }
104
+
99
105
  public static void defineGlobalConstant(String name, Object object) {
100
106
  ruby.defineGlobalConstant(name, JavaUtil.convertJavaToRuby(ruby, object));
101
107
  }
@@ -146,30 +152,37 @@ public class Script {
146
152
  for (String f : assets.list(from)) {
147
153
  File dest = new File(to, f);
148
154
 
149
- if (dest.exists())
155
+ if (dest.exists()) {
150
156
  continue;
151
-
152
- Log.d(TAG, "copying file " + f);
153
-
154
- InputStream is = assets.open(from+ "/" +f);
155
- OutputStream fos = new BufferedOutputStream(new FileOutputStream(dest));
156
-
157
- int n;
158
- while ((n = is.read(buffer, 0, buffer.length)) != -1)
159
- fos.write(buffer, 0, n);
160
-
161
- is.close();
162
- fos.close();
157
+ }
158
+
159
+ Log.d(TAG, "copying file from " + from + "/" + f + " to " + dest);
160
+
161
+ if (assets.list(from + "/" + f).length == 0) {
162
+ InputStream is = assets.open(from + "/" + f);
163
+ OutputStream fos = new BufferedOutputStream(new FileOutputStream(dest), 8192);
164
+
165
+ int n;
166
+ while ((n = is.read(buffer, 0, buffer.length)) != -1) {
167
+ fos.write(buffer, 0, n);
168
+ }
169
+ is.close();
170
+ fos.close();
171
+ } else {
172
+ dest.mkdir();
173
+ copyScripts(from + "/" + f, dest, assets);
174
+ }
163
175
  }
164
176
  } catch (IOException iox) {
165
- Log.e(TAG, "error copying demo scripts", iox);
177
+ Log.e(TAG, "error copying scripts", iox);
166
178
  }
167
179
  }
168
180
 
169
181
  public static void copyScriptsIfNeeded(String to, AssetManager assets) {
170
182
  /* the if makes sure we only do this the first time */
171
- if (configDir(to))
183
+ if (configDir(to)) {
172
184
  copyScripts("scripts", scriptsDirFile, assets);
185
+ }
173
186
  }
174
187
 
175
188
 
@@ -208,7 +221,7 @@ public class Script {
208
221
  }
209
222
 
210
223
  public String getContents() throws IOException {
211
- BufferedReader buffer = new BufferedReader(new FileReader(getFile()));
224
+ BufferedReader buffer = new BufferedReader(new FileReader(getFile()), 8192);
212
225
  StringBuilder source = new StringBuilder();
213
226
  while (true) {
214
227
  String line = buffer.readLine();
@@ -0,0 +1,58 @@
1
+ package org.ruboto.test;
2
+
3
+ import android.app.Activity;
4
+ import android.app.ProgressDialog;
5
+ import android.test.ActivityInstrumentationTestCase2;
6
+ import android.util.Log;
7
+ import java.io.BufferedReader;
8
+ import java.io.FileReader;
9
+ import java.io.InputStream;
10
+ import java.io.InputStreamReader;
11
+ import java.io.IOException;
12
+ import junit.framework.AssertionFailedError;
13
+ import junit.framework.Test;
14
+ import junit.framework.TestResult;
15
+ import junit.framework.TestSuite;
16
+ import org.jruby.exceptions.RaiseException;
17
+ import org.jruby.javasupport.JavaUtil;
18
+ import org.jruby.javasupport.util.RuntimeHelpers;
19
+ import org.jruby.runtime.builtin.IRubyObject;
20
+ import org.ruboto.Script;
21
+
22
+ public class ActivityTest extends ActivityInstrumentationTestCase2 {
23
+ private final IRubyObject setup;
24
+ private final IRubyObject block;
25
+
26
+ public ActivityTest(Class activityClass, IRubyObject setup, String name, IRubyObject block) {
27
+ super(activityClass);
28
+ setName(name);
29
+ this.setup = setup;
30
+ this.block = block;
31
+ Log.d(getClass().getName(), "Instance: " + name);
32
+ }
33
+
34
+ public void runTest() throws Exception {
35
+ Log.d(getClass().getName(), "runTest");
36
+ Log.d(getClass().getName(), "runTest: " + getName());
37
+ Script.setUpJRuby(null);
38
+ Log.d(getClass().getName(), "ruby ok");
39
+ try {
40
+ final Activity activity = getActivity();
41
+ Log.d(getClass().getName(), "activity ok");
42
+ runTestOnUiThread(new Runnable() {
43
+ public void run() {
44
+ Log.d(getClass().getName(), "calling setup");
45
+ RuntimeHelpers.invoke(setup.getRuntime().getCurrentContext(), setup, "call",
46
+ JavaUtil.convertJavaToRuby(Script.getRuby(), activity));
47
+ Log.d(getClass().getName(), "setup ok");
48
+ RuntimeHelpers.invoke(block.getRuntime().getCurrentContext(), block, "call",
49
+ JavaUtil.convertJavaToRuby(Script.getRuby(), activity));
50
+ }
51
+ });
52
+ } catch (Throwable t) {
53
+ throw new AssertionFailedError(t.getMessage());
54
+ }
55
+ Log.d(getClass().getName(), "runTest ok");
56
+ }
57
+
58
+ }
@@ -0,0 +1,102 @@
1
+ package org.ruboto.test;
2
+
3
+ import android.test.ActivityInstrumentationTestCase2;
4
+ import android.util.Log;
5
+ import java.io.BufferedReader;
6
+ import java.io.File;
7
+ import java.io.InputStream;
8
+ import java.io.InputStreamReader;
9
+ import java.io.IOException;
10
+ import java.io.UnsupportedEncodingException;
11
+ import java.net.JarURLConnection;
12
+ import java.net.URL;
13
+ import java.net.URLDecoder;
14
+ import java.util.ArrayList;
15
+ import java.util.Arrays;
16
+ import java.util.Collections;
17
+ import java.util.Enumeration;
18
+ import java.util.jar.JarFile;
19
+ import java.util.jar.JarEntry;
20
+ import java.util.List;
21
+ import junit.framework.Test;
22
+ import junit.framework.TestCase;
23
+ import junit.framework.TestSuite;
24
+ import org.jruby.exceptions.RaiseException;
25
+ import org.jruby.javasupport.JavaEmbedUtils;
26
+ import org.jruby.RubyClass;
27
+ import org.jruby.runtime.builtin.IRubyObject;
28
+ import org.ruboto.Script;
29
+
30
+ public class InstrumentationTestRunner extends android.test.InstrumentationTestRunner {
31
+ private Class activityClass;
32
+ private IRubyObject setup;
33
+ private TestSuite suite;
34
+
35
+ public TestSuite getAllTests() {
36
+ Log.i(getClass().getName(), "Finding test scripts");
37
+ suite = new TestSuite("Sweet");
38
+
39
+ try {
40
+ Script.setUpJRuby(null);
41
+ Script.defineGlobalVariable("$runner", this);
42
+ Script.defineGlobalVariable("$test", this);
43
+ Script.defineGlobalVariable("$suite", suite);
44
+ loadScript("test_helper.rb");
45
+ String[] scripts = getContext().getResources().getAssets().list("scripts");
46
+ for (String f : scripts) {
47
+ if (f.equals("test_helper.rb")) continue;
48
+ Log.i(getClass().getName(), "Found script: " + f);
49
+ loadScript(f);
50
+ }
51
+ } catch (IOException e) {
52
+ addError(suite, e);
53
+ } catch (RaiseException e) {
54
+ addError(suite, e);
55
+ }
56
+ return suite;
57
+ }
58
+
59
+ public void activity(Class activityClass) {
60
+ this.activityClass = activityClass;
61
+ }
62
+
63
+ public void setup(IRubyObject block) {
64
+ this.setup = block;
65
+ }
66
+
67
+ public void test(String name, IRubyObject block) {
68
+ if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.FROYO) {
69
+ name ="runTest";
70
+ }
71
+ Test test = new ActivityTest(activityClass, setup, name, block);
72
+ suite.addTest(test);
73
+ Log.d(getClass().getName(), "Made test instance: " + test);
74
+ }
75
+
76
+ private void addError(TestSuite suite, final Throwable t) {
77
+ Log.e(getClass().getName(), "Exception loading tests: " + t);
78
+ suite.addTest(new TestCase(t.getMessage()) {
79
+ public void runTest() throws java.lang.Throwable {
80
+ throw t;
81
+ }
82
+ });
83
+ }
84
+
85
+ private void loadScript(String f) throws IOException {
86
+ InputStream is = getContext().getResources().getAssets().open("scripts/" + f);
87
+ BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
88
+ StringBuilder source = new StringBuilder();
89
+ while (true) {
90
+ String line = buffer.readLine();
91
+ if (line == null) break;
92
+ source.append(line).append("\n");
93
+ }
94
+ buffer.close();
95
+
96
+ Log.d(getClass().getName(), "Loading test script: " + f);
97
+ Script.defineGlobalVariable("$script_code", source.toString());
98
+ Script.exec("$test.instance_eval($script_code)");
99
+ Log.d(getClass().getName(), "Test script loaded");
100
+ }
101
+
102
+ }
@@ -0,0 +1,9 @@
1
+ require 'java'
2
+
3
+ def assert(value, message = "#{value.inspect} expected to be true")
4
+ raise message unless value
5
+ end
6
+
7
+ def assert_equal(expected, actual, message = "'#{expected}' expected, but got '#{actual}'")
8
+ raise message unless expected == actual
9
+ end
data/bin/ruboto CHANGED
@@ -1,922 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ require 'ruboto/commands/base'
2
3
 
3
- begin
4
- require 'jruby'
5
- JRuby.objectspace = true
6
- rescue LoadError
7
- end
8
-
9
- require 'rubygems'
10
- require 'main'
11
- require 'fileutils'
12
- require 'rexml/document'
13
- require 'jruby-jars'
14
-
15
- # fix main (to an extent)
16
- module Main
17
- class Program
18
- module InstanceMethods
19
- def setup_finalizers
20
- @finalizers ||= []
21
- ObjectSpace.define_finalizer(self) do
22
- while((f = @finalizers.pop)); f.call; end
23
- end
24
- end
25
- end
26
- end
27
- end
28
-
29
- $gem_root = File.expand_path(__FILE__+ "/../..")
30
- $assets = File.expand_path(__FILE__ + "/../../assets")
31
-
32
- class AssetCopier
33
- def initialize(from, to)
34
- @from = from
35
- @to = to
36
- end
37
-
38
- def copy(from, to='')
39
- FileUtils.mkdir_p(File.join(@to, to))
40
- FileUtils.cp_r(Dir[File.join(@from, from)], File.join(@to, to))
41
- end
42
-
43
- def copy_from_absolute_path(from, to='')
44
- FileUtils.mkdir_p(File.join(@to, to))
45
- FileUtils.cp_r(Dir[from], File.join(@to, to))
46
- end
47
- end
48
-
49
- ###########################################################################
50
- #
51
- # log_action: put text to stdout around the execution of a block
52
- #
53
-
54
- def log_action(initial_text, final_text="Done.", &block)
55
- $stdout.sync = true
56
- print initial_text, "..."
57
- yield
58
- puts final_text
59
- end
60
-
61
- ###########################################################################
62
- #
63
- # XMLElement:
64
- # Extends Hash to simulate a REXML::Element (but much faster) and provides
65
- # information in the necessary format to generate Java code.
66
- #
67
-
68
- class XMLElement < Hash
69
- def root
70
- $api
71
- end
72
-
73
- def name
74
- self["name"]
75
- end
76
-
77
- def attribute(name)
78
- self["values"][name]
79
- end
80
-
81
- def add_element(name, attributes)
82
- new_element = XMLElement.new
83
- new_element["name"] = name
84
- new_element["values"] = attributes
85
-
86
- self[name] = [] unless self[name]
87
- self[name] << new_element
88
-
89
- new_element
90
- end
91
-
92
- def get_elements(name)
93
- self[name] or []
94
- end
95
-
96
- def find_class_or_interface(klass, a_type)
97
- abort "ERROR: Can't parse package from #{klass}" unless klass.match(/([a-z.]+)\.([A-Z][A-Za-z.]+)/)
98
- package = self["package"].find{|i| i.attribute("name") == $1}
99
- abort "ERROR: Can't find package #{$1}" unless package
100
- if a_type == "either"
101
- package["class"].find{|i| i.attribute("name") == $2} or package["interface"].find{|i| i.attribute("name") == $2}
102
- else
103
- package[a_type].find{|i| i.attribute("name") == $2}
104
- end
105
- end
106
-
107
- def find_class(package_and_class)
108
- find_class_or_interface(package_and_class, "class")
109
- end
110
-
111
- def find_interface(package_and_interface)
112
- find_class_or_interface(package_and_interface, "interface")
113
- end
114
-
115
- def all_methods(method_base="all", method_include="", method_exclude="", implements="")
116
- # get all the methogs
117
- all_methods = get_elements("method")
118
-
119
- # establish the base set of methods
120
- working_methods = case method_base
121
- when "all"
122
- all_methods
123
- when "none"
124
- []
125
- when "abstract"
126
- all_methods.select{|i| i.attribute("abstract") == "true"}
127
- when "on"
128
- all_methods.select{|i| i.attribute("name").match(/^on[A-Z]/)}
129
- end
130
-
131
- # make sure to include requested methods
132
- include_methods = method_include.split(",") if method_include.is_a?(String)
133
- all_methods.each{|i| working_methods << i if include_methods.include?(i.attribute("name"))}
134
-
135
- # make sure to exclude rejected methods
136
- exclude_methods = method_exclude.split(",") if method_exclude.is_a?(String)
137
- working_methods = working_methods.select{|i| not exclude_methods.include?(i.attribute("name"))}
138
-
139
- # remove methods marked final
140
- working_methods = working_methods.select{|i| (not i.attribute("final")) or i.attribute("final") == "false"}
141
-
142
- # get additional methods from parent
143
- if name =="class" and attribute("extends")
144
- parent = root.find_class(attribute("extends"))
145
- parent_methods = parent.all_methods(method_base, method_include, method_exclude)
146
- working_signatures = working_methods.map(&:method_signature)
147
- working_methods += parent_methods.select{|i| not working_signatures.include?(i.method_signature)}
148
- end
149
-
150
- # get additional methods from interfaces
151
- if name =="class" and implements != ""
152
- implements.split(",").each do |i|
153
- interface = root.find_interface(i)
154
- abort("Unkown interface: #{i}") unless interface
155
- working_signatures = working_methods.map(&:method_signature)
156
- working_methods += interface.all_methods.select{|j| not working_signatures.include?(j.method_signature)}
157
- end
158
- end
159
-
160
- working_methods
161
- end
162
-
163
- def parameters
164
- get_elements("parameter").map {|p| [p.attribute("name"), p.attribute("type").gsub("&lt;", "<").gsub("&gt;", ">")]}
165
- end
166
-
167
- def method_signature
168
- "#{attribute("name")}(#{parameters.map{|i| i[1]}.join(',')})"
169
- end
170
-
171
- def constant_string
172
- "CB_" + attribute("name").gsub(/[A-Z]/) {|i| "_#{i}"}.upcase.gsub(/^ON_/, "")
173
- end
174
-
175
- def super_string
176
- if attribute("api_added") and
177
- attribute("api_added").to_i > verify_min_sdk.to_i and
178
- attribute("api_added").to_i <= verify_target_sdk.to_i
179
- nil
180
- elsif attribute("abstract") == "true"
181
- nil
182
- elsif name == "method"
183
- "super.#{attribute("name")}(#{parameters.map{|i| i[0]}.join(", ")});"
184
- elsif name == "constructor"
185
- "super(#{parameters.map{|i| i[0]}.join(", ")});"
186
- end
187
- end
188
-
189
- def default_return
190
- return nil unless attribute("return")
191
- case attribute("return")
192
- when "boolean": "return false;"
193
- when "int": "return 0;"
194
- when "void": nil
195
- else "return null;"
196
- end
197
- end
198
-
199
- def super_return
200
- rv = super_string
201
- return rv unless attribute("return")
202
- rv ? "return #{rv}" : default_return
203
- end
204
-
205
- def ruby_call
206
- rv = []
207
-
208
- params = parameters
209
- args = ""
210
- if params.size > 3
211
- args = ", args"
212
- rv << "IRubyObject[] args = {" + params.map{|i| "JavaUtil.convertJavaToRuby(getRuby(), #{i[0]})"}.join(", ") + "};"
213
- elsif params.size > 0
214
- args = ", " + params.map{|i| "JavaUtil.convertJavaToRuby(getRuby(), #{i[0]})"}.join(", ")
215
- end
216
-
217
- return_cast = ""
218
- if attribute("return") and (attribute("return").include?(".") or attribute("return") == "int[]")
219
- return_cast = "return (#{attribute("return")})"
220
- elsif attribute("return") and attribute("return") == "int"
221
- return_cast = "return (Integer)"
222
- elsif attribute("return") and attribute("return") != "void"
223
- return_cast = "return (#{attribute("return").capitalize})"
224
- end
225
- return_cast = return_cast.gsub("&lt;", "<").gsub("&gt;", ">")
226
-
227
- convert_return = ""
228
- if attribute("return") and attribute("return") != "void"
229
- convert_return = ".toJava(#{attribute("return")}.class)"
230
- end
231
-
232
- rv << "#{return_cast}RuntimeHelpers.invoke(getRuby().getCurrentContext(), callbackProcs[#{constant_string}], \"call\" #{args})#{convert_return};"
233
- rv
234
- end
235
-
236
- def method_definition
237
- method_call((attribute("return") ? attribute("return") : "void"), attribute("name"), parameters,
238
- if_else("callbackProcs[#{constant_string}] != null",
239
- [super_string] + try_catch(ruby_call, ["re.printStackTrace();", default_return]),
240
- [super_return])).indent.join("\n")
241
- end
242
-
243
- def constructor_definition(class_name)
244
- method_call("", class_name, parameters, [super_string]).indent.join("\n")
245
- end
246
- end
247
-
248
- ###########################################################################
249
- #
250
- # Methods for formatting code
251
- #
252
-
253
- def method_call(return_type=nil, method_name="", parameters=[], body_clause=[])
254
- ["public #{return_type || ""} #{method_name}(" + parameters.map{|i| "#{i[1]} #{i[0]}"}.join(", ") + ") {",
255
- body_clause.indent, "}"]
256
- end
257
-
258
- def if_else(condition, if_clause, else_clause)
259
- ["if (#{condition}) {", if_clause.indent, else_clause.compact.empty? ? nil : "} else {", else_clause.indent, "}"]
260
- end
261
-
262
- def try_catch(try_clause, catch_clause)
263
- ["try {", try_clause.indent, "} catch (RaiseException re) {", catch_clause.indent, "}"]
264
- end
265
-
266
- class Array
267
- def indent
268
- flatten.compact.map{|i| " " + i}
269
- end
270
- end
271
-
272
- ###########################################################################
273
- #
274
- # Build Subclass or Interface:
275
- #
276
-
277
- #
278
- # build_file: Reads the src from the appropriate location,
279
- # uses the substitutions hash to modify the contents,
280
- # and writes to the new location
281
- #
282
- def build_file(src, package, name, substitutions, dest='.')
283
- to = File.join(dest, "src/#{package.gsub('.', '/')}")
284
- Dir.mkdir(to) unless File.directory?(to)
285
-
286
- text = File.read(File.expand_path($gem_root + "/assets/src/#{src}.java"))
287
- substitutions.each {|k,v| text.gsub!(k, v)}
288
-
289
- File.open(File.join(to, "#{name}.java"), 'w') {|f| f << text}
290
- end
291
-
292
- #
293
- # get_class_or_interface: Opens the xml file and locates the specified class.
294
- # Aborts if the class is not found or if it is not available for
295
- # all api levels
296
- #
297
- def get_class_or_interface(klass, force=false)
298
- element = verify_api.find_class_or_interface(klass, "either")
299
-
300
- abort "ERROR: #{klass} not found" unless element
301
-
302
- unless force
303
- abort "#{klass} not available in minSdkVersion, added in #{element.attribute('api_added')}; use --force to create it" if
304
- element.attribute('api_added') and element.attribute('api_added').to_i > verify_min_sdk.to_i
305
- abort "#{klass} deprecated for targetSdkVersion, deprecatrd in #{element.attribute('deprecated')}; use --force to create it" if
306
- element.attribute('deprecated') and element.attribute('deprecated').to_i <= verify_target_sdk.to_i
307
- end
308
-
309
- abort "#{klass} removed for targetSdkVersion, removed in #{element.attribute('api_removed')}" if
310
- element.attribute('api_removed') and element.attribute('api_removed').to_i <= verify_target_sdk.to_i
311
-
312
- element
313
- end
314
-
315
- #
316
- # check_methods: Checks the methods to see if they are available for all api levels
317
- #
318
- def check_methods(methods, force=false)
319
- min_api = verify_min_sdk.to_i
320
- target_api = verify_target_sdk.to_i
321
-
322
- # Remove methods changed outside of the scope of the sdk versions
323
- methods = methods.select{|i| not i.attribute('api_added') or i.attribute('api_added').to_i <= target_api}
324
- methods = methods.select{|i| not i.attribute('deprecated') or i.attribute('deprecated').to_i > min_api}
325
- methods = methods.select{|i| not i.attribute('api_removed') or i.attribute('api_removed').to_i > min_api}
326
-
327
- # Inform and remove methods that do not exist in one of the sdk versions
328
- methods = methods.select do |i|
329
- if i.attribute('api_removed') and i.attribute('api_removed').to_i <= target_api
330
- puts "Can't create #{i.method_signature} -- removed in #{i.attribute('api_removed')}"
331
- false
332
- else
333
- true
334
- end
335
- end
336
-
337
- new_methods = methods
338
- unless force
339
- # Inform and remove methods changed inside the scope of the sdk versions
340
- new_methods = methods.select do |i|
341
- if i.attribute('api_added') and i.attribute('api_added').to_i > min_api
342
- puts "Can't create #{i.method_signature} -- added in #{i.attribute('api_added')} -- exclude or force"
343
- false
344
- elsif i.attribute('deprecated') and i.attribute('deprecated').to_i <= target_api
345
- puts "Can't create #{i.method_signature} -- deprecated in #{i.attribute('deprecated')} -- exclude or force"
346
- false
347
- else
348
- true
349
- end
350
- end
351
-
352
- abort("Aborting!") if methods.count != new_methods.count
353
- end
354
-
355
- new_methods
356
- end
357
-
358
- #
359
- # generate_subclass_or_interface: Creates a subclass or interface based on the specifications.
360
- #
361
- def generate_subclass_or_interface(params)
362
- defaults = {:template => "InheritingClass", :method_base => "all", :method_include => "", :method_exclude => "", :force => false, :implements => ""}
363
- params = defaults.merge(params)
364
- params[:package] = verify_package unless params[:package]
365
-
366
- class_desc = get_class_or_interface(params[:class] || params[:interface], params[:force])
367
-
368
- puts "Generating methods for #{params[:name]}..."
369
- methods = class_desc.all_methods(params[:method_base], params[:method_include], params[:method_exclude], params[:implements])
370
- methods = check_methods(methods, params[:force])
371
- puts "Done. Methods created: #{methods.count}"
372
-
373
- # Remove any duplicate constants (use *args handle multiple parameter lists)
374
- constants = methods.map(&:constant_string).uniq
375
-
376
- build_file params[:template], params[:package], params[:name], {
377
- "THE_PACKAGE" => params[:package],
378
- "THE_ACTION" => class_desc.name == "class" ? "extends" : "implements",
379
- "THE_ANDROID_CLASS" => (params[:class] || params[:interface]) +
380
- (params[:implements] == "" ? "" : (" implements " + params[:implements].split(",").join(", "))),
381
- "THE_RUBOTO_CLASS" => params[:name],
382
- "THE_CONSTANTS" => constants.map {|i| "public static final int #{i} = #{constants.index(i)};"}.indent.join("\n"),
383
- "CONSTANTS_COUNT" => methods.count.to_s,
384
- "THE_CONSTRUCTORS" => class_desc.name == "class" ?
385
- class_desc.get_elements("constructor").map{|i| i.constructor_definition(params[:name])}.join("\n\n") : "",
386
- "THE_METHODS" => methods.map{|i| i.method_definition}.join("\n\n")
387
- }
388
- end
389
-
390
- #
391
- # generate_core_classe: generates RubotoActivity, RubotoService, etc. based
392
- # on the API specifications.
393
- #
394
- def generate_core_classes(params)
395
- %w(android.view.View.OnClickListener android.widget.AdapterView.OnItemClickListener).each do |i|
396
- name = i.split(".")[-1]
397
- if(params[:class] == name or params[:class] == "all")
398
- generate_subclass_or_interface({:package => "org.ruboto.callbacks", :class => i, :name => "Ruboto#{name}"})
399
- end
400
- end
401
-
402
- hash = {:package => "org.ruboto"}
403
- %w(method_base method_include implements force).inject(hash) {|h, i| h[i.to_sym] = params[i.to_sym]; h}
404
- hash[:method_exclude] = params[:method_exclude].split(",").push("onCreate").push("onReceive").join(",")
405
-
406
- %w(android.app.Activity android.app.Service android.content.BroadcastReceiver android.view.View).each do |i|
407
- name = i.split(".")[-1]
408
- if(params[:class] == name or params[:class] == "all")
409
- generate_subclass_or_interface(
410
- hash.merge({:template => name == "View" ? "InheritingClass" : "Ruboto#{name}", :class => i, :name => "Ruboto#{name}"}))
411
- end
412
- end
413
-
414
- # Activities that can be created, but only directly (i.e., not included in all)
415
- %w(android.preference.PreferenceActivity android.app.TabActivity).each do |i|
416
- name = i.split(".")[-1]
417
- if params[:class] == name
418
- generate_subclass_or_interface(hash.merge({:template => "RubotoActivity", :class => i, :name => "Ruboto#{name}"}))
419
- end
420
- end
421
- end
422
-
423
- ###########################################################################
424
- #
425
- # Updating components
426
- #
427
-
428
- def update_jruby(force=nil)
429
- jruby_core = Dir.glob("libs/jruby-core-*.jar")[0]
430
- jruby_stdlib = Dir.glob("libs/jruby-stdlib-*.jar")[0]
431
- new_jruby_version = JRubyJars::core_jar_path.split('/')[-1][11..-5]
432
-
433
- unless force
434
- abort "cannot find existing jruby jars in libs. Make sure you're in the root directory of your app" if
435
- (not jruby_core or not jruby_stdlib)
436
-
437
- current_jruby_version = jruby_core ? jruby_core[16..-5] : "None"
438
- abort "both jruby versions are #{new_jruby_version}. Nothing to update. Make sure you 'gem update jruby-jars' if there is a new version" if
439
- current_jruby_version == new_jruby_version
440
-
441
- puts "Current jruby version: #{current_jruby_version}"
442
- puts "New jruby version: #{new_jruby_version}"
443
- end
444
-
445
- copier = AssetCopier.new $assets, File.expand_path(".")
446
- log_action("Removing #{jruby_core}") {File.delete jruby_core} if jruby_core
447
- log_action("Removing #{jruby_stdlib}") {File.delete jruby_stdlib} if jruby_stdlib
448
- log_action("Copying #{JRubyJars::core_jar_path} to libs") {copier.copy_from_absolute_path JRubyJars::core_jar_path, "libs"}
449
- log_action("Copying #{JRubyJars::stdlib_jar_path} to libs") {copier.copy_from_absolute_path JRubyJars::stdlib_jar_path, "libs"}
450
-
451
- reconfigure_jruby_libs
452
-
453
- puts "JRuby version is now: #{new_jruby_version}"
454
- end
455
-
456
- def update_ruboto(force=nil)
457
- verify_manifest
458
-
459
- from = File.expand_path($gem_root + "/assets/assets/scripts/ruboto.rb")
460
- to = File.expand_path("./assets/scripts/ruboto.rb")
461
-
462
- from_text = File.read(from)
463
- to_text = File.read(to) if File.exists?(to)
464
-
465
- unless force
466
- puts "New version: #{from_text[/\$RUBOTO_VERSION = (\d+)/, 1]}"
467
- puts "Old version: #{to_text ? to_text[/\$RUBOTO_VERSION = (\d+)/, 1] : 'none'}"
468
-
469
- abort "The ruboto.rb verion has not changed. Use --force to force update." if
470
- from_text[/\$RUBOTO_VERSION = (\d+)/, 1] == to_text[/\$RUBOTO_VERSION = (\d+)/, 1]
471
- end
472
-
473
- log_action("Copying ruboto.rb and setting the package name") do
474
- File.open(to, 'w') {|f| f << from_text.gsub("THE_PACKAGE", verify_package).gsub("ACTIVITY_NAME", verify_activity)}
475
- end
476
- end
477
-
478
- #
479
- # reconfigure_jruby_libs:
480
- # - removes unneeded code from jruby-core
481
- # - moves ruby stdlib to the root of the ruby-stdlib jar
482
- #
483
-
484
- def reconfigure_jruby_libs
485
- jruby_core = JRubyJars::core_jar_path.split('/')[-1]
486
- log_action("Removing unneeded classes from #{jruby_core}") do
487
- Dir.mkdir "libs/tmp"
488
- Dir.chdir "libs/tmp"
489
- FileUtils.move "../#{jruby_core}", "."
490
- `jar -xf #{jruby_core}`
491
- File.delete jruby_core
492
- ['jni', 'org/jruby/ant', 'org/jruby/compiler/ir', 'org/jruby/demo', 'org/jruby/embed/bsf',
493
- 'org/jruby/embed/jsr223', 'org/jruby/ext/ffi','org/jruby/javasupport/bsf'
494
- ].each {|i| FileUtils.remove_dir i, true}
495
- `jar -cf ../#{jruby_core} .`
496
- Dir.chdir "../.."
497
- FileUtils.remove_dir "libs/tmp", true
498
- end
499
-
500
- jruby_stdlib = JRubyJars::stdlib_jar_path.split('/')[-1]
501
- log_action("Reformatting #{jruby_stdlib}") do
502
- Dir.mkdir "libs/tmp"
503
- Dir.chdir "libs/tmp"
504
- FileUtils.move "../#{jruby_stdlib}", "."
505
- `jar -xf #{jruby_stdlib}`
506
- File.delete jruby_stdlib
507
- FileUtils.move "META-INF/jruby.home/lib/ruby/1.8", ".."
508
- Dir.chdir "../1.8"
509
- FileUtils.remove_dir "../tmp", true
510
- `jar -cf ../#{jruby_stdlib} .`
511
- Dir.chdir "../.."
512
- FileUtils.remove_dir "libs/1.8", true
513
- end
514
- end
515
-
516
- ###########################################################################
517
- #
518
- # Verify the presence of important components
519
- #
520
-
521
- def verify_manifest
522
- abort "cannot find your AndroidManifest.xml to extract info from it. Make sure you're in the root directory of your app" unless
523
- File.exists? 'AndroidManifest.xml'
524
- @manifest ||= REXML::Document.new(File.read('AndroidManifest.xml')).root
525
- end
526
-
527
- def verify_package
528
- verify_manifest
529
- @package ||= @manifest.attribute('package').value
530
- end
531
-
532
- def verify_activity
533
- verify_manifest
534
- @activity ||= @manifest.elements['application/activity'].attribute('android:name').value
535
- end
536
-
537
- def verify_sdk_versions
538
- verify_manifest
539
- @uses_sdk ||= @manifest.elements["uses-sdk"]
540
- abort "you must specify your sdk level in the manifest (e.g., <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='8' />)" unless @uses_sdk
541
- @uses_sdk
542
- end
543
-
544
- def verify_min_sdk
545
- verify_sdk_versions
546
- @min_sdk ||= @uses_sdk.attribute('android:minSdkVersion').value
547
- abort "you must specify a minimum sdk level in the manifest (e.g., <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='8' />)" unless @min_sdk
548
- @min_sdk
549
- end
550
-
551
- def verify_target_sdk
552
- verify_sdk_versions
553
- @target_sdk ||= @uses_sdk.attribute('android:targetSdkVersion').value
554
- abort "you must specify a target sdk level in the manifest (e.g., <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='8' />)" unless @target_sdk
555
- @target_sdk
556
- end
557
-
558
- def verify_api
559
- unless $api
560
- api = File.expand_path($gem_root + "/lib/java_class_gen/android_api.xml")
561
- abort "cannot find android_api.xml to extract info from it." unless File.exists? api
562
- log_action("Loading Android API") {$api = scan_in_api(File.read(api))["api"][0]}
563
- end
564
- $api
565
- end
566
-
567
- ###########################################################################
568
- #
569
- # Scan the XML file. Much faster than using REXML.
570
- #
571
-
572
- def scan_in_api(file)
573
- require 'strscan'
574
- doc = StringScanner.new(file)
575
- $api = XMLElement.new
576
- parents = [$api]
577
-
578
- while not doc.eos?
579
- doc.scan(/</)
580
- if doc.scan(/\/\w+>/)
581
- parents.pop
582
- else
583
- name = doc.scan(/\w+/)
584
- doc.scan(/\s+/)
585
- values = {}
586
- while not (term = doc.scan(/[\/>]/))
587
- key = doc.scan(/\w+/)
588
- doc.scan(/='/)
589
- value = doc.scan(/[^']*/)
590
- doc.scan(/'\s*/)
591
- values[key] = value
592
- end
593
- element = parents[-1].add_element(name, values)
594
- parents.push(element) if term == ">"
595
- doc.scan(/>/) if term == "/"
596
- end
597
- end
598
- $api
599
- end
600
-
601
- ###########################################################################
602
- #
603
- # generate_inheriting_file:
604
- # Builds a script based subclass of Activity, Service, or BroadcastReceiver
605
- #
606
-
607
- def generate_inheriting_file(klass, name, package, script_name, dest='.')
608
- to = File.join(dest, "src/#{package.gsub('.', '/')}")
609
-
610
- FileUtils.cp(File.expand_path(__FILE__ + "/../../assets/src/Inheriting#{klass}.java"), to)
611
- FileUtils.move(File.join(to, "Inheriting#{klass}.java"), File.join(to, "#{name}.java"))
612
-
613
- file = File.join(to, "#{name}.java")
614
- text = File.read(file)
615
- File.open(file, 'w') do |f|
616
- f << text.gsub("THE_PACKAGE", package).gsub("Inheriting#{klass}", name).gsub("start.rb", script_name)
617
- end
618
-
619
- sample_source = File.read(File.join($assets, "samples/sample_#{underscore klass}.rb"))
620
- File.open File.join(dest, "assets/scripts/#{script_name}"), "a" do |f|
621
- f << sample_source
622
- end
623
- end
624
-
625
- # active_support/inflector.rb
626
- def underscore(camel_cased_word)
627
- camel_cased_word.to_s.gsub(/::/, '/').
628
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
629
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
630
- tr("-", "_").
631
- downcase
632
- end
633
-
634
-
635
-
636
- Main {
637
- mode "gen" do
638
- mode "app" do
639
- option("name"){
640
- required
641
- argument :required
642
- description "Name of your app"
643
- }
644
- option("target") {
645
- required
646
- argument :required
647
- defaults 'android-9'
648
- description "android version to target. must begin with 'android-' (e.g., 'android-8' for froyo)"
649
- }
650
- option("min_sdk") {
651
- required
652
- argument :required
653
- defaults 'android-7'
654
- description "minimum android version supported. must begin with 'android-'. (default 'android-3')"
655
- }
656
- option("path"){
657
- required
658
- argument :required
659
- description "path to where you want your app."
660
- }
661
- option("package"){
662
- required
663
- argument :required
664
- defaults 'org.ruboto.example'
665
- description "Name of package. Must be unique for every app. A common pattern is yourtld.yourdomain.appname (Ex. org.ruboto.irb)"
666
- }
667
- option("activity"){
668
- required
669
- argument :required
670
- defaults 'Main'
671
- description "name of your primary Activity"
672
- }
673
-
674
- def run
675
- path = params['path'].value
676
- name = params['name'].value
677
- target = params['target'].value
678
- min_sdk = params['min_sdk'].value
679
- package = params['package'].value
680
- activity = params['activity'].value
681
-
682
- abort "path must be to a directory that does not yet exist. it will be created" if
683
- File.exists?(path)
684
-
685
- root = File.expand_path(path)
686
- print "\nGenerating Android app #{name} in #{root}..."
687
- `android create project -n #{name} -t #{target} -p #{path} -k #{package} -a #{activity}`
688
- puts "Done"
689
-
690
- puts "\nCopying files:"
691
- copier = AssetCopier.new $assets, root
692
-
693
- %w{Rakefile .gitignore assets}.each do |f|
694
- log_action(f) {copier.copy f}
695
- end
696
-
697
- log_action("Ruboto java classes"){copier.copy "src/org/ruboto/*.java", "src/org/ruboto"}
698
-
699
- # Remember the current directory and chdir to the new app directory
700
- current_dir = Dir.pwd
701
- Dir.chdir root
702
-
703
- update_jruby true
704
-
705
- log_action("\nAdding activities (RubotoActivity and RubotoDialog) and SDK versions to the manifest") do
706
- verify_manifest.elements['application'].add_element 'activity', {"android:name" => "org.ruboto.RubotoActivity"}
707
- verify_manifest.elements['application'].add_element 'activity', {"android:name" => "org.ruboto.RubotoDialog",
708
- "android:theme" => "@android:style/Theme.Dialog"}
709
- verify_manifest.add_element 'uses-sdk', {"android:minSdkVersion" => min_sdk[/\d+/], "android:targetSdkVersion" => target[/\d+/]}
710
- File.open("AndroidManifest.xml", 'w') {|f| verify_manifest.document.write(f, 4)}
711
- end
712
-
713
- update_ruboto true
714
-
715
- generate_core_classes(:class => "all", :method_base => "on", :method_include => "", :method_exclude => "", :force => true, :implements => "")
716
-
717
- # Go back to whence we came
718
- Dir.chdir current_dir
719
-
720
- log_action("Generating the default Activity and script") do
721
- generate_inheriting_file "Activity", activity, package, "#{underscore(activity)}.rb", path
722
- end
723
-
724
- puts "\nHello, #{name}\n"
725
- end
726
- end
727
-
728
- mode "class" do
729
- argument("class"){
730
- required
731
- description "the Android Class that you want."
732
- }
733
-
734
- option("script_name"){
735
- argument :required
736
- description "name of the ruby script in assets/scripts/ that this class will execute. should end in .rb. optional"
737
- }
738
-
739
- option("name"){
740
- required
741
- argument :required
742
- description "name of the class (and file). Should be CamelCase"
743
- }
744
-
745
-
746
- def run
747
- name = params['name'].value
748
- script_name = params['script_name'].value || "#{underscore(name)}.rb"
749
- klass = params['class'].value
750
-
751
- generate_inheriting_file klass, name, verify_package, script_name
752
- end
753
- end
754
-
755
- mode "subclass" do
756
- argument("class"){
757
- required
758
- description "the Android Class that you want to subclass (e.g., package.Class)."
759
- }
760
-
761
- option("name"){
762
- required
763
- argument :required
764
- description "name of the class (and file). Should be CamelCase"
765
- }
766
-
767
- option("method_base"){
768
- required
769
- validate {|i| %w(all on none abstract).include?(i)}
770
- argument :required
771
- description "the base set of methods to generate (adjusted with method_include and method_exclude): all, none, abstract, on (e.g., onClick)"
772
- }
773
-
774
- option("method_include"){
775
- argument :required
776
- defaults ""
777
- description "additional methods to add to the base list"
778
- }
779
-
780
- option("method_exclude"){
781
- argument :required
782
- defaults ""
783
- description "methods to remove from the base list"
784
- }
785
-
786
- option("implements"){
787
- required
788
- argument :required
789
- defaults ""
790
- description "comma separated list interfaces to implement"
791
- }
792
-
793
- option("force"){
794
- cast :boolean
795
- description "force added and deprecated methods not excluded to be create"
796
- }
797
-
798
- def run
799
- generate_subclass_or_interface(
800
- %w(class name method_base method_include method_exclude implements force).inject({}) {|h, i| h[i.to_sym] = params[i].value; h})
801
- end
802
- end
803
-
804
- mode "interface" do
805
- argument("interface"){
806
- required
807
- description "the Android Interface that you want to implement (e.g., package.Interface)."
808
- }
809
-
810
- option("name"){
811
- required
812
- argument :required
813
- description "name of the class (and file) that will implement the interface. Should be CamelCase"
814
- }
815
-
816
- option("force"){
817
- cast :boolean
818
- description "force added and deprecated interfaces to be create"
819
- }
820
-
821
- def run
822
- generate_subclass_or_interface %w(interface name force).inject({}) {|h, i| h[i.to_sym] = params[i].value; h}
823
- end
824
- end
825
-
826
- mode "core" do
827
- argument("class"){
828
- required
829
- validate {|i| %w(Activity Service BroadcastReceiver View PreferenceActivity TabActivity OnClickListener OnItemClickListener all).include?(i)}
830
- description "Activity, Service, BroadcastReceiver, View, OnClickListener, OnItemClickListener, or all (default = all); Other activities not included in 'all': PreferenceActivity, TabActivity"
831
- }
832
-
833
- option("method_base"){
834
- required
835
- argument :required
836
- validate {|i| %w(all on none).include?(i)}
837
- defaults "on"
838
- description "the base set of methods to generate (adjusted with method_include and method_exclude): all, none, on (e.g., onClick)"
839
- }
840
-
841
- option("method_include"){
842
- required
843
- argument :required
844
- defaults ""
845
- description "additional methods to add to the base list"
846
- }
847
-
848
- option("method_exclude"){
849
- required
850
- argument :required
851
- defaults ""
852
- description "methods to remove from the base list"
853
- }
854
-
855
- option("implements"){
856
- required
857
- argument :required
858
- defaults ""
859
- description "for classes only, interfaces to implement (cannot be used with 'gen core all')"
860
- }
861
-
862
- option("force"){
863
- cast :boolean
864
- description "force added and deprecated methods not excluded to be create"
865
- }
866
-
867
- def run
868
- abort("specify 'implements' only for Activity, Service, BroadcastReceiver, PreferenceActivity, or TabActivity") unless
869
- %w(Activity Service BroadcastReceiver PreferenceActivity TabActivity).include?(params["class"].value) or params["implements"].value == ""
870
- generate_core_classes [:class, :method_base, :method_include, :method_exclude, :implements, :force].inject({}) {|h, i| h[i] = params[i.to_s].value; h}
871
- end
872
- end
873
-
874
- mode "key" do
875
- option("keystore"){
876
- default "~/.android/production.keystore"
877
- description "path to where the keystore will be saved. defaults to ~/.android/production.keystore"
878
- }
879
-
880
- option("alias"){
881
- required
882
- description "The 'alias' for the key. Identifies the key within the keystore. Required"
883
- }
884
-
885
- def run
886
- keystore = params['keystore'].value
887
- key_alias = params['alias'].value
888
-
889
- `keytool -genkey -keyalg rsa -keysize 4096 -validity 1000000 -keystore #{keystore} -alias #{key_alias}`
890
- end
891
- end
892
- end
893
-
894
- mode "update-jruby" do
895
- argument("what"){
896
- required
897
- validate {|i| %w(jruby ruboto).include?(i)}
898
- description "What do you want to update: 'jruby' or 'ruboto'"
899
- }
900
-
901
- option("force"){
902
- description "force and update even if the version hasn't changed"
903
- }
904
-
905
- def run
906
- case params['what'].value
907
- when "jruby": update_jruby(params['force'].value);
908
- when "ruboto": update_ruboto(params['force'].value);
909
- end
910
- end
911
- end
912
-
913
- # just running `ruboto`
914
- def run
915
- puts %Q{
916
- Ruboto -- Ruby for Android
917
- Execute `ruboto gen app --help` for instructions on how to generate a fresh Ruby-enabled Android app
918
- Execute `ruboto --help` for other options
919
- }
920
- end
921
- }
922
-
4
+ # Run Base, which will handle actual commands
5
+ Ruboto::Commands::Base.main