reactive-core 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,231 @@
1
+ module Reactive
2
+ class GemDependency #:nodoc:
3
+ attr_accessor :name, :requirement, :version, :lib, :source
4
+
5
+ def initialize(name, options = {})
6
+ require 'rubygems' unless Object.const_defined?(:Gem)
7
+
8
+ options = {:version => options} if options.is_a? String
9
+
10
+ if options[:requirement]
11
+ @requirement = options[:requirement]
12
+ elsif options[:version]
13
+ @requirement = Gem::Requirement.create(options[:version])
14
+ end
15
+
16
+ @version = @requirement.instance_variable_get("@requirements").first.last if @requirement
17
+ @name = name.to_s
18
+ @lib = options[:lib]
19
+ @source = options[:source]
20
+ @init = options[:init]
21
+ @loaded = false
22
+ end
23
+
24
+ def dependencies
25
+ if spec = specification
26
+ all_dependencies = spec.dependencies.map do |dependency|
27
+ GemDependency.new(dependency.name, :requirement => dependency.version_requirements)
28
+ end
29
+ all_dependencies += all_dependencies.map(&:dependencies).flatten
30
+ all_dependencies.uniq
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+ def load
37
+ return if @loaded
38
+ args = [@name]
39
+ args << @requirement.to_s if @requirement
40
+ gem *args
41
+ # Now, run its reactive init code
42
+ init_plugin
43
+ @loaded = true
44
+ end
45
+
46
+ def init_plugin
47
+ if @init == :rails
48
+ require(@lib) if @lib
49
+ else
50
+ require(@lib || @name) unless @lib == false
51
+ end
52
+ gem_path = File.expand_path(specification.full_gem_path)
53
+ specification.require_paths.map {|path| File.join(gem_path, path) }.each do |path|
54
+ begin
55
+ require File.join(path, 'reactive', 'init.rb')
56
+ return
57
+ rescue LoadError
58
+ # No reactive init code, let's try a Rails init code
59
+ if @init == :rails
60
+ return if load_rails_init(path)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def embedded?
67
+ return true if @embedded
68
+ return false unless specification
69
+ File.expand_path(specification.installation_path) == File.expand_path(Reactive.path_for('gems'))
70
+ end
71
+
72
+ def installed?
73
+ loaded? || specification
74
+ end
75
+
76
+ def loaded?
77
+ @loaded
78
+ end
79
+
80
+ def loaded_spec
81
+ Gem.loaded_specs[@name]
82
+ end
83
+
84
+ def install
85
+ cmd = "#{gem_command} #{install_command.join(' ')}"
86
+ [cmd, %x(#{cmd})]
87
+ end
88
+
89
+ def embed
90
+ return "#{name} #{requirement} is already embedded." if embedded?
91
+ # If the gem is already installed, use Gem::Installer to embed because we only need gem files and the spec
92
+ if installed?
93
+ require 'rubygems/installer'
94
+ path = get_gem_path(name, requirement)
95
+ basename = File.basename(path).sub(/\.gem$/, '')
96
+ installer = Gem::Installer.new(path, :install_dir => Reactive.path_for('gems'))
97
+ installer.unpack Reactive.path_for('gems', 'gems', basename)
98
+ installer.write_spec
99
+ @embedded = true
100
+ "Successfully embedded #{name} #{installer.spec.version}"
101
+ else
102
+ cmd = "#{gem_command} #{install_command.join(' ')} -i #{Reactive.path_for('gems')} --ignore-dependencies"
103
+ [cmd, %x(#{cmd})]
104
+ end
105
+ end
106
+
107
+ def ==(other)
108
+ self.name == other.name && self.requirement == other.requirement
109
+ end
110
+ alias :eql? :==
111
+ def hash
112
+ "#{name}#{requirement}".hash
113
+ end
114
+
115
+ protected
116
+
117
+ def specification
118
+ @spec ||= Gem.source_index.search(Gem::Dependency.new(@name, @requirement)).sort_by { |s| s.version }.last
119
+ end
120
+
121
+ def gem_command
122
+ RUBY_PLATFORM =~ /win32/ ? 'gem.bat' : 'gem'
123
+ end
124
+
125
+ def install_command
126
+ cmd = %w(install) << @name
127
+ cmd << "--version" << %("#{@requirement.to_s}") if @requirement
128
+ cmd << "--source" << @source if @source
129
+ cmd
130
+ end
131
+
132
+ # >> taken from rubygems/commands/unpack_command.rb <<
133
+ # Return the full path to the cached gem file matching the given
134
+ # name and version requirement. Returns 'nil' if no match.
135
+ #
136
+ # Example:
137
+ #
138
+ # get_path('rake', '> 0.4') # -> '/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem'
139
+ # get_path('rake', '< 0.1') # -> nil
140
+ # get_path('rak') # -> nil (exact name required)
141
+ #--
142
+ # TODO: This should be refactored so that it's a general service. I don't
143
+ # think any of our existing classes are the right place though. Just maybe
144
+ # 'Cache'?
145
+ #
146
+ # TODO: It just uses Gem.dir for now. What's an easy way to get the list of
147
+ # source directories?
148
+ def get_gem_path(gemname, version_req)
149
+ return gemname if gemname =~ /\.gem$/i
150
+
151
+ specs = Gem::source_index.search(/\A#{gemname}\z/, version_req)
152
+ selected = specs.sort_by { |s| s.version }.last
153
+ return nil if selected.nil?
154
+
155
+ # We expect to find (basename).gem in the 'cache' directory.
156
+ # Furthermore, the name match must be exact (ignoring case).
157
+ if gemname =~ /^#{selected.name}$/i
158
+ filename = selected.full_name + '.gem'
159
+ path = nil
160
+
161
+ Gem.path.find do |gem_dir|
162
+ path = File.join gem_dir, 'cache', filename
163
+ File.exist? path
164
+ end
165
+
166
+ path
167
+ end
168
+ end
169
+
170
+ def load_rails_init(path)
171
+ # define_rails_equivalence
172
+ if init_file = [File.join(path, 'rails', 'init.rb'), File.join(specification.full_gem_path, 'init.rb')].find {|file| File.file? file}
173
+ Reactive::Initializer.register("init_rails_plugin_#{specification.name}", :init_rails_plugin) do
174
+ Kernel.load(init_file)
175
+ end
176
+ end
177
+ rescue LoadError
178
+ # Don't care, not having init code is legal (at least in reactive).
179
+ false
180
+ end
181
+
182
+ =begin
183
+ @@rails_equivalence_set = false
184
+ def define_rails_equivalence
185
+ return if @@rails_equivalence_set
186
+ @@rails_equivalence_set = true
187
+ puts "defininf rials qeui"
188
+ Reactive::Initializer.configure :rails_equivalence do
189
+ puts "rails equi"
190
+ self.class.module_eval <<-EOC
191
+ module ::Rails
192
+ Generator = RubiGen
193
+ end
194
+ EOC
195
+ end
196
+ end
197
+ =end
198
+
199
+ public
200
+
201
+ def self.report_gems_state(with_dependencies = true)
202
+ Reactive.configuration.gems.inject('') do |report, gem|
203
+ installed_dependency_count = 0
204
+ dependencies_status = gem.dependencies.collect do |dependency|
205
+ if dependency.installed?
206
+ code = dependency.embedded? ? "E" : "I"
207
+ installed_dependency_count += 1
208
+ else
209
+ code = " "
210
+ end
211
+ " [#{code}] #{dependency.name} #{dependency.requirement.to_s}\n"
212
+ end.uniq
213
+
214
+ code = gem.installed? ? (gem.embedded? ? "E" : "I") : " "
215
+ code.downcase! if installed_dependency_count != gem.dependencies.size
216
+ report << "[#{code}] #{gem.name} #{gem.requirement.to_s}\n"
217
+ report << dependencies_status.join('') if with_dependencies
218
+ report
219
+ end << "I = Installed, i = Installed but missing dependencies\nE = Embedded, e = Embedded but missing dependencies"
220
+ end
221
+
222
+ # Returns an Array of missing gems
223
+ def self.missing_gems
224
+ Reactive.configuration.gems.inject([]) do |missing, gem|
225
+ missing << gem unless gem.installed?
226
+ gem.dependencies.inject(missing) {|missing, dependency| dependency.installed? ? missing : missing << dependency}
227
+ end.uniq
228
+ end
229
+
230
+ end
231
+ end
@@ -0,0 +1,14 @@
1
+ class HelperModule < Module
2
+ def initialize(names, code)
3
+ self.class.send(:define_method, :helpers) do
4
+ # reverse because this array will be passed to #extend which put precedence in the first passed module
5
+ helpers = names.collect {|name| const_get(name.split('.').first.classify) }.reverse
6
+ #constants doesn't preserve the order in which they are declared! #helpers = constants.collect{|name| const_get(name)}.select{|const| const.is_a? Module}
7
+ # We provide ourself (which is an empty module) if no nested modules are defined because calling #extend with no argument isn't legal.
8
+ helpers.empty? ? self : helpers
9
+ end
10
+ @name = names.join('_')
11
+ class_eval(code)
12
+ end
13
+ attr_reader :name
14
+ end
@@ -0,0 +1,488 @@
1
+ require 'activesupport'
2
+ require 'singleton'
3
+ require 'reactive-core/core_ext'
4
+ require 'reactive-core/version'
5
+ require 'reactive-core/gem_dependency'
6
+
7
+ module Reactive
8
+ class << self
9
+ # The reactive configuration used for the application.
10
+ attr_accessor :configuration
11
+
12
+ # The logger instance used by the framework. Plugins may copy this reference,
13
+ # thus changing it lately (after init time) may not have the desired effect.
14
+ attr_accessor :logger
15
+
16
+ # Returns the Reactive version as a String
17
+ def version
18
+ VERSION::STRING
19
+ end
20
+
21
+ # Returns the directory for the type passed.
22
+ # Returns nil if no directory is configured for the passed type.
23
+ #
24
+ # Examples:
25
+ # dir_for(:config) => "/home/lambda/mysales/config"
26
+ # dir_for(:views) => "/home/lambda/mysales/app/views"
27
+ def dir_for(type)
28
+ dirs_for(type).first
29
+ end
30
+
31
+ # Returns an array of directories for the type passed.
32
+ # Returns an empty array if no directory is configured for the passed type.
33
+ #
34
+ # Examples:
35
+ # dirs_for(:config) => ["/home/lambda/mysales/config"]
36
+ # dirs_for(:views) => ["/home/lambda/mysales/app/views", "/home/lambda/mysales/app/special_views"]
37
+ def dirs_for(type)
38
+ [configuration.paths[type]].flatten.compact
39
+ end
40
+
41
+ # Constructs an absolute path with parts relative to the application root or relative
42
+ # to a typed directory.
43
+ #
44
+ # Raises an ArgumentError if no directory is configured for the passed type.
45
+ #
46
+ # Examples:
47
+ # path_for("log", "production.log") # => "/home/lambda/mysales/log/production.log"
48
+ # path_for(:views, "main", "run.rb") # => /home/lambda/mysales/app/views/main/run.rb"
49
+ def path_for(*parts)
50
+ root_dir = parts.first.is_a?(Symbol) ? dir_for(path_type = parts.shift) : configuration.root_dir
51
+ raise ArgumentError, defined?(path_type) ? "No defined path for #{path_type}" : "No root dir defined!" unless root_dir
52
+ File.join(root_dir, *parts)
53
+ end
54
+
55
+ # Returns the complete pathname for a given file (the last argument) in a given path (other parts) specified with a similar form than #path_for.
56
+ # You may also pass wildcards as defined in Dir#glob, when this is the case, the first matching file is returned.
57
+ def file_for(*parts)
58
+ files_for(*parts).first
59
+ end
60
+
61
+ # Returns the complete pathnames for a given glob path relative to the application root, see #path_for for more information.
62
+ def files_for(*parts)
63
+ root_dirs = parts.first.is_a?(Symbol) ? dirs_for(path_type = parts.shift) : [configuration.root_dir]
64
+ raise ArgumentError, defined?(path_type) ? "No defined path for #{path_type}" : "No root dir defined!" if root_dirs.empty?
65
+ root_dirs.each do |dir|
66
+ pathname = File.join(dir, *parts)
67
+ filenames = Dir.glob(pathname).select {|item| File.file? item}
68
+ return filenames unless filenames.empty?
69
+ end
70
+ []
71
+ end
72
+
73
+ # Returns the complete pathname for a given file (the last argument) in a given path (other parts) specified with a similar form than #path_for.
74
+ # Raises an Errno::ENOENT exception if none matched.
75
+ def file_for!(*parts)
76
+ file_for(*parts) or raise Errno::ENOENT
77
+ end
78
+
79
+ # Constructs a relative path (to the application root) with parts relative to the application root or relative
80
+ # to a typed directory.
81
+ #
82
+ # Examples:
83
+ # relative_path_for("log", "production.log") # => "log/production.log"
84
+ # relative_path_for(:views, "main", "run.rb") # => "app/views/main/run.rb"
85
+ def relative_path_for(*parts)
86
+ path_for(*parts).sub("#{configuration.root_dir}/", '')
87
+ end
88
+ end
89
+
90
+ self.logger = ActiveSupport::BufferedLogger.new(STDERR)
91
+ self.logger.level = (ENV['DEBUG'] || $DEBUG) ? ActiveSupport::BufferedLogger::DEBUG : ActiveSupport::BufferedLogger::WARN
92
+
93
+ #
94
+ # Holds the configuration of the framework and plugins.
95
+ #
96
+ # The configuration object is a hash-like object with access to values with
97
+ # accessors. So these are equivalent:
98
+ # config[:use_donuts] = true
99
+ # config.use_donuts = true
100
+ #
101
+ #
102
+ # It is populated by the framework itself, plugins and also the application through
103
+ # the config/config.rb file and its specialized config/environments/*.rb forms.
104
+ class Configuration < OrderedOptions
105
+ def initialize(*args)
106
+ super()
107
+ self[:paths] = {}
108
+ self[:gems] = []
109
+ parse(args.first) if args.first
110
+ end
111
+
112
+ # Merges the passed options with the configuration.
113
+ # Pass a hash-like object for the options. You may either pass a block which
114
+ # will be yielded with a fresh configuration object that will be merged after
115
+ # the end of the block.
116
+ def merge(options = nil, &block)
117
+ raise ArgumentError, "Pass either an options object or a block, not both!" unless options.nil? ^ block.nil?
118
+ block.call(options = Configuration.new) if block
119
+ options.each {|key, value| self[key] = value }
120
+
121
+ # special handling for paths and gems
122
+ (options.paths || {}).each {|type, path| self[:paths][type] = path }
123
+ (options.gems || []).each {|gem| self[:gems] << gem }
124
+ end
125
+
126
+ # Reverse merge the passed options with the configuration. See #merge for details about the arguments.
127
+ # A reverse merge is a non destructive merge, that is if a value already exists in the current configuration
128
+ # it has precedence over the one you pass in the arguments.
129
+ def reverse_merge(options = nil, &block)
130
+ raise ArgumentError, "Pass either an options object or a block, not both!" unless options.nil? ^ block.nil?
131
+ block.call(options = Configuration.new) if block
132
+ the_keys = self.keys
133
+ options.each {|key, value| self[key] = value unless the_keys.include?(key) }
134
+
135
+ # special handling for paths and gems
136
+ (options.paths || {}).each {|type, path| self[:paths][type] = path unless self[:paths][type] }
137
+ (options.gems || []).each {|gem| self[:gems] << gem }
138
+ end
139
+
140
+ # Adds a single Gem dependency to the reactive application.
141
+ # You may pass :init => :rails to specifiy that the plugin should
142
+ # be initialized through the rails init code. Note that it is not
143
+ # assured to work.
144
+ #
145
+ # config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0'
146
+ # config.gem 'acts_as_tree', :init => :rails
147
+ #
148
+ def gem(name, options = {})
149
+ self[:gems] << Reactive::GemDependency.new(name, options)
150
+
151
+ name = name.gsub(/\W/, '_').squeeze('_')
152
+ self[name] = OrderedOptions.new
153
+ end
154
+
155
+ def app_gem_spec(spec = nil, &block) # :nodoc:
156
+ @app_gem_spec = block if block
157
+ if @app_gem_spec
158
+ if spec
159
+ @app_gem_spec.call(spec)
160
+ else
161
+ options = OrderedOptions.new
162
+ @app_gem_spec.call(options)
163
+ options
164
+ end
165
+ end
166
+ end
167
+
168
+ # Defines the application root. Normally the boot process will take care of it.
169
+ def root_dir=(value) # :nodoc:
170
+ self[:root_dir] = File.expand_path(value)
171
+ end
172
+
173
+ protected
174
+
175
+ def parse(argv)
176
+ if index = argv.rindex(argv.find {|arg| (arg == '-e') || (arg == '--environment')})
177
+ self.environment = argv[index+1]
178
+ end
179
+ if index = argv.rindex(argv.find {|arg| arg == '--root'})
180
+ self.root_dir = argv[index+1]
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ # Handles Reactive initialization process.
187
+ #
188
+ # The initialization process is sliced in short parts named stages.
189
+ # Each stage is responsible for a specific initialization. Plugins will also
190
+ # register stages in this system.
191
+ #
192
+ # Running the application is a three steps process:
193
+ # 1. Booting: loads the reactive-core gem (see config/boot.rb)
194
+ # 2. Initialization (the #run method is called)
195
+ # 3. Dispatching the initial request (as configured in config/config.rb)
196
+ #
197
+ # The application developer may also register initialization stages by example
198
+ # in the files under config/initializers/*.rb like so:
199
+ # Reactive::Initializer.init :donuts do
200
+ # require 'donuts'
201
+ # Donuts.setup_factory
202
+ # end
203
+ #
204
+ # Registering stages is done with the general #register method or the more conveniant
205
+ # methods: #configure, #before_init, #init and #after_init.
206
+ #
207
+ # The application or any plugin may observe the initilization process by registering an
208
+ # observer with #add_observer. It acts like a callback triggered before processing each
209
+ # stage.
210
+ #
211
+ # The stages are left visible with the #stages accessor, but be careful with it.
212
+ class Initializer
213
+ STAGE_LEVELS = {:configure => -400, :before_init => -200, :init => 0, :init_rails_plugin => 100, :after_init => 200}
214
+
215
+ include Singleton
216
+
217
+ class << self
218
+ # Registers an initialization stage. Pass a _name_, then either a level or an option to specify
219
+ # the relation against another stage.
220
+ # register(:mvc_configure, -100) { some_code }
221
+ # register(:mvc_configure, :before => :init_logger) { some_code }
222
+ # register(:mvc_configure, :after => :wx_configure, :proc => a_proc)
223
+ def register(name, options = {}, &block)
224
+ Initializer.instance.register(name, options, &block)
225
+ end
226
+
227
+ # convenience aliases for standard priorities
228
+ def configure(name, options = {}, &block)
229
+ name = "configure_#{name}" unless name.to_s =~ /^configure_/
230
+ register(name, :configure, &block)
231
+ end
232
+
233
+ def before_init(name, options = {}, &block)
234
+ name = "before_init_#{name}" unless name.to_s =~ /^before_init_/
235
+ register(name, :before_init, &block)
236
+ end
237
+
238
+ def init(name, options = {}, &block)
239
+ name = "init_#{name}" unless name.to_s =~ /^init_/
240
+ register(name, :init, &block)
241
+ end
242
+
243
+ def after_init(name, options = {}, &block)
244
+ name = "after_init_#{name}" unless name.to_s =~ /^after_init_/
245
+ register(name, :after_init, &block)
246
+ end
247
+
248
+ # Runs the initialization process.
249
+ #
250
+ # _stage_ is either a level which will also be processed or
251
+ # a stage name that will also be processed.
252
+ def run(stage = :final, configuration = Reactive.configuration || Configuration.new) # :yields: configuration
253
+ yield configuration if block_given?
254
+ Reactive.configuration = configuration
255
+ Initializer.instance.run_stages(stage)
256
+ end
257
+
258
+ def add_observer(proc = nil, &block)
259
+ raise ArgumentError, "Pass either a proc or a block, not both!" unless proc.nil? ^ block.nil?
260
+ raise ArgumentError, "Passed object is not callable!" if proc && !proc.respond_to?(:call)
261
+ Initializer.instance.observers << (proc || block)
262
+ end
263
+
264
+ def stages
265
+ Initializer.instance.stages
266
+ end
267
+
268
+ end
269
+
270
+ class Stage
271
+ attr_accessor :name, :level, :proc
272
+ def initialize(name, level, proc)
273
+ @name, @level, @proc = name, level, proc
274
+ end
275
+ # Checks equivalence based on #name.
276
+ def ==(other)
277
+ other.is_a?(Stage) ? @name == other.name : @name == other
278
+ end
279
+ end
280
+
281
+ attr_reader :stages, :observers # :nodoc:
282
+
283
+ def initialize # :nodoc:
284
+ @stages = []
285
+ @finished = []
286
+ @observers = []
287
+ end
288
+
289
+ def register(name, options, &block) # :nodoc:
290
+ options = options.is_a?(Hash) ? options : {:level => STAGE_LEVELS[options] || options}
291
+ proc = options[:proc]
292
+ raise ArgumentError, "Pass either a proc or a block, not both!" unless proc.nil? ^ block.nil?
293
+ raise ArgumentError, "Proc object is not callable!" if proc && !proc.respond_to?(:call)
294
+ block ||= proc
295
+ # raise ArgumentError, "register options are mutually exclusive!" if options[:level]
296
+ case
297
+ when level = options[:level]
298
+ insert_index = stages.index(stages.find {|item| item.level > level}) || -1
299
+ when before = options[:before]
300
+ insert_index = stages.index(stages.find{|item| item.name == before.to_sym})
301
+ raise ArgumentError, "Can't insert before '#{before}', no stage of that name!" unless insert_index
302
+ items = [insert_index-1 < 0 ? nil : insert_index-1, insert_index].compact
303
+ level = stages.values_at(*items).inject {|sum, stage| sum + stage.level} / items.size
304
+ when after = options[:after]
305
+ insert_index = stages.index(stages.find{|item| item.name == after.to_sym})
306
+ raise ArgumentError, "Can't insert after '#{after}', no stage of that name!" unless insert_index
307
+ items = stages.values_at([index, index+1]).compact
308
+ level = items.inject {|sum, stage| sum + stage.level} / items.size
309
+ end
310
+
311
+ stages.insert(insert_index, Stage.new(name, level, block))
312
+ end
313
+
314
+ # last_stage is either a level which WILL also be processed or
315
+ # a stage name that WILL also be processed.
316
+ def run_stages(last_stage) # :nodoc:
317
+ last_stage = STAGE_LEVELS[last_stage] || last_stage
318
+ stage = stages.first
319
+ while stage
320
+ break if last_stage.is_a?(Integer) && stage.level > last_stage
321
+ unless @finished.include? stage
322
+ observers.each {|block| block.call(stage) }
323
+ benchlog("Stage: #{stage.name}") { stage.proc.call }
324
+ @finished << stage
325
+ break if stage.name == last_stage
326
+ end
327
+ stage = stages[stages.index(stage).succ]
328
+ end
329
+ end
330
+
331
+ protected
332
+
333
+ def benchlog(caption) # :nodoc:
334
+ @bench_level ||= 0
335
+ time = Time.now.to_f
336
+ Reactive.logger.debug "#{' '*@bench_level}#{caption}" if @bench_level > 0
337
+ @bench_level += 2
338
+ yield if block_given?
339
+ seconds = Time.now.to_f - time
340
+ @bench_level -= 2
341
+ if @bench_level > 0
342
+ Reactive.logger.debug "#{' '*@bench_level}(#{'%.1f' % (seconds * 1000)}ms)"
343
+ else
344
+ Reactive.logger.debug "#{caption} (#{'%.1f' % (seconds * 1000)}ms)"
345
+ end
346
+ end
347
+
348
+ # Here is the list of all initializers setup up by the framework. Listed in level order.
349
+ # *patch_base_gems*:: -1000,
350
+ # *default_environment*:: -950,
351
+ # *default_config*:: -760,
352
+ # *load_config*:: -750
353
+ # *load_initializers*:: -740
354
+ # *require_plugins*:: -550
355
+ # *require_framework*:: -360
356
+ # *check_plugins*:: -350
357
+ # *init_logger*:: -160
358
+ # *init_depedency_mechanism*:: -150
359
+ # *init_framwork*:: -140
360
+ # *finish*:: +1000
361
+ class Default < Initializer
362
+ register :patch_base_gems, -1000 do
363
+ # Patch missing activesupport features
364
+ ActiveSupport.const_set(:Dependencies, ::Dependencies) unless defined? ActiveSupport::Dependencies
365
+ require 'reactive-core/ext/object_instance' unless Object.respond_to? :instance_variable_names
366
+ require 'reactive-core/ext/memoizable' unless defined? ActiveSupport::Memoizable
367
+ end
368
+
369
+ register :default_environment, -950 do
370
+ unless Reactive.configuration.environment
371
+ Reactive.configuration.environment = Gem.source_index.find_name('reactive-dev').empty? ? 'production' : 'development'
372
+ end
373
+ if Reactive.configuration.environment == 'development'
374
+ # gem 'reactive-dev', core-gem-version
375
+ require 'reactive-dev'
376
+ end
377
+ end
378
+
379
+ register :default_config, -760 do
380
+ Reactive.configuration.reverse_merge do |config|
381
+ config.paths = {
382
+ :config => Reactive.path_for("config"),
383
+ :log => Reactive.path_for("log"),
384
+ :assets => [Reactive.path_for("assets", "default")]
385
+ }
386
+ end
387
+ Reactive.configuration.reverse_merge do |config|
388
+ config.global_configfile = Reactive.path_for(:config, "config.rb")
389
+ config.environment_configfile = Reactive.path_for(:config, "environments", "#{Reactive.configuration.environment}.rb")
390
+
391
+ config.log_path = Reactive.path_for(:log, "#{Reactive.configuration.environment}.log")
392
+ config.log_level = Reactive.configuration.environment == 'production' ? :warn : :info
393
+
394
+ config.dependency_mechanism = :load
395
+ config.cache_classes = false
396
+
397
+ # stubs for setting options on the framework base classes
398
+ config.dispatcher = OrderedOptions.new
399
+ config.output_handler = OrderedOptions.new
400
+ end
401
+ end
402
+
403
+ # Loads the environment configuration specified by Configuration#global_configfile and #environment_configfile
404
+ # which is typically one of development, test, or production.
405
+ register :load_config, -750 do
406
+ load(Reactive.configuration.global_configfile) if File.exists?(Reactive.configuration.global_configfile)
407
+ load(Reactive.configuration.environment_configfile) if File.exists?(Reactive.configuration.environment_configfile)
408
+ end
409
+
410
+ register :load_initializers, -740 do
411
+ Dir[Reactive.path_for(:config, "initializers/**/*.rb")].sort.each do |initializer|
412
+ load(initializer)
413
+ end
414
+ end
415
+
416
+ # load all plugins gems listed in configurations.
417
+ register :require_plugins, -550 do
418
+ Reactive.configuration.gems.each do |gem|
419
+ begin
420
+ Reactive.logger.info "Loading gem #{gem.name} #{gem.requirement}"
421
+ gem.load
422
+ rescue LoadError
423
+ end
424
+ end
425
+ end
426
+
427
+ register :require_framework, -360 do
428
+ require 'reactive-core/errors'
429
+ require 'reactive-core/request'
430
+ require 'reactive-core/response'
431
+ require 'reactive-core/dispatcher'
432
+ require 'reactive-core/output_handler'
433
+ require 'reactive-core/meta_model'
434
+ end
435
+
436
+ # Abort execution if any of the configured plugins isn't loaded
437
+ register :check_plugins, -350 do
438
+ unloaded_gems = Reactive.configuration.gems.reject {|gem| gem.loaded? }
439
+ unless unloaded_gems.empty?
440
+ message = <<-EOS
441
+ Missing these required gems:
442
+ #{unloaded_gems.map {|gem| "#{gem.name} #{gem.requirement}"}.join("\n ")}
443
+ Run `rake gems:install` to install the missing gems.
444
+ EOS
445
+ Reactive.logger.fatal message
446
+ raise LoadError, message
447
+ end
448
+ end
449
+
450
+ register :init_logger, -160 do
451
+ begin
452
+ unless logger = Reactive.configuration.logger
453
+ Reactive.logger.info "Further logs should go to #{Reactive.configuration.log_path}"
454
+ logger = ActiveSupport::BufferedLogger.new(Reactive.configuration.log_path)
455
+ logger.level = Reactive.configuration.log_level.is_a?(Integer) ? Reactive.configuration.log_level : ActiveSupport::BufferedLogger.const_get(Reactive.configuration.log_level.to_s.upcase)
456
+ logger.auto_flushing = false if Reactive.configuration.environment == "production"
457
+ end
458
+ Reactive.logger = logger
459
+ rescue StandardError => e
460
+ Reactive.logger.warn "Reactive Error: Unable to access log file. Please ensure that #{Reactive.configuration.log_path} exists and is chmod 0666."
461
+ ensure
462
+ Reactive.logger.unknown "\n\nApplication initialized at #{Time.now.to_s(:db)}"
463
+ end
464
+ end
465
+
466
+ register :init_depedency_mechanism, -150 do
467
+ ActiveSupport::Dependencies.mechanism = Reactive.configuration.cache_classes ? :require : :load
468
+ end
469
+
470
+ register :init_framework, -140 do
471
+ [:dispatcher, :output_handler].each do |framework|
472
+ base_class = Reactive.const_get(framework.to_s.classify)::Base
473
+ base_class.logger = Reactive.logger
474
+ Reactive.configuration.send(framework).each do |setting, value|
475
+ base_class.send("#{setting}=", value)
476
+ end
477
+ end
478
+ end
479
+
480
+ register :finish, +1000 do
481
+ end
482
+ end
483
+
484
+ end
485
+
486
+
487
+ end
488
+