rsence 2.0.9.23 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/INSTALL.rdoc +61 -49
  2. data/README.rdoc +20 -4
  3. data/VERSION +1 -1
  4. data/conf/default_conf.yaml +8 -0
  5. data/conf/rsence_command_strings.yaml +31 -20
  6. data/docs/ExampleGuiPlugin.rdoc +2 -2
  7. data/js/comm/comm.js +27 -5
  8. data/js/comm/transporter/transporter.js +1 -1
  9. data/js/comm/values/values.js +12 -5
  10. data/js/controls/button/button.js +12 -2
  11. data/js/controls/dialogs/alert_sheet/alert_sheet.js +13 -1
  12. data/js/controls/dialogs/confirm_sheet/confirm_sheet.js +13 -2
  13. data/js/controls/dialogs/sheet/sheet.js +35 -28
  14. data/js/controls/imageview/imageview.js +13 -13
  15. data/js/controls/progress/progressindicator/progressindicator.js +5 -5
  16. data/js/controls/sliders/slider/slider.js +4 -31
  17. data/js/controls/stepper/stepper.js +12 -19
  18. data/js/controls/textcontrol/textcontrol.js +0 -50
  19. data/js/controls/textcontrol/themes/default/textcontrol.html +1 -1
  20. data/js/controls/window/window.js +1 -1
  21. data/js/core/elem/elem.js +146 -160
  22. data/js/core/rsence_ns/rsence_ns.js +7 -0
  23. data/js/foundation/control/eventresponder/eventresponder.js +8 -7
  24. data/js/foundation/eventmanager/eventmanager.js +81 -48
  25. data/js/foundation/geom/rect/rect.js +1 -1
  26. data/js/foundation/json_renderer/json_renderer.js +4 -1
  27. data/js/foundation/system/system.js +37 -34
  28. data/js/foundation/view/morphanimation/morphanimation.js +53 -43
  29. data/js/foundation/view/view.js +119 -118
  30. data/js/lists/listitems/listitems.js +10 -10
  31. data/js/lists/propertylist/js.inc +0 -0
  32. data/js/lists/propertylist/propertylist.js +574 -0
  33. data/js/lists/propertylist/propertylisteditor/js.inc +0 -0
  34. data/js/lists/propertylist/propertylisteditor/propertylisteditor.js +233 -0
  35. data/js/lists/radiobuttonlist/radiobuttonlist.js +15 -8
  36. data/js/menus/minimenu/js.inc +0 -0
  37. data/js/menus/minimenu/minimenu.js +139 -0
  38. data/js/menus/minimenu/minimenuitem/js.inc +0 -0
  39. data/js/menus/minimenu/minimenuitem/minimenuitem.js +33 -0
  40. data/js/menus/minimenu/minimenuitem/themes/default/minimenuitem.css +45 -0
  41. data/js/menus/minimenu/minimenuitem/themes/default/minimenuitem.html +4 -0
  42. data/js/menus/minimenu/minimenuitem/themes/default/minimenuitem_checkmark.png +0 -0
  43. data/js/menus/minimenu/themes/default/minimenu.css +63 -0
  44. data/js/menus/minimenu/themes/default/minimenu.html +7 -0
  45. data/js/menus/minimenu/themes/default/minimenu.png +0 -0
  46. data/js/util/reloadapp/reloadapp.js +1 -1
  47. data/lib/conf/argv.rb +40 -11
  48. data/lib/daemon/daemon.rb +63 -22
  49. data/lib/plugins/gui_plugin.rb +28 -31
  50. data/lib/plugins/guiparser.rb +37 -7
  51. data/lib/plugins/plugin.rb +260 -28
  52. data/lib/plugins/plugin_base.rb +14 -0
  53. data/lib/plugins/plugin_plugins.rb +11 -1
  54. data/lib/plugins/pluginmanager.rb +127 -44
  55. data/lib/plugins/plugins.rb +10 -1
  56. data/lib/session/msg.rb +25 -1
  57. data/lib/session/sessionmanager.rb +11 -2
  58. data/lib/session/sessionstorage.rb +14 -14
  59. data/lib/transporter/transporter.rb +29 -13
  60. data/lib/values/hvalue.rb +30 -0
  61. data/plugins/client_pkg/info.yaml +2 -2
  62. data/plugins/{index_html → main}/img/loading.gif +0 -0
  63. data/plugins/{index_html → main}/img/riassence.gif +0 -0
  64. data/plugins/main/info.yaml +5 -4
  65. data/plugins/main/main.rb +180 -24
  66. data/plugins/{index_html → main}/tmpl/index.html +4 -2
  67. data/plugins/ticket/info.yaml +2 -2
  68. data/plugins/ticket/lib/upload.rb +57 -5
  69. data/plugins/ticket/ticket.rb +10 -4
  70. data/setup/welcome/info.yaml +2 -2
  71. data/setup/welcome/text/welcome.html +1 -1
  72. metadata +22 -11
  73. data/plugins/index_html/index_html.rb +0 -120
  74. data/plugins/index_html/info.yaml +0 -18
