rsence 2.2.1 → 2.2.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/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.1
1
+ 2.2.2
@@ -78,13 +78,18 @@
78
78
  :latency: 0
79
79
  #
80
80
  # HTTP Port number to listen to.
81
- :port: 8001
81
+ :port: '8001'
82
82
  #
83
83
  # Bind this ip address ('0.0.0.0' means all)
84
84
  :bind_address: '127.0.0.1'
85
85
  #
86
- # Rack handler to use, defaults to thin
87
- :rack_require: mongrel
86
+ # Rack handler to use, defaults to puma
87
+ :rack_require: puma
88
+ #
89
+ # These are default options. Ymmv, but these work fine for puma
90
+ :handler_options:
91
+ :Verbose: false
92
+ :Threads: '4:64' # puma default is '0:16'
88
93
  #
89
94
  # When enabled, sets http cache headers
90
95
  # to cache content as long as possible.
@@ -39,7 +39,7 @@
39
39
  :plugin_directory_not_a_directory: |
40
40
  Plugin directory not a directory, expected:
41
41
  <%%= plugin_path.inspect %>
42
- :warn_no_plugin_directory_in_project: |
42
+ :warn_no_directory_creating: |
43
43
  Warning! No <%%= dir_name %> directory in project, creating:
44
44
  <%%= dir_path.inspect %>
45
45
  :invalid_environment: |
@@ -70,20 +70,18 @@ COMM.Transporter = HApplication.extend({
70
70
  * to report js errors to the server.
71
71
  * If no error, returns an empty string.
72
72
  **/
73
- getClientEvalError: function(){
74
- var _this = COMM.Transporter;
75
- return _this._clientEvalError?'&err_msg=' +
76
- COMM.Values._encodeString(_this._clientEvalError):'';
77
- },
73
+ // getClientEvalError: function(){
74
+ // var _this = COMM.Transporter;
75
+ // return _this._clientEvalError?'&err_msg=' +
76
+ // COMM.Values._encodeString(_this._clientEvalError):'';
77
+ // },
78
78
 
79
79
  parseResponseArray: function( _responseText ){
80
- var _arr = eval( _responseText );
81
- return _arr;
80
+ return HVM.decode( _responseText );
82
81
  },
83
82
 
84
83
  _nativeParseResponseArray: function( _responseText ){
85
- var _arr = JSON.parse( _responseText );
86
- return _arr;
84
+ return JSON.parse( _responseText );
87
85
  },
88
86
 
89
87
  setValues: function( _values ){
@@ -176,7 +174,9 @@ COMM.Transporter = HApplication.extend({
176
174
  flushBusy: function(){
177
175
  var _this = COMM.Transporter;
178
176
  _this.busy = false;
179
- COMM.Values.tosync.length !== 0 && _this.sync();
177
+ if( COMM.Values.tosync.length !== 0 ){
178
+ _this.sync();
179
+ }
180
180
  },
181
181
  failMessage: function(_title,_message){
182
182
  var _this = COMM.Transporter,
@@ -320,18 +320,34 @@ COMM.Transporter = HApplication.extend({
320
320
  }
321
321
  // console.log('sync.');
322
322
  this.busy = true;
323
+ var _now = new Date().getTime();
323
324
  if(window['sesWatcher'] && window.sesWatcher['sesTimeoutValue']){
324
325
  // Sets the value of the session watcher to the current time. It could cause an unnecessary re-sync poll immediately after this sync otherwise.
325
- sesWatcher.sesTimeoutValue.set( new Date().getTime() );
326
+ sesWatcher.sesTimeoutValue.set( _now );
326
327
  }
327
- var _this = this,
328
- _values = COMM.Values.sync(),
329
- _sesKey = 'ses_key='+COMM.Session.ses_key,
330
- _errorMessage = _this.getClientEvalError(),
331
- _body = [_sesKey,_errorMessage,_values?'&values='+_values:''].join('');
328
+ var
329
+ _this = this,
330
+ // _values = HVM.sync(),
331
+ // _boundary = _now.toString(36)+(Math.random()*10000).toString(36)+(Math.random()*10000).toString(36),
332
+ // _separator = '--'+_boundary,
333
+ // _errorMessage = _this.getClientEvalError(),
334
+ _body = HVM.sync();
335
+ // _body = _separator+
336
+ // '\r\nContent-Disposition: form-data; name="ses_key"\r\nContent-Type: text/plain\r\n'+
337
+ // '\r\n'+COMM.Session.ses_key+'\r\n'+_separator;
338
+ // if( _values ){
339
+ // _body += '\r\nContent-Disposition: form-data; name="values"\r\nContent-Type: application/json; charset=UTF-8\r\n'+
340
+ // '\r\n'+_values+'\r\n'+_separator+'--\r\n';
341
+ // }
342
+ // else {
343
+ // _body += '--\r\n';
344
+ // }
345
+ // _body = [_sesKey,_errorMessage,_values?'&values='+_values:''].join('');
332
346
  COMM.request(
333
347
  _this.url, {
334
348
  _this: _this,
349
+ // contentType: 'multipart/form-data; boundary='+_boundary,
350
+ contentType: 'application/json',
335
351
  onSuccess: COMM.Transporter.success,
336
352
  onFailure: COMM.Transporter.failure,
337
353
  method: 'POST',
@@ -468,22 +468,54 @@ COMM.Values = HClass.extend({
468
468
  * An encoded string representation of values to synchronize.
469
469
  **/
470
470
  sync: function(){
471
- if(this.tosync.length===0){
472
- return false;
471
+ var
472
+ _this = this,
473
+ _response = [ COMM.Session.ses_key,{},[] ],
474
+ _error = COMM.Transporter._clientEvalError;
475
+
476
+ if( _error ){
477
+ _response[2].push({'err_msg':_error});
473
478
  }
474
- var _syncValues = {},
475
- _this = this,
476
- _values = _this.values,
477
- _tosync = _this.tosync,
478
- _len = _tosync.length,
479
- i = 0, _id, _value;
480
- for(;i<_len;i++){
481
- _id = _tosync.shift();
482
- _value = _values[_id].value;
483
- _syncValues[_id] = _value;
479
+
480
+ // new implementation, symmetric with the server response format
481
+ if( this.tosync.length > 0 ){
482
+ _response[1].set=[];
483
+ var
484
+ _syncValues = _response[1].set,
485
+ _values = _this.values,
486
+ _tosync = _this.tosync,
487
+ i = _tosync.length,
488
+ _id, _value;
489
+ while(i--){
490
+ _id = _tosync.shift();
491
+ _value = _values[_id].value;
492
+ _syncValues.push( [ _id, _value ] );
493
+ }
484
494
  }
485
- return encodeURIComponent(_this.encode(_syncValues));
495
+ // console.log('response:',_response);
496
+ // console.log('encoded:',_this.encode(_response));
497
+ return _this.encode(_response);
486
498
  },
499
+
500
+ // Old sync implementation:
501
+ // sync: function(){
502
+ // if(this.tosync.length===0){
503
+ // return false;
504
+ // }
505
+ // var
506
+ // _syncValues = {},
507
+ // _this = this,
508
+ // _values = _this.values,
509
+ // _tosync = _this.tosync,
510
+ // _len = _tosync.length,
511
+ // i = 0, _id, _value;
512
+ // for(;i<_len;i++){
513
+ // _id = _tosync.shift();
514
+ // _value = _values[_id].value;
515
+ // _syncValues[_id] = _value;
516
+ // }
517
+ // return encodeURIComponent(_this.encode(_syncValues));
518
+ // },
487
519
 
488
520
  _detectNativeJSONSupport: function(){
489
521
  if(window['JSON']){
@@ -157,7 +157,7 @@ module ArgvUtil
157
157
  say @strs[:initenv][:enter_http_port]
158
158
  str_http_port = @strs[:initenv][:http_port]
159
159
  config[:http_server][:port] = ask(str_http_port) do |q|
160
- q.default = config[:http_server][:port]
160
+ q.default = config[:http_server][:port].to_s
161
161
  end
162
162
 
163
163
  say @strs[:initenv][:enter_tcp_ip]
@@ -115,18 +115,18 @@ class Broker
115
115
  puts conf.inspect if RSence.args[:debug]
116
116
 
117
117
  require rack_require
118
+ require 'rack/handler/puma.rb' if rack_require == 'puma'
118
119
 
119
120
  # Selects the handler for Rack
120
121
  handler = {
121
- 'mongrel2' => lambda { Rack::Handler::Mongrel2 },
122
122
  'webrick' => lambda { Rack::Handler::WEBrick },
123
- 'ebb' => lambda { Rack::Handler::Ebb },
124
123
  'thin' => lambda { Rack::Handler::Thin },
125
124
  'mongrel' => lambda { Rack::Handler::Mongrel },
126
- 'unicorn' => lambda { Rack::Handler::Unicorn },
127
- 'rainbows' => lambda { Rack::Handler::Rainbows }
125
+ 'puma' => lambda { Rack::Handler::Puma }
128
126
  }[rack_require].call
129
- handler.run( Rack::Lint.new(self.new), :Host => host, :Port => port )
127
+ handler.run( self.new, {
128
+ :Host => host, :Port => port
129
+ }.merge( conf[:handler_options] ) )
130
130
  end
131
131
 
132
132
  # Generic 404 error handler. Just sets up response status, headers, body as a small "Page Not Found" html page
data/lib/rsence/msg.rb CHANGED
@@ -135,7 +135,7 @@ module RSence
135
135
  if options[:servlet]
136
136
  @do_gzip = false
137
137
  else
138
- @response['Content-Type'] = 'text/javascript; charset=utf-8'
138
+ @response['Content-Type'] = 'application/json; charset=utf-8'
139
139
  @response['Cache-Control'] = 'no-cache'
140
140
 
141
141
  # gnu-zipped responses:
@@ -84,7 +84,7 @@ module RSence
84
84
  # session id, used internally
85
85
  :ses_id => ses_id,
86
86
 
87
- # session key, used externally (client xhr)
87
+ # session key, used externally (client sync)
88
88
  :ses_key => ses_sha,
89
89
 
90
90
  # session key, used externally (client cookies)
@@ -137,7 +137,7 @@ module RSence
137
137
  # new time-out
138
138
  ses_data[:timeout] = Time.now.to_i + @config[:timeout_secs]
139
139
 
140
- # re-generates the ses_key for each xhr
140
+ # re-generates the ses_key for each sync
141
141
  if @config[:disposable_keys]
142
142
 
143
143
  # disposes the old (current) ses_key:
@@ -233,7 +233,7 @@ module RSence
233
233
  ### Otherwise stops the client and returns false.
234
234
  def check_ses( msg, ses_key, ses_seed=false )
235
235
 
236
- # first, check if the session key exists (xhr)
236
+ # first, check if the session key exists (sync)
237
237
  if @session_keys.has_key?( ses_key )
238
238
 
239
239
  # get the session's id based on its key
@@ -455,7 +455,7 @@ module RSence
455
455
  ses_cookie_max_age = @config[:timeout_secs]
456
456
 
457
457
  ## Only match the handshaking address of rsence,
458
- ## prevents unnecessary cookie-juggling in xhr's
458
+ ## prevents unnecessary cookie-juggling in sync's
459
459
  if @config[:trust_cookies]
460
460
  ses_cookie_path = '/'
461
461
  elsif ses_cookie_path == nil
@@ -527,19 +527,19 @@ module RSence
527
527
  query = request.query
528
528
  end
529
529
 
530
- ## Perform old-session cleanup on all xhr:s
530
+ ## Perform old-session cleanup on all sync:s
531
531
  expire_sessions
532
532
 
533
533
  ## The 'ses_id' request query key is required.
534
534
  ## The client defaults to '0', which means the
535
535
  ## client needs to be initialized.
536
536
  ## The client's ses_id is the server's ses_key.
537
- if not query.has_key?( 'ses_key' )
537
+ if not options.has_key?( :ses_key )
538
538
  return Message.new( @transporter, request, response, options )
539
539
  else
540
540
 
541
541
  ## get the ses_key from the request query:
542
- ses_key = query[ 'ses_key' ]
542
+ ses_key = options[:ses_key]
543
543
 
544
544
  ## The message object binds request, response
545
545
  ## and all user/session -related data to one
@@ -94,13 +94,13 @@ module RSence
94
94
  uri = request.fullpath
95
95
 
96
96
  if request_type == :post
97
- ## /x handles xhr without cookies
97
+ ## /x handles sync without cookies
98
98
  if uri == broker_urls[:x] and @sessions.accept_requests
99
- xhr( request, response, { :cookies => true, :servlet => false } )
99
+ sync( request, response, { :cookies => true, :servlet => false } )
100
100
  return true
101
- ## /hello handles the first xhr (with cookies, for session key)
101
+ ## /hello handles the first sync (with cookies, for session key)
102
102
  elsif uri == broker_urls[:hello] and @sessions.accept_requests
103
- xhr( request, response, { :cookies => true, :servlet => false } )
103
+ sync( request, response, { :cookies => true, :servlet => false } )
104
104
  return true
105
105
  end
106
106
  end
@@ -108,7 +108,7 @@ module RSence
108
108
  end
109
109
 
110
110
  # wrapper for the session manager stop client functionality
111
- def xhr_error_handler(msg,err_name,err_extra_descr='')
111
+ def sync_error_handler(msg,err_name,err_extra_descr='')
112
112
  @sessions.stop_client_with_message( msg,
113
113
  @config[:messages][err_name][:title],
114
114
  @config[:messages][err_name][:descr]+err_extra_descr,
@@ -116,8 +116,8 @@ module RSence
116
116
  )
117
117
  end
118
118
 
119
- # wrapper for tracebacks in xhr
120
- def xhr_traceback_handler(e,err_descr='Transporter::UnspecifiedError')
119
+ # wrapper for tracebacks in sync
120
+ def sync_traceback_handler(e,err_descr='Transporter::UnspecifiedError')
121
121
  puts "=="*40 if RSence.args[:debug]
122
122
  puts err_descr
123
123
  if RSence.args[:debug]
@@ -127,10 +127,32 @@ module RSence
127
127
  puts "=="*40
128
128
  end
129
129
  end
130
+
131
+ def find_client_sync_error( options )
132
+ return false if options.length == 0
133
+ errors = []
134
+ options.each do |err|
135
+ if err.class == Hash and err.has_key?('err_msg')
136
+ errors.push( err['err_msg'] )
137
+ end
138
+ end
139
+ return false if errors.length == 0
140
+ return errors
141
+ end
130
142
 
131
143
  ## handles incoming XMLHttpRequests from the browser
132
- def xhr(request, response, options = { :cookies => false, :servlet => false } )
133
-
144
+ def sync(request, response, options = { :cookies => false, :servlet => false } )
145
+ request_body = request.body.read
146
+ begin
147
+ request_content = JSON.parse( request_body )
148
+ rescue JSON::ParseError
149
+ warn "Request body isn't valid JSON: #{request_body}"
150
+ request_content = ['-1:.o.:INVALID',{},[]]
151
+ end
152
+ options[:ses_key] = request_content[0]
153
+ options[:values] = request_content[1]
154
+ options[:messages] = request_content[2]
155
+
134
156
  session_conf = RSence.config[:session_conf]
135
157
 
136
158
  options[:cookies] = false unless options.has_key?(:cookies)
@@ -143,13 +165,15 @@ module RSence
143
165
  msg = @sessions.init_msg( request, response, options )
144
166
 
145
167
  response_success = true
146
-
168
+
169
+ client_errors = find_client_sync_error( options[:messages] )
170
+
147
171
  # If the client encounters an error, display error message
148
- if request.query.has_key?('err_msg')
172
+ if client_errors #request.query.has_key?('err_msg')
149
173
  response_success = false
150
- client_error_msg = request.query['err_msg'].inspect
174
+ client_error_msg = client_errors.inspect
151
175
  puts "\nCLIENT ERROR:\n#{client_error_msg}\n" if RSence.args[:debug]
152
- xhr_error_handler(msg,:client_error,client_error_msg)
176
+ sync_error_handler(msg,:client_error,client_error_msg)
153
177
  end
154
178
 
155
179
  # If the session is valid, continue:
@@ -175,14 +199,14 @@ module RSence
175
199
  end
176
200
 
177
201
  ## Pass the client XML to the value manager
178
- if request.query.has_key?( 'values' )
179
- syncdata_str = request.query[ 'values' ]
202
+ if options[:values].has_key?('set')#request.query.has_key?( 'values' )
203
+ # syncdata_str = request.query[ 'values' ]
180
204
  begin
181
- @valuemanager.xhr( msg, syncdata_str )
205
+ @valuemanager.sync( msg, options[:values]['set'] )
182
206
  rescue => e
183
207
  response_success = false
184
- xhr_error_handler( msg, :valuemanager_xhr_error, e.message )
185
- xhr_traceback_handler( e, "Transporter::ValueManagerXHRError: @valuemanager.xhr failed." )
208
+ sync_error_handler( msg, :valuemanager_sync_error, e.message )
209
+ sync_traceback_handler( e, "Transporter::ValueManagerXHRError: @valuemanager.sync failed." )
186
210
  end
187
211
  end
188
212
 
@@ -195,8 +219,8 @@ module RSence
195
219
  @plugins.delegate( :cloned_target, msg, msg.cloned_source )
196
220
  rescue => e
197
221
  response_success = false
198
- xhr_error_handler( msg, :plugin_delegate_cloned_target_error, e.message )
199
- xhr_traceback_handler( e, "Transporter::PluginDelegateClonedTargetError: @plugins.delegate 'cloned_target' failed." )
222
+ sync_error_handler( msg, :plugin_delegate_cloned_target_error, e.message )
223
+ sync_traceback_handler( e, "Transporter::PluginDelegateClonedTargetError: @plugins.delegate 'cloned_target' failed." )
200
224
  end
201
225
  end
202
226
 
@@ -205,8 +229,8 @@ module RSence
205
229
  msg.session[:plugin_incr] = @plugins.incr
206
230
  rescue => e
207
231
  response_success = false
208
- xhr_error_handler( msg, :plugin_delegate_restore_ses_error, e.message )
209
- xhr_traceback_handler( e, "Transporter::PluginDelegateRestoreSesError: @plugins.delegate 'restore_ses' failed." )
232
+ sync_error_handler( msg, :plugin_delegate_restore_ses_error, e.message )
233
+ sync_traceback_handler( e, "Transporter::PluginDelegateRestoreSesError: @plugins.delegate 'restore_ses' failed." )
210
234
  end
211
235
 
212
236
  elsif msg.new_session
@@ -216,8 +240,8 @@ module RSence
216
240
  msg.session[:plugin_incr] = @plugins.incr
217
241
  rescue => e
218
242
  response_success = false
219
- xhr_error_handler( msg, :plugin_delegate_init_ses_error, e.message )
220
- xhr_traceback_handler( e, "Transporter::PluginDelegateInitSesError: @plugins.delegate 'init_ses' failed." )
243
+ sync_error_handler( msg, :plugin_delegate_init_ses_error, e.message )
244
+ sync_traceback_handler( e, "Transporter::PluginDelegateInitSesError: @plugins.delegate 'init_ses' failed." )
221
245
  end
222
246
 
223
247
  elsif msg.cloned_targets
@@ -226,8 +250,8 @@ module RSence
226
250
  @plugins.delegate( :cloned_source, msg, msg.cloned_targets )
227
251
  rescue => e
228
252
  response_success = false
229
- xhr_error_handler( msg, :plugin_delegate_cloned_source_error, e.message )
230
- xhr_traceback_handler( e, "Transporter::PluginDelegateClonedSourceError: @plugins.delegate 'cloned_source' failed." )
253
+ sync_error_handler( msg, :plugin_delegate_cloned_source_error, e.message )
254
+ sync_traceback_handler( e, "Transporter::PluginDelegateClonedSourceError: @plugins.delegate 'cloned_source' failed." )
231
255
  end
232
256
 
233
257
  elsif msg.refresh_page?( @plugins.incr ) and @config[:client_autoreload]
@@ -244,8 +268,8 @@ module RSence
244
268
  @valuemanager.validate( msg )
245
269
  rescue => e
246
270
  response_success = false
247
- xhr_error_handler( msg, :valuemanager_validate_error, e.message )
248
- xhr_traceback_handler( e, "Transporter::ValueManagerValidateError: @valuemanager.validate failed." )
271
+ sync_error_handler( msg, :valuemanager_validate_error, e.message )
272
+ sync_traceback_handler( e, "Transporter::ValueManagerValidateError: @valuemanager.validate failed." )
249
273
  end
250
274
 
251
275
  ### Allows every plugin to respond to the idle call
@@ -253,8 +277,8 @@ module RSence
253
277
  @plugins.delegate( :idle, msg )
254
278
  rescue => e
255
279
  response_success = false
256
- xhr_error_handler( msg, :plugin_idle_error, e.message )
257
- xhr_traceback_handler( e, "Transporter::PluginIdleError: @plugins.idle failed." )
280
+ sync_error_handler( msg, :plugin_idle_error, e.message )
281
+ sync_traceback_handler( e, "Transporter::PluginIdleError: @plugins.idle failed." )
258
282
  end
259
283
 
260
284
  ### Processes outgoing values to client
@@ -262,8 +286,8 @@ module RSence
262
286
  @valuemanager.sync_client( msg )
263
287
  rescue => e
264
288
  response_success = false
265
- xhr_error_handler( msg, :valuemanager_sync_client_error, e.message )
266
- xhr_traceback_handler( e, "Transporter::ValueManagerSyncClientError: @valuemanager.sync_client failed." )
289
+ sync_error_handler( msg, :valuemanager_sync_client_error, e.message )
290
+ sync_traceback_handler( e, "Transporter::ValueManagerSyncClientError: @valuemanager.sync_client failed." )
267
291
  end
268
292
 
269
293
  else
@@ -114,10 +114,10 @@ module RSence
114
114
  end
115
115
 
116
116
  # @private Parses the json from the client and passes it on to associated values
117
- def xhr( msg, syncdata_str )
117
+ def sync( msg, syncdata )
118
118
 
119
119
  # parses the json data sent by the client
120
- syncdata = JSON.parse( syncdata_str )
120
+ # syncdata = JSON.parse( syncdata_str )
121
121
 
122
122
  session_values = msg.session[:values][:by_id]
123
123
  syncdata.each do |value_key, value_data|
@@ -125,7 +125,7 @@ module RSence
125
125
  value_obj = session_values[ value_key ]
126
126
  value_obj.from_client( msg, value_data )
127
127
  else
128
- raise "HValue; unassigned value id! (#{val_id.inspect})"
128
+ warn "HValue; unassigned value key: (#{value_key.inspect})"
129
129
  end
130
130
  end
131
131
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsence
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-04-27 00:00:00.000000000 Z
13
+ date: 2012-04-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rsence-deps
@@ -19,7 +19,7 @@ dependencies:
19
19
  requirements:
20
20
  - - '='
21
21
  - !ruby/object:Gem::Version
22
- version: '968'
22
+ version: '971'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,7 +27,7 @@ dependencies:
27
27
  requirements:
28
28
  - - '='
29
29
  - !ruby/object:Gem::Version
30
- version: '968'
30
+ version: '971'
31
31
  description: ! 'RSence is a different and unique development model and software frameworks
32
32
  designed first-hand for real-time web applications. RSence consists of separate,
33
33
  but tigtly integrated data- and user interface frameworks.
@@ -358,8 +358,7 @@ files:
358
358
  - docs/Values.rdoc
359
359
  - VERSION
360
360
  - .yardopts
361
- - !binary |-
362
- YmluL3JzZW5jZQ==
361
+ - bin/rsence
363
362
  homepage: http://www.rsence.org/
364
363
  licenses:
365
364
  - GPL-3
@@ -381,8 +380,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
381
380
  version: '0'
382
381
  requirements: []
383
382
  rubyforge_project: rsence-
384
- rubygems_version: 1.8.21
383
+ rubygems_version: 1.8.24
385
384
  signing_key:
386
385
  specification_version: 3
387
386
  summary: Release 2.2 version of RSence.
388
387
  test_files: []
388
+ has_rdoc: