rave 0.1.1 → 0.1.2
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.
- data/bin/rave +27 -23
- data/lib/commands/appcfg.rb +9 -0
- data/lib/commands/create.rb +153 -147
- data/lib/commands/server.rb +7 -7
- data/lib/commands/task.rb +156 -0
- data/lib/commands/usage.rb +18 -12
- data/lib/commands/war.rb +27 -50
- data/lib/exceptions.rb +19 -5
- data/lib/ext/logger.rb +7 -0
- data/lib/gems.yaml +9 -0
- data/lib/jars/{appengine-api-1.0-sdk-1.2.1.jar → appengine-api-1.0-sdk-1.3.0.jar} +0 -0
- data/lib/mixins/controller.rb +72 -40
- data/lib/mixins/data_format.rb +206 -168
- data/lib/mixins/logger.rb +19 -0
- data/lib/mixins/object_factory.rb +87 -0
- data/lib/mixins/time_utils.rb +19 -0
- data/lib/models/annotation.rb +148 -18
- data/lib/models/blip.rb +305 -61
- data/lib/models/component.rb +42 -0
- data/lib/models/context.rb +174 -45
- data/lib/models/document.rb +8 -8
- data/lib/models/element.rb +113 -0
- data/lib/models/event.rb +230 -48
- data/lib/models/operation.rb +79 -89
- data/lib/models/range.rb +14 -0
- data/lib/models/robot.rb +78 -60
- data/lib/models/user.rb +62 -0
- data/lib/models/wave.rb +45 -19
- data/lib/models/wavelet.rb +269 -69
- data/lib/ops/blip_ops.rb +233 -134
- data/lib/rave.rb +27 -22
- metadata +96 -77
- data/lib/jars/jruby-core.jar +0 -0
- data/lib/jars/ruby-stdlib.jar +0 -0
data/bin/rave
CHANGED
@@ -1,24 +1,28 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
here = File.dirname(__FILE__)
|
4
|
-
%w( create server usage war ).each do |cmd|
|
5
|
-
require File.join(here, "..", "lib", "commands", cmd)
|
6
|
-
end
|
7
|
-
|
8
|
-
args = ARGV
|
9
|
-
cmd = args.shift
|
10
|
-
|
11
|
-
if cmd
|
12
|
-
case cmd
|
13
|
-
when "create"
|
14
|
-
create_robot(args)
|
15
|
-
when "server"
|
16
|
-
start_robot(args)
|
17
|
-
when "war"
|
18
|
-
create_war(args)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
here = File.dirname(__FILE__)
|
4
|
+
%w( create server usage war appcfg ).each do |cmd|
|
5
|
+
require File.join(here, "..", "lib", "commands", cmd)
|
6
|
+
end
|
7
|
+
|
8
|
+
args = ARGV
|
9
|
+
cmd = args.shift
|
10
|
+
|
11
|
+
if cmd
|
12
|
+
case cmd
|
13
|
+
when "create"
|
14
|
+
create_robot(args)
|
15
|
+
when "server"
|
16
|
+
start_robot(args)
|
17
|
+
when "war"
|
18
|
+
create_war(args)
|
19
|
+
when "cleanup"
|
20
|
+
cleanup_war(args)
|
21
|
+
when "appengine_deploy"
|
22
|
+
appcfg_update(args)
|
23
|
+
else
|
24
|
+
display_usage
|
25
|
+
end
|
26
|
+
else
|
27
|
+
display_usage
|
24
28
|
end
|
data/lib/commands/create.rb
CHANGED
@@ -1,147 +1,153 @@
|
|
1
|
-
require 'ftools'
|
2
|
-
|
3
|
-
#Creates a project for a robot. Args are:
|
4
|
-
# => robot_name (required)
|
5
|
-
# => image_url=http://imageurl.com/ (optional)
|
6
|
-
# => profile_url=http://profileurl.com/ (optional)
|
7
|
-
# e.g. rave my_robot image_url=http://appropriate-casey.appspot.com/image.png profile_url=http://appropriate-casey.appspot.com/profile.json
|
8
|
-
def create_robot(args)
|
9
|
-
robot_name = args.first
|
10
|
-
module_name = robot_name.split(/_|-/).collect { |word| word
|
11
|
-
robot_class_name = "#{module_name}::Robot"
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
File.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
File.
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
puts "Creating
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
# def
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
1
|
+
require 'ftools'
|
2
|
+
|
3
|
+
#Creates a project for a robot. Args are:
|
4
|
+
# => robot_name (required)
|
5
|
+
# => image_url=http://imageurl.com/icon.png (optional)
|
6
|
+
# => profile_url=http://profileurl.com/ (optional)
|
7
|
+
# e.g. rave my_robot image_url=http://appropriate-casey.appspot.com/image.png profile_url=http://appropriate-casey.appspot.com/profile.json
|
8
|
+
def create_robot(args)
|
9
|
+
robot_name = args.first
|
10
|
+
module_name = robot_name.split(/_|-/).collect { |word| word.capitalize }.join
|
11
|
+
robot_class_name = "#{module_name}::Robot"
|
12
|
+
|
13
|
+
options = { :name => robot_name, :version => 1, :id => "#{robot_name}@appspot.com" }
|
14
|
+
args[1, args.length-1].each do |arg|
|
15
|
+
key, value = arg.split("=").collect { |part| part.strip }
|
16
|
+
options[key.to_sym] = value
|
17
|
+
end
|
18
|
+
|
19
|
+
dir = File.join(".", robot_name)
|
20
|
+
if File.exist? dir
|
21
|
+
puts "Directory #{dir}/ already exists. Exiting..."
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
lib = File.join(dir, "lib")
|
26
|
+
config_dir = File.join(dir, "config")
|
27
|
+
file = File.join(dir, "robot.rb")
|
28
|
+
run = File.join(dir, "config.ru")
|
29
|
+
config = File.join(dir, "config.yaml")
|
30
|
+
public_folder = File.join(dir, "public")
|
31
|
+
html = File.join(public_folder, "index.html")
|
32
|
+
|
33
|
+
#Create the project dir
|
34
|
+
puts "Creating directory #{File.expand_path(dir)}"
|
35
|
+
Dir.mkdir(dir)
|
36
|
+
|
37
|
+
puts "Creating robot class #{File.expand_path(file)}"
|
38
|
+
#Make the base robot class
|
39
|
+
File.open(file, "w") do |f|
|
40
|
+
f.puts robot_file_contents(module_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Make the rackup run file.
|
44
|
+
puts "Creating rackup config file #{File.expand_path(run)}"
|
45
|
+
File.open(run, "w") do |f|
|
46
|
+
f.puts run_file_contents(robot_class_name, file)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Make up the yaml config file.
|
50
|
+
puts "Creating configuration file #{File.expand_path(config)}"
|
51
|
+
File.open(config, "w") do |f|
|
52
|
+
f.puts config_file_contents(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
#Make the public folder for static resources
|
56
|
+
puts "Creating public folder"
|
57
|
+
Dir.mkdir(public_folder)
|
58
|
+
|
59
|
+
# Make up the html index file.
|
60
|
+
puts "Creating html index file #{File.expand_path(html)}"
|
61
|
+
File.open(html, "w") do |f|
|
62
|
+
f.puts html_file_contents(robot_name, options[:id], options[:image_url])
|
63
|
+
end
|
64
|
+
|
65
|
+
#Create lib directory
|
66
|
+
puts "Creating lib directory #{File.expand_path(lib)}"
|
67
|
+
Dir.mkdir(lib)
|
68
|
+
end
|
69
|
+
|
70
|
+
def robot_file_contents(module_name)
|
71
|
+
<<-ROBOT
|
72
|
+
require 'rubygems'
|
73
|
+
require 'rave'
|
74
|
+
|
75
|
+
module #{module_name}
|
76
|
+
class Robot < Rave::Models::Robot
|
77
|
+
#Define handlers here:
|
78
|
+
# e.g. if the robot should act on a DOCUMENT_CHANGED event:
|
79
|
+
#
|
80
|
+
# def document_changed(event, context)
|
81
|
+
# #Do some stuff
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# Events are:
|
85
|
+
#
|
86
|
+
# WAVELET_BLIP_CREATED, WAVELET_BLIP_REMOVED, WAVELET_PARTICIPANTS_CHANGED,
|
87
|
+
# WAVELET_TIMESTAMP_CHANGED, WAVELET_TITLE_CHANGED, WAVELET_VERSION_CHANGED,
|
88
|
+
# BLIP_CONTRIBUTORS_CHANGED, BLIP_DELETED, BLIP_SUBMITTED, BLIP_TIMESTAMP_CHANGED,
|
89
|
+
# BLIP_VERSION_CHANGED, DOCUMENT_CHANGED, FORM_BUTTON_CLICKED
|
90
|
+
#
|
91
|
+
# If you want to name your event handler something other than the default name,
|
92
|
+
# or you need to have more than one handler for an event, you can register handlers
|
93
|
+
# in the robot's constructor:
|
94
|
+
#
|
95
|
+
# def initialize(options={})
|
96
|
+
# super
|
97
|
+
# register_handler(Rave::Models::Event::DOCUMENT_CHANGED, :custom_doc_changed_handler)
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# def custom_doc_changed_handler(event, context)
|
101
|
+
# #Do some stuff
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# Note: Don't forget to call super if you define #initialize
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
ROBOT
|
109
|
+
end
|
110
|
+
|
111
|
+
def run_file_contents(robot_class_name, robot_file)
|
112
|
+
<<-CONFIG
|
113
|
+
require '#{File.basename(robot_file).chomp(File.extname(robot_file))}'
|
114
|
+
run #{robot_class_name}.instance
|
115
|
+
CONFIG
|
116
|
+
end
|
117
|
+
|
118
|
+
def config_file_contents(options)
|
119
|
+
<<-CONFIG
|
120
|
+
robot:
|
121
|
+
id: #{options[:id]}
|
122
|
+
name: #{options[:name]}
|
123
|
+
image_url: #{options[:image_url]}
|
124
|
+
profile_url: #{options[:profile_url]}
|
125
|
+
version: #{options[:version]}
|
126
|
+
appcfg:
|
127
|
+
version: 1
|
128
|
+
# Uncomment this section to add gems required by your robot.
|
129
|
+
# They will unpacked in your project at appengine deploy time
|
130
|
+
# gems:
|
131
|
+
# - some_gem # Replace this with the name of the gem you require
|
132
|
+
# - some_other_gem # Replace this with the name of the gem you require
|
133
|
+
CONFIG
|
134
|
+
end
|
135
|
+
|
136
|
+
def html_file_contents(name, id, image_url)
|
137
|
+
img_tag = image_url ? "\n <img src=\"#{image_url}\" alt=\"#{name} icon\" />\n" : ""
|
138
|
+
<<-HTML
|
139
|
+
<html>
|
140
|
+
<head>
|
141
|
+
<title>#{name}</title>
|
142
|
+
</head>
|
143
|
+
<body>
|
144
|
+
<h1>#{name}</h1>
|
145
|
+
#{img_tag}
|
146
|
+
<p>This is a Google Wave robot using <a href="http://github.com/diminish7/rave">Rave</a> running in JRuby.
|
147
|
+
Use this robot in your Google Waves by adding <em>#{id}</em> as a participant</p>
|
148
|
+
|
149
|
+
<img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" alt="Powered by Google App Engine" />
|
150
|
+
</body>
|
151
|
+
</html>
|
152
|
+
HTML
|
153
|
+
end
|
data/lib/commands/server.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
#Starts up rack based on the config.ru file in the working directory
|
2
|
-
# Note that this is of limited use right now, because robots have to
|
3
|
-
# run on appengine. Better to test locally with the appengine sdk
|
4
|
-
def start_robot(args)
|
5
|
-
cmd = (RUBY_PLATFORM == 'java') ? "jruby -S rackup" : "rackup"
|
6
|
-
cmd += " " + args.join(" ") if args
|
7
|
-
exec(cmd)
|
1
|
+
#Starts up rack based on the config.ru file in the working directory
|
2
|
+
# Note that this is of limited use right now, because robots have to
|
3
|
+
# run on appengine. Better to test locally with the appengine sdk
|
4
|
+
def start_robot(args)
|
5
|
+
cmd = (RUBY_PLATFORM == 'java') ? "jruby -S rackup" : "rackup"
|
6
|
+
cmd += " " + args.join(" ") if args
|
7
|
+
exec(cmd)
|
8
8
|
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'yaml'
|
5
|
+
require 'warbler'
|
6
|
+
|
7
|
+
module Rave
|
8
|
+
class Task < Warbler::Task
|
9
|
+
|
10
|
+
REQUIRED_GEMS = ["rave", "json-jruby", "rack", "builder", "RedCloth"]
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
warbler_config = Warbler::Config.new do |config|
|
14
|
+
config.gems = ((robot_config['gems'] || []) + REQUIRED_GEMS).uniq
|
15
|
+
config.includes = %w( robot.rb config.yaml )
|
16
|
+
end
|
17
|
+
super(:rave, warbler_config)
|
18
|
+
define_post_war_processes
|
19
|
+
define_deploy_task
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def robot_config
|
25
|
+
@robot_config ||= YAML::load(File.open(File.join(".", "config.yaml")))
|
26
|
+
end
|
27
|
+
|
28
|
+
def define_post_war_processes
|
29
|
+
namespace :rave do
|
30
|
+
desc "Post-War cleanup"
|
31
|
+
task :create_war => 'rave' do
|
32
|
+
#TODO: This needs to only run through this if the files have changed
|
33
|
+
#Get config info
|
34
|
+
web_inf = File.join(".", "tmp", "war", "WEB-INF")
|
35
|
+
rave_jars = File.join(File.dirname(__FILE__), "..", "jars")
|
36
|
+
#Cleanup unneeded gems that warbler copies in
|
37
|
+
cleanup_gems(File.join(web_inf, "gems", "gems"), robot_config['gems'] || [])
|
38
|
+
#Copy the appengine sdk jar to the robot
|
39
|
+
copy_appengine_jar_to_robot(rave_jars, File.join(web_inf, "lib"))
|
40
|
+
#Fix the broken paths in json-jruby
|
41
|
+
fix_json_jruby_paths(File.join(web_inf, "gems", "gems"))
|
42
|
+
#Add the appengine-web.xml file
|
43
|
+
robot_name = robot_config['robot']['id'].gsub(/@.+/, '')
|
44
|
+
version = robot_config['appcfg'] && robot_config['appcfg']['version'] ? robot_config['appcfg']['version'] : 1
|
45
|
+
create_appengine_web(File.join(web_inf, "appengine-web.xml"), robot_name, version)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_deploy_task
|
51
|
+
namespace :rave do
|
52
|
+
desc "Deploy to Appengine"
|
53
|
+
task :appcfg_update => :create_war do
|
54
|
+
staging_folder = File.join(".", "tmp", "war")
|
55
|
+
sdk_path = find_sdk
|
56
|
+
if sdk_path
|
57
|
+
appcfg_jar = File.expand_path(File.join(sdk_path, 'lib', 'appengine-tools-api.jar'))
|
58
|
+
require appcfg_jar
|
59
|
+
Java::ComGoogleAppengineToolsAdmin::AppCfg.main(["update", staging_folder].to_java(:string))
|
60
|
+
else
|
61
|
+
puts "Unable to find the Google Appengine Java SDK"
|
62
|
+
puts "You can either"
|
63
|
+
puts "1. Define the path to the main SDK folder in config.yaml - e.g.:"
|
64
|
+
puts "appcfg:"
|
65
|
+
puts " sdk: /usr/local/appengine-java-sdk/"
|
66
|
+
puts "2. Add the SDK bin folder to your PATH, or"
|
67
|
+
puts "3. Create an environment variable APPENGINE_JAVA_SDK that defines the path to the main SDK folder"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#Remove warbler and jruby-jars - added by warbler but unneeded
|
74
|
+
def cleanup_gems(gem_dir, gems)
|
75
|
+
["warbler", "jruby-jars"].each do |g|
|
76
|
+
dir = Dir[File.join(gem_dir, "#{g}*")].first
|
77
|
+
unless dir.nil? || gems.include?(g)
|
78
|
+
puts "Removing #{g} from war"
|
79
|
+
FileUtils.rm_rf(dir)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def copy_appengine_jar_to_robot(rave_jar_dir, warbler_jar_dir)
|
85
|
+
jar = "appengine-api-1.0-sdk-1.3.0.jar"
|
86
|
+
rave_jar = File.join(rave_jar_dir, jar)
|
87
|
+
warbler_jar = File.join(warbler_jar_dir, jar)
|
88
|
+
puts "Copying appengine jar from #{rave_jar} to #{warbler_jar}"
|
89
|
+
File.copy(rave_jar, warbler_jar)
|
90
|
+
end
|
91
|
+
|
92
|
+
def fix_json_jruby_paths(web_inf_gems)
|
93
|
+
#TODO: Why is this necessary? Is this an appengine issue?
|
94
|
+
puts "Fixing paths in json-jruby"
|
95
|
+
ext = Dir[File.join(web_inf_gems, "json-jruby-*", "lib", "json", "ext.rb")].first
|
96
|
+
if ext
|
97
|
+
text = File.open(ext, "r") { |f| f.read }
|
98
|
+
text.gsub!("require 'json/ext/parser'", "require 'ext/parser'")
|
99
|
+
text.gsub!("require 'json/ext/generator'", "require 'ext/generator'")
|
100
|
+
File.open(ext, "w") { |f| f.write(text) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def create_appengine_web(path, robot_name, version)
|
105
|
+
puts "Creating appengine config file #{File.expand_path(path)}"
|
106
|
+
File.open(path, "w") do |f|
|
107
|
+
f.puts appengine_web_contents(robot_name, version)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def appengine_web_contents(robot_name, version)
|
112
|
+
<<-APPENGINE
|
113
|
+
<?xml version="1.0" encoding="utf-8"?>
|
114
|
+
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
115
|
+
<application>#{robot_name}</application>
|
116
|
+
<version>#{version}</version>
|
117
|
+
<static-files />
|
118
|
+
<resource-files />
|
119
|
+
<sessions-enabled>false</sessions-enabled>
|
120
|
+
<system-properties>
|
121
|
+
<property name="jruby.management.enabled" value="false" />
|
122
|
+
<property name="os.arch" value="" />
|
123
|
+
<property name="jruby.compile.mode" value="JIT"/> <!-- JIT|FORCE|OFF -->
|
124
|
+
<property name="jruby.compile.fastest" value="true"/>
|
125
|
+
<property name="jruby.compile.frameless" value="true"/>
|
126
|
+
<property name="jruby.compile.positionless" value="true"/>
|
127
|
+
<property name="jruby.compile.threadless" value="false"/>
|
128
|
+
<property name="jruby.compile.fastops" value="false"/>
|
129
|
+
<property name="jruby.compile.fastcase" value="false"/>
|
130
|
+
<property name="jruby.compile.chainsize" value="500"/>
|
131
|
+
<property name="jruby.compile.lazyHandles" value="false"/>
|
132
|
+
<property name="jruby.compile.peephole" value="true"/>
|
133
|
+
</system-properties>
|
134
|
+
</appengine-web-app>
|
135
|
+
APPENGINE
|
136
|
+
end
|
137
|
+
|
138
|
+
def find_sdk
|
139
|
+
unless @sdk_path
|
140
|
+
@sdk_path = robot_config['appcfg']['sdk'] if robot_config['appcfg'] && robot_config['appcfg']['sdk'] # Points at main SDK dir.
|
141
|
+
@sdk_path ||= ENV['APPENGINE_JAVA_SDK'] # Points at main SDK dir.
|
142
|
+
unless @sdk_path
|
143
|
+
# Check everything in the PATH, which would point at the bin directory in the SDK.
|
144
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
145
|
+
if File.exists?(File.join(path, "appcfg.sh")) or File.exists?(File.join("appcfg.cmd"))
|
146
|
+
@sdk_path = File.dirname(path)
|
147
|
+
break
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
@sdk_path
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|