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.
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