ruboto 0.11.0 → 0.12.0.rc.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.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