@@ -28,12 +28,10 @@ module RSence
28
28
 
29
29
  # include ::RSence
30
30
 
31
- # Use this method to send the client all commands required to construct the GUI Tree using JSONRenderer.
32
- # @param [Message] msg The +Message+ instance +msg+ used all over the place.
33
- # @param [Hash] params Containing all parameters referred from the YAML file, see: {GUIPlugin__#gui_params GUIPlugin#gui_params}
34
- def init( msg, params )
31
+ def struct( msg, params )
35
32
  gui_data = YAML.load( @yaml_src )
36
33
  parse_gui( gui_data, params )
34
+ return gui_data if gui_data.class == Array
37
35
  if gui_data.has_key?('dependencies')
38
36
  @parent.include_js( msg, gui_data['dependencies'] )
39
37
  gui_data.delete('dependencies')
@@ -44,9 +42,36 @@ module RSence
44
42
  msg.reply( js_src )
45
43
  end
46
44
  end
47
- gui_name = @parent.name
45
+ return gui_data
46
+ end
47
+
48
+ # Use this method to send the client all commands required to construct the GUI Tree using JSONRenderer.
49
+ # @param [Message] msg The +Message+ instance +msg+ used all over the place.
50
+ # @param [Hash] params Containing all parameters referred from the YAML file, see: {GUIPlugin__#gui_params GUIPlugin#gui_params}
51
+ def init( msg, params )
52
+ gui_data = struct( msg, params )
53
+ if gui_data.class == Array
54
+ gui_data = {
55
+ 'type' => 'GUITree',
56
+ 'version' => 0.7,
57
+ 'class' => 'RSence.GUIApp',
58
+ 'options' => {
59
+ 'priority' => 0,
60
+ 'label' => "#{@gui_name.capitalize} (autogenerated)"
61
+ },
62
+ 'subviews' => gui_data
63
+ }
64
+ end
48
65
  json_data = JSON.dump( gui_data )
49
- msg.reply( "JSONRenderer.nu(#{json_data});", true )
66
+ msg.reply( "RSence.guiTrees[#{@gui_name.to_json}]=JSONRenderer.nu(#{json_data});", true )
67
+ end
68
+
69
+ def kill( msg )
70
+ gui_name = @parent.name
71
+ gui_ns = "RSence.guiTrees[#{@gui_name.to_json}]"
72
+ msg.reply( "#{gui_ns}.die();", true )
73
+ msg.reply( "#{gui_ns}=null;", true )
74
+ msg.reply( "delete #{gui_ns};", true )
50
75
  end
51
76
 
52
77
  # Use this method to extract all the value id's of the +ses+ hash.
@@ -113,8 +138,13 @@ module RSence
113
138
  # = Parameters
114
139
  # +parent+:: The Plugin instance called from, use +self+ when constructing in a Plugin method.
115
140
  # +yaml_src+:: The YAML source template for the GUI
116
- def initialize( parent, yaml_src )
141
+ def initialize( parent, yaml_src, gui_name=false )
117
142
  @parent = parent
143
+ if gui_name
144
+ @gui_name = gui_name.to_s
145
+ else
146
+ @gui_name = parent.name.to_s
147
+ end
118
148
  @yaml_src = yaml_src
119
149
  end
120
150
 
@@ -178,20 +178,31 @@ module RSence
178
178
  @inited = true
179
179
  end
180
180
  end
