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 +20 -0
- data/Manifest +30 -0
- data/README +25 -0
- data/Rakefile +19 -0
- data/lib/reactive-core.rb +8 -0
- data/lib/reactive-core/console_app.rb +9 -0
- data/lib/reactive-core/core_ext.rb +3 -0
- data/lib/reactive-core/dispatcher.rb +63 -0
- data/lib/reactive-core/errors.rb +99 -0
- data/lib/reactive-core/ext/memoizable.rb +6 -0
- data/lib/reactive-core/ext/object.rb +14 -0
- data/lib/reactive-core/ext/object_instance.rb +9 -0
- data/lib/reactive-core/ext/ordered_options.rb +1 -0
- data/lib/reactive-core/ext/rubygems_activate_patch.rb +140 -0
- data/lib/reactive-core/gem_dependency.rb +231 -0
- data/lib/reactive-core/helper_module.rb +14 -0
- data/lib/reactive-core/initializer.rb +488 -0
- data/lib/reactive-core/meta_model.rb +92 -0
- data/lib/reactive-core/output_handler.rb +143 -0
- data/lib/reactive-core/request.rb +27 -0
- data/lib/reactive-core/response.rb +14 -0
- data/lib/reactive-core/tasks/app.rake +29 -0
- data/lib/reactive-core/tasks/gems.rake +43 -0
- data/lib/reactive-core/tasks/log.rake +9 -0
- data/lib/reactive-core/tasks/misc.rake +5 -0
- data/lib/reactive-core/tasks/reactive.rb +18 -0
- data/lib/reactive-core/updater/base.rb +24 -0
- data/lib/reactive-core/updater/cli.rb +54 -0
- data/lib/reactive-core/updater/gui.rb +13 -0
- data/lib/reactive-core/version.rb +9 -0
- metadata +109 -0
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.
|
data/Manifest
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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,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,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 @@
|
|
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
|