reactive-core 0.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.
@@ -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
+