ruboto 0.11.0 → 0.12.0.rc.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.lock +1 -1
  2. data/README.md +7 -5
  3. data/Rakefile +3 -4
  4. data/assets/rakelib/ruboto.rake +138 -87
  5. data/assets/samples/sample_broadcast_receiver.rb +1 -1
  6. data/assets/src/InheritingActivity.java +0 -6
  7. data/assets/src/RubotoActivity.java +4 -1
  8. data/assets/src/RubotoBroadcastReceiver.java +2 -11
  9. data/assets/src/RubotoService.java +6 -52
  10. data/assets/src/org/ruboto/EntryPointActivity.java +276 -36
  11. data/assets/src/org/ruboto/JRubyAdapter.java +5 -152
  12. data/assets/src/org/ruboto/Script.java +1 -1
  13. data/assets/src/org/ruboto/ScriptLoader.java +26 -44
  14. data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +4 -21
  15. data/bin/ruboto +0 -6
  16. data/lib/ruboto/commands/base.rb +58 -48
  17. data/lib/ruboto/sdk_locations.rb +23 -0
  18. data/lib/ruboto/sdk_versions.rb +1 -19
  19. data/lib/ruboto/util/build.rb +32 -32
  20. data/lib/ruboto/util/setup.rb +240 -0
  21. data/lib/ruboto/util/update.rb +12 -25
  22. data/lib/ruboto/util/verify.rb +7 -4
  23. data/lib/ruboto/util/xml_element.rb +62 -76
  24. data/lib/ruboto/version.rb +1 -1
  25. data/test/activity/image_button_activity_test.rb +2 -2
  26. data/test/activity/image_button_and_button_activity_test.rb +2 -2
  27. data/test/activity/json_activity.rb +1 -1
  28. data/test/activity/navigation_activity.rb +12 -12
  29. data/test/activity/navigation_activity_test.rb +7 -7
  30. data/test/activity/option_menu_activity.rb +0 -1
  31. data/test/activity/option_menu_activity_test.rb +2 -2
  32. data/test/activity/stack_activity_test.rb +10 -20
  33. data/test/app_test_methods.rb +0 -4
  34. data/test/broadcast_receiver_test.rb +16 -6
  35. data/test/minimal_app_test.rb +4 -12
  36. data/test/rake_test.rb +37 -23
  37. data/test/ruboto_gen_test.rb +12 -15
  38. data/test/sqldroid_test.rb +3 -6
  39. data/test/test_helper.rb +2 -0
  40. data/test/update_test_methods.rb +9 -9
  41. metadata +7 -11
@@ -0,0 +1,23 @@
1
+ require 'pathname'
2
+
3
+ module Ruboto
4
+ module SdkLocations
5
+ if ENV['ANDROID_HOME']
6
+ ANDROID_HOME = ENV['ANDROID_HOME']
7
+ else
8
+ adb_location = `#{RUBY_PLATFORM =~ /mingw|mswin/ ? 'where' : 'which'} adb`.chomp
9
+ if adb_location.empty?
10
+ raise 'Unable to locate the "adb" command. Either set the ANDROID_HOME environment variable or add the location of the "adb" command to your path.'
11
+ end
12
+ ANDROID_HOME = File.dirname(File.dirname(Pathname.new(adb_location).realpath))
13
+ unless File.exists? "#{ANDROID_HOME}/tools"
14
+ puts "Found '#{adb_location}' but it is not in a proper Android SDK installation."
15
+ end
16
+ end
17
+ unless File.exists? "#{ANDROID_HOME}/tools"
18
+ raise "The '<ANDROID_HOME>/tools' directory is missing.
19
+ Please set the ANDROID_HOME environment variable to a proper Android SDK installation."
20
+ end
21
+ ANDROID_TOOLS_REVISION = File.read("#{ANDROID_HOME}/tools/source.properties").slice(/Pkg.Revision=\d+/).slice(/\d+$/).to_i
22
+ end
23
+ end
@@ -3,7 +3,6 @@ require 'pathname'
3
3
  module Ruboto
4
4
  module SdkVersions
