rugui 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/LICENSE +165 -0
  2. data/README +67 -0
  3. data/README.rdoc +67 -0
  4. data/Rakefile +56 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/rugui +16 -0
  7. data/lib/rugui/base_controller.rb +194 -0
  8. data/lib/rugui/base_model.rb +22 -0
  9. data/lib/rugui/base_object.rb +73 -0
  10. data/lib/rugui/base_view.rb +302 -0
  11. data/lib/rugui/base_view_helper.rb +23 -0
  12. data/lib/rugui/configuration.rb +136 -0
  13. data/lib/rugui/framework_adapters/GTK.rb +233 -0
  14. data/lib/rugui/framework_adapters/Qt4.rb +171 -0
  15. data/lib/rugui/framework_adapters/base_framework_adapter.rb +90 -0
  16. data/lib/rugui/framework_adapters/framework_adapter_support.rb +35 -0
  17. data/lib/rugui/gem_builder.rb +21 -0
  18. data/lib/rugui/gem_dependency.rb +282 -0
  19. data/lib/rugui/initialize_hooks.rb +36 -0
  20. data/lib/rugui/initializer.rb +162 -0
  21. data/lib/rugui/log_support.rb +118 -0
  22. data/lib/rugui/observable_property_proxy.rb +73 -0
  23. data/lib/rugui/observable_property_support.rb +251 -0
  24. data/lib/rugui/plugin/loader.rb +77 -0
  25. data/lib/rugui/property_observer.rb +58 -0
  26. data/lib/rugui/signal_support.rb +57 -0
  27. data/lib/rugui/tasks/gems_application.rake +71 -0
  28. data/lib/rugui/tasks/rugui.rb +8 -0
  29. data/lib/rugui/tasks/rugui_framework.rb +4 -0
  30. data/lib/rugui/tasks/runner_application.rake +4 -0
  31. data/lib/rugui/tasks/spec_application.rake +64 -0
  32. data/lib/rugui/tasks/spec_framework.rake +27 -0
  33. data/lib/rugui/tasks/test_application.rake +77 -0
  34. data/lib/rugui/vendor_gem_source_index.rb +140 -0
  35. data/lib/rugui/version.rb +9 -0
  36. data/lib/rugui.rb +37 -0
  37. data/spec/framework/base_controller_spec.rb +48 -0
  38. data/spec/framework/base_model_spec.rb +13 -0
  39. data/spec/framework/base_view_helper_spec.rb +13 -0
  40. data/spec/framework/base_view_spec.rb +92 -0
  41. data/spec/framework/log_support_spec.rb +16 -0
  42. data/spec/framework/observable_property_proxy_spec.rb +67 -0
  43. data/spec/framework/observable_property_support_spec.rb +283 -0
  44. data/spec/framework/property_observer_spec.rb +88 -0
  45. data/spec/helpers/controllers.rb +29 -0
  46. data/spec/helpers/initialize_hooks_helper.rb +18 -0
  47. data/spec/helpers/models.rb +9 -0
  48. data/spec/helpers/observables.rb +210 -0
  49. data/spec/helpers/view_helpers.rb +9 -0
  50. data/spec/helpers/views.rb +72 -0
  51. data/spec/rcov.opts +1 -0
  52. data/spec/resource_files/my_other_view.glade +46 -0
  53. data/spec/resource_files/my_view.glade +46 -0
  54. data/spec/spec.opts +4 -0
  55. data/spec/spec_helper.rb +15 -0
  56. metadata +149 -0
