Class: Ruber::Application

Inherits:
KDE::Application show all
Includes:
PluginLike
Defined in:
lib/ruber/application/application.rb

Constant Summary

DEFAULT_PLUGIN_PATHS =

The default paths where to look for plugins.

It includes $KDEHOME/share/apps/ruber/plugins and the plugins subdirectory of the ruber installation directory

[0], File.join('ruber','plugins')),
  RUBER_PLUGIN_DIR
]
DEFAULT_PLUGINS =

The default plugins to load

Currently, they are: ruby_development, find_in_files, syntax_checker, command and state

[
  File.join(KDE::Global.dirs.find_dirs( 'data', '')[0], File.join('ruber','plugins')),
  RUBER_PLUGIN_DIR
]
=begin rdoc
The default plugins to load

Currently, they are: ruby_development, find_in_files, syntax_checker, command and state
=end
DEFAULT_PLUGINS = %w[ruby_development find_in_files rake command syntax_checker
state auto_end project_browser ruby_syntax_checker yaml_syntax_checker]

Instance Attribute Summary (collapse)

Attributes included from PluginLike

plugin_description

Instance Method Summary (collapse)

Methods included from PluginLike

#add_extensions_to_project, #add_options_to_project, #add_widgets_to_project, #create_tool_widget, #delayed_initialize, #initialize_plugin, #plugin_name, #query_close, #register_options, #register_with_project, #remove_extensions_from_project, #remove_from_project, #remove_options_from_project, #remove_widgets_from_project, #restore_session, #save_settings, #session_data, #setup_action, #setup_actions, #shutdown, #unload, #update_project

Methods inherited from KDE::Application

with_override_cursor, #with_override_cursor

Constructor Details

- (Application) initialize(manager, psf)

Creates a new instance of Ruber::Application

It loads the core components and sets up a single shot timer which calls #setup and is fired as soon as the event loop starts.

Parameters:



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ruber/application/application.rb', line 106

def initialize manager, psf
  super()
  @components = manager
  @components.parent = self
  Ruber.instance_variable_set :@components, @components
  initialize_plugin psf
  KDE::Global.dirs.addPrefix File.expand_path(File.join( RUBER_DATA_DIR, '..', 'data'))
  icon_path = KDE::Global.dirs.find_resource('icon', 'ruber')
  self.window_icon = Qt::Icon.new icon_path
  KDE::Global.main_component.about_data.program_icon_name = icon_path
  @cmd_line_options = KDE::CmdLineArgs.parsed_args
  @plugin_dirs = []
  load_core_components
  @status = :starting
  @chdir_lock = Mutex.new
  Qt::Timer.single_shot(0, self, SLOT(:setup))
end

Instance Attribute Details

- (Hash) cmd_line_options (readonly)

The command line options passed to ruber (after they’ve been processed)

Returns:

  • (Hash)

    the command line options passed to ruber (after they’ve been processed)



74
75
76
# File 'lib/ruber/application/application.rb', line 74

def cmd_line_options
  @cmd_line_options
end

- (Symbol) status (readonly)

=== The state Ruber is in

Ruber can be in three states:

starting
from the time it’s launched to when #setup returns
running
from after #setup has returend to when the user chooses to quit it (either with the File/Quit menu entry or clicking on the button on the title bar)
quitting
from when the user chooses to quit Ruber onwards
asking_to_quit
while asking the user to confirm quitting Ruber

Returns:

  • (Symbol)

    the status of the application. It can be: :starting, :running or :quitting



94
95
96
# File 'lib/ruber/application/application.rb', line 94

def status
  @status
end

Instance Method Details

- (Boolean) ask_to_quit

Asks the user to confirm quitting Ruber

This method is called whenever Ruber needs to be closed and, in turn, calls the query_close method of each plugin and component (using ComponentManager#query_close).

During the execution of this method, #status returns :asking_to_quit. After this method returns, the status returns what it was before.

Returns:

  • (Boolean)

    true if the application can be closed and false otherwise



156
157
158
159
160
161
162
# File 'lib/ruber/application/application.rb', line 156

def ask_to_quit
  old_status = @status
  @status = :asking_to_quit
  res = @components.query_close
  @status = old_status
  res
end

- (Object) chdir(dir) { ... }

Thread-safe Dir.chdir

Dir.chdir is not thread-safe, meaning that a Dir.chdir done in a thread will change the directory in all other threads. To avoid this unpleasant behaviour, don’t use Dir.chdir. Instead, use this method, which uses a mutex to control calls to Dir.chdir.

Parameters:

  • dir (String)

    the directory to change to

Yields:

  • the block to execute from within dir. The current directory will be changed back after executing the block.

Returns:

  • (Object)

    the value returned by the block otherwise

Raises:

  • (LocalJumpError)

    if no block is given



268
269
270
271
272
# File 'lib/ruber/application/application.rb', line 268

def chdir dir
  @chdir_lock.synchronize do
    Dir.chdir(dir){yield}
  end
end

- (nil) load_core_components (private)

Loads the core components

In loading order, the core components are:

  • the configuration manager
  • the document list
  • the project list
  • the main window

In case loading one of the core components raises an exception, the user is warned with a dialog and ruber is closed.

After creating the configuration manager, #register_with_config is called.

If a previous session is being restored, MainWindow#restore is called, otherwise an empty document is created (unless the user specified some files or project on the command line)

Returns:

  • (nil)


367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/ruber/application/application.rb', line 367

def load_core_components
  begin
    current = 'config'
    @components.load_component 'config'
    register_with_config
    %w[world main_window].each do |i|
      current = i
      @components.load_component i
    end
  rescue Exception => e
    ComponentLoadingErrorDialog.new(current, e, nil, true).exec
    Qt::Internal.application_terminated = true
    exit 1
  end
  if sessionRestored? then Ruber[:main_window].restore 1, false
  else open_command_line_files
  end
end

- (nil) load_settings (private)

Override of PluginLike#load_settings

It reads the list of plugin directories from the configuration object, replacing all mentions of the installation paths for a different version of Ruber with the installation path for the current version, and adds to the load path all missing directories

Returns:

  • (nil)


266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/ruber/application/application.rb', line 266

def load_settings
  @plugin_dirs = Ruber[:config][:general].plugin_dirs
  ruber_base_dir = File.dirname(RUBER_DIR)
  @plugin_dirs.map! do |d| 
    if d =~ /#{File.join Regexp.quote(ruber_base_dir), 'ruber-\d+\.\d+\.\d+'}/
      RUBER_PLUGIN_DIR
    else d
    end
  end
  new_dirs = @plugin_dirs - $:
  new_dirs.each{|d| $:.unshift d}
  nil
end

Slot Signature:

load_settings()

- (Object) open_command_line_files (private)

Opens the files and/or project listed on the command line

If neither files nor projects have been specified on the command line, a single empty document is created. @return [nil]


412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/ruber/application/application.rb', line 412

def open_command_line_files
  urls = @cmd_line_options.urls
  win = Ruber[:main_window]
  projects, files = urls.partition{|u| File.extname(u.to_local_file || '') == '.ruprj'}
  prj = win.safe_open_project projects.last.to_local_file unless projects.empty?
  files += @cmd_line_options.getOptionList('file').map do |f|
    url = KDE::Url.new f
    url.path = File.expand_path(f) if url.protocol.empty?
    url
  end
  files.each do |f| 
    win.display_document f
  end
  nil
end

- (Array<String>) plugin_directories Also known as: plugin_dirs

A list of the directories where Ruber looks for plugins

Returns:

  • (Array<String>)

    a list of the directories where Ruber looks for plugins



126
127
128
# File 'lib/ruber/application/application.rb', line 126

def plugin_directories
  @plugin_dirs.dup
end

- (Object) plugin_directories=(dirs) Also known as: plugin_dirs=

Sets the list of directories where Ruber looks for plugins

This also changes the setting in the global configuration object, but it doesn’t write the change to file. It’s up to whoever called this method to do so.

Parameters:

  • the (Array<String> dirs)

    directories Ruber should search for plugins



139
140
141
142
# File 'lib/ruber/application/application.rb', line 139

def plugin_directories= dirs
  @plugin_directories = dirs
  Ruber[:config][:general, :plugin_dirs] = @plugin_directories
end

- (nil) quit_ruber

Quits ruber

Sets the application status to :quitting and calls ComponentManager#shutdown

Returns:

  • (nil)


170
171
172
173
174
# File 'lib/ruber/application/application.rb', line 170

def quit_ruber
  @status = :quitting
  @components.shutdown
  nil
end

- (Boolean) quitting?

Whether the application is quitting or not

otherwise

Returns:

  • (Boolean)

    true if the application status is quitting and false

See Also:



209
210
211
# File 'lib/ruber/application/application.rb', line 209

def quitting?
  @status == :quitting
end

- (nil) register_with_config (private)

Registers the configuration options with the configuration manager

This means, calling #load_settings and connecting to the configuration manager’s settings_changedsignal.

This tasks are usually performed by PluginLike#initialize_plugin, but as the config manager didn’t exist when that method was called, it is necessary to to them later

Returns:

  • (nil)


397
398
399
400
401
402
403
404
# File 'lib/ruber/application/application.rb', line 397

def register_with_config
  config = Ruber[:config]
  @plugin_description.config_options.each_value{|o| config.add_option o}
  @plugin_description.config_widgets.each{|w| config.add_widget w}
  load_settings
  connect config, SIGNAL(:settings_changed), self, SLOT(:load_settings)
  nil
end

- (Boolean) running?

Whether the application is running or not

otherwise

Returns:

  • (Boolean)

    true if the application status is running and false

See Also:



198
199
200
# File 'lib/ruber/application/application.rb', line 198

def running?
  @status == :running
end

- (Boolen) safe_load_plugins(plugins, silent = false, dirs = nil) {|pso, ex| ... }

Loads plugins handling the exceptions they may raise in the meanwhile

It is a wrapper around ComponentManager#load_plugins.

If it’s given a block, it simply calls ComponentManager#load_plugins passing it the block.

If no block is given, the behaviour in case of an exception depends on the silent argument:

  • if true, then the error will be silently ignored and the component manager will go on loading the remaining plugins. Errors caused by those will be ignored as well
  • if false, the user will be shown a ComponentLoadingErrorDialog. According to what the user chooses in the dialog, the component manager will behave in a different way, as described in ComponentManager#load_plugins

Note: this method doesn’t attempt to handle exceptions raised while computing or sorting dependencies.

Parameters:

  • plugins (Array<Symbol>)

    the names of the plugins to load. It doesn’t need to include dependencies, as they’re computed automatically

  • silent (Boolean) (defaults to: false)

    whether errors while loading plugins should be silently ignored or not

  • dirs (Array<String>, nil) (defaults to: nil)

    the directories where to look for plugins. If nil, then the value returned by #plugin_directories will be used

Yields:

  • (pso, ex)

    block called when loading a plugin raises an exception

Yield Parameters:

  • pso (PluginSpecification)

    the plugin specification object associated with the plugin which raised the exception

  • ex (Exception)

    the exception raised while loading the plugin

Returns:

  • (Boolen)

    true if the plugins were loaded successfully and false otherwise

See Also:



246
247
248
249
250
251
252
253
# File 'lib/ruber/application/application.rb', line 246

def safe_load_plugins plugins, silent = false, dirs = nil, &blk
  if blk.nil? and silent then blk = proc{|_pl, _e| :silent}
  elsif blk.nil?
    blk = Proc.new{|pl, e| ComponentLoadingErrorDialog.new(pl.name, e, nil).exec}
  end
  @components.load_plugins plugins, dirs || @plugin_dirs, &blk
  
end

- (nil) setup (private)

Prepares the application for running

It loads the plugins chosen by the user according to the configuration file. If an error occurs while finding plugin dependencies (either because of a missing dependency or a circular dependency), the user is shown a dialog asking what to do (quit ruber or load no plugin). If an exception is raised while loading a plugin, it’s handled according to the behaviour specified in #safe_load_plugins with no block given.

It also takes care of the command line options, opening the files and projects specified in it.

At the end, the main window is shown

Returns:

  • (nil)


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/ruber/application/application.rb', line 296

def setup
  # Create $KDEHOME/share/apps/ruber/plugins if it's missing
  FileUtils.mkdir_p DEFAULT_PLUGIN_PATHS[0]
  chosen_plugins = Ruber[:config][:general, :plugins].map(&:to_sym)
  needed_plugins = []
  deps_problem_msg = Proc.new do |e|
    <<-EOS
    The following errors have occurred while attempting to resolve the dependencies among plugins you chose:
    #{e.message}
    
    Ruber will start with no plugin loaded. Please, use the Choose Plugins menu entry in the Settings menu to solve the issue.
EOS
  end
  begin 
    availlable_plugins = ComponentManager.find_plugins @plugin_dirs, true
    chosen_data = chosen_plugins.map{|i| availlable_plugins[i]}.compact
    found = chosen_data.map{|i| i.name}
    if found.size != chosen_plugins.size
      missing = chosen_plugins - found
      question = <<-EOS
Ruber couldn't find some plugins it has been configured to automatically load at startup. They are:
#{missing.join("\n")}
Do you want to start the application without them or to quit Ruber?
EOS
      ans = KDE::MessageBox.question_yes_no nil, question, 'Missing plugins',
          KDE::GuiItem.new('Start Ruber'), KDE::GuiItem.new('Quit')
      exit if ans == KDE::MessageBox::No
      chosen_plugins = found
    end
    needed_plugins = ComponentManager.fill_dependencies chosen_data, availlable_plugins.values
  rescue ComponentManager::DependencyError => e
    KDE::MessageBox.sorry nil, deps_problem_msg.call(e)
  end
  plugins = chosen_plugins + needed_plugins
  begin 
    res = safe_load_plugins(plugins) 
    unless res
      Qt::Internal.application_terminated = true
      exit 1
    end
  rescue ComponentManager::DependencyError => e
    KDE::MessageBox.error nil, deps_problem_msg.call(e)
  end
  if sessionRestored?
    Ruber[:components].restore_session Ruber[:main_window].last_session_data
  end
  @status = :running
  Ruber[:main_window].show
  nil
end

Slot Signature:

setup()

- (Boolean) starting?

Whether the application is starting or has already started

You should seldom need this method. It’s mostly useful for plugins which need to erform different actions depending on whether they’re loaded at application startup (in which case it’ll return true) or later (when it’ll return false)

Returns:

  • (Boolean)

    true if the application status is starting and false otherwise

See Also:



187
188
189
# File 'lib/ruber/application/application.rb', line 187

def starting?
  @status == :starting
end

- (Object) within_dir(dir) { ... }

Synchronized directory changed

Dir.chdir is not thread-safe, meaning that a Dir.Chdir done in a thread will change the directory in all other threads. To avoid this unpleasant behaviour, don’t use Dir.chdir. Instead, use this method, which uses a mutex to control calls to Dir.chdir.

Parameters:

  • dir (String)

    the directory to change to

Yields:

  • the block to execute from within dir. If given the current directory will be changed back after executing the block. If not given, the current directory will remain dir after this method returns

Returns:

  • (Object)

    0 if no block was given; the value returned by the block otherwise



268
269
270
271
272
# File 'lib/ruber/application/application.rb', line 268

def within_dir dir, &blk
  @chdir_lock.synchronize do
    Dir.chdir dir, &blk
  end
end