ruboto-core 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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