Class: Ruber::AbstractProject

Inherits:
Qt::Object
  • Object
show all
Includes:
SettingsContainer
Defined in:
lib/ruber/project.rb

Overview

Base class for all projects. It must be sublcassed to be used.

It has two main functionalities

  • store configuration options specific to each project (called project options), and read/write them to file, allow access to them and provide a way to configure them

  • store the projects extensions relative to this projects and allow access to them.

Project option management is almost all done by the included SettingsContainer module. The backend may be choose by subclasses, with the only restriction that it should provide a file method returning the name of the file associated with it.

Subclasses must reimplement the :scope method, which should return :global if the project is a global one (that is a project managed by the ProjectList) component or :document if the project is associated with a single document.

A project can be in two states: active and inactive, depending on whether the user chose it as active project or not. Signals are emitted when the state changes. Note: there can be at most one active project at any given time.

Signals

option_changed(QString, QString)

Signal emitted when the value of an option changes. The two parameters are the group and the name of the option, converted to strings. You’ll have to convert them back to symbols if you want to use them to access the option’s value

closing(QObject*)

Signal emitted when the project is about to close The argument is the project itself. This is mostly used by project extensions which need to do some cleanup. They shouldn’t use it to store settings or similar, as there’s the save_settings method for that.

saving()

Signal emitted just before the project is saved to file. This can be used by extensions to write their options to the project.

Direct Known Subclasses

DocumentProject, Project

Defined Under Namespace

Classes: InvalidProjectFile

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from SettingsContainer

#[], #add_setting, #add_widget, #default, #dialog, #has_setting?, #relative_path?, #remove_setting, #remove_widget

Constructor Details

#initialize(parent, backend, name = nil) ⇒ AbstractProject

Creates a new Project. parent is the projects parent object (derived from Qt::Object); file is the name of the project file, which may already exist or not, while name is the name of the project. name can only be specified if the project file doesn’t exist, otherwise ArgumentError will be raised.

The project file, if existing, must follow the format described in the documentation for YamlSettingsBackend and must contain a :project_name entry under the :general group, otherwise it will be considered invalid. In this case, InvalidProjectFile will be raised.

The new project asks each component to register itself with it, so that project options, project widgets (widgets to be shown in the project’s configuration dialog) and project extensions are added. It also connects to the component_loaded and unloading_component signals of the component manager. The first allow each newly loaded plugin to register itself with the project, while the second allows any unloading plugin to unregister itself.

When the project is created, it’s not active.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ruber/project.rb', line 112

def initialize parent, backend, name = nil
  super(parent)
  @active = false
  @project_file = backend.file
  setup_container backend, project_dir
  @dialog_class = ProjectDialog
  self.dialog_title = 'Configure Project'
  add_option OpenStruct.new(:group => :general, :name => :project_name, :default => nil)
  @project_name = self[:general, :project_name]
  if @project_name and name
    raise ArgumentError, "You can't specify a file name for an already existing project"
  elsif name 
    self[:general, :project_name] = name
    @project_name = name
  elsif !@project_name and File.exist? @project_file
    raise InvalidProjectFile, "The project file #{@project_file} isn't invalid because it doesn't contain a project name entry" 
  elsif !name and !File.exist? @project_file
    raise InvalidProjectFile, "You need to specify a project name for a new project"
  end
  @project_extensions = {}
  Ruber[:components].named_connect(SIGNAL('component_loaded(QObject*)'), "register_component_with_project #{object_id}"){|c| c.register_with_project self}
  Ruber[:components].named_connect(SIGNAL('unloading_component(QObject*)'), "remove_component_from_project #{object_id}"){|c| c.remove_from_project self}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &blk) ⇒ Object

Returns the project extension with name name. If a project extension with that name doesn’t exist, or if args is not empty, ArgumentError is raised.



256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/ruber/project.rb', line 256

def method_missing name, *args, &blk
  begin super
  rescue NoMethodError, NameError, TypeError, ArgumentError => e
    if e.is_a? ArgumentError
      puts e.message
      puts e.backtrace.join("\n")
      puts "Method name: #{name}"
      puts "Arguments: #{args.empty? ? '[]' : args.join( ', ')}"
    end
    raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
    @project_extensions[name] || super
  end
end

Instance Attribute Details

#project_fileObject (readonly)

The absolute path of the project file



90
91
92
# File 'lib/ruber/project.rb', line 90

def project_file
  @project_file
end

#project_nameObject (readonly)

A string containing the name of the project



85
86
87
# File 'lib/ruber/project.rb', line 85

def project_name
  @project_name
end

Instance Method Details

#[]=(group, name, value) ⇒ Object

Override of SettingsContainer#[]= which after changing the value of the option, emits the option_changed(QString, QString) message if the value of the option changed (according to eql?).



188
189
190
191
192
# File 'lib/ruber/project.rb', line 188

def []= group, name, value
  old = @options[[group, name]]
  super
  emit option_changed group.to_s, name.to_s unless old.eql? value
end

#add_extension(name, ext) ⇒ Object

Adds the project extension ext to the project, under the name name. If an extension is already stored under that name, ArgumentError is raised.



166
167
168
169
170
171
# File 'lib/ruber/project.rb', line 166

def add_extension name, ext
  if @project_extensions[name]
    raise ArgumentError, "An extension called '#{name}' already exists"
  end
  @project_extensions[name] = ext
end

#close(save = true) ⇒ Boolean

Closes the project

According to the save parameter, the project may save itself and its extensions' settings or not. In the first case, extensions may stop the project from closing by having their @query_close@ method return false. If save is false, nothing will be saved and the closing can’t be interrupted.

Before closing the project, the #closing signal is emitted. After that, all extensions will be removed (calling their @remove_from_project@ method if they have one).

Parameters:

  • save (Boolean) (defaults to: true)

    whether or not to save the project and the extensions' settings. If true, the extensions will also have a chance to abort closing by returning false from their @query_close@ method

Returns:

  • (Boolean)

    true if the project was closed correctly and false if the project couldn't be closed, either because some of the extensions' @query_close@ method returned false or because the project itself couldn't be saved for some reason.



310
311
312
313
314
315
316
317
318
319
320
# File 'lib/ruber/project.rb', line 310

def close save = true
  if save
    return false unless query_close
    return false unless self.save
  end
  emit closing(self)
  @project_extensions.each_key{|k| remove_extension k}
  Ruber[:components].named_disconnect "remove_component_from_project #{object_id}"
  Ruber[:components].named_disconnect "register_component_with_project #{object_id}"
  true
end

#each_extensionObject

If called with a block, calls it for each extension passing the extension name and the extension object itself as argument. If called without a block, returns an Enumerator whose each method works as explained above



207
208
209
210
211
212
# File 'lib/ruber/project.rb', line 207

def each_extension
  if block_given?
    @project_extensions.each_pair{|name, ext| yield name, ext}
  else self.to_enum(:each_extension)
  end
end

#extension(name) ⇒ Object Also known as: project_extension

Returns the project extension with name name.



197
198
199
# File 'lib/ruber/project.rb', line 197

def extension name
  @project_extensions[name]
end

#extensionsObject Also known as: project_extensions

Returns a hash having the extension names as keys and the extension objects as values.

Note: modifiying the hash doesn’t change the internal list of extensions



220
221
222
# File 'lib/ruber/project.rb', line 220

def extensions
  @project_extensions.dup
end

#filesObject

Returns an array containing the name of the files belonging to the project.

This method should be reimplemented in derived classes to return the actual list of files. The base class’s version always returns an empty array.



248
249
250
# File 'lib/ruber/project.rb', line 248

def files
  []
end

#finalizenil

Note:

This method has nothing to do with finalizers

Registers each component with the project

This is done in #initialize because, at least for DocumentProject, the extensions may try to access the project (directly or not) before it has fully been created.

This method should only be called from the object calling new

Returns:

  • (nil)


338
339
340
341
# File 'lib/ruber/project.rb', line 338

def finalize
  Ruber[:components].each_component{|c| c.register_with_project self}
  nil
end

#has_extension?(name) ⇒ Boolean

Returns true if the project contains an extension corresponding to the name :name and false otherwise

Returns:

  • (Boolean)


229
230
231
# File 'lib/ruber/project.rb', line 229

def has_extension? name
  @project_extensions.has_key? name
end

#match_rule?(obj) ⇒ Boolean

Tells whether the project matches the rule specified in the object obj. obj is an object with at least the following methods:

  • scope

  • mimetype

  • file_extension

This implementation returns true if obj.scope includes the value returned by self.scope (using this method requires subclassing AbstractProject, since AbstractProject#scope raises an exception). Subclasses may override this method to introduce other conditions. However, they’ll most likely always want to call the base class implementation.

Returns:

  • (Boolean)


158
159
160
# File 'lib/ruber/project.rb', line 158

def match_rule? obj
  obj.scope.include? self.scope
end

#project_directoryObject Also known as: project_dir

Returns the absolute path of project directory, that is the directory where the project file lies.



237
238
239
# File 'lib/ruber/project.rb', line 237

def project_directory
  File.dirname(@project_file)
end

#query_closeObject



322
323
324
325
# File 'lib/ruber/project.rb', line 322

def query_close
  @project_extensions.each_value{|v| return false unless v.query_close}
  true
end

#remove_extension(name) ⇒ Object

Removes the project extension with name name. If an extension with that name doesn’t exist, nothing is done.



177
178
179
180
181
# File 'lib/ruber/project.rb', line 177

def remove_extension name
  ext = @project_extensions[name]
  ext.remove_from_project if ext.respond_to? :remove_from_project
  @project_extensions.delete name
end

#saveObject



271
272
273
274
275
276
277
278
279
280
# File 'lib/ruber/project.rb', line 271

def save
  emit saving
  @project_extensions.each_value{|v| v.save_settings}
  begin 
    write
    true
  rescue Exception
    false
  end
end

#scopeObject

Returns the scope of the project (currently it must be either :global or document).

This method must be overridden in derived classes, as it only raises NoMethodError

Raises:

  • (NoMethodError)


141
142
143
# File 'lib/ruber/project.rb', line 141

def scope
  raise NoMethodError, "Undefined method `scope' for #{self}:#{self.class}"
end

#writeObject

Override of SettingsContainer#write which emits the settings_changed signal after writing the settings to file



286
287
288
289
# File 'lib/ruber/project.rb', line 286

def write
  super
  emit settings_changed
end