5
5
  VERSION_TO_API_LEVEL = {
6
- '2.1' => 'android-7', '2.1-update1' => 'android-7', '2.2' => 'android-8',
7
6
  '2.3' => 'android-9', '2.3.1' => 'android-9', '2.3.2' => 'android-9',
8
7
  '2.3.3' => 'android-10', '2.3.4' => 'android-10',
9
8
  '3.0' => 'android-11', '3.1' => 'android-12', '3.2' => 'android-13',
@@ -11,26 +10,9 @@ module Ruboto
11
10
  '4.1' => 'android-16', '4.1.1' => 'android-16', '4.1.2' => 'android-16',
12
11
  '4.2' => 'android-17', '4.2.2' => 'android-17',
13
12
  }
14
- MINIMUM_SUPPORTED_SDK_LEVEL = 7
13
+ MINIMUM_SUPPORTED_SDK_LEVEL = 10
15
14
  MINIMUM_SUPPORTED_SDK = "android-#{MINIMUM_SUPPORTED_SDK_LEVEL}"
16
15
  DEFAULT_TARGET_SDK_LEVEL = 10
17
16
  DEFAULT_TARGET_SDK = "android-#{DEFAULT_TARGET_SDK_LEVEL}"
18
- if ENV['ANDROID_HOME']
19
- ANDROID_HOME = ENV['ANDROID_HOME']
20
- else
21
- adb_location = `#{RUBY_PLATFORM =~ /mingw|mswin/ ? 'where' : 'which'} adb`.chomp
22
- if adb_location.empty?
23
- raise 'Unable to locate the "adb" command. Either set the ANDROID_HOME environment variable or add the location of the "adb" command to your path.'
24
- end
25
- ANDROID_HOME = File.dirname(File.dirname(Pathname.new(adb_location).realpath))
26
- unless File.exists? "#{ANDROID_HOME}/tools"
27
- puts "Found '#{adb_location}' but it is not in a proper Android SDK installation."
28
- end
29
- end
30
- unless File.exists? "#{ANDROID_HOME}/tools"
31
- raise "The '<ANDROID_HOME>/tools' directory is missing.
32
- Please set the ANDROID_HOME environment variable to a proper Android SDK installation."
33
- end
34
- ANDROID_TOOLS_REVISION = File.read("#{ANDROID_HOME}/tools/source.properties").slice(/Pkg.Revision=\d+/).slice(/\d+$/).to_i
35
17
  end
36
18
  end
@@ -30,11 +30,11 @@ module Ruboto
30
30
  # all api levels
31
31
  #
32
32
  def get_class_or_interface(klass, force=nil)
33
- element = verify_api.find_class_or_interface(klass, "either")
33
+ element = verify_api.find_class_or_interface(klass, 'either')
34
34
 
35
35
  abort "ERROR: #{klass} not found" unless element
36
36
 
37
- unless force == "include"
37
+ unless force == 'include'
38
38
  abort "#{klass} not available in minSdkVersion, added in #{element.attribute('api_added')}; use '--force include' to create it" if
39
39
  element.attribute('api_added') and element.attribute('api_added').to_i > verify_min_sdk.to_i
40
40
  abort "#{klass} deprecated for targetSdkVersion, deprecated in #{element.attribute('deprecated')}; use '--force include' to create it" if
@@ -71,16 +71,16 @@ module Ruboto
71
71
 
72
72
  abort = false
73
73
  new_methods = methods
74
- unless force == "include"
74
+ unless force == 'include'
75
75
  # Inform and remove methods changed inside the scope of the sdk versions
76
76
  new_methods = methods.select do |i|
77
- if i.attribute('api_added') and i.attribute('api_added').to_i > min_api and force == "exclude"
77
+ if i.attribute('api_added') and i.attribute('api_added').to_i > min_api and force == 'exclude'
78
78
  false
79
79
  elsif i.attribute('api_added') and i.attribute('api_added').to_i > min_api
80
80
  puts "Can't create #{i.method_signature} -- added in #{i.attribute('api_added')} -- use method_exclude or force exclude"
81
81
  abort = true
82
82
  false
83
- elsif i.attribute('deprecated') and i.attribute('deprecated').to_i <= target_api and force == "exclude"
83
+ elsif i.attribute('deprecated') and i.attribute('deprecated').to_i <= target_api and force == 'exclude'
84
84
  false
85
85
  elsif i.attribute('deprecated') and i.attribute('deprecated').to_i <= target_api
