rave 0.1.2-java

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 ADDED
@@ -0,0 +1,28 @@
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
28
+ end
@@ -0,0 +1,9 @@
1
+ #Does an appcfg update to deploy the tmp/war folder to appengine
2
+ def appcfg_update(args)
3
+ Rake.application.standard_exception_handling do
4
+ Rake.application.init
5
+ Rave::Task.new
6
+ task(:default => "rave:appcfg_update")
7
+ Rake.application.top_level
8
+ end
9
+ end
@@ -0,0 +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/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
@@ -0,0 +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)
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
@@ -0,0 +1,19 @@
1
+ #Display usage for rave command
2
+ def display_usage
3
+ puts "Useage: rave [create | server | war] [robot_name] [options]"
4
+ puts "'create' generates a Google Wave robot client stub application."
5
+ puts "e.g."
6
+ puts "rave create my_robot image_url=http://my_robot.appspot.com/image.png profile_url=http://my_robot.appspot.com/"
7
+ puts "'server' launches the robot"
8
+ puts "e.g."
9
+ puts "rave server"
10
+ puts "'war' creates a war file suitable for deploying to Google AppEngine"
11
+ puts "e.g."
12
+ puts "rave war"
13
+ puts "'appengine_deploy' deploys the tmp/war folder to Google AppEngine"
14
+ puts "e.g."
15
+ puts "rave appengine_deploy"
16
+ puts "'cleanup' removes the .war file and the tmp/war staging directory"
17
+ puts "e.g."
18
+ puts "rave cleanup"
19
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require File.join(File.dirname(__FILE__), 'task.rb')
4
+
5
+ #Runs warbler to package up the robot
6
+ # then does some cleanup that is specific to App Engine:
7
+ # => Deletes the complete JRuby jar from both the app's lib folder and
8
+ # the frozen warbler gem, and replaces them with a broken up version
9
+ # => Changes the file path json-jruby
10
+ # TODO: Not sure why this is necessary, but it doesn't run on appengine without it
11
+ def create_war(args)
12
+ Rake.application.standard_exception_handling do
13
+ Rake.application.init
14
+ Rave::Task.new
15
+ task(:default => "rave:create_war")
16
+ Rake.application.top_level
17
+ end
18
+ end
19
+
20
+ #Runs warbler's cleanup to get rid of the .war file and the tmp/war folder
21
+ def cleanup_war(args)
22
+ Rake.application.standard_exception_handling do
23
+ Rake.application.init
24
+ Rave::Task.new
25
+ task(:default => "rave:clean")
26
+ Rake.application.top_level
27
+ end
28
+ end
data/lib/exceptions.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Rave
2
+ #Exception raised when registering an invalid event
3
+ class InvalidEventException < Exception ; end
4
+
5
+ #Exception raised when registering an event with an invalid handler
6
+ class InvalidHandlerException < Exception ; end
7
+
8
+ # Raised when trying to create an object with the same ID as one that already exists.
9
+ class DuplicatedIDError < Exception; end
10
+
11
+ # Raised if an unimplemented method is called.
12
+ class NotImplementedError < Exception; end
13
+
14
+ # A method option was not one of the values allowed.
15
+ class BadOptionError < ArgumentError
16
+ def initialize(option_name, valid_options, received) # :nodoc:
17
+ super("#{option_name.inspect} option must be one of #{valid_options.inspect}, not #{received.inspect}")
18
+ end
19
+ end
20
+ end
data/lib/ext/logger.rb ADDED
@@ -0,0 +1,7 @@
1
+ unless RUBY_PLATFORM == 'java'
2
+ require 'logger'
3
+ #Need to alias :warn as :warning to match the java logger
4
+ class Logger
5
+ alias :warning :warn
6
+ end
7
+ end
data/lib/gems.yaml ADDED
@@ -0,0 +1,9 @@
1
+ all:
2
+ rack: >=1.0
3
+ builder: >=2.1.2
4
+ warbler: >=0.9.14
5
+ RedCloth: >=4.2.2
6
+ jruby:
7
+ json-jruby: >=1.1.6
8
+ mri:
9
+ json: >=1.2.0