rugui 1.2.0

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.
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,171 @@
1
+ require 'Qt4'
2
+ require 'qtuitools'
3
+
4
+ module Qt
5
+ def Qt.create_application
6
+ @@application = Qt::Application.new(ARGV)
7
+ end
8
+
9
+ def Qt.application
10
+ @@application
11
+ end
12
+ end
13
+
14
+ Qt.create_application
15
+
16
+ module RuGUI
17
+ module FrameworkAdapters
18
+ module Qt4
19
+ class BaseController < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseController
20
+ def queue(&block)
21
+ block.call
22
+ end
23
+ end
24
+
25
+ class BaseMainController < RuGUI::FrameworkAdapters::Qt4::BaseController
26
+ def run
27
+ Qt.application.exec
28
+ end
29
+
30
+ def quit
31
+ Qt.application.exit
32
+ end
33
+ end
34
+
35
+ class BaseView < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseView
36
+ # Queues the block call, so that it is only gets executed in the main thread.
37
+ def queue(&block)
38
+ block.call
39
+ end
40
+
41
+ # Adds a widget to the given container widget.
42
+ def add_widget_to_container(widget, container_widget)
43
+ widget.parent = container_widget
44
+ end
45
+
46
+ # Removes a widget from the given container widget.
47
+ def remove_widget_from_container(widget, container_widget)
48
+ widget.parent = nil
49
+ end
50
+
51
+ # Removes all children from the given container widget.
52
+ def remove_all_children(container_widget)
53
+ container_widget.children.each do |child|
54
+ child.parent = nil
55
+ end
56
+ end
57
+
58
+ # Sets the widget name for the given widget if given.
59
+ def set_widget_name(widget, widget_name)
60
+ widget.object_name = widget_name
61
+ end
62
+
63
+ # Autoconnects signals handlers for the view. If +other_target+ is given
64
+ # it is used instead of the view itself.
65
+ def autoconnect_signals(view, other_target = nil)
66
+ # Qt4 doesn't provides a method for autoconnecting signals.
67
+ end
68
+
69
+ # Connects the signal from the widget to the given receiver block.
70
+ # The block is executed in the context of the receiver.
71
+ def connect_declared_signal_block(widget, signal, receiver, block)
72
+ widget.connect(SIGNAL(signal)) do |*args|
73
+ receiver.instance_exec(*args, &block)
74
+ end
75
+ end
76
+
77
+ # Connects the signal from the widget to the given receiver method.
78
+ def connect_declared_signal(widget, signal, receiver, method)
79
+ widget.connect(SIGNAL(signal)) do |*args|
80
+ receiver.send(method, *args)
81
+ end
82
+ end
83
+
84
+ # Builds widgets from the given filename, using the proper builder.
85
+ def build_widgets_from(filename)
86
+ ui_file_root_widget = load_ui_file(filename)
87
+ @view_root_widget = root_widget_from(ui_file_root_widget)
88
+ create_attributes_for_widget_and_children(@view_root_widget)
89
+ @view_root_widget.show if self.adapted_object.display_root?
90
+ end
91
+
92
+ # Registers widgets as attributes of the view class.
93
+ def register_widgets
94
+ register_widget_and_children(@view_root_widget)
95
+ end
96
+
97
+ class << self
98
+ # Returns the builder file extension to be used for this view class.
99
+ def builder_file_extension
100
+ 'ui'
101
+ end
102
+ end
103
+
104
+ private
105
+ def load_ui_file(filename)
106
+ file = Qt::File.new(filename)
107
+ file.open(Qt::File::ReadOnly)
108
+ loader = Qt::UiLoader.new
109
+ loader.load(file, nil)
110
+ end
111
+
112
+ def root_widget_from(ui_file_root_widget)
113
+ self.adapted_object.root.nil? ? ui_file_root_widget : find_child(ui_file_root_widget, self.adapted_object.root)
114
+ end
115
+
116
+ def create_attributes_for_widget_and_children(widget)
117
+ self.adapted_object.send(:create_attribute_for_widget, widget.object_name)
118
+ widget.children.each do |child|
119
+ create_attributes_for_widget_and_children(child) unless child.object_name.blank?
120
+ end
121
+ end
122
+
123
+ # Registers widgets as attributes of the view class.
124
+ def register_widget_and_children(widget)
125
+ register_widget(widget)
126
+ widget.children.each do |child|
127
+ register_widget_and_children(child)
128
+ end
129
+ end
130
+
131
+ def register_widget(widget)
132
+ unless widget.object_name.nil?
133
+ self.adapted_object.send("#{widget.object_name}=", widget)
134
+ self.adapted_object.widgets[widget.object_name] = widget
135
+ else
136
+ self.adapted_object.unnamed_widgets << widget
137
+ end
138
+ end
139
+
140
+ # XXX: Qt's find_child is not working, so we do it ourselves, this is surely not optimal.
141
+ def find_child(widget, widget_name)
142
+ for child in widget.children
143
+ if child.object_name == widget_name
144
+ return child
145
+ else
146
+ child_found = find_child(child, widget_name)
147
+ return child_found unless child_found.blank?
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ module RuGUI
157
+ class BaseView < BaseObject
158
+ # An utility method to connect Qt signals between two Qt::Object.
159
+ #
160
+ # If receiver is given, it will be used instead of the view itself.
161
+ def connect(sender, signal, slot, receiver = nil)
162
+ sender = from_widget_or_name(sender)
163
+ receiver = receiver.nil? ? self : from_widget_or_name(receiver)
164
+ if receiver.is_a?(Qt::Object)
165
+ Qt::Object.connect(sender, SIGNAL(signal), receiver, SLOT(slot))
166
+ elsif receiver.is_a?(RuGUI::BaseObject)
167
+ sender.connect(SIGNAL(signal)) { |*args| receiver.send(slot, *args) if receiver.respond_to?(slot) }
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,90 @@
1
+ module RuGUI
2
+ module FrameworkAdapters
3
+ module BaseFrameworkAdapter
4
+ class Base
5
+ attr_accessor :adapted_object
6
+
7
+ def initialize(adapted_object)
8
+ self.adapted_object = adapted_object
9
+ end
10
+ end
11
+
12
+ # Adapts the BaseController methods specific for the framework.
13
+ class BaseController < Base
14
+ # Queues the block call, so that it is only gets executed in the main thread.
15
+ def queue(&block)
16
+ end
17
+ end
18
+
19
+ # Adapts the BaseMainController methods specific for the framework.
20
+ class BaseMainController < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseController
21
+ # Runs the application, starting anything the framework needs.
22
+ def run
23
+ end
24
+
25
+ # Exits the application, freeing any resources used by the framework.
26
+ def quit
27
+ end
28
+ end
29
+
30
+ # Adapts the BaseModel methods specific for the framework
31
+ class BaseModel < Base
32
+ end
33
+
34
+ # Adapts the BaseView methods specific for the framework
35
+ class BaseView < Base
36
+ # Queues the block call, so that it is only gets executed in the main thread.
37
+ def queue(&block)
38
+ end
39
+
40
+ # Adds a widget to the given container widget.
41
+ def add_widget_to_container(widget, container_widget)
42
+ end
43
+
44
+ # Removes a widget from the given container widget.
45
+ def remove_widget_from_container(widget, container_widget)
46
+ end
47
+
48
+ # Removes all children from the given container widget.
49
+ def remove_all_children(container_widget)
50
+ end
51
+
52
+ # Sets the widget name for the given widget if given.
53
+ def set_widget_name(widget, widget_name)
54
+ end
55
+
56
+ # Autoconnects signals handlers for the view. If +other_target+ is given
57
+ # it is used instead of the view itself.
58
+ def autoconnect_signals(view, other_target = nil)
59
+ end
60
+
61
+ # Connects the signal from the widget to the given receiver block.
62
+ # The block is executed in the context of the receiver.
63
+ def connect_declared_signal_block(widget, signal, receiver, block)
64
+ end
65
+
66
+ # Connects the signal from the widget to the given receiver method.
67
+ def connect_declared_signal(widget, signal, receiver, method)
68
+ end
69
+
70
+ # Builds widgets from the given filename, using the proper builder.
71
+ def build_widgets_from(filename)
72
+ end
73
+
74
+ # Registers widgets as attributes of the view class.
75
+ def register_widgets
76
+ end
77
+
78
+ class << self
79
+ # Returns the builder file extension to be used for this view class.
80
+ def builder_file_extension
81
+ end
82
+ end
83
+ end
84
+
85
+ # Adapts the BaseViewHelper methods specific for the framework
86
+ class BaseViewHelper
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,35 @@
1
+ require 'rugui/framework_adapters/base_framework_adapter'
2
+
3
+ module RuGUI
4
+ module FrameworkAdapters
5
+ module FrameworkAdapterSupport
6
+ def framework_adapter_for(class_name)
7
+ @framework_adapter ||= {}
8
+ load_framework_adapter(class_name) unless @framework_adapter[class_name]
9
+ @framework_adapter[class_name]
10
+ end
11
+
12
+ def load_framework_adapter(class_name)
13
+ @framework_adapter[class_name] = class_adapter_for(class_name).new(self)
14
+ end
15
+
16
+ module CommonClassAndInstanceMethods
17
+ def adapter_module_name(framework_adapter = RuGUI.configuration.framework_adapter)
18
+ "RuGUI::FrameworkAdapters::#{framework_adapter.camelize}"
19
+ end
20
+
21
+ def class_adapter_for(class_name)
22
+ "#{adapter_module_name}::#{class_name}".constantize
23
+ rescue
24
+ # Fallback to the base_framework_adapter.
25
+ "#{adapter_module_name('base_framework_adapter')}::#{class_name}".constantize
26
+ end
27
+ end
28
+
29
+ def self.included(base)
30
+ base.send(:include, CommonClassAndInstanceMethods)
31
+ base.extend(CommonClassAndInstanceMethods)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'rubygems/installer'
3
+
4
+ module RuGUI
5
+
6
+ # this class hijacks the functionality of Gem::Installer by overloading its
7
+ # initializer to only provide the information needed by
8
+ # Gem::Installer#build_extensions (which happens to be what we have)
9
+ class GemBuilder < Gem::Installer
10
+
11
+ def initialize(spec, gem_dir)
12
+ @spec = spec
13
+ @gem_dir = gem_dir
14
+ end
15
+
16
+ # silence the underlying builder
17
+ def say(message)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,282 @@
1
+ require 'rugui/vendor_gem_source_index'
2
+
3
+ module Gem
4
+ def self.source_index=(index)
5
+ @@source_index = index
6
+ end
7
+ end
8
+
9
+ module RuGUI
10
+ class GemDependency < Gem::Dependency
11
+ attr_accessor :lib, :source, :dep
12
+
13
+ def self.unpacked_path
14
+ @unpacked_path ||= File.join(APPLICATION_ROOT, 'vendor', 'gems')
15
+ end
16
+
17
+ @@framework_gems = {}
18
+
19
+ def self.add_frozen_gem_path
20
+ @@paths_loaded ||= begin
21
+ source_index = RuGUI::VendorGemSourceIndex.new(Gem.source_index)
22
+ Gem.clear_paths
23
+ Gem.source_index = source_index
24
+ # loaded before us - we can't change them, so mark them
25
+ Gem.loaded_specs.each do |name, spec|
26
+ @@framework_gems[name] = spec
27
+ end
28
+ true
29
+ end
30
+ end
31
+
32
+ def initialize(name, options = {})
33
+ require 'rubygems' unless Object.const_defined?(:Gem)
34
+
35
+ if options[:requirement]
36
+ req = options[:requirement]
37
+ elsif options[:version]
38
+ req = Gem::Requirement.create(options[:version])
39
+ else
40
+ req = Gem::Requirement.default
41
+ end
42
+
43
+ @lib = options[:lib]
44
+ @source = options[:source]
45
+ @loaded = @frozen = @load_paths_added = false
46
+
47
+ super(name, req)
48
+ end
49
+
50
+ def add_load_paths
51
+ self.class.add_frozen_gem_path
52
+ return if @loaded || @load_paths_added
53
+ if framework_gem?
54
+ @load_paths_added = @loaded = @frozen = true
55
+ return
56
+ end
57
+ gem self
58
+ @spec = Gem.loaded_specs[name]
59
+ @frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec
60
+ @load_paths_added = true
61
+ rescue Gem::LoadError
62
+ end
63
+
64
+ def dependencies
65
+ return [] if framework_gem?
66
+ return [] unless installed?
67
+ specification.dependencies.reject do |dependency|
68
+ dependency.type == :development
69
+ end.map do |dependency|
70
+ GemDependency.new(dependency.name, :requirement => dependency.version_requirements)
71
+ end
72
+ end
73
+
74
+ def specification
75
+ # code repeated from Gem.activate. Find a matching spec, or the currently loaded version.
76
+ # error out if loaded version and requested version are incompatible.
77
+ @spec ||= begin
78
+ matches = Gem.source_index.search(self)
79
+ matches << @@framework_gems[name] if framework_gem?
80
+ if Gem.loaded_specs[name] then
81
+ # This gem is already loaded. If the currently loaded gem is not in the
82
+ # list of candidate gems, then we have a version conflict.
83
+ existing_spec = Gem.loaded_specs[name]
84
+ unless matches.any? { |spec| spec.version == existing_spec.version } then
85
+ raise Gem::Exception,
86
+ "can't activate #{@dep}, already activated #{existing_spec.full_name}"
87
+ end
88
+ # we're stuck with it, so change to match
89
+ version_requirements = Gem::Requirement.create("=#{existing_spec.version}")
90
+ existing_spec
91
+ else
92
+ # new load
93
+ matches.last
94
+ end
95
+ end
96
+ end
97
+
98
+ def requirement
99
+ r = version_requirements
100
+ (r == Gem::Requirement.default) ? nil : r
101
+ end
102
+
103
+ def built?
104
+ # TODO: If Rubygems ever gives us a way to detect this, we should use it
105
+ false
106
+ end
107
+
108
+ def framework_gem?
109
+ @@framework_gems.has_key?(name)
110
+ end
111
+
112
+ def frozen?
113
+ @frozen ||= vendor_rugui? || vendor_gem?
114
+ end
115
+
116
+ def installed?
117
+ Gem.loaded_specs.keys.include?(name)
118
+ end
119
+
120
+ def load_paths_added?
121
+ # always try to add load paths - even if a gem is loaded, it may not
122
+ # be a compatible version (ie random_gem 0.4 is loaded and a later spec
123
+ # needs >= 0.5 - gem 'random_gem' will catch this and error out)
124
+ @load_paths_added
125
+ end
126
+
127
+ def loaded?
128
+ @loaded ||= begin
129
+ if vendor_rugui?
130
+ true
131
+ elsif specification.nil?
132
+ false
133
+ else
134
+ # check if the gem is loaded by inspecting $"
135
+ # specification.files lists all the files contained in the gem
136
+ gem_files = specification.files
137
+ # select only the files contained in require_paths - typically in bin and lib
138
+ require_paths_regexp = Regexp.new("^(#{specification.require_paths*'|'})/")
139
+ gem_lib_files = gem_files.select { |f| require_paths_regexp.match(f) }
140
+ # chop the leading directory off - a typical file might be in
141
+ # lib/gem_name/file_name.rb, but it will be 'require'd as gem_name/file_name.rb
142
+ gem_lib_files.map! { |f| f.split('/', 2)[1] }
143
+ # if any of the files from the above list appear in $", the gem is assumed to
144
+ # have been loaded
145
+ !(gem_lib_files & $").empty?
146
+ end
147
+ end
148
+ end
149
+
150
+ def vendor_rugui?
151
+ Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.empty?
152
+ end
153
+
154
+ def vendor_gem?
155
+ specification && File.exists?(unpacked_gem_directory)
156
+ end
157
+
158
+ def build
159
+ require 'rugui/gem_builder'
160
+ unless built?
161
+ return unless File.exists?(unpacked_specification_filename)
162
+ spec = YAML::load_file(unpacked_specification_filename)
163
+ RuGUI::GemBuilder.new(spec, unpacked_gem_directory).build_extensions
164
+ puts "Built gem: '#{unpacked_gem_directory}'"
165
+ end
166
+ dependencies.each { |dep| dep.build }
167
+ end
168
+
169
+ def install
170
+ unless installed?
171
+ cmd = "#{gem_command} #{install_command.join(' ')}"
172
+ puts cmd
173
+ puts %x(#{cmd})
174
+ end
175
+ end
176
+
177
+ def load
178
+ return if @loaded || @load_paths_added == false
179
+ require(@lib || name) unless @lib == false
180
+ @loaded = true
181
+ rescue LoadError
182
+ puts $!.to_s
183
+ $!.backtrace.each { |b| puts b }
184
+ end
185
+
186
+ def refresh
187
+ RuGUI::VendorGemSourceIndex.silence_spec_warnings = true
188
+ real_gems = Gem.source_index.installed_source_index
189
+ exact_dep = Gem::Dependency.new(name, "= #{specification.version}")
190
+ matches = real_gems.search(exact_dep)
191
+ installed_spec = matches.first
192
+ if frozen?
193
+ if installed_spec
194
+ # we have a real copy
195
+ # get a fresh spec - matches should only have one element
196
+ # note that there is no reliable method to check that the loaded
197
+ # spec is the same as the copy from real_gems - Gem.activate changes
198
+ # some of the fields
199
+ real_spec = Gem::Specification.load(matches.first.loaded_from)
200
+ write_specification(real_spec)
201
+ puts "Reloaded specification for #{name} from installed gems."
202
+ else
203
+ # the gem isn't installed locally - write out our current specs
204
+ write_specification(specification)
205
+ puts "Gem #{name} not loaded locally - writing out current spec."
206
+ end
207
+ else
208
+ if framework_gem?
209
+ puts "Gem directory for #{name} not found - check if it's loading before rugui."
210
+ else
211
+ puts "Something bad is going on - gem directory not found for #{name}."
212
+ end
213
+ end
214
+ end
215
+
216
+ def unpack(options={})
217
+ unless frozen? || framework_gem?
218
+ FileUtils.mkdir_p unpack_base
219
+ Dir.chdir unpack_base do
220
+ Gem::GemRunner.new.run(unpack_command)
221
+ end
222
+ # Gem.activate changes the spec - get the original
223
+ real_spec = Gem::Specification.load(specification.loaded_from)
224
+ write_specification(real_spec)
225
+ end
226
+ dependencies.each { |dep| dep.unpack } if options[:recursive]
227
+ end
228
+
229
+ def write_specification(spec)
230
+ # copy the gem's specification into GEMDIR/.specification so that
231
+ # we can access information about the gem on deployment systems
232
+ # without having the gem installed
233
+ File.open(unpacked_specification_filename, 'w') do |file|
234
+ file.puts spec.to_yaml
235
+ end
236
+ end
237
+
238
+ def ==(other)
239
+ self.name == other.name && self.requirement == other.requirement
240
+ end
241
+ alias_method :"eql?", :"=="
242
+
243
+ private
244
+
245
+ def gem_command
246
+ case RUBY_PLATFORM
247
+ when /win32/
248
+ 'gem.bat'
249
+ when /java/
250
+ 'jruby -S gem'
251
+ else
252
+ 'gem'
253
+ end
254
+ end
255
+
256
+ def install_command
257
+ cmd = %w(install) << name
258
+ cmd << "--version" << %("#{requirement.to_s}") if requirement
259
+ cmd << "--source" << @source if @source
260
+ cmd
261
+ end
262
+
263
+ def unpack_command
264
+ cmd = %w(unpack) << name
265
+ cmd << "--version" << "= "+specification.version.to_s if requirement
266
+ cmd
267
+ end
268
+
269
+ def unpack_base
270
+ RuGUI::GemDependency.unpacked_path
271
+ end
272
+
273
+ def unpacked_gem_directory
274
+ File.join(unpack_base, specification.full_name)
275
+ end
276
+
277
+ def unpacked_specification_filename
278
+ File.join(unpacked_gem_directory, '.specification')
279
+ end
280
+
281
+ end
282
+ end
@@ -0,0 +1,36 @@
1
+ module RuGUI
2
+ # Adds before/after hooks for initialize method of a class.
3
+ module InitializeHooks
4
+ def self.included(base)
5
+ self.update_initialize_method(base)
6
+ end
7
+
8
+ def self.update_initialize_method(base)
9
+ base.class_eval <<-class_eval
10
+ alias :original_initialize :initialize
11
+
12
+ def initialize(*args)
13
+ initialize_with_hooks(*args)
14
+ end
15
+ class_eval
16
+ end
17
+
18
+ # Calls the original initialize method with before/after hooks.
19
+ def initialize_with_hooks(*args)
20
+ before_initialize
21
+ original_initialize(*args)
22
+ after_initialize
23
+ end
24
+
25
+ protected
26
+ # Called before the initialize method. Subclasses can reimplement this in
27
+ # order to have custom behavior.
28
+ def before_initialize
29
+ end
30
+
31
+ # Called after the initialize method. Subclasses can reimplement this in
32
+ # order to have custom behavior.
33
+ def after_initialize
34
+ end
35
+ end
36
+ end