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
@@ -0,0 +1,183 @@
1
+ module Ruboto
2
+ module Util
3
+ module Build
4
+ include Verify
5
+ ###########################################################################
6
+ #
7
+ # Build Subclass or Interface:
8
+ #
9
+
10
+ #
11
+ # build_file: Reads the src from the appropriate location,
12
+ # uses the substitutions hash to modify the contents,
13
+ # and writes to the new location
14
+ #
15
+ def build_file(src, package, name, substitutions, dest='.')
16
+ to = File.join(dest, "src/#{package.gsub('.', '/')}")
17
+ Dir.mkdir(to) unless File.directory?(to)
18
+
19
+ text = File.read(File.expand_path(Ruboto::GEM_ROOT + "/assets/src/#{src}.java"))
20
+ substitutions.each {|k,v| text.gsub!(k, v)}
21
+
22
+ File.open(File.join(to, "#{name}.java"), 'w') {|f| f << text}
23
+ end
24
+
25
+ #
26
+ # get_class_or_interface: Opens the xml file and locates the specified class.
27
+ # Aborts if the class is not found or if it is not available for
28
+ # all api levels
29
+ #
30
+ def get_class_or_interface(klass, force=false)
31
+ element = verify_api.find_class_or_interface(klass, "either")
32
+
33
+ abort "ERROR: #{klass} not found" unless element
34
+
35
+ unless force
36
+ abort "#{klass} not available in minSdkVersion, added in #{element.attribute('api_added')}; use --force to create it" if
37
+ element.attribute('api_added') and element.attribute('api_added').to_i > verify_min_sdk.to_i
38
+ abort "#{klass} deprecated for targetSdkVersion, deprecatrd in #{element.attribute('deprecated')}; use --force to create it" if
39
+ element.attribute('deprecated') and element.attribute('deprecated').to_i <= verify_target_sdk.to_i
40
+ end
41
+
42
+ abort "#{klass} removed for targetSdkVersion, removed in #{element.attribute('api_removed')}" if
43
+ element.attribute('api_removed') and element.attribute('api_removed').to_i <= verify_target_sdk.to_i
44
+
45
+ element
46
+ end
47
+
48
+ #
49
+ # check_methods: Checks the methods to see if they are available for all api levels
50
+ #
51
+ def check_methods(methods, force=false)
52
+ min_api = verify_min_sdk.to_i
53
+ target_api = verify_target_sdk.to_i
54
+
55
+ # Remove methods changed outside of the scope of the sdk versions
56
+ methods = methods.select{|i| not i.attribute('api_added') or i.attribute('api_added').to_i <= target_api}
57
+ methods = methods.select{|i| not i.attribute('deprecated') or i.attribute('deprecated').to_i > min_api}
58
+ methods = methods.select{|i| not i.attribute('api_removed') or i.attribute('api_removed').to_i > min_api}
59
+
60
+ # Inform and remove methods that do not exist in one of the sdk versions
61
+ methods = methods.select do |i|
62
+ if i.attribute('api_removed') and i.attribute('api_removed').to_i <= target_api
63
+ puts "Can't create #{i.method_signature} -- removed in #{i.attribute('api_removed')}"
64
+ false
65
+ else
66
+ true
67
+ end
68
+ end
69
+
70
+ new_methods = methods
71
+ unless force
72
+ # Inform and remove methods changed inside the scope of the sdk versions
73
+ new_methods = methods.select do |i|
74
+ if i.attribute('api_added') and i.attribute('api_added').to_i > min_api
75
+ puts "Can't create #{i.method_signature} -- added in #{i.attribute('api_added')} -- exclude or force"
76
+ false
77
+ elsif i.attribute('deprecated') and i.attribute('deprecated').to_i <= target_api
78
+ puts "Can't create #{i.method_signature} -- deprecated in #{i.attribute('deprecated')} -- exclude or force"
79
+ false
80
+ else
81
+ true
82
+ end
83
+ end
84
+
85
+ abort("Aborting!") if methods.count != new_methods.count
86
+ end
87
+
88
+ new_methods
89
+ end
90
+
91
+ #
92
+ # generate_subclass_or_interface: Creates a subclass or interface based on the specifications.
93
+ #
94
+ def generate_subclass_or_interface(params)
95
+ defaults = {:template => "InheritingClass", :method_base => "all", :method_include => "", :method_exclude => "", :force => false, :implements => ""}
96
+ params = defaults.merge(params)
97
+ params[:package] = verify_package unless params[:package]
98
+
99
+ class_desc = get_class_or_interface(params[:class] || params[:interface], params[:force])
100
+
101
+ puts "Generating methods for #{params[:name]}..."
102
+ methods = class_desc.all_methods(params[:method_base], params[:method_include], params[:method_exclude], params[:implements])
103
+ methods = check_methods(methods, params[:force])
104
+ puts "Done. Methods created: #{methods.count}"
105
+
106
+ # Remove any duplicate constants (use *args handle multiple parameter lists)
107
+ constants = methods.map(&:constant_string).uniq
108
+
109
+ build_file params[:template], params[:package], params[:name], {
110
+ "THE_PACKAGE" => params[:package],
111
+ "THE_ACTION" => class_desc.name == "class" ? "extends" : "implements",
112
+ "THE_ANDROID_CLASS" => (params[:class] || params[:interface]) +
113
+ (params[:implements] == "" ? "" : (" implements " + params[:implements].split(",").join(", "))),
114
+ "THE_RUBOTO_CLASS" => params[:name],
115
+ "THE_CONSTANTS" => constants.map {|i| "public static final int #{i} = #{constants.index(i)};"}.indent.join("\n"),
116
+ "CONSTANTS_COUNT" => methods.count.to_s,
117
+ "THE_CONSTRUCTORS" => class_desc.name == "class" ?
118
+ class_desc.get_elements("constructor").map{|i| i.constructor_definition(params[:name])}.join("\n\n") : "",
119
+ "THE_METHODS" => methods.map{|i| i.method_definition}.join("\n\n")
120
+ }
121
+ end
122
+
123
+ #
124
+ # generate_core_classe: generates RubotoActivity, RubotoService, etc. based
125
+ # on the API specifications.
126
+ #
127
+ def generate_core_classes(params)
128
+ %w(android.view.View.OnClickListener android.widget.AdapterView.OnItemClickListener).each do |i|
129
+ name = i.split(".")[-1]
130
+ if(params[:class] == name or params[:class] == "all")
131
+ generate_subclass_or_interface({:package => "org.ruboto.callbacks", :class => i, :name => "Ruboto#{name}"})
132
+ end
133
+ end
134
+
135
+ hash = {:package => "org.ruboto"}
136
+ %w(method_base method_include implements force).inject(hash) {|h, i| h[i.to_sym] = params[i.to_sym]; h}
137
+ hash[:method_exclude] = params[:method_exclude].split(",").push("onCreate").push("onReceive").join(",")
138
+
139
+ %w(android.app.Activity android.app.Service android.content.BroadcastReceiver android.view.View).each do |i|
140
+ name = i.split(".")[-1]
141
+ if(params[:class] == name or params[:class] == "all")
142
+ generate_subclass_or_interface(
143
+ hash.merge({:template => name == "View" ? "InheritingClass" : "Ruboto#{name}", :class => i, :name => "Ruboto#{name}"}))
144
+ end
145
+ end
146
+
147
+ # Activities that can be created, but only directly (i.e., not included in all)
148
+ %w(android.preference.PreferenceActivity android.app.TabActivity).each do |i|
149
+ name = i.split(".")[-1]
150
+ if params[:class] == name
151
+ generate_subclass_or_interface(hash.merge({:template => "RubotoActivity", :class => i, :name => "Ruboto#{name}"}))
152
+ end
153
+ end
154
+ end
155
+
156
+ ###########################################################################
157
+ #
158
+ # generate_inheriting_file:
159
+ # Builds a script based subclass of Activity, Service, or BroadcastReceiver
160
+ #
161
+
162
+ def generate_inheriting_file(klass, name, package, script_name, dest='.', filename = name)
163
+ file = File.join(dest, "src/#{package.gsub('.', '/')}", "#{filename}.java")
164
+ text = File.read(File.join(Ruboto::ASSETS, "src/Inheriting#{klass}.java"))
165
+ File.open(file, 'w') do |f|
166
+ f << text.gsub("THE_PACKAGE", package).gsub("Inheriting#{klass}", name).gsub("start.rb", script_name)
167
+ end
168
+
169
+ sample_source = File.read(File.join(Ruboto::ASSETS, "samples/sample_#{underscore klass}.rb")).gsub("THE_PACKAGE", package).gsub("Sample#{klass}", name).gsub("start.rb", script_name)
170
+ FileUtils.mkdir_p File.join(dest, 'assets/scripts')
171
+ File.open File.join(dest, "assets/scripts/#{script_name}"), "a" do |f|
172
+ f << sample_source
173
+ end
174
+
175
+ sample_test_source = File.read(File.join(Ruboto::ASSETS, "samples/sample_#{underscore klass}_test.rb")).gsub("THE_PACKAGE", package).gsub("Sample#{klass}", name)
176
+ FileUtils.mkdir_p File.join(dest, 'test/assets/scripts')
177
+ File.open File.join(dest, "test/assets/scripts/#{script_name.chomp('.rb')}_test.rb"), "a" do |f|
178
+ f << sample_test_source
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,22 @@
1
+ module Ruboto
2
+ module Util
3
+ module CodeFormatting
4
+ ###########################################################################
5
+ #
6
+ # Methods for formatting code
7
+ #
8
+ def method_call(return_type=nil, method_name="", parameters=[], body_clause=[])
9
+ ["public #{return_type || ""} #{method_name}(" + parameters.map{|i| "#{i[1]} #{i[0]}"}.join(", ") + ") {",
10
+ body_clause.indent, "}"]
11
+ end
12
+
13
+ def if_else(condition, if_clause, else_clause)
14
+ ["if (#{condition}) {", if_clause.indent, else_clause.compact.empty? ? nil : "} else {", else_clause.indent, "}"]
15
+ end
16
+
17
+ def try_catch(try_clause, catch_clause)
18
+ ["try {", try_clause.indent, "} catch (RaiseException re) {", catch_clause.indent, "}"]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Ruboto
2
+ module Util
3
+ module LogAction
4
+ ###########################################################################
5
+ #
6
+ # log_action: put text to stdout around the execution of a block
7
+ #
8
+
9
+ def log_action(initial_text, final_text="Done.", &block)
10
+ $stdout.sync = true
11
+
12
+ print initial_text, "..."
13
+ result = yield
14
+ puts final_text
15
+
16
+ result
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # fix main (to an extent)
2
+ module Main
3
+ class Program
4
+ module InstanceMethods
5
+ def setup_finalizers
6
+ @finalizers ||= []
7
+ ObjectSpace.define_finalizer(self) do
8
+ while((f = @finalizers.pop)); f.call; end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Ruboto
2
+ # Enable ObjectSpace if running in JRuby, for the "main" lib
3
+ def self.enable_objectspace
4
+ require 'jruby'
5
+ JRuby.objectspace = true
6
+ rescue LoadError
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ module Ruboto
2
+ module Util
3
+ module ScanInAPI
4
+ ###########################################################################
5
+ #
6
+ # Scan the XML file. Much faster than using REXML.
7
+ #
8
+
9
+ def scan_in_api(file)
10
+ require 'strscan'
11
+ doc = StringScanner.new(file)
12
+ Thread.current[:api] = XMLElement.new
13
+ parents = [Thread.current[:api]]
14
+
15
+ while not doc.eos?
16
+ doc.scan(/</)
17
+ if doc.scan(/\/\w+>/)
18
+ parents.pop
19
+ else
20
+ name = doc.scan(/\w+/)
21
+ doc.scan(/\s+/)
22
+ values = {}
23
+ while not (term = doc.scan(/[\/>]/))
24
+ key = doc.scan(/\w+/)
25
+ doc.scan(/='/)
26
+ value = doc.scan(/[^']*/)
27
+ doc.scan(/'\s*/)
28
+ values[key] = value.include?("&") ? value.gsub('&lt;', '<').gsub('&gt;', '>').gsub('&quot;', "\"") : value
29
+ end
30
+ element = parents[-1].add_element(name, values)
31
+ parents.push(element) if term == ">"
32
+ doc.scan(/>/) if term == "/"
33
+ end
34
+ end
35
+
36
+ Thread.current[:api]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,102 @@
1
+ module Ruboto
2
+ module Util
3
+ module Update
4
+ ###########################################################################
5
+ #
6
+ # Updating components
7
+ #
8
+
9
+ def update_jruby(force=nil)
10
+ jruby_core = Dir.glob("libs/jruby-core-*.jar")[0]
11
+ jruby_stdlib = Dir.glob("libs/jruby-stdlib-*.jar")[0]
12
+ new_jruby_version = JRubyJars::core_jar_path.split('/')[-1][11..-5]
13
+
14
+ unless force
15
+ abort "cannot find existing jruby jars in libs. Make sure you're in the root directory of your app" if
16
+ (not jruby_core or not jruby_stdlib)
17
+
18
+ current_jruby_version = jruby_core ? jruby_core[16..-5] : "None"
19
+ 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
20
+ current_jruby_version == new_jruby_version
21
+
22
+ puts "Current jruby version: #{current_jruby_version}"
23
+ puts "New jruby version: #{new_jruby_version}"
24
+ end
25
+
26
+ copier = AssetCopier.new Ruboto::ASSETS, File.expand_path(".")
27
+ log_action("Removing #{jruby_core}") {File.delete jruby_core} if jruby_core
28
+ log_action("Removing #{jruby_stdlib}") {File.delete jruby_stdlib} if jruby_stdlib
29
+ log_action("Copying #{JRubyJars::core_jar_path} to libs") {copier.copy_from_absolute_path JRubyJars::core_jar_path, "libs"}
30
+ log_action("Copying #{JRubyJars::stdlib_jar_path} to libs") {copier.copy_from_absolute_path JRubyJars::stdlib_jar_path, "libs"}
31
+
32
+ reconfigure_jruby_libs
33
+
34
+ puts "JRuby version is now: #{new_jruby_version}"
35
+ end
36
+
37
+ def update_ruboto(force=nil)
38
+ verify_manifest
39
+
40
+ from = File.expand_path(Ruboto::GEM_ROOT + "/assets/assets/scripts/ruboto.rb")
41
+ to = File.expand_path("./assets/scripts/ruboto.rb")
42
+
43
+ from_text = File.read(from)
44
+ to_text = File.read(to) if File.exists?(to)
45
+
46
+ unless force
47
+ puts "New version: #{from_text[/\$RUBOTO_VERSION = (\d+)/, 1]}"
48
+ puts "Old version: #{to_text ? to_text[/\$RUBOTO_VERSION = (\d+)/, 1] : 'none'}"
49
+
50
+ abort "The ruboto.rb verion has not changed. Use --force to force update." if
51
+ from_text[/\$RUBOTO_VERSION = (\d+)/, 1] == to_text[/\$RUBOTO_VERSION = (\d+)/, 1]
52
+ end
53
+
54
+ log_action("Copying ruboto.rb and setting the package name") do
55
+ File.open(to, 'w') {|f| f << from_text.gsub("THE_PACKAGE", verify_package).gsub("ACTIVITY_NAME", verify_activity)}
56
+ end
57
+ end
58
+
59
+ #
60
+ # reconfigure_jruby_libs:
61
+ # - removes unneeded code from jruby-core
62
+ # - moves ruby stdlib to the root of the ruby-stdlib jar
63
+ #
64
+
65
+ def reconfigure_jruby_libs
66
+ jruby_core = JRubyJars::core_jar_path.split('/')[-1]
67
+ log_action("Removing unneeded classes from #{jruby_core}") do
68
+ Dir.mkdir "libs/tmp"
69
+ Dir.chdir "libs/tmp"
70
+ FileUtils.move "../#{jruby_core}", "."
71
+ `jar -xf #{jruby_core}`
72
+ File.delete jruby_core
73
+ ['cext', 'jni', 'org/jruby/ant', 'org/jruby/compiler/ir', 'org/jruby/demo', 'org/jruby/embed/bsf',
74
+ 'org/jruby/embed/jsr223', 'org/jruby/ext/ffi','org/jruby/javasupport/bsf'
75
+ ].each {|i| FileUtils.remove_dir i, true}
76
+ `jar -cf ../#{jruby_core} .`
77
+ Dir.chdir "../.."
78
+ FileUtils.remove_dir "libs/tmp", true
79
+ end
80
+
81
+ jruby_stdlib = JRubyJars::stdlib_jar_path.split('/')[-1]
82
+ log_action("Reformatting #{jruby_stdlib}") do
83
+ Dir.mkdir "libs/tmp"
84
+ Dir.chdir "libs/tmp"
85
+ FileUtils.move "../#{jruby_stdlib}", "."
86
+ `jar -xf #{jruby_stdlib}`
87
+ File.delete jruby_stdlib
88
+ FileUtils.move "META-INF/jruby.home/lib/ruby/1.8", ".."
89
+ Dir["META-INF/jruby.home/lib/ruby/site_ruby/1.8/*"].each do |f|
90
+ next if File.basename(f) =~ /^..?$/
91
+ FileUtils.move f, "../1.8/" + File.basename(f)
92
+ end
93
+ Dir.chdir "../1.8"
94
+ FileUtils.remove_dir "../tmp", true
95
+ `jar -cf ../#{jruby_stdlib} .`
96
+ Dir.chdir "../.."
97
+ FileUtils.remove_dir "libs/1.8", true
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,59 @@
1
+ require 'ruboto/api'
2
+
3
+ module Ruboto
4
+ module Util
5
+ module Verify
6
+ ###########################################################################
7
+ #
8
+ # Verify the presence of important components
9
+ #
10
+
11
+ def verify_manifest
12
+ abort "cannot find your AndroidManifest.xml to extract info from it. Make sure you're in the root directory of your app" unless
13
+ File.exists? 'AndroidManifest.xml'
14
+ @manifest ||= REXML::Document.new(File.read('AndroidManifest.xml')).root
15
+ end
16
+
17
+ def verify_package
18
+ verify_manifest
19
+ @package ||= @manifest.attribute('package').value
20
+ end
21
+
22
+ def verify_activity
23
+ verify_manifest
24
+ @activity ||= @manifest.elements['application/activity'].attribute('android:name').value
25
+ end
26
+
27
+ def verify_sdk_versions
28
+ verify_manifest
29
+ @uses_sdk ||= @manifest.elements["uses-sdk"]
30
+ abort "you must specify your sdk level in the manifest (e.g., <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='8' />)" unless @uses_sdk
31
+ @uses_sdk
32
+ end
33
+
34
+ def verify_min_sdk
35
+ verify_sdk_versions
36
+ @min_sdk ||= @uses_sdk.attribute('android:minSdkVersion').value
37
+ abort "you must specify a minimum sdk level in the manifest (e.g., <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='8' />)" unless @min_sdk
38
+ @min_sdk
39
+ end
40
+
41
+ def verify_target_sdk
42
+ verify_sdk_versions
43
+ @target_sdk ||= @uses_sdk.attribute('android:targetSdkVersion').value
44
+ abort "you must specify a target sdk level in the manifest (e.g., <uses-sdk android:minSdkVersion='3' android:targetSdkVersion='8' />)" unless @target_sdk
45
+ @target_sdk
46
+ end
47
+
48
+ def verify_strings
49
+ abort "cannot find your strings.xml to extract info from it. Make sure you're in the root directory of your app" unless
50
+ File.exists? 'res/values/strings.xml'
51
+ @strings ||= REXML::Document.new(File.read('res/values/strings.xml'))
52
+ end
53
+
54
+ def verify_api
55
+ Ruboto::API.api
56
+ end
57
+ end
58
+ end
59
+ end