86
86
  puts "Can't create #{i.method_signature} -- deprecated in #{i.attribute('deprecated')} -- use method_exclude or force exclude"
@@ -91,7 +91,7 @@ module Ruboto
91
91
  end
92
92
  end
93
93
 
94
- abort("Aborting!") if abort
94
+ abort('Aborting!') if abort
95
95
  end
96
96
 
97
97
  new_methods
@@ -101,7 +101,7 @@ module Ruboto
101
101
  # generate_subclass_or_interface: Creates a subclass or interface based on the specifications.
102
102
  #
103
103
  def generate_subclass_or_interface(params)
104
- defaults = {:template => "InheritingClass", :method_base => "all", :method_include => "", :method_exclude => "", :force => nil, :implements => ""}
104
+ defaults = {:template => 'InheritingClass', :method_base => 'all', :method_include => '', :method_exclude => '', :force => nil, :implements => ''}
105
105
  params = defaults.merge(params)
106
106
  params[:package] ||= verify_package
107
107
 
@@ -112,19 +112,19 @@ module Ruboto
112
112
  methods = check_methods(methods, params[:force])
113
113
  puts "Done. Methods created: #{methods.count}"
114
114
 
115
- params[:implements] = params[:implements].split(",").push('org.ruboto.RubotoComponent').join(",")
115
+ params[:implements] = params[:implements].split(',').push('org.ruboto.RubotoComponent').join(',')
116
116
 
117
- action = class_desc.name == "class" ? "extends" : "implements"
117
+ action = class_desc.name == 'class' ? 'extends' : 'implements'
118
118
  build_file params[:template], params[:package], params[:name], {
119
- "THE_METHOD_BASE" => params[:method_base].to_s,
120
- "THE_PACKAGE" => params[:package],
121
- "THE_ACTION" => action,
122
- "THE_ANDROID_CLASS" => (params[:class] || params[:interface]) +
123
- (params[:implements] == "" ? "" : ((action != 'implements' ? " implements " : ', ') + params[:implements].split(",").join(", "))),
124
- "THE_RUBOTO_CLASS" => params[:name],
125
- "THE_CONSTRUCTORS" => class_desc.name == "class" ?
126
- class_desc.get_elements("constructor").map { |i| i.constructor_definition(params[:name]) }.join("\n\n") : "",
127
- "THE_METHODS" => methods.map { |i| i.method_definition(params[:name]) }.join("\n\n")
119
+ 'THE_METHOD_BASE' => params[:method_base].to_s,
120
+ 'THE_PACKAGE' => params[:package],
121
+ 'THE_ACTION' => action,
122
+ 'THE_ANDROID_CLASS' => (params[:class] || params[:interface]) +
123
+ (params[:implements] == '' ? '' : ((action != 'implements' ? ' implements ' : ', ') + params[:implements].split(',').join(', '))),
124
+ 'THE_RUBOTO_CLASS' => params[:name],
125
+ 'THE_CONSTRUCTORS' => class_desc.name == 'class' ?
126
+ class_desc.get_elements('constructor').map { |i| i.constructor_definition(params[:name]) }.join("\n\n") : '',
127
+ 'THE_METHODS' => methods.map { |i| i.method_definition(params[:name]) }.join("\n\n")
128
128
  }
129
129
  end
130
130
 
@@ -133,22 +133,22 @@ module Ruboto
133
133
  # on the API specifications.
134
134
  #
135
135
  def generate_core_classes(params)
136
- hash = {:package => "org.ruboto"}
136
+ hash = {:package => 'org.ruboto'}
137
137
  %w(method_base method_include implements force).inject(hash) {|h, i| h[i.to_sym] = params[i.to_sym]; h}
138
- hash[:method_exclude] = params[:method_exclude].split(",").push("onCreate").push("onBind").push("onStartCommand").join(",")
138
+ hash[:method_exclude] = params[:method_exclude].split(',').push('onCreate').push('onBind').push('onStartCommand').join(',')
139
139
 
140
140
  %w(android.app.Activity android.app.Service android.content.BroadcastReceiver).each do |i|
