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
@@ -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