181
-
181
+
182
+ def name_with_manager_s
183
+ if @info[:manager]
184
+ return "#{@info[:manager].to_s}:#{@name.to_s}"
185
+ else
186
+ return @name.to_s
187
+ end
188
+ end
189
+
182
190
  # This method returns (or creates and returns) the entry in the session based on the name your plugin is registered as. It's advised to use this call instead of manually managing {Message#session msg#session} in most cases.
183
191
  #
184
192
  # Uses the first name registered for the plugin and converts it to a symbol.
185
193
  #
186
194
  # @param [Message] msg The message is supplied by the system.
195
+ # @param [Symbol] key (optional) returns a ses key, if defined.
187
196
  #
188
197
  # @return [Hash] Plugin-specific session hash
189
- def get_ses( msg )
190
- name_sym = name.to_sym
198
+ def get_ses( msg, key=false )
199
+ name_sym = name_with_manager_s.to_sym
191
200
  unless msg.session.has_key?( name_sym )
192
201
  msg.session[ name_sym ] = {}
193
202
  end
194
- return msg.session[ name_sym ]
203
+ ses = msg.session[ name_sym ]
204
+ return ses[key] if key
205
+ return ses
195
206
  end
196
207
 
197
208
  # Returns the source code of the javascript file +js_name+ in the 'js' subdirectory of the plugin bundle. Use it to send raw javascript command code to the client. Use {#read_js_once} for libraries.
@@ -278,13 +289,155 @@ module RSence
278
289
  end
279
290
  end
280
291
 