@@ -0,0 +1,162 @@
1
+ require 'rugui'
2
+ require 'rugui/gem_dependency'
3
+
4
+ module RuGUI
5
+ class Initializer
6
+ include RuGUI::LogSupport
7
+
8
+ # The configuration for this application.
9
+ attr_reader :configuration
10
+
11
+ # Whether or not all the gem dependencies have been met
12
+ attr_reader :gems_dependencies_loaded
13
+
14
+ @@processed = false
15
+
16
+ # Runs the initializer.
17
+ #
18
+ # It runs the process procedure by default, by this can be changed by
19
+ # specifying a command when calling this method. A block can be given
20
+ # in order to change the application configuration.
21
+ #
22
+ def self.run(command = :process, configuration = Configuration.new)
23
+ yield configuration if block_given?
24
+ initializer = new configuration
25
+ RuGUI.configuration = configuration
26
+ initializer.send(command)
27
+ @@processed = (command == :process) ? true : false
28
+ initializer
29
+ end
30
+
31
+ # Create a new Initializer instance that references the given Configuration
32
+ # instance.
33
+ def initialize(configuration)
34
+ @configuration = configuration
35
+ end
36
+
37
+ # Sequentially step through all of the available initialization routines,
38
+ # in order (view execution order in source).
39
+ def process
40
+ load_environment
41
+ load_logger
42
+
43
+ start_initialization_process_log
44
+
45
+ set_load_path
46
+ add_gem_load_paths
47
+
48
+ set_autoload_paths
49
+ load_framework_adapter
50
+
51
+ load_gems
52
+ load_plugins
53
+
54
+ # pick up any gems that plugins depend on
55
+ add_gem_load_paths
56
+ load_gems
57
+ check_gem_dependencies
58
+
59
+ # bail out if gems are missing - note that check_gem_dependencies will have
60
+ # already called abort() unless $gems_rake_task is set
61
+ return unless gems_dependencies_loaded
62
+
63
+ finish_initialization_process_log
64
+ end
65
+
66
+ # Set the <tt>$LOAD_PATH</tt> based on the value of
67
+ # Configuration#load_paths. Duplicates are removed.
68
+ def set_load_path
69
+ load_paths = configuration.load_paths
70
+ load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
71
+ $LOAD_PATH.uniq!
72
+ end
73
+
74
+ # Set the paths from which RuGUI will automatically load source files.
75
+ def set_autoload_paths
76
+ ActiveSupport::Dependencies.load_paths = configuration.load_paths.uniq
77
+ end
78
+
79
+ # Loads the environment specified by Configuration#environment_path, which
80
+ # is typically one of development, or production.
81
+ def load_environment
82
+ return if @environment_loaded
83
+ @environment_loaded = true
84
+
85
+ if File.exist?(configuration.environment_path)
86
+ config = configuration
87
+ constants = self.class.constants
88
+
89
+ eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
90
+
91
+ (self.class.constants - constants).each do |const|
92
+ Object.const_set(const, self.class.const_get(const))
93
+ end
94
+ end
95
+ end
96
+
97
+ def load_plugins
98
+ plugin_loader.load_plugins
99
+ end
100
+
101
+ def add_gem_load_paths
102
+ RuGUI::GemDependency.add_frozen_gem_path
103
+ unless @configuration.gems.empty?
104
+ require "rubygems"
105
+ @configuration.gems.each { |gem| gem.add_load_paths }
106
+ end
107
+ end
108
+
109
+ def load_gems
110
+ unless $gems_build_rake_task
111
+ @configuration.gems.each { |gem| gem.load }
112
+ end
113
+ end
114
+
115
+ def check_gem_dependencies
116
+ unloaded_gems = @configuration.gems.reject { |g| g.loaded? }
117
+ if unloaded_gems.size > 0
118
+ @gems_dependencies_loaded = false
119
+ # don't print if the gems rake tasks are being run
120
+ unless $gems_rake_task
121
+ abort <<-end_error
122
+ Missing these required gems:
123
+ #{unloaded_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "}
124
+
125
+ You're running:
126
+ ruby #{Gem.ruby_version} at #{Gem.ruby}
127
+ rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '}
128
+
129
+ Run `rake gems:install` to install the missing gems.
130
+ end_error
131
+ end
132
+ else
133
+ @gems_dependencies_loaded = true
134
+ end
135
+ end
136
+
137
+ def load_logger
138
+ RuGUILogger.logger
139
+ end
140
+
141
+ def start_initialization_process_log
142
+ logger.info "Starting RuGUI application with #{configuration.environment} environment..." unless silence_logs?
143
+ end
144
+
145
+ def finish_initialization_process_log
146
+ logger.info "RuGUI application configurations loaded." unless silence_logs?
147
+ end
148
+
149
+ def plugin_loader
150
+ @plugin_loader || RuGUI::Plugin::Loader.new(self, configuration)
151
+ end
152
+
153
+ def load_framework_adapter
154
+ require "rugui/framework_adapters/#{RuGUI.configuration.framework_adapter}"
155
+ end
156
+
157
+ private
158
+ def silence_logs?
159
+ @@processed or $gems_build_rake_task or $gems_rake_task
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,118 @@
1
+ require 'logger'
2
+
3
+ module RuGUI
4
+ #
5
+ # A simple log support for registering problems, infos and debugs.
6
+ #
7
+ module LogSupport
8
+ # Returns the logger object.
9
+ def logger
10
+ @logger ||= RuGUILogger.logger
11
+ @logger.formatter = RuGUI::LogSupport::Formatter.new(self.class.name)
12
+ @logger
13
+ end
14
+
15
+ module ClassMethods
16
+ def logger
17
+ @@logger ||= RuGUILogger.logger
18
+ @@logger.formatter = RuGUI::LogSupport::Formatter.new(self.name)
19
+ @@logger
20
+ end
21
+ end
22
+
23
+ def self.included(base)
24
+ base.extend(ClassMethods)
25
+ end
26
+
27
+ private
28
+ class Formatter
29
+ def initialize(classname = nil)
30
+ @classname = classname
31
+ end
32
+
33
+ def call(severity, timestamp, progname, msg)
34
+ timestamp = timestamp.strftime(RuGUI.configuration.logger[:format] || "%Y-%m-%d %H:%M:%S")
35
+ "#{timestamp} (#{severity}) (#{@classname}) #{msg}\n"
36
+ end
37
+ end
38
+ end
39
+
40
+ class RuGUILogger
41
+ class << self
42
+ def logger
43
+ @@rugui_logger ||= RuGUILogger.new
44
+ @@rugui_logger.logger
45
+ end
46
+ end
47
+
48
+ def logger
49
+ @@logger
50
+ end
51
+
52
+ private
53
+ def initialize
54
+ setup_logger
55
+ end
56
+
57
+ #
58
+ # Setup a new log support object. If a problem occurs a logger is setted up
59
+ # to warn level.
60
+ #
61
+ def setup_logger
62
+ begin
63
+ @@logger = Logger.new(defined_output)
64
+ @@logger.level = defined_level
65
+ rescue StandardError => e
66
+ @@logger = Logger.new(STDERR)
67
+ @@logger.level = LEVELS[:warn]
68
+ @@logger.warn "Log support problems: The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
69
+ @@logger.error "#{e} #{e.backtrace.join("\n")}"
70
+ end
71
+ end
72
+
73
+ #
74
+ # Defines a output based on params informed by user, params setted up in
75
+ # the configuration file, or default values.
76
+ #
77
+ def defined_output
78
+ output = RuGUI.configuration.logger[:output]
79
+ if output.nil? or [:stdout, :stderr].include?(output)
80
+ DEFAULT_OUTPUT
81
+ else
82
+ File.join(RuGUI.root, 'log', output)
83
+ end
84
+ end
85
+
86
+ #
87
+ # Defines a level based on params informed by user, params setted up in
88
+ # the configuration file, or default values.
89
+ #
90
+ def defined_level
91
+ level = RuGUI.configuration.logger[:level]
92
+ unless level
93
+ level = DEFAULT_LEVEL
94
+ else
95
+ level = LEVELS[level]
96
+ end
97
+ level
98
+ end
99
+
100
+ def defined_classname(classname)
101
+ classname || self.class.name
102
+ end
103
+
104
+ LEVELS = {
105
+ :debug => Logger::DEBUG,
106
+ :info => Logger::INFO,
107
+ :warn => Logger::WARN,
108
+ :error => Logger::ERROR,
109
+ :fatal => Logger::FATAL
110
+ }
111
+
112
+ #
113
+ # Default values to the log support object - aka logger
114
+ #
115
+ DEFAULT_OUTPUT = STDOUT
116
+ DEFAULT_LEVEL = LEVELS[:debug]
117
+ end
118
+ end
@@ -0,0 +1,73 @@
1
+ module RuGUI
2
+ # A proxy class for observable properties.
3
+ #
4
+ # When creating an <code>ObservablePropertyProxy</code> you pass an
5
+ # instance of any object to it (which will now be the context for this
6
+ # proxy), the observable and the property name.
7
+ #
8
+ # The <code>ObservablePropertyProxy</code> instance will work as a
9
+ # proxy for each method send to the context. If the context is changed,
10
+ # it will notify any <code>PropertyObservers</code> registered for its
11
+ # <code>observable</code>, by calling their
12
+ # <code>property_changed</code> method.
13
+ #
14
+ # CAUTION: When using observable string properties as keys in a Hash make
15
+ # sure you call the Object#to_s or Object#to_sym methods before putting
16
+ # the property value as key. Hashes uses the method Object#eql? when
17
+ # comparing keys, and for some unknown reason it is always returning false
18
+ # when comparing observable string properties.
19
+ #
20
+ class ObservablePropertyProxy < BaseObject
21
+ include RuGUI::LogSupport
22
+
23
+ def initialize(context, observable, property)
24
+ @context = context
25
+ @observable = observable
26
+ @property = property
27
+ end
28
+
29
+ private
30
+ NON_DELEGATABLE_METHODS = ['__id__', '__send__']
31
+
32
+ # Delegating Object's methods to context. Since none of these methods
33
+ # really change the object we just send them to the context.
34
+ self.methods.each do |method_name|
35
+ unless NON_DELEGATABLE_METHODS.include?(method_name)
36
+ self.class_eval <<-class_eval
37
+ def #{method_name}(*args)
38
+ @context.send(:#{method_name}, *args)
39
+ end
40
+ class_eval
41
+ end
42
+ end
43
+
44
+ # Here we reimplement the method missing, adding code to notify observers
45
+ # when the property has changed, i.e., when the context before calling the
46
+ # method is different than the context after the method is called.
47
+ def method_missing(method, *args, &block)
48
+ old_context = get_context_copy
49
+ return_value = @context.send(method, *args, &block)
50
+
51
+ context_changed(@context, old_context) unless @context == old_context
52
+ return_value
53
+ end
54
+
55
+ # Returns a copy of the context.
56
+ def get_context_copy
57
+ begin
58
+ return @context.clone
59
+ rescue TypeError
60
+ return @context
61
+ end
62
+ end
63
+
64
+ #
65
+ # Called when the context has changed.
66
+ #
67
+ # Notifies all registered observers
68
+ #
69
+ def context_changed(new_value, old_value)
70
+ @observable.property_changed(@property, new_value, old_value)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,251 @@
1
+ require 'rugui/observable_property_proxy'
2
+
3
+ module RuGUI
4
+ # Adds support to observable properties.
5
+ module ObservablePropertySupport
6
+ # Initializes the observable properties. If you override this method, make
7
+ # sure that you call the <code>initialize_observable_property_support</code>
8
+ # method, so that the observable properties are initialized.
9
+ def initialize(observable_properties_values = {})
10
+ initialize_observable_property_support(observable_properties_values)
11
+ end
12
+
13
+ # Initializes observable properties, setting their initial value.
14
+ def initialize_observable_property_support(observable_properties_values = {})
15
+ self.class.observable_properties_options.each do |property, options|
16
+ value = (observable_properties_values.with_indifferent_access[property] || clone_if_possible(options[:initial_value]))
17
+ send("#{property}=", value)
18
+ end
19
+ end
20
+
21
+ # Registers an observer for this model.
22
+ #
23
+ # The observer must implement a method with this signature:
24
+ #
25
+ # property_updated(observable, property, new_value, old_value)
26
+ #
27
+ # This method is called whenever a property has changed its value. One
28
+ # option is to include the PropertyObserver module in the observer class.
29
+ #
30
+ # Optionally, if <code>observable_name</code> can be given, a method with
31
+ # this signature will also be called:
32
+ #
33
+ # named_observable_property_updated(observable_name, observable, property, new_value, old_value)
34
+ #
35
+ def register_observer(observer, observable_name = nil)
36
+ initialize_observers_if_needed
37
+ @observers << observer
38
+ @named_observers[observable_name] = observer unless observable_name.nil?
39
+ end
40
+
41
+ # Called whenver the a property has changed.
42
+ def property_changed(property, new_value, old_value)
43
+ initialize_observers_if_needed
44
+ @observers.each do |observer|
45
+ observer.property_updated(self, property, new_value, old_value) if observer.respond_to?(:property_updated)
46
+ end
47
+ @named_observers.each do |observable_name, observer|
48
+ observer.named_observable_property_updated(observable_name, self, property, new_value, old_value) if observer.respond_to?(:named_observable_property_updated)
49
+ end
50
+ end
51
+
52
+ # Resets all observable properties for this observer.
53
+ #
54
+ # Since an observable property may be another observable there may exist
55
+ # some observers observing this other observable. In this scenario one
56
+ # should not attempt to set a new object into the observable property,
57
+ # because the observers would still be looking for the old observable.
58
+ #
59
+ # By calling <code>reset!</code> all observable properties are reset to the
60
+ # values specified when creating it. Also if the property respond to reset
61
+ # the method will be called, unless a *reset_value* is configured, i.e., it
62
+ # is not <code>nil</code>. Also, if *prevent_reset* is true, that property
63
+ # will not be reseted, even if it has a *reset_value* configured.
64
+ def reset!
65
+ self.class.observable_properties_options.each do |property, options|
66
+ unless options[:prevent_reset]
67
+ property_value = send(property)
68
+ if options[:reset_value].nil? and property_value.respond_to?(:reset!)
69
+ property_value.reset!
70
+ else
71
+ send("#{property}=", clone_if_possible(options[:reset_value]))
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ # Returns <code>true</code> if <code>obj</code> is equals to
78
+ # <code>self</code>.
79
+ #
80
+ # This method checks if <code>obj</code> is of the same type of
81
+ # <code>self</code> and if all *core* observable_properties are equals.
82
+ def ==(obj)
83
+ if obj.is_a?(self.class)
84
+ self.class.core_observable_properties.each do |property|
85
+ return false unless obj.respond_to?(property) and respond_to?(property)
86
+ return false unless send(property) == obj.send(property)
87
+ end
88
+ return true
89
+ end
90
+ end
91
+
92
+ # Copies all observable properties from _other_observable_ to _self_
93
+ def copy_observable_properties_from(other_observable, deep = true)
94
+ self.class.observable_properties.each do |property|
95
+ if other_observable.respond_to?(property)
96
+ other_property_value = other_observable.send(property)
97
+ if other_property_value.class.include?(ObservablePropertySupport)
98
+ send(property).copy_observable_properties_from(other_property_value) if deep
99
+ else
100
+ send("#{property}=", other_property_value)
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ # Returns a map of all observable properties with theirs values.
107
+ def observable_properties
108
+ self.class.observable_properties.inject({}) { |properties, property| properties.merge!({ property => send(property) }) }
109
+ end
110
+
111
+ # Update observable properties values given a map of values
112
+ def update_observable_properties(values = {})
113
+ values.each { |property, value| send("#{property}=", value) if self.respond_to?("#{property}=") }
114
+ end
115
+
116
+ module ClassMethods
117
+ # Creates the necessary class inheritable attributes an initializes them.
118
+ def create_class_inheritable_attributes
119
+ self.class_inheritable_accessor :observable_properties_options
120
+
121
+ self.observable_properties_options = {}
122
+ end
123
+
124
+ # Register a observable properties for this model.
125
+ #
126
+ # Properties may be given as symbols, or strings. You can pass some
127
+ # options, in a hash, which will be used when the observable is created:
128
+ #
129
+ # - *initial_value*: The initial value for the property. This value will
130
+ # be set when the observable instance is initialized (i.e., when the
131
+ # <code>initialize</code> method is called). Defaults to <code>nil</code>.
132
+ # - *reset_value*: The reset value for the property. This value will be
133
+ # set when the observable instance is reset (i.e., when the
134
+ # <code>reset!</code> method is called). If this is not given, the
135
+ # <code>initial_value</code> will be used instead.
136
+ # - *core*: Defines whether the property should be used when comparing two
137
+ # observables. Defaults to <code>false</code>.
138
+ # - *prevent_reset*: If this is <code>true</code> the property will not be
139
+ # reseted. Defaults to false.
140
+ # - *boolean*: If this is <code>true</code> a "question" method will be
141
+ # created for the property (i.e., for a property named <code>foo</code>
142
+ # a method named <code>foo?</code> will be created).
143
+ #
144
+ # Examples:
145
+ #
146
+ # class MyObservable
147
+ # include RuGUI::ObservablePropertySupport
148
+ #
149
+ # observable_property :foo, :initial_value => "bar"
150
+ # observable_property :bar, :initial_value => "foo", :reset_value => "bar"
151
+ # observable_property :core_property, :core => true
152
+ # observable_property :non_resetable_property, :prevent_reset => true
153
+ #
154
+ # # And so on...
155
+ # end
156
+ def observable_property(property, options = {})
157
+ create_observable_property_options(property, options)
158
+ create_observable_property_accessors(property)
159
+ create_observable_property_boolean_readers(property, options)
160
+ end
161
+
162
+ # Returns the names of core observable properties for this class.
163
+ def core_observable_properties
164
+ core_observable_properties = []
165
+ observable_properties_options.each do |property, options|
166
+ core_observable_properties << property if options[:core] == true
167
+ end
168
+ core_observable_properties
169
+ end
170
+
171
+ # Returns the names of all observable properties for this class.
172
+ def observable_properties
173
+ observable_properties_options.keys
174
+ end
175
+
176
+ private
177
+ def create_observable_property_options(property, options = {})
178
+ self.observable_properties_options[property.to_sym] = prepare_options(options)
179
+ end
180
+
181
+ def create_observable_property_accessors(property)
182
+ self.class_eval <<-class_eval
183
+ def #{property}
184
+ @#{property}
185
+ end
186
+
187
+ def #{property}=(value)
188
+ old_value = get_old_value(@#{property})
189
+ if has_changed?(value, old_value)
190
+ @#{property} = ObservablePropertyProxy.new(value, self, '#{property}')
191
+ property_changed('#{property}', value, old_value)
192
+ end
193
+ end
194
+ class_eval
195
+ end
196
+
197
+ def create_observable_property_boolean_readers(property, options)
198
+ if options[:boolean]
199
+ self.class_eval <<-class_eval
200
+ def #{property}?
201
+ self.#{property} == true
202
+ end
203
+ class_eval
204
+ end
205
+ end
206
+
207
+ def prepare_options(options)
208
+ options = default_options.merge(options)
209
+ if options[:reset_value].nil? and not options[:initial_value].class.include?(ObservablePropertySupport)
210
+ options[:reset_value] = options[:initial_value]
211
+ end
212
+ options
213
+ end
214
+
215
+ def default_options
216
+ { :core => false,
217
+ :initial_value => nil,
218
+ :reset_value => nil }
219
+ end
220
+ end
221
+
222
+ def self.included(base)
223
+ base.extend(ClassMethods)
224
+ base.create_class_inheritable_attributes
225
+ end
226
+
227
+ private
228
+ def initialize_observers_if_needed
229
+ @observers = [] if not defined?(@observers) or @observers.nil?
230
+ @named_observers = {} if not defined?(@named_observers) or @named_observers.nil?
231
+ end
232
+
233
+ def get_old_value(property)
234
+ begin
235
+ return property.clone
236
+ rescue TypeError
237
+ return property
238
+ end
239
+ end
240
+
241
+ def has_changed?(new_value, old_value)
242
+ !(new_value.kind_of?(old_value.class) && old_value == new_value)
243
+ end
244
+
245
+ def clone_if_possible(value)
246
+ value.clone
247
+ rescue TypeError
248
+ value
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,77 @@
1
+ module RuGUI
2
+ module Plugin
3
+ class Loader
4
+ attr_accessor :initializer
5
+ attr_accessor :configurations
6
+ cattr_accessor :located_plugins
7
+
8
+ def initialize(initializer, configurations)
9
+ self.initializer = initializer
10
+ self.configurations = configurations
11
+ @@located_plugins ||= []
12
+ end
13
+
14
+ def load_plugins
15
+ plugins.each do |plugin|
16
+ plugin.load unless plugin.loaded?
17
+ register_as_loaded(plugin)
18
+ end
19
+ end
20
+
21
+ def plugins
22
+ @plugins ||= locate_plugins
23
+ end
24
+
25
+ protected
26
+ # Locate all plugins in APPLICATION_ROOT/vendor/plugins
27
+ def locate_plugins
28
+ Dir.glob(File.join(APPLICATION_ROOT, "vendor", "plugins", "*")).each do |dir|
29
+ @@located_plugins << Location.new(dir)
30
+ end
31
+ @@located_plugins
32
+ end
33
+
34
+ # Register plugins as loaded.
35
+ def register_as_loaded(plugin)
36
+ plugin.loaded = true
37
+ end
38
+ end
39
+
40
+ # This class is a representation of RuGUI plugins.
41
+ class Location
42
+ attr_accessor :plugin_name
43
+ attr_accessor :dir
44
+ attr_accessor :loaded
45
+
46
+ include RuGUI::LogSupport
47
+
48
+ def initialize(dir)
49
+ self.dir = dir
50
+ self.plugin_name = dir.split(File::SEPARATOR).last
51
+ end
52
+
53
+ # Load plugins.
54
+ def load
55
+ $LOAD_PATH.unshift(File.join(self.dir, "lib")) if File.directory?(self.dir)
56
+ $LOAD_PATH.uniq!
57
+
58
+ init_file = File.expand_path(File.join(self.dir, "init.rb"))
59
+ if File.exist?(init_file)
60
+ require_for init_file
61
+ else
62
+ logger.warn "The init file for (#{self.plugin_name}) was not found."
63
+ end
64
+ end
65
+
66
+ def require_for(init_file)
67
+ require init_file
68
+ rescue Exception
69
+ logger.error "An error occurred while loading #{self.plugin_name}. Checks its init file: #{$!}"
70
+ end
71
+
72
+ def loaded?
73
+ self.loaded ||= false
74
+ end
75
+ end
76
+ end
77
+ end