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.
@@ -0,0 +1,79 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+
4
+ module Rave
5
+ module Models
6
+ # Contains Robot data, event handlers and cron jobs.
7
+ class Robot < User
8
+ include Rave::Mixins::DataFormat
9
+ include Rave::Mixins::Controller
10
+ include Singleton
11
+
12
+ CONFIG_FILE = 'config.yaml' # :nodoc:
13
+
14
+ # Version of the robot, as in the yaml config [String]
15
+ def version # :nodoc:
16
+ @version.dup
17
+ end
18
+
19
+ def initialize() # :nodoc:
20
+ config = config_from_file
21
+ super(config)
22
+ @handlers = {}
23
+ @cron_jobs = []
24
+ @version = config[:version] || '1'
25
+ register_default_handlers
26
+ end
27
+
28
+ # Read options from user-edited yaml config file.
29
+ def config_from_file # :nodoc:
30
+ config = YAML::load(File.open(CONFIG_FILE))
31
+ hash = {}
32
+ config['robot'].each_pair { |k, v| hash[k.to_sym] = v }
33
+ hash
34
+ end
35
+
36
+ # Register a handler. Multiple handlers may be applied to a single event.
37
+ # +event_type+:: Must be one of Rave::Models::Event::*::TYPE [String]
38
+ def register_handler(event_type, handler)
39
+ raise Rave::InvalidEventException.new("Unknown event: #{event_type}") unless Rave::Models::Event.valid_type?(event_type)
40
+ raise Rave::InvalidHandlerException.new("Unknown handler: #{handler}") unless self.respond_to?(handler)
41
+ @handlers[event_type] ||= []
42
+ @handlers[event_type] << handler unless @handlers[event_type].include?(handler)
43
+ end
44
+
45
+ #Dispatches events to the appropriate handler
46
+ def handle_event(event, context) # :nodoc:
47
+ #Ignore unhandled events
48
+ if (handlers = @handlers[event.type])
49
+ handlers.each do |handler|
50
+ self.send(handler, event, context)
51
+ end
52
+ end
53
+ end
54
+
55
+ #Registers a cron job
56
+ def register_cron_job(handler, seconds)
57
+ @cron_jobs << { :path => "/_wave/cron/#{handler}", :handler => handler, :seconds => seconds }
58
+ end
59
+
60
+ # Creates a new wave with initial participants set.
61
+ # +participants+:: Humans and/or robots to start in the new wave [Array of String/User]
62
+ # Returns: The new wave, which contains a root wavelet which itself contains a root blip [Wave]
63
+ def create_wavelet(participants)
64
+ @context.create_wavelet(participants)
65
+ end
66
+
67
+ protected
68
+ #Register any handlers that are defined through naming convention
69
+ def register_default_handlers # :nodoc:
70
+ Event.types.each do |type|
71
+ listener = type.downcase.to_sym
72
+ if respond_to?(listener)
73
+ register_handler(type, listener)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,62 @@
1
+ module Rave
2
+ module Models
3
+ # A wave client, acting as a wave creator, blip contributor and/or a wavelet participant.
4
+ class User < Component
5
+ ROBOT_PATTERN = /@appspot\.com$/ # :nodoc:
6
+ NOBODY_ID = "@@@nobody@@@" # :nodoc: Used as a default in certain circumstances.
7
+
8
+ # Url link to the profile of the User [String].
9
+ # NOTE: Due to a limitation in Wave, for all users except the local robot
10
+ # the url will be empty.
11
+ def profile_url # :nodoc:
12
+ @profile_url.dup
13
+ end
14
+
15
+ # Url link to the image of the User [String].
16
+ # NOTE: Due to a limitation in Wave, for all users except the local robot
17
+ # the url will be empty.
18
+ def image_url # :nodoc:
19
+ @image_url.dup
20
+ end
21
+
22
+ # Unlike other components, Users are never generated [Boolean].
23
+ def generated? # :nodoc:
24
+ false
25
+ end
26
+
27
+ # - :id
28
+ # - :name
29
+ # - :profile_url
30
+ # - :image_url
31
+ # - :context
32
+ def initialize(options = {}) # :nodoc:
33
+ options[:id].downcase! if options[:id]
34
+ super(options)
35
+ @name = options[:name]
36
+ @profile_url = options[:profile_url] || ''
37
+ @image_url = options[:image_url] || ''
38
+ end
39
+
40
+ # Printable name of the User [String].
41
+ # NOTE: Due to a limitation in Wave, for all users except the local robot
42
+ # the name is the same as the @id.
43
+ def name # :nodoc:
44
+ @name || @id
45
+ end
46
+
47
+ # Is the User a robot client rather than a human client? [Boolean]
48
+ def robot? # :nodoc:
49
+ not (@id =~ ROBOT_PATTERN).nil?
50
+ end
51
+
52
+ # Convert to string [String]
53
+ def to_s
54
+ @id
55
+ end
56
+
57
+ def to_json # :nodoc:
58
+ @id.to_json
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,45 @@
1
+ module Rave
2
+ module Models
3
+ # Represents a Wave
4
+ class Wave < Component
5
+ # IDs for all wavelets that are part of the wave [Array of String]
6
+ def wavelet_ids # :nodoc:
7
+ @wavelet_ids.map { |id| id.dup }
8
+ end
9
+
10
+ #Options include:
11
+ # - :wavelet_ids
12
+ # - :id
13
+ def initialize(options = {}) # :nodoc:
14
+ if options[:id].nil? and options[:context]
15
+ super(:id => "#{GENERATED_PREFIX}_wave_#{unique_id}", :context => options[:context])
16
+ else
17
+ super(options)
18
+ end
19
+
20
+ @wavelet_ids = options[:wavelet_ids] || []
21
+ end
22
+
23
+ # All wavelets that are part of the wave [Array of Wavelet]
24
+ def wavelets # :nodoc:
25
+ @wavelet_ids.map { |id| @context.wavelets[id] }
26
+ end
27
+
28
+ # The root wavelet (it will be nil if the event refers to a private subwavelet) [Wavelet]
29
+ def root_wavelet # :nodoc:
30
+ wavelets.find { |wavelet| wavelet and wavelet.root? }
31
+ end
32
+
33
+ def print_structure(indent = 0) # :nodoc:
34
+ str = ''
35
+ str << "#{' ' * indent}Wave:#{@id}\n"
36
+
37
+ wavelets.each do |wavelet|
38
+ str << wavelet.print_structure(indent + 1)
39
+ end
40
+
41
+ str
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,269 @@
1
+ module Rave
2
+ module Models
3
+ # Represents a Wavelet, owned by a Wave
4
+ class Wavelet < Component
5
+ include Rave::Mixins::TimeUtils
6
+ include Rave::Mixins::Logger
7
+
8
+ # Creator of the wavelet if it was generated via an operation.
9
+ GENERATED_CREATOR = "rusty@a.gwave.com" # :nodoc:
10
+
11
+ # Current version number of the wavelet [Integer]
12
+ attr_reader :version
13
+
14
+ # ID of the creator [String]
15
+ def creator_id # :nodoc:
16
+ @creator_id.dup
17
+ end
18
+
19
+ # Time the wavelet was created [Time]
20
+ def creation_time # :nodoc:
21
+ @creation_time.dup
22
+ end
23
+
24
+ # Documents contained within the wavelet [Array of Document]
25
+ def data_documents # :nodoc:
26
+ @data_documents.dup
27
+ end
28
+
29
+ # The last time the wavelet was modified [Time]
30
+ def last_modified_time # :nodoc:
31
+ @last_modified_time.dup
32
+ end
33
+
34
+ # ID for the root blip [String]
35
+ def root_blip_id # :nodoc:
36
+ @root_blip_id.dup
37
+ end
38
+
39
+ # Wavelet title [String]
40
+ def title # :nodoc:
41
+ @title.dup
42
+ end
43
+
44
+ # ID of the wave that the wavelet is a part of [String]
45
+ def wave_id # :nodoc:
46
+ @wave_id.dup
47
+ end
48
+
49
+ # IDs of all those who are currently members of the wavelet [Array of String]
50
+ def participant_ids # :nodoc:
51
+ @participant_ids.map { |id| id.dup }
52
+ end
53
+
54
+ JAVA_CLASS = 'com.google.wave.api.impl.WaveletData'
55
+ ROOT_ID_SUFFIX = "conv+root" #The suffix for the root wavelet in a wave]
56
+ ROOT_ID_REGEXP = /#{Regexp.escape(ROOT_ID_SUFFIX)}$/
57
+
58
+ #
59
+ # Options include:
60
+ # - :creator
61
+ # - :creation_time
62
+ # - :data_documents
63
+ # - :last_modifed_time
64
+ # - :participants
65
+ # - :root_blip_id
66
+ # - :title
67
+ # - :version
68
+ # - :wave_id
69
+ # - :context
70
+ # - :id
71
+ def initialize(options = {}) # :nodoc:
72
+ @participant_ids = options[:participants] || []
73
+
74
+ if options[:id].nil? and options[:context]
75
+ # Generate the wavelet from scratch.
76
+ super(:id => "#{GENERATED_PREFIX}_wavelet_#{unique_id}_#{ROOT_ID_SUFFIX}", :context => options[:context])
77
+
78
+ # Create a wave to live in.
79
+ wave = Wave.new(:wavelet_ids => [@id], :context => @context)
80
+ @wave_id = wave.id
81
+ @context.add_wave(wave)
82
+
83
+ # Ensure the newly created wavelet has a root blip.
84
+ blip = Blip.new(:wave_id => wave.id, :wavelet_id => @id,
85
+ :creator => @context.robot.id, :contributors => [@context.robot.id])
86
+ @context.add_blip(blip)
87
+ @root_blip_id = blip.id
88
+
89
+ @participant_ids.each do |id|
90
+ @context.add_user(:id => id) unless @context.users[id]
91
+ end
92
+
93
+ @creator_id = GENERATED_CREATOR
94
+ @context.add_user(:id => @creator_id) unless @context.users[@creator_id]
95
+ else
96
+ super(options)
97
+ @root_blip_id = options[:root_blip_id]
98
+ @creator_id = options[:creator] || User::NOBODY_ID
99
+ @wave_id = options[:wave_id]
100
+ end
101
+
102
+ @creation_time = time_from_json(options[:creation_time]) || Time.now
103
+ @data_documents = options[:data_documents] || {}
104
+ @last_modified_time = time_from_json(options[:last_modified_time]) || Time.now
105
+ @title = options[:title] || ''
106
+ @version = options[:version] || 0
107
+ end
108
+
109
+ # Users that are currently have access the wavelet [Array of User]
110
+ def participants # :nodoc:
111
+ @participant_ids.map { |p| @context.users[p] }
112
+ end
113
+
114
+ # Users that originally created the wavelet [User]
115
+ def creator # :nodoc:
116
+ @context.users[@creator_id]
117
+ end
118
+
119
+ # Is this the root wavelet for its wave? [Boolean]
120
+ def root? # :nodoc:
121
+ not (id =~ ROOT_ID_REGEXP).nil?
122
+ end
123
+
124
+ #Creates a blip for this wavelet
125
+ # Returns: Gererated blip [Blip]
126
+ def create_blip
127
+ parent = final_blip
128
+ blip = Blip.new(:wave_id => @wave_id, :parent_blip_id => parent.id,
129
+ :wavelet_id => @id, :context => @context)
130
+ parent.add_child_blip(blip)
131
+
132
+ @context.add_operation(:type => Operation::WAVELET_APPEND_BLIP, :wave_id => @wave_id, :wavelet_id => @id, :property => blip)
133
+ blip
134
+ end
135
+
136
+ # Find the last blip in the main thread [Blip]
137
+ def final_blip # :nodoc:
138
+ blip = @context.blips[@root_blip_id]
139
+ if blip
140
+ while blip
141
+ # Find the first blip that is defined, if at all.
142
+ child_blip = blip.child_blips.find { |b| not b.nil? }
143
+ break unless child_blip
144
+ blip = child_blip
145
+ end
146
+ end
147
+ blip
148
+ end
149
+
150
+ # Adds a participant (human or robot) to the wavelet
151
+ # +user+:: User to add, as ID or object [String or User]
152
+ # Returns: The user that was added [User or nil]
153
+ def add_participant(user) # :nodoc:
154
+ id = user.to_s.downcase
155
+ if @participant_ids.include?(id)
156
+ logger.warning("Attempted to add a participant who was already in the wavelet(#{@id}): #{id}")
157
+ return nil
158
+ end
159
+
160
+ # Allow string names to be used as participant.
161
+ user = if @context.users[id]
162
+ @context.users[id]
163
+ else
164
+ @context.add_user(:id => id)
165
+ end
166
+
167
+ @context.add_operation(:type => Operation::WAVELET_ADD_PARTICIPANT,
168
+ :wave_id => @wave_id, :wavelet_id => @id, :property => user)
169
+ @participant_ids << id
170
+
171
+ user
172
+ end
173
+
174
+ # Removes a participant (robot only) from the wavelet.
175
+ # +user+:: User to remove, as ID or object [String or User]
176
+ # Returns: The user that was removed [User or nil]
177
+ def remove_participant(user) # :nodoc:
178
+ id = user.to_s.downcase
179
+ unless @participant_ids.include?(id)
180
+ logger.warning("Attempted to remove a participant who was not in the wavelet(#{@id}): #{id}")
181
+ return nil
182
+ end
183
+
184
+ # Allow string names to be used as participant.
185
+ user = @context.users[id]
186
+
187
+ unless user.robot?
188
+ logger.warning("Attempted to remove a non-robot from wavelet(#{@id}): #{id}")
189
+ return nil
190
+ end
191
+
192
+ if user == @context.robot
193
+ return remove_robot
194
+ end
195
+
196
+ @context.add_operation(:type => Operation::WAVELET_REMOVE_PARTICIPANT,
197
+ :wave_id => @wave_id, :wavelet_id => @id, :property => user)
198
+ @participant_ids.delete id
199
+
200
+ user
201
+ end
202
+
203
+ # Removes the local robot from the wavelet.
204
+ # Returns: The local robot [Robot]
205
+ def remove_robot
206
+ robot = @context.robot
207
+ @context.add_operation(:type => Operation::WAVELET_REMOVE_SELF,
208
+ :wave_id => @wave_id, :wavelet_id => @id)
209
+ @participant_ids.delete robot.id
210
+
211
+ robot
212
+ end
213
+
214
+ #Sets the data document for the wavelet
215
+ #
216
+ # NOT IMPLEMENTED
217
+ def set_data_document(name, data)
218
+ raise NotImplementedError
219
+ end
220
+
221
+ #Set the title
222
+ #
223
+ def title=(title) # :nodoc: Documented by title() as accessor.
224
+ title = title.to_s
225
+ @context.add_operation(:type => Operation::WAVELET_SET_TITLE,
226
+ :wave_id => @wave_id, :wavelet_id => @id, :property => title)
227
+ @title = title
228
+ end
229
+
230
+ # First blip in the wavelet [Blip]
231
+ def root_blip # :nodoc:
232
+ @context.blips[@root_blip_id]
233
+ end
234
+
235
+ # Wave that the wavelet is contained within.
236
+ def wave# :nodoc:
237
+ @context.waves[@wave_id]
238
+ end
239
+
240
+ # *INTERNAL*
241
+ # Convert to json for sending in an operation.
242
+ def to_json # :nodoc:
243
+ {
244
+ 'waveletId' => @id,
245
+ 'javaClass' => JAVA_CLASS,
246
+ 'waveId' => @wave_id,
247
+ 'rootBlipId' => @root_blip_id,
248
+ 'participants' => { "javaClass" => "java.util.ArrayList", "list" => @participant_ids }
249
+ }.to_json
250
+ end
251
+
252
+ # Convert to string.
253
+ def to_s
254
+ text = @title.length > 24 ? "#{@title[0..20]}..." : @title
255
+ "#{super}:#{participants.join(',')}:#{text}"
256
+ end
257
+
258
+ def print_structure(indent = 0) # :nodoc:
259
+ str = "#{' ' * indent}#{to_s}\n"
260
+
261
+ if root_blip
262
+ str << root_blip.print_structure(indent + 1)
263
+ end
264
+
265
+ str
266
+ end
267
+ end
268
+ end
269
+ end