292
+ # @private Returns a hash with valid symbol keys for +#value_call+.
293
+ def sanitize_value_call_hash( hash_dirty )
294
+ if hash_dirty.class == Symbol
295
+ return { :method => hash_dirty }
296
+ elsif hash_dirty.class == String
297
+ if hash_dirty.include?('.')
298
+ last_dot_index = hash_dirty.rindex('.')
299
+ call_plugin = hash_dirty[0..(last_dot_index-1)].to_sym
300
+ call_method = hash_dirty[(last_dot_index+1)..-1].to_sym
301
+ return { :method => call_method, :plugin => call_plugin }
302
+ else
303
+ return { :method => hash_dirty.to_sym }
304
+ end
305
+ end
306
+ hash_clean = {}
307
+ hash_dirty.each do | key, value |
308
+ if key.to_sym == :method
309
+ hash_clean[:method] = value.to_sym
310
+ elsif key.to_sym == :plugin
311
+ hash_clean[:plugin] = value.to_sym
312
+ elsif key.to_sym == :args
313
+ if value.class == Array
314
+ hash_clean[:args] = value
315
+ else
316
+ warn "Invalid arguments (#{value.inspect}). Expected Array."
317
+ end
318
+ elsif key.to_sym == :uses_msg
319
+ value == false if value == nil
320
+ if value.class == TrueClass or value.class == FalseClass
321
+ hash_clean[:uses_msg] = value
322
+ else
323
+ warn "Invalid argument for :uses_msg (#{value.inspect}), expected boolean."
324
+ end
325
+ else
326
+ warn "Undefined value_call key: #{key.inspect}"
327
+ end
328
+ end
329
+ return hash_clean
330
+ end
331
+
332
+ # @private Returns a sanitized copy of a single responder specification.
333
+ def sanitize_value_responders( responders_dirty )
334
+ if responders_dirty.class != Array
335
+ warn "Unsupported responders type: #{responders_dirty.inspect} (expected Array or Hash). Trying work-around.." if responders_dirty.class != Hash and RSence.args[:debug]
336
+ responders_dirty = [ responders_dirty ]
337
+ end
338
+ responders_clean = []
339
+ responders_dirty.each do |responder_dirty|
340
+ if responder_dirty.class != Hash
341
+ if responder_dirty.class == Symbol
342
+ responder_dirty = { :method => responder_dirty }
343
+ elsif responder_dirty.class == String
344
+ if responder_dirty.include?('.')
345
+ last_dot_index = responder_dirty.rindex('.')
346
+ responder_plugin = responder_dirty[0..(last_dot_index-1)].to_sym
347
+ responder_method = responder_dirty[(last_dot_index+1)..-1].to_sym
348
+ responder_dirty = { :method => responder_method, :plugin => responder_plugin }
349
+ else
350
+ responder_dirty = { :method => responder_dirty.to_sym }
351
+ end
352
+ else
353
+ warn "Unsupported responder type: #{responder_dirty.inspect}. Skipping.."
354
+ next
355
+ end
356
+ end
357
+ responder_clean = {}
358
+ responder_dirty.each do |key, value|
359
+ if key.to_sym == :method
360
+ responder_clean[ :method ] = value.to_sym
361
+ elsif key.to_sym == :plugin
362
+ responder_clean[ :plugin ] = value.to_sym
363
+ else
364
+ warn "Unsupported responder key: #{key.inspect} => #{value.inspect}. Ignoring.."
365
+ end
366
+ end
367
+ if responder_clean.has_key?( :method )
368
+ responders_clean.push( responder_clean )
369
+ else
370
+ warn "Responder (#{responder_clean.inspect}) is missing a :method specification. Skipping.."
371
+ end
372
+ end
373
+ return responders_clean
374
+ end
375
+
376
+ # @private Returns a sanitized copy of a single value item.
377
+ def sanitize_value_item( value_item_dirty )
378
+ value_item_clean = {}
379
+ value_item_dirty.each do | key, value |
380
+ if key.to_sym == :value or key.to_sym == :data
381
+ if [Array, Hash, String, TrueClass, FalseClass, Fixnum, Bignum, Float, NilClass].include? value.class
382
+ value_item_clean[:value] = value
383
+ else
384
+ warn "Unsupported value class (#{value.class.inspect}) for value: #{value.inspect}. Using 0 instead."
385
+ value_item_clean[:value] = 0
386
+ end
387
+ elsif key.to_sym == :value_call or key.to_sym == :call
388
+ value_item_clean[:value_call] = sanitize_value_call_hash( value )
389
+ elsif key.to_sym == :msg_call
390
+ value_item_clean[:value_call] = sanitize_value_call_hash( value )
391
+ value_item_clean[:value_call][:uses_msg] = true unless value_item_clean[:value_call].has_key?(:uses_msg)
392
+ elsif key.to_sym == :restore_default or key.to_sym == :restore
393
+ if [TrueClass, FalseClass].include? value.class
394
+ value_item_clean[:restore_default] = value
395
+ else
396
+ warn "Unsupported type of restore (expected true or false): #{value.inspect}. Using true instead."
397
+ value_item_clean[:restore_default] = true
398
+ end
399
+ elsif key.to_sym == :responders or key.to_sym == :responder
400
+ sanitized_responders = sanitize_value_responders( value )
401
+ value_item_clean[:responders] = sanitized_responders unless sanitized_responders.empty?
402
+ else
403
+ warn "Unsupported value specification key: #{key.inspect}."
404
+ end
405
+ end
406
+ return value_item_clean
407
+ end
408
+
409
+ # @private Returns sanitized hash of the structure specified in values.yaml
410
+ def sanitize_values_yaml( values_path )
411
+ values_dirty = yaml_read( values_path )
412
+ return false if values_dirty == false
413
+ if values_dirty.class == Hash
414
+ values_clean = {}
415
+ values_dirty.each do |key, value|
416
+ values_clean[ key.to_sym ] = sanitize_value_item( value )
417
+ end
418
+ return values_clean unless values_clean.empty?
419
+ elsif values_dirty.class == Array
420
+ values_clean = []
421
+ values_dirty.each do |values_dirty_segment|
422
+ values_clean_segment = {}
423
+ values_dirty_segment.each do |key, value|
424
+ values_clean_segment[ key.to_sym ] = sanitize_value_item( value )
425
+ end
426
+ values_clean.push( values_clean_segment )
427
+ end
428
+ return values_clean unless values_clean.empty?
429
+ else
430
+ warn "Unsupported format of values.yaml, got: #{values_dirty.inspect}, expected Hash or Array."
431
+ end
432
+ return false
433
+ end
281
434
 
282
435
  # @private This method looks looks for a file called "values.yaml" in the plugin's bundle directory.
283
436
  # If this file is found, it loads it for initial value definitions.
284
437
  # These definitions are accessible as the +@values+ attribute.
285
438
  def init_values
286
439
  values_path = bundle_path( 'values.yaml' )
