reactive-core 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Pascal Hurni
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ LICENSE
2
+ Manifest
3
+ README
4
+ Rakefile
5
+ lib/reactive-core.rb
6
+ lib/reactive-core/console_app.rb
7
+ lib/reactive-core/core_ext.rb
8
+ lib/reactive-core/dispatcher.rb
9
+ lib/reactive-core/errors.rb
10
+ lib/reactive-core/ext/memoizable.rb
11
+ lib/reactive-core/ext/object.rb
12
+ lib/reactive-core/ext/object_instance.rb
13
+ lib/reactive-core/ext/ordered_options.rb
14
+ lib/reactive-core/ext/rubygems_activate_patch.rb
15
+ lib/reactive-core/gem_dependency.rb
16
+ lib/reactive-core/helper_module.rb
17
+ lib/reactive-core/initializer.rb
18
+ lib/reactive-core/meta_model.rb
19
+ lib/reactive-core/output_handler.rb
20
+ lib/reactive-core/request.rb
21
+ lib/reactive-core/response.rb
22
+ lib/reactive-core/tasks/app.rake
23
+ lib/reactive-core/tasks/gems.rake
24
+ lib/reactive-core/tasks/log.rake
25
+ lib/reactive-core/tasks/misc.rake
26
+ lib/reactive-core/tasks/reactive.rb
27
+ lib/reactive-core/updater/base.rb
28
+ lib/reactive-core/updater/cli.rb
29
+ lib/reactive-core/updater/gui.rb
30
+ lib/reactive-core/version.rb
data/README ADDED
@@ -0,0 +1,25 @@
1
+ == Welcome to Reactive
2
+
3
+ Reactive is a desktop application framework that gives everything
4
+ needed to create n-tiers applications which includes database-backed
5
+ applications.
6
+
7
+ The application can leverage the use of an object server made with Rails
8
+ or any other system. It may also use local machinery according to the
9
+ Model-View-Control pattern of separation.
10
+
11
+ Please visit www.ruby-reactive.org for further informations.
12
+
13
+ == Description
14
+
15
+ This is reactive-core which as its names suggest is the core system for any
16
+ reactive application.
17
+
18
+ Application developers will only be interested in the configuration and initialization
19
+ process, look in Reactive::Initializer.
20
+
21
+ Plugin developers may also be interested in either the Reactive::Dispatcher system or the
22
+ Reactive::OutputHandler.
23
+
24
+ Please get the *reactive-dev* gem for further developer information like code
25
+ generators.
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require './lib/reactive-core/version'
3
+
4
+ @spec = Gem::Specification.new do |s|
5
+ s.name = "reactive-core"
6
+ s.version = Reactive::VERSION::STRING
7
+ s.author = "Pascal Hurni"
8
+ s.email = "phi@ruby-reactive.org"
9
+ s.homepage = "http://www.ruby-reactive.org"
10
+ s.summary = "Reactive - The desktop application framework"
11
+ s.description = "reactive-core\nRuntime for reactive applications"
12
+ s.rubyforge_project = "reactive-core"
13
+ s.add_dependency 'rake', '>= 0.7.0'
14
+ s.add_dependency 'activesupport', '>= 2.0.0'
15
+ end
16
+
17
+ unless Gem.source_index.find_name('reactive-dev').empty?
18
+ require 'reactive-dev/tasks/plugin'
19
+ end
@@ -0,0 +1,8 @@
1
+ #require 'active_support'
2
+ # Plugins will simply require 'reactive', so we must provide the base classes
3
+ require 'reactive-core/core_ext'
4
+ require 'reactive-core/version'
5
+ require 'reactive-core/errors'
6
+ require 'reactive-core/request'
7
+ require 'reactive-core/response'
8
+ require 'reactive-core/meta_model'
@@ -0,0 +1,9 @@
1
+ boot_location = [
2
+ ["#{File.expand_path('.')}/config/boot.rb", "."],
3
+ ["#{File.expand_path('.')}/boot.rb", "."],
4
+ ["#{File.expand_path('.')}/../config/boot.rb", ".."],
5
+ ["#{File.expand_path('.')}/../boot.rb", ".."]
6
+ ].find{|path, root| File.file? path}
7
+
8
+ require boot_location.first
9
+ Reactive.init(:final, ['--root', File.expand_path(boot_location.last)])
@@ -0,0 +1,3 @@
1
+ require 'reactive-core/ext/object'
2
+ require 'reactive-core/ext/rubygems_activate_patch' if Gem::RubyGemsVersion < '1.4'
3
+ require 'reactive-core/ext/ordered_options'
@@ -0,0 +1,63 @@
1
+ module Reactive
2
+ module Dispatcher
3
+
4
+ class AssetNotFound < Error
5
+ end
6
+
7
+ class NoAvailableDispatcher < Error
8
+ end
9
+
10
+ class Base
11
+ cattr_accessor :logger
12
+ cattr_accessor :default_dispatcher
13
+
14
+ attr_accessor :request, :response
15
+
16
+ @@dispatchers = {}
17
+ class << self
18
+ def dispatch(request)
19
+ name, dispatcher = @@dispatchers.find {|name, item| item.candidate?(request)}
20
+ unless dispatcher ||= default_dispatcher
21
+ msg = "No dispatcher found for request #{request.inspect} (did you specify an initial_request in config.rb?)\nYou may choose a default dispatcher in config.rb with: config.default_dispatcher = :the_one"
22
+ logger.error msg
23
+ raise NoAvailableDispatcher, msg
24
+ end
25
+ dispatcher.instance_for(request).dispatch(request)
26
+ =begin
27
+ # choose the matching registered dispatcher
28
+ #TODO: the request has to pass in a "candidate" method in each registered dispatcher. The first that replies YES will be used for that
29
+ # request. So a Route mechanism may be setup up and thus the dispatcher chooser may be route oriented.
30
+ =end
31
+ end
32
+
33
+ def default_dispatcher
34
+ @@default_dispatcher.is_a?(Symbol) ? @@dispatchers[@@default_dispatcher] : @@default_dispatcher
35
+ end
36
+
37
+ def register(name, klass)
38
+ @@dispatchers[name.to_sym] = klass
39
+ end
40
+ end
41
+
42
+ def log_processing(caption = nil)
43
+ caption ||= "#{request.params.inspect}"
44
+ logger.info "\n\nDispatching #{caption} at #{Time.now.to_s(:db)}"
45
+ end
46
+
47
+ def dispatch(request)
48
+ raise NotImplementedError
49
+ end
50
+
51
+ def handle_response
52
+ if response.redirected_to
53
+ # This recursive call is intended, do not refactor in a loop. This way when a redirect cycle occurs, it will end up in a stack overflow
54
+ self.class.dispatch(Request.new(response.redirected_to))
55
+ else
56
+ # handle the response,
57
+ OutputHandler::Base.process(request, response)
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,99 @@
1
+ module Reactive
2
+ # The root class for all Reactive exceptions
3
+ class Error < StandardError
4
+ end
5
+
6
+ # For all kind of errors that may occur in eval'ed code, include this module
7
+ # This allow for a more precise diagnostic
8
+ module TemplateError
9
+ SOURCE_CODE_RADIUS = 5
10
+
11
+ attr_reader :original_exception, :file_name, :template_error # the last accessor is a duck typing check for TemplateError identification
12
+
13
+ def initialize(file_name, source_code, original_exception)
14
+ @file_name, @source_code, @original_exception = file_name, source_code, original_exception
15
+ @backtrace = compute_backtrace
16
+ end
17
+
18
+ def message
19
+ ActiveSupport::Deprecation.silence { original_exception.message }
20
+ end
21
+
22
+ def clean_backtrace
23
+ original_exception.clean_backtrace
24
+ end
25
+
26
+ def sub_template_message
27
+ if @sub_templates
28
+ "Trace of template inclusion: #{@sub_templates.join(", ")}"
29
+ else
30
+ ""
31
+ end
32
+ end
33
+
34
+ def source_extract(indentation = 0)
35
+ return unless num = line_number
36
+ num = num.to_i
37
+
38
+ source_code = @source_code.split("\n")
39
+
40
+ start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
41
+ end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
42
+
43
+ indent = ' ' * indentation
44
+ line_counter = start_on_line
45
+ return unless source_code = source_code[start_on_line..end_on_line]
46
+
47
+ source_code.sum do |line|
48
+ line_counter += 1
49
+ "#{indent}#{line_counter}: #{line}\n"
50
+ end
51
+ end
52
+
53
+ def sub_template_of(template_path)
54
+ @sub_templates ||= []
55
+ @sub_templates << template_path
56
+ end
57
+
58
+ def line_number
59
+ @line_number ||=
60
+ if file_name
61
+ regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
62
+
63
+ $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
64
+ end
65
+ end
66
+
67
+ def to_s
68
+ "\n\n#{self.class} (#{message}) #{source_location}:\n" +
69
+ "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
70
+ end
71
+
72
+ # don't do anything nontrivial here. Any raised exception from here becomes fatal
73
+ # (and can't be rescued).
74
+ def backtrace
75
+ @backtrace
76
+ end
77
+
78
+ private
79
+ def compute_backtrace
80
+ [
81
+ "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
82
+ clean_backtrace.join("\n ")
83
+ ]
84
+ end
85
+
86
+ def source_location
87
+ if line_number
88
+ "on line ##{line_number} of "
89
+ else
90
+ 'in '
91
+ end + file_name
92
+ end
93
+ end
94
+
95
+ if defined?(Exception::TraceSubstitutions)
96
+ Exception::TraceSubstitutions << [/:in\s+`_run_.*'\s*$/, '']
97
+ Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape Reactive.configuration.root_dir}/}, '']
98
+ end
99
+ end
@@ -0,0 +1,6 @@
1
+ module ActiveSupport #:nodoc: all
2
+ module Memoizable
3
+ def memoize(*args)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ class Object
2
+ # Runs the block for every object passed.
3
+ # Pass either a single object or a collection of objects (that must respond to #each)
4
+ # The block may accept a single argument or none. If it accepts one it is the object,
5
+ # if none, then the block is run in the context of the object (self is the object)
6
+ def with(*objects, &block)
7
+ if block.arity == 0
8
+ objects.each {|object| object.instance_eval(&block) }
9
+ else
10
+ objects.each {|object| yield object }
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,9 @@
1
+ class Object
2
+ if RUBY_VERSION >= '1.9'
3
+ def instance_variable_names #:nodoc:
4
+ instance_variables.map { |var| var.to_s }
5
+ end
6
+ else
7
+ alias_method :instance_variable_names, :instance_variables
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ OrderedOptions = ActiveSupport::OrderedOptions unless defined? OrderedOptions
@@ -0,0 +1,140 @@
1
+ # Monkey patch rubygems to let Gem.activate activate gems matching version requirements but not necessary the latest version
2
+
3
+ module Gem #:nodoc: all
4
+
5
+ $indent = 0
6
+ def self.puts(*args)
7
+ return nil
8
+ args.each do |item|
9
+ Kernel.puts item.to_s.gsub(/^/, " "*($indent*2))
10
+ end
11
+ end
12
+ def self.activate(gem, *version_requirements)
13
+ $indent += 1
14
+ puts ">>activating #{gem.inspect}"
15
+ _activate(gem, *version_requirements)
16
+ puts "<<activated #{gem.inspect}"
17
+ ensure
18
+ $indent -= 1
19
+ end
20
+
21
+ def self._activate(gem, *version_requirements)
22
+ if version_requirements.empty? then
23
+ version_requirements = Gem::Requirement.default
24
+ end
25
+
26
+ unless gem.respond_to?(:name) and
27
+ gem.respond_to?(:version_requirements) then
28
+ gem = Gem::Dependency.new(gem, version_requirements)
29
+ end
30
+ puts "gem #{gem.inspect}"
31
+
32
+ matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
33
+ report_activate_error(gem) if matches.empty?
34
+
35
+ if @loaded_specs[gem.name] then
36
+ # This gem is already loaded. If the currently loaded gem is not in the
37
+ # list of candidate gems, then we have a version conflict.
38
+ existing_spec = @loaded_specs[gem.name]
39
+
40
+ unless matches.any? { |spec| spec.version == existing_spec.version } then
41
+ raise Gem::Exception,
42
+ "can't activate #{gem}, already activated #{existing_spec.full_name}"
43
+ end
44
+
45
+ return false
46
+ end
47
+
48
+ unhonored = nil
49
+ spec = matches.reverse.find do |candidate|
50
+ # Job done if Gem already loaded
51
+ puts "LOADED #{candidate}" and return false if candidate.loaded?
52
+ puts "candidate #{candidate}"
53
+
54
+ candidate.loaded = true
55
+ @loaded_specs[candidate.name] = candidate
56
+
57
+ # Push the candidate to the stack
58
+ @dependency_stack ||= Array.new
59
+ @dependency_stack << candidate
60
+ mark = @dependency_stack.size - 1
61
+
62
+ # Load dependent gems first
63
+ begin
64
+ candidate.runtime_dependencies.each do |dep_gem|
65
+ activate dep_gem
66
+ end
67
+ # Clean up stack
68
+ @dependency_stack.pop
69
+ # We found a valid candidate
70
+ true
71
+ rescue Gem::Exception => e
72
+ unhonored = e.message
73
+ # Deactivate every stacked gem until mark
74
+ @dependency_stack.slice!(mark..-1).each {|spec| deactivate(spec) }
75
+ # Continue with the previous version
76
+ false
77
+ end
78
+ end
79
+ raise Gem::Exception, "can't activate #{gem}, one of its dependencies can't be honored, the last was:\n #{unhonored}\n#{matches.join("\n")}" if spec.nil?
80
+
81
+ # bin directory must come before library directories
82
+ spec.require_paths.unshift spec.bindir if spec.bindir
83
+
84
+ require_paths = spec.require_paths.map do |path|
85
+ File.join spec.full_gem_path, path
86
+ end
87
+
88
+ sitelibdir = ConfigMap[:sitelibdir]
89
+
90
+ # gem directories must come after -I and ENV['RUBYLIB']
91
+ insert_index = load_path_insert_index
92
+
93
+ if insert_index then
94
+ # gem directories must come after -I and ENV['RUBYLIB']
95
+ $LOAD_PATH.insert(insert_index, *require_paths)
96
+ else
97
+ # we are probably testing in core, -I and RUBYLIB don't apply
98
+ $LOAD_PATH.unshift(*require_paths)
99
+ end
100
+
101
+ return true
102
+ end
103
+
104
+ # deactivate a gem. This method is used internally to rollback a gem activation after a dependency mismatch.
105
+ def self.deactivate(spec)
106
+ require_paths = spec.require_paths.map do |path|
107
+ File.join spec.full_gem_path, path
108
+ end
109
+
110
+ $LOAD_PATH.reject! {|path| require_paths.include? path}
111
+
112
+ spec.loaded = false
113
+ @loaded_specs.delete(spec.name)
114
+ end
115
+
116
+ end
117
+
118
+ module Kernel #:nodoc:
119
+
120
+ def require(path) # :doc:
121
+ gem_original_require path
122
+ rescue LoadError => load_error
123
+ if load_error.message =~ /#{Regexp.escape path}\z/ and
124
+ !(specs = Gem.searcher.find_all(path)).blank? then
125
+ exception = specs.inject(nil) do |except, spec|
126
+ begin
127
+ Gem.activate(spec.name, "= #{spec.version}")
128
+ break nil
129
+ rescue Gem::Exception => e
130
+ e
131
+ end
132
+ end
133
+ raise exception if exception === Exception
134
+ gem_original_require path
135
+ else
136
+ raise load_error
137
+ end
138
+ end
139
+
140
+ end