rave 0.1.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,305 @@
1
+ module Rave
2
+ module Models
3
+ # Represents a blip, containing formated text, gadgets and other elements.
4
+ # It is part of a Wavelet within a Wave.
5
+ class Blip < Component
6
+ include Rave::Mixins::TimeUtils
7
+ include Rave::Mixins::Logger
8
+
9
+ JAVA_CLASS = 'com.google.wave.api.impl.BlipData' # :nodoc:
10
+
11
+ # Version number of the contents of the blip [Integer]
12
+ def version
13
+ @version.dup
14
+ end
15
+
16
+ # Annotations on the blip [Array of Annotation]
17
+ def annotations # :nodoc:
18
+ @annotations.dup
19
+ end
20
+
21
+ # IDs of the children of this blip [Array of String]
22
+ def child_blip_ids # :nodoc:
23
+ @child_blip_ids.map { |id| id.dup }
24
+ end
25
+
26
+ # IDs (email addresses) of those who have altered this blip [Array of String]
27
+ def contributor_ids # :nodoc:
28
+ @contributor_ids.map { |id| id.dup }
29
+ end
30
+
31
+ # Elements contained within this blip [Array of Element]
32
+ def elements # :nodoc:
33
+ @elements.dup
34
+ end
35
+
36
+ # Last time the blip was altered [Time]
37
+ def last_modified_time # :nodoc:
38
+ @last_modified_time.dup
39
+ end
40
+
41
+ # ID of this blip's parent [String or nil for a root blip]
42
+ def parent_blip_id # :nodoc:
43
+ @parent_blip_id.nil? ? nil : @parent_blip_id.dup
44
+ end
45
+
46
+ # ID of the wave this blip belongs to [String]
47
+ def wave_id # :nodoc:
48
+ @wave_id.nil? ? nil : @wave_id.dup
49
+ end
50
+
51
+ # ID of the wavelet this blip belongs to [String]
52
+ def wavelet_id # :nodoc:
53
+ @wavelet_id.nil? ? nil : @wavelet_id.dup
54
+ end
55
+
56
+ # Wavelet that the blip is a part of [Wavelet]
57
+ def wavelet # :nodoc:
58
+ @context.wavelets[@wavelet_id]
59
+ end
60
+
61
+ # Wave that this blip is a part of [Wave]
62
+ def wave # :nodoc:
63
+ @context.waves[@wave_id]
64
+ end
65
+
66
+ # Blip that this Blip is a direct reply to. Will be nil if the root blip
67
+ # in a wavelet [Blip or nil for a root blip]
68
+ def parent_blip # :nodoc:
69
+ @context.blips[@parent_blip_id]
70
+ end
71
+
72
+ # Returns true if this is a root blip (no parent blip) [Boolean]
73
+ def root? # :nodoc:
74
+ @parent_blip_id.nil?
75
+ end
76
+
77
+ # Returns true if this is a leaf node (has no children). [Boolean]
78
+ def leaf? # :nodoc:
79
+ @child_blip_ids.empty?
80
+ end
81
+
82
+ # Has the blip been deleted? [Boolean]
83
+ def deleted? # :nodoc:
84
+ [:deleted, :null].include? @state
85
+ end
86
+
87
+ # Has the blip been completely destroyed? [Boolean]
88
+ def null? # :nodoc:
89
+ @state == :null
90
+ end
91
+
92
+ # Text contained in the blip [String]
93
+ def content # :nodoc:
94
+ @content.dup
95
+ end
96
+
97
+ # Users that have made a contribution to the blip [Array of User]
98
+ def contributors # :nodoc:
99
+ @contributor_ids.map { |c| @context.users[c] }
100
+ end
101
+
102
+ # Original creator of the blip [User]
103
+ def creator # :nodoc:
104
+ @context.users[@creator]
105
+ end
106
+
107
+ # List of direct children of this blip. The first one will be continuing
108
+ # the thread, others will be indented replies [Array of Blip]
109
+ def child_blips # :nodoc:
110
+ @child_blip_ids.map { |id| @context.blips[id] }
111
+ end
112
+
113
+ # Ensure that all elements within the blip are given a context.
114
+ def context=(value) # :nodoc:
115
+ super(value)
116
+ @elements.each_value { |e| e.context = value }
117
+ end
118
+
119
+ VALID_STATES = [:normal, :null, :deleted] # :nodoc: As passed to initializer in :state option.
120
+
121
+ #Options include:
122
+ # - :annotations
123
+ # - :child_blip_ids
124
+ # - :content
125
+ # - :contributors
126
+ # - :creator
127
+ # - :elements
128
+ # - :last_modified_time
129
+ # - :parent_blip_id
130
+ # - :version
131
+ # - :wave_id
132
+ # - :wavelet_id
133
+ # - :id
134
+ # - :context
135
+ # - :state
136
+ def initialize(options = {}) # :nodoc:
137
+ @annotations = options[:annotations] || []
138
+ @child_blip_ids = options[:child_blip_ids] || []
139
+ @content = options[:content] || ''
140
+ @contributor_ids = options[:contributors] || []
141
+ @creator = options[:creator] || User::NOBODY_ID
142
+ @elements = options[:elements] || {}
143
+ @last_modified_time = time_from_json(options[:last_modified_time]) || Time.now
144
+ @parent_blip_id = options[:parent_blip_id]
145
+ @version = options[:version] || -1
146
+ @wave_id = options[:wave_id]
147
+ @wavelet_id = options[:wavelet_id]
148
+ @state = options[:state] || :normal
149
+
150
+ unless VALID_STATES.include? @state
151
+ raise ArgumentError.new("Bad state #{options[:state]}. Should be one of #{VALID_STATES.join(', ')}")
152
+ end
153
+
154
+ # If the blip doesn't have a defined ID, since we just created it,
155
+ # assign a temporary, though unique, ID, based on the ID of the wavelet.
156
+ if options[:id].nil?
157
+ options[:id] = "#{GENERATED_PREFIX}_blip_#{unique_id}"
158
+ end
159
+
160
+ super(options)
161
+ end
162
+
163
+ #Returns true if an annotation with the given name exists in this blip
164
+ def has_annotation?(name)
165
+ @annotations.any? { |a| a.name == name }
166
+ end
167
+
168
+ # Adds an annotation to the Blip.
169
+ def add_annotation(annotation)
170
+ @annotations << annotation
171
+ self
172
+ end
173
+
174
+ #Creates a child blip under this blip
175
+ def create_child_blip
176
+ blip = Blip.new(:wave_id => @wave_id, :parent_blip_id => @id, :wavelet_id => @wavelet_id,
177
+ :context => @context, :contributors => [Robot.instance.id])
178
+ @context.add_operation(:type => Operation::BLIP_CREATE_CHILD, :blip_id => @id, :wave_id => @wave_id, :wavelet_id => @wavelet_id, :property => blip)
179
+ add_child_blip(blip)
180
+ blip
181
+ end
182
+
183
+ # Adds a created child blip to this blip.
184
+ def add_child_blip(blip) # :nodoc:
185
+ @child_blip_ids << blip.id
186
+ @context.add_blip(blip)
187
+ end
188
+
189
+ # INTERNAL
190
+ # Removed a child blip.
191
+ def remove_child_blip(blip) # :nodoc:
192
+ @child_blip_ids.delete(blip.id)
193
+
194
+ # Destroy oneself completely if you are no longer useful to structure.
195
+ destroy_me if deleted? and leaf? and not root?
196
+ end
197
+
198
+ # Delete this blip from its wavelet.
199
+ # Returns the blip id.
200
+ def delete
201
+ if deleted?
202
+ logger.warning("Attempt to delete blip that has already been deleted: #{id}")
203
+ elsif root?
204
+ logger.warning("Attempt to delete root blip: #{id}")
205
+ else
206
+ @context.add_operation(:type => Operation::BLIP_DELETE,
207
+ :blip_id => @id, :wave_id => @wave_id, :wavelet_id => @wavelet_id)
208
+ delete_me
209
+ end
210
+ end
211
+
212
+ # Convert to string.
213
+ def to_s
214
+ str = @content.gsub(/\n/, "\\n")
215
+ str = str.length > 24 ? "#{str[0..20]}..." : str
216
+
217
+ str = case @state
218
+ when :normal
219
+ "#{contributors.join(',')}:#{str}"
220
+ when :deleted
221
+ '<DELETED>'
222
+ when :null
223
+ '<NULL>'
224
+ end
225
+
226
+ "#{super}:#{str}"
227
+ end
228
+
229
+ # *INTERNAL*
230
+ # Write out a formatted block of text showing the blip and its descendants.
231
+ def print_structure(indent = 0) # :nodoc:
232
+ str = "#{' ' * indent}#{to_s}\n"
233
+
234
+ unless @child_blip_ids.empty?
235
+ # Move the first blip to the end, since it will be looked at last.
236
+ blip_ids = @child_blip_ids
237
+ blip_ids.push(blip_ids.shift)
238
+
239
+ # All children, except the first, should be indented.
240
+ blip_ids.each_with_index do |blip_id, index|
241
+ is_last_blip = (index == blip_ids.size - 1)
242
+
243
+ # All except the last one should be indented again.
244
+ ind = is_last_blip ? indent : indent + 1
245
+ blip = @context.blips[blip_id]
246
+ if blip
247
+ str << blip.print_structure(ind)
248
+ else
249
+ str << "#{' ' * ind}<undefined-blip>:#{blip_id}\n"
250
+ end
251
+
252
+ str << "\n" unless is_last_blip # Gap between reply chains.
253
+ end
254
+ end
255
+
256
+ str
257
+ end
258
+
259
+ # *INTERNAL*
260
+ # Convert to json for sending in an operation. We should never need to
261
+ # send more data than this, although blips we receive will have more data.
262
+ def to_json # :nodoc:
263
+ {
264
+ 'blipId' => @id,
265
+ 'javaClass' => JAVA_CLASS,
266
+ 'waveId' => @wave_id,
267
+ 'waveletId' => @wavelet_id
268
+ }.to_json
269
+ end
270
+
271
+ # *INTERNAL*
272
+ # Delete the blip or, if appropriate, destroy it instead.
273
+ def delete_me(allow_destroy = true) # :nodoc:
274
+ raise "Can't delete root blip" if root?
275
+
276
+ if leaf? and allow_destroy
277
+ destroy_me
278
+ else
279
+ # Blip is marked as deleted, but stays in place to maintain structure.
280
+ @state = :deleted
281
+ @content = ''
282
+ end
283
+
284
+ @id
285
+ end
286
+
287
+ protected
288
+ # *INTERNAL*
289
+ # Remove the blip entirely, leaving it null.
290
+ def destroy_me # :nodoc:
291
+ raise "Can't destroy root blip" if root?
292
+ raise "Can't destroy non-leaf blip" unless leaf?
293
+
294
+ # Remove the blip entirely to the realm of oblivion.
295
+ parent_blip.remove_child_blip(self)
296
+ @parent_blip_id = nil
297
+ @context.remove_blip(self)
298
+ @state = :null
299
+ @content = ''
300
+
301
+ @id
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,42 @@
1
+ module Rave
2
+ module Models
3
+ # A wave or wave component.
4
+ # This is an abstract class.
5
+ class Component
6
+ include Rave::Mixins::Logger
7
+
8
+ GENERATED_PREFIX = 'TBD' # :nodoc: Prefixes blips and wavelets that are created by the robot.
9
+ GENERATED_PATTERN = /^#{GENERATED_PREFIX}/ # :nodoc:
10
+
11
+ @@last_id = 0 # For generated components, this is a unique ID number for them.
12
+
13
+ attr_writer :context # :nodoc: Allow context to set link to it.
14
+
15
+ # Has this component been generated by the robot [Boolean]
16
+ def generated? # :nodoc:
17
+ # This is true for all components except Users, who would override this.
18
+ not (@id =~ /^#{GENERATED_PREFIX}/).nil?
19
+ end
20
+
21
+ # Generate a unique id number (from 1) [Integer]
22
+ def unique_id # :nodoc:
23
+ @@last_id += 1
24
+ end
25
+
26
+ # ID [String]
27
+ def id # :nodoc:
28
+ @id.dup
29
+ end
30
+
31
+ def initialize(options = {}) # :nodoc:
32
+ @id = options[:id] or raise ArgumentError.new(":id option is required for #{self.class.name}")
33
+ @context = options[:context]
34
+ end
35
+
36
+ # Convert to string.
37
+ def to_s
38
+ "#{self.class.name[/[^:]*$/]}:#{@id}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,174 @@
1
+ module Rave
2
+ module Models
3
+ # Contains server request information including current waves and operations.
4
+ class Context
5
+
6
+ attr_reader :primary_wavelet # :nodoc: API users should use Event#wavelet
7
+ attr_reader :robot # The robot managing this context.
8
+
9
+ # All waves by ID [Hash of String => Wave]
10
+ def waves # :nodoc:
11
+ @waves.dup
12
+ end
13
+
14
+ # All wavelets by ID [Hash of String => Wavelet]
15
+ def wavelets # :nodoc:
16
+ @wavelets.dup
17
+ end
18
+
19
+ # All wavelets by ID [Hash of String => Wavelet]
20
+ def blips # :nodoc:
21
+ @blips.dup
22
+ end
23
+
24
+ # All operations [Array of Operation]
25
+ def operations # :nodoc:
26
+ @operations.dup
27
+ end
28
+
29
+ # All users by ID [Hash of String => User]
30
+ def users # :nodoc:
31
+ @users.dup
32
+ end
33
+
34
+ JAVA_CLASS = 'com.google.wave.api.impl.OperationMessageBundle' # :nodoc:
35
+
36
+ #Options include:
37
+ # - :waves
38
+ # - :wavelets
39
+ # - :blips
40
+ # - :operations
41
+ # - :users
42
+ def initialize(options = {}) # :nodoc:
43
+ @waves = options[:waves] || {}
44
+ @waves.values.each { |wave| wave.context = self } #Set up self as this wave's context
45
+
46
+ @wavelets = options[:wavelets] || {}
47
+ @wavelets.values.each { |wavelet| wavelet.context = self } #Set up self as this wavelet's context
48
+ @primary_wavelet = @wavelets.values[0] # As opposed to any that are created later.
49
+
50
+
51
+ @blips = options[:blips] || {}
52
+ @blips.values.each { |blip| blip.context = self } #Set up self as this blip's context
53
+
54
+ @operations = options[:operations] || []
55
+
56
+ @users = options[:users] || {}
57
+ @users.values.each { |user| user.context = self } #Set up self as this user's context
58
+
59
+ resolve_user_references(options[:robot])
60
+ end
61
+
62
+ protected
63
+ # Create users for every reference to one in the wave.
64
+ def resolve_user_references(robot) # :nodoc:
65
+ if robot
66
+ @users[robot.id] = robot
67
+ robot.context = self
68
+ @robot = robot
69
+ end
70
+
71
+ @wavelets.each_value do |wavelet|
72
+ wavelet.participant_ids.each do |id|
73
+ unless @users[id]
74
+ add_user(:id => id)
75
+ end
76
+ end
77
+
78
+ unless @users[wavelet.creator_id]
79
+ add_user(:id => wavelet.creator_id)
80
+ end
81
+ end
82
+
83
+ @blips.each_value do |blip|
84
+ blip.contributor_ids.each do |id|
85
+ unless @users[id]
86
+ add_user(:id => id)
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ public
93
+ # Add a blip to blips (Use an Operation to actually add the blip to the Wave).
94
+ # Returns: The blip [Blip].
95
+ def add_blip(blip) # :nodoc:
96
+ @blips[blip.id] = blip
97
+ blip.context = self
98
+ blip
99
+ end
100
+
101
+ # Add an operation to the list to be executed.
102
+ # Returns: self [Context]
103
+ def add_operation(options) # :nodoc:
104
+ @operations << Operation.new(options)
105
+ self
106
+ end
107
+
108
+ # Add a wavelet to wavelets (Use an Operation to actually add the blip to the Wave).
109
+ # Returns: The wavelet [Wavelet].
110
+ def add_wavelet(wavelet)# :nodoc:
111
+ @wavelets[wavelet.id] = wavelet
112
+ wavelet.context = self
113
+ wavelet
114
+ end
115
+
116
+ # Add a wave to waves (Use an Operation to actually add the wave).
117
+ # Returns: The wave [Wave].
118
+ def add_wave(wave)# :nodoc:
119
+ @waves[wave.id] = wave
120
+ wave.context = self
121
+ wave
122
+ end
123
+
124
+ # +participants+:: Participants to exist in the new wavelet, as IDs or objects [Array of String/User]
125
+ # Returns: Newly created wave [Wave]
126
+ def create_wavelet(participants) # :nodoc:
127
+ # Map participants to strings, since they could be Users.
128
+ participant_ids = participants.map {|p| p.to_s.downcase }
129
+ participant_ids << @robot.id unless participant_ids.include? @robot.id
130
+
131
+ wavelet = Wavelet.new(:context => self, :participants => participant_ids)
132
+ add_wavelet(wavelet)
133
+
134
+ # TODO: Get wave id from sensible place?
135
+ add_operation(:type => Operation::WAVELET_CREATE, :wave_id => @waves.keys[0],
136
+ :property => wavelet)
137
+
138
+ wavelet
139
+ end
140
+
141
+ # Remove a blip.
142
+ def remove_blip(blip) # :nodoc:
143
+ @blips.delete(blip.id)
144
+ end
145
+
146
+ # Add a user to users (Use an Operation to actually add the blip to the Wave).
147
+ def add_user(options) # :nodoc:
148
+ options[:id].downcase! if options[:id]
149
+ raise DuplicatedIDError.new("Can't add another User with id #{options[:id]}") if @users.has_key? options[:id].downcase
150
+ user = User.new(options)
151
+ @users[user.id] = user
152
+ user.context = self
153
+ user
154
+ end
155
+
156
+ #Serialize the context for use in the line protocol.
157
+ def to_json # :nodoc:
158
+ hash = {
159
+ 'operations' => { 'javaClass' => 'java.util.ArrayList', 'list' => @operations },
160
+ 'javaClass' => JAVA_CLASS
161
+ }
162
+ hash.to_json
163
+ end
164
+
165
+ def print_structure(indent = 0) # :nodoc:
166
+ str = ''
167
+ waves.each_value do |wave|
168
+ str << wave.print_structure(indent)
169
+ end
170
+ str
171
+ end
172
+ end
173
+ end
174
+ end