287
- return yaml_read( values_path )
440
+ return sanitize_values_yaml( values_path )
288
441
  end
289
442
 
290
443
  # @private Creates a new instance of HValue, assigns it as +value_name+ into the
@@ -333,27 +486,88 @@ module RSence
333
486
  else
334
487
  default_value = 0
335
488
  end
336
- ses[value_name] = HValue.new( msg, default_value, { :name => "#{@name}.#{value_name}" } )
489
+ name = name_with_manager_s
490
+ ses[value_name] = HValue.new( msg, default_value, { :name => "#{name}.#{value_name}" } )
337
491
  if value_properties.has_key?(:responders)
338
- value_properties[:responders].each do |responder|
339
- if responder.has_key?(:plugin)
340
- responder_plugin = responder[:plugin]
341
- else
342
- responder_plugin = @name
492
+ init_responders( msg, ses[value_name], value_properties[:responders] )
493
+ end
494
+ end
495
+
496
+ # @private Initialize a responder for a value.
497
+ def init_responder( msg, value, responder )
498
+ name = name_with_manager_s
499
+ if responder.has_key?(:plugin)
500
+ responder_plugin = responder[:plugin]
501
+ else
502
+ responder_plugin = name
503
+ end
504
+ if responder.has_key?(:method)
505
+ responder_method = responder[:method]
506
+ if not value.bound?( responder_plugin, responder_method )
507
+ value.bind( responder_plugin, responder[:method] )
508
+ end
509
+ end
510
+ end
511
+
512
+ # @private Initialize several responders for a value
513
+ def init_responders( msg, value, responders )
514
+ members = value.members
515
+ release_list = []
516
+ members.each_key do |pre_plugin|
517
+ members[pre_plugin].each do |pre_method|
518
+ found = false
519
+ responders.each do |responder|
520
+ name = name_with_manager_s
521
+ if responder.has_key?(:plugin)
522
+ responder_plugin = responder[:plugin]
523
+ else
524
+ responder_plugin = name
525
+ end
526
+ if responder.has_key?(:method)
527
+ responder_method = responder[:method]
528
+ if responder_plugin == pre_plugin and responder_method == pre_method
529
+ found = true
530
+ break
531
+ end
532
+ end
343
533
  end
344
- if responder.has_key?(:method)
345
- ses[value_name].bind( responder_plugin, responder[:method] )
534
+ unless found
535
+ release_list.push( [ pre_plugin, pre_method ] )
346
536
  end
347
537
  end
348
538
  end
539
+ release_list.each do | rel_plugin, rel_method |
540
+ value.release( rel_plugin, rel_method )
541
+ end
542
+ responders.each do |responder|
543
+ init_responder( msg, value, responder )
544
+ end
545
+ end
546
+
547
+ # @private Releases all responders of a value
548
+ def release_responders( msg, value )
549
+ members = value.members
550
+ members.each_key do |responder_plugin|
551
+ members.each do |responder_method|
552
+ value.release( responder_plugin, responder_method )
553
+ end
554
+ end
349
555
  end
350
556
 
351
557
  # @private Initializes session values, if the contents of the +values.yaml+
352
558
  # file is defined in the bundle directory and loaded in +#init_values+.
353
559
  def init_ses_values( msg )
354
560
  return unless @values
355
- @values.each do | value_name, value_properties |
356
- init_ses_value( msg, value_name, value_properties )
561
+ if @values.class == Array
562
+ @values.each do | value_item |
563
+ value_item.each do | value_name, value_properties |
564
+ init_ses_value( msg, value_name, value_properties )
565
+ end
566
+ end
567
+ elsif @values.class == Hash
568
+ @values.each do | value_name, value_properties |
569
+ init_ses_value( msg, value_name, value_properties )
570
+ end
357
571
  end
358
572
  end
359
573
 
@@ -409,26 +623,44 @@ module RSence
409
623
  end
410
624
  end
411
625
 