141
- name = i.split(".")[-1]
142
- if(params[:class] == name or params[:class] == "all")
141
+ name = i.split('.')[-1]
142
+ if params[:class] == name or params[:class] == 'all'
143
143
  generate_subclass_or_interface(hash.merge({:template => "Ruboto#{name}", :class => i, :name => "Ruboto#{name}"}))
144
144
  end
145
145
  end
146
146
 
147
147
  # Activities that can be created, but only directly (i.e., not included in all)
148
148
  %w(android.preference.PreferenceActivity android.app.TabActivity).each do |i|
149
- name = i.split(".")[-1]
149
+ name = i.split('.')[-1]
150
150
  if params[:class] == name
151
- generate_subclass_or_interface(hash.merge({:template => "RubotoActivity", :class => i, :name => "Ruboto#{name}"}))
151
+ generate_subclass_or_interface(hash.merge({:template => 'RubotoActivity', :class => i, :name => "Ruboto#{name}"}))
152
152
  end
153
153
  end
154
154
  end
@@ -164,30 +164,30 @@ module Ruboto
164
164
  text = File.read(File.join(Ruboto::ASSETS, "src/Inheriting#{klass}.java"))
165
165
  file_existed = File.exists?(file)
166
166
  File.open(file, 'w') do |f|
167
- f << text.gsub("THE_PACKAGE", package).gsub("Sample#{klass}", name).gsub("Inheriting#{klass}", name).gsub("sample_#{underscore(klass)}.rb", script_name)
167
+ f << text.gsub('THE_PACKAGE', package).gsub("Sample#{klass}", name).gsub("Inheriting#{klass}", name).gsub("sample_#{underscore(klass)}.rb", script_name)
168
168
  end
169
169
  puts "#{file_existed ? 'Updated' : 'Added'} file #{file}."
170
170
 
171
171
  script_file = File.expand_path("#{SCRIPTS_DIR}/#{script_name}", dest)
172
- if !File.exists? script_file
172
+ unless File.exists? script_file
173
173
  sample_source = File.read(File.join(Ruboto::ASSETS, "samples/sample_#{underscore klass}.rb"))
174
- sample_source.gsub!("THE_PACKAGE", package)
174
+ sample_source.gsub!('THE_PACKAGE', package)
175
175
  sample_source.gsub!("Sample#{klass}", name)
176
- sample_source.gsub!("start.rb", script_name)
176
+ sample_source.gsub!('start.rb', script_name)
177
177
  FileUtils.mkdir_p File.join(dest, SCRIPTS_DIR)
178
- File.open script_file, "a" do |f|
178
+ File.open script_file, 'a' do |f|
179
179
  f << sample_source
180
180
  end
181
181
  puts "Added file #{script_file}."
182
182
  end
183
183
 
184
184
  test_file = File.expand_path("test/src/#{script_name.chomp('.rb')}_test.rb", dest)
185
- if !File.exists? test_file
185
+ unless File.exists? test_file
186
186
  sample_test_source = File.read(File.join(Ruboto::ASSETS, "samples/sample_#{underscore klass}_test.rb"))
187
- sample_test_source.gsub!("THE_PACKAGE", package)
187
+ sample_test_source.gsub!('THE_PACKAGE', package)
188
188
  sample_test_source.gsub!("Sample#{klass}", name)
189
189
  sample_test_source.gsub!('SampleActivity', verify_activity)
190
- File.open test_file, "a" do |f|
190
+ File.open test_file, 'a' do |f|
191
191
  f << sample_test_source
192
192
  end
193
193
  puts "Added file #{test_file}."
@@ -0,0 +1,240 @@
1
+ require 'ruboto/sdk_versions'
2
+
3
+ module Ruboto
4
+ module Util
5
+ module Setup
6
+ # Todo: Find a way to look this up
7
+ ANDROID_SDK_VERSION = '21.1'
8
+
9
+ #########################################
10
+ #
11
+ # Core Set up Method
12
+ #
13
+
14
+ def setup_ruboto
15
+ check = check_all
16
+
17
+ if not check and RbConfig::CONFIG['host_os'] == /^windows(.*)/
18
+ puts "\nWe can't directly install Android on Windows."
19
+ puts 'If you would like to contribute to the setup for Windows,'
20
+ puts 'please file an issue at https://github.com/ruboto/ruboto/issues'
21
+ puts
22
+ return
23
+ end
24
+
25
+ install_all if not check
26
+ config_path
27
+ end
28
+
29
+ #########################################
30
+ #
31
+ # Utility Methods
32
+ #
33
+
34
+ def android_package_os_id
35
+ case RbConfig::CONFIG['host_os']
36
+ when /^darwin(.*)/ then 'macosx'
37
+ when /^linux(.*)/ then 'linux'
38
+ when /^windows(.*)/ then 'windows'
39
+ else
40
+ ## Error
41
+ nil
42
+ end
43
+ end
44
+
45
+ def android_package_directory
46
+ "android-sdk-#{android_package_os_id}"
47
+ end
48
+
49
+ def api_level
50
+ begin
51
+ return $1 if File.read('project.properties') =~ /target=(.*)/
52
+ rescue
53
+ end
54
+
55
+ Ruboto::SdkVersions::DEFAULT_TARGET_SDK
56
+ end
57
+
58
+ def path_setup_file
59
+ case RbConfig::CONFIG['host_os']
60
+ when /^darwin(.*)/ then '.profile'
61
+ when /^linux(.*)/ then '.bashrc'
62
+ when /^windows(.*)/ then 'windows'
63
+ ## Error
64
+ else
65
+ ## Error
66
+ nil
67
+ end
68
+ end
69
+
70
+ #
71
+ # OS independent "which"
72
+ # From: http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
73
+ #
74
+ def which(cmd)
75
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
76
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
77
+ exts.each { |ext|
78
+ exe = File.join(path, "#{cmd}#{ext}")
79
+ return exe if File.executable? exe
80
+ }
81
+ end
82
+ nil
83
+ end
84
+
85
+ #########################################
86
+ #
87
+ # Check Methods
88
+ #
89
+
90
+ def check_all
91
+ @missing_paths = []
92
+
93
+ @java_loc = check_for('java', 'Java')
94
+ @javac_loc = check_for('javac', 'Java Compiler')
95
+ @ant_loc = check_for('ant', 'Apache ANT')
96
+ @android_loc = check_for('android', 'Android Package Installer',
97
+ File.join(File.expand_path('~'), android_package_directory, 'tools', 'android'))
98
+ @emulator_loc = check_for('emulator', 'Android Emulator')
99
+ @adb_loc = check_for('adb', 'Android SDK Command adb',
100
+ File.join(File.expand_path('~'), android_package_directory, 'platform-tools', 'adb'))
101
+ @dx_loc = check_for('dx', 'Android SDK Command dx')
102
+ check_for_android_platform
103
+
104
+ puts
105
+ if @java_loc && @javac_loc && @adb_loc && @dx_loc && @emulator_loc && @platform_sdk_loc
106
+ puts " *** Ruboto setup is OK! ***\n"
107
+ true
108
+ else
109
+ puts " !!! Ruboto setup is NOT OK !!!\n"
110
+ false
111
+ end
112
+ end
113
+
114
+ def check_for(cmd, pretty_name=nil, alt_dir=nil)
115
+ rv = which(cmd)
116
+ rv = nil if rv.nil? or rv.empty?
117
+
118
+ if rv.nil? and alt_dir and File.exists?(alt_dir)
119
+ rv = alt_dir
120
+ ENV['PATH'] = "#{File.dirname(rv)}:#{ENV['PATH']}"
121
+ @missing_paths << "#{File.dirname(rv)}"
122
+ end
123
+
124
+ puts "#{pretty_name || cmd}: " + (rv ? "Found at #{rv}" : 'Not found')
125
+ rv
126
+ end
127
+
128
+ def check_for_android_platform
129
+ begin
130
+ @platform_sdk_loc = File.expand_path "#{@dx_loc}/../../platforms/#{api_level}"
131
+ if File.exists? @platform_sdk_loc
132
+ puts "Android platform SDK: Found at #{@platform_sdk_loc}"
133
+ else
134
+ puts 'Android platform SDK: Not found'
135
+ @platform_sdk_loc = nil
136
+ end
137
+ rescue
138
+ @platform_sdk_loc = nil
139
+ end
140
+ end
141
+
142
+ #########################################
143
+ #
144
+ # Install Methods
145
+ #
146
+
147
+ def install_all
148
+ install_android
149
+ install_adb
150
+ install_platform
151
+ end
152
+
153
+ def install_android
154
+ unless @android_loc
155
+ puts 'Android package installer not found.'
156
+ print 'Would you like to download and install it? (Y/n): '
157
+ a = STDIN.gets.chomp.upcase
158
+ if a == 'Y' || a.empty?
159
+ Dir.chdir File.expand_path('~/') do
160
+ case RbConfig::CONFIG['host_os']
161
+ when /^darwin(.*)/
162
+ asdk_file_name = "android-sdk_r#{ANDROID_SDK_VERSION}-#{android_package_os_id}.zip"
163
+ system "wget http://dl.google.com/android/#{asdk_file_name}"
164
+ system "unzip #{asdk_file_name}"
165
+ system "rm #{asdk_file_name}"
166
+ when /^linux(.*)/
167
+ asdk_file_name = "android-sdk_r#{ANDROID_SDK_VERSION}-#{android_package_os_id}.tgz"
168
+ system "wget http://dl.google.com/android/#{asdk_file_name}"
169
+ system "tar -xzf #{asdk_file_name}"
170
+ system "rm #{asdk_file_name}"
171
+ when /^windows(.*)/
172
+ # Todo: Need platform independent download
173
+ ## Error
174
+ asdk_file_name = "installer_r#{ANDROID_SDK_VERSION}-#{android_package_os_id}.exe"
175
+ return
176
+ end
177
+ end
178
+ @android_loc = File.join(File.expand_path('~'), android_package_directory, 'tools', 'android')
179
+ ENV['PATH'] = "#{File.dirname(@android_loc)}:#{ENV['PATH']}"
180
+ @missing_paths << "#{File.dirname(@android_loc)}"
181
+ end
182
+ end
183
+ end
184
+
185
+ def install_adb
186
+ if @android_loc and not @adb_loc
187
+ puts 'Android command adb not found.'
188
+ print 'Would you like to download and install it? (Y/n): '
189
+ a = STDIN.gets.chomp.upcase
190
+ if a == 'Y' || a.empty?
191
+ system 'android update sdk --no-ui --filter tool,platform-tool'
192
+ @adb_loc = File.join(File.expand_path('~'), android_package_directory, 'platform-tools', 'adb')
193
+ ENV['PATH'] = "#{File.dirname(@adb_loc)}:#{ENV['PATH']}"
194
+ @missing_paths << "#{File.dirname(@adb_loc)}"
195
+ end
196
+ end
197
+ end
198
+
199
+ def install_platform
200
+ if @android_loc and not @platform_sdk_loc
201
+ puts "Android platform SDK for #{api_level} not found."
202
+ print 'Would you like to download and install it? (Y/n): '
203
+ a = STDIN.gets.chomp.upcase
204
+ if a == 'Y' || a.empty?
205
+ system "android update sdk --no-ui --filter #{api_level},sysimg-#{api_level.slice(/\d+$/)} --all"
206
+ @platform_sdk_loc = File.expand_path "#{@dx_loc}/../../platforms/#{api_level}"
207
+ end
208
+ end
209
+ end
210
+
211
+ #########################################
212
+ #
213
+ # Path Config Method
214
+ #
215
+
216
+ def config_path
217
+ unless @missing_paths.empty?
218
+ puts "\nYou are missing some paths. Execute these lines to add them:\n\n"
219
+ @missing_paths.each do |path|
220
+ puts %Q{ export PATH="#{path}:$PATH"}
221
+ end
222
+ print "\nWould you like to append these lines to your configuration script? (Y/n): "
223
+ a = STDIN.gets.chomp.upcase
224
+ if a == 'Y' || a.empty?
225
+ print "What script do you use to configure your PATH? (#{path_setup_file}): "
226
+ a = STDIN.gets.chomp.downcase
227
+
228
+ File.open(File.expand_path("~/#{a.empty? ? path_setup_file : a}"), 'a') do |f|
229
+ f.puts "\n# BEGIN Ruboto PATH setup"
230
+ @missing_paths.each{|path| f.puts %Q{export PATH="#{path}:$PATH"}}
231
+ f.puts '# END Ruboto PATH setup'
232
+ f.puts
233
+ end
234
+ puts 'Path updated. Please close your command window and reopen.'
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end