626
+ def restore_ses_value( msg, value_name, value_properties )
627
+ ses = get_ses( msg )
628
+ if ses.has_key?( value_name ) and ses[ value_name ].class == HValue
629
+ if value_properties.has_key?(:responders)
630
+ init_responders( msg, ses[value_name], value_properties[:responders] )
631
+ else
632
+ release_responders( msg, ses[value_name] )
633
+ end
634
+ unless value_properties[:restore_default] == false
635
+ if value_properties.has_key?(:value_call)
636
+ default_value = init_value_call( msg, value_properties[:value_call] )
637
+ elsif value_properties.has_key?(:value)
638
+ default_value = value_properties[:value]
639
+ else
640
+ default_value = 0
641
+ end
642
+ ses[value_name].set( msg, default_value )
643
+ end
644
+ else
645
+ init_ses_value( msg, value_name, value_properties )
646
+ end
647
+ end
648
+
412
649
  # @private Restores session values to default, unless specified otherwise.
413
650
  #
414
651
  # Called from +#restore_ses+
415
652
  def restore_ses_values( msg )
416
653
  return unless @values
417
654
  ses = get_ses( msg )
418
- @values.each do | value_name, value_properties |
419
- if ses.has_key?( value_name ) and ses[ value_name ].class == HValue
420
- unless value_properties[:restore_default] == false
421
- if value_properties.has_key?(:value_call)
422
- default_value = init_value_call( msg, value_properties[:value_call] )
423
- elsif value_properties.has_key?(:value)
424
- default_value = value_properties[:value]
425
- else
426
- default_value = 0
427
- end
428
- ses[value_name].set( msg, default_value )
655
+ if @values.class == Array
656
+ @values.each do | value_item |
657
+ value_item.each do | value_name, value_properties |
658
+ restore_ses_value( msg, value_name, value_properties )
429
659
  end
430
- else
431
- init_ses_value( msg, value_name, value_properties )
660
+ end
661
+ elsif @values.class == Hash
662
+ @values.each do | value_name, value_properties |
663
+ restore_ses_value( msg, value_name, value_properties )
432
664
  end
433
665
  end
434
666
  end
@@ -36,6 +36,11 @@ module RSence
36
36
  # * {GUIPlugin__ GUIPlugin} -- The GUIPlugin base class
37
37
  module PluginBase
38
38
 
39
+ # When a method is missing, tries a call via pluginmanager. (Eliminates plugins.plugin_name.method_name calls -> plugin_name.method_name)
40
+ def method_missing( sym, *args, &block )
41
+ @plugins.method_missing( sym, *args, &block )
42
+ end
43
+
39
44
  # @private External accessor for @plugins
40
45
  # @return [PluginManager] The PluginManager the instance belongs to.
41
46
  attr_reader :plugins
@@ -124,6 +129,15 @@ module RSence
124
129
  end
125
130
  alias file_save file_write
126
131
 
132
+ # Yaml writer utility.
133
+ #
134
+ # Wrapper for #file_write
135
+ # Converts +data+ to yaml and then calls file_write with the result.
136
+ def yaml_write( path, data )
137
+ file_write( path, data.to_yaml )
138
+ end
139
+
140
+
127
141
  # Path utility
128
142
  #
129
143
  # Makes a full, absolute path using the plugin bundle as the default path when a relative path is given. Returns just the bundle's local path, if no parameters given.
@@ -22,10 +22,19 @@ module RSence
22
22
  # Install your sub-plugins into a directory named +plugins+ inside your plugin bundle.
23
23
  module PluginPlugins
24
24
 
25
+ # Makes @plugin_plugins accessible
26
+ attr :plugin_plugins
27
+
25
28
  # Extended {#init}, delegates calls to the sub-plugins.
26
29
  def init
27
30
  super
28
- @plugin_plugins = RSence::PluginManager.new( [ bundle_path('plugins') ] )
31
+ @plugin_plugins = RSence::PluginManager.new({
32
+ :plugin_paths => [ bundle_path('plugins') ],
33
+ :autoreload => false,
34
+ :name_prefix => name_with_manager_s.to_sym,
35
+ :parent_manager => @plugins,
36
+ :resolved_deps => [:system]
37
+ })
29
38
  end
30
39
 
31
40
  # Extended {#open}, delegates calls to the sub-plugins.
@@ -38,6 +47,7 @@ module RSence
38
47
  def close
39
48
  super
40
49
  @plugin_plugins.delegate(:close)
50
+ @plugin_plugins.shutdown
41
51
  end
42
52
 
43
53
  # Extended {#flush}, delegates calls to the sub-plugins.