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,92 @@
1
+ module Reactive
2
+ module MetaModel
3
+ class Instance
4
+ def new_record?
5
+ end
6
+
7
+ def changed?
8
+ end
9
+
10
+ def mark_for_destruction
11
+ end
12
+
13
+ # Determine if this record is marked for destruction.
14
+ # Destruction will occur ???
15
+ def marked_for_destruction?
16
+ end
17
+
18
+ # Determine validness of the record.
19
+ # Note that it triggers the validations
20
+ def valid?
21
+ end
22
+
23
+ # Returns an array of error object. Empty array when valid.
24
+ def errors
25
+ end
26
+ end
27
+
28
+ class Column
29
+ attr_reader :name, :type, :primary
30
+ alias :primary? :primary
31
+ end
32
+
33
+ class Association
34
+ attr_reader :name, :kind, :klass, :polymorphic
35
+ alias :polymorphic? :polymorphic
36
+
37
+ # returns an array of column names that are handled by the association
38
+ # By default, foreign keys are in this array, counter cache and also _type columns for
39
+ # polymorphic assocations may be there too.
40
+ attr_reader :columns
41
+ end
42
+
43
+ class Model
44
+ # registers the passed object as an observer.
45
+ # the object may receive notification through these methods:
46
+ # (see ActiveRecord::Callbacks)
47
+ # Note that each of these methods will receive a unique argument which
48
+ # is the record that has been treated.
49
+ def add_observer(object)
50
+ end
51
+
52
+ # remove the object from the registered observers
53
+ def delete_observer(object)
54
+ end
55
+
56
+ def has_manys
57
+ @attributes.select {|name,value| value.is_a?(Association) && value.kind == :has_many}
58
+ end
59
+
60
+ def has_ones
61
+ @attributes.select {|name,value| value.is_a?(Association) && value.kind == :has_one}
62
+ end
63
+
64
+ def belong_tos
65
+ @attributes.select {|name,value| value.is_a?(Association) && value.kind == :belongs_to}
66
+ end
67
+
68
+ def associations
69
+ @attributes.select {|name,value| value.is_a? Association}
70
+ end
71
+
72
+ def columns
73
+ @attributes.select {|name,value| value.is_a? Column}
74
+ end
75
+
76
+ def attributes
77
+ @attributes.values
78
+ end
79
+
80
+ def attribute(name)
81
+ @attributes[name.to_sym]
82
+ end
83
+
84
+ def cooked_attributes
85
+ association_columns = associations.collect {|assoc| assoc.columns}.flatten
86
+ associations + columns.reject {|col| association_columns.include?(col.name) || col.primary? }
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,143 @@
1
+ require 'reactive-core/helper_module'
2
+
3
+ module Reactive
4
+ module OutputHandler
5
+ class Error < Reactive::Error #:nodoc:
6
+ end
7
+
8
+ class NoHandlerFound < Error
9
+ end
10
+
11
+ class Base
12
+ cattr_accessor :logger
13
+
14
+ @@output_handlers = {}
15
+
16
+ class << self
17
+ def register_output_handler(format, handler)
18
+ output_handlers(format) << handler
19
+ end
20
+
21
+ def output_handlers(format)
22
+ @@output_handlers[format] ||= []
23
+ end
24
+
25
+ def output_handler(format, name)
26
+ output_handlers(format).find {|handler| handler.handler_name == name}
27
+ end
28
+
29
+ def helper(format, mod)
30
+ helper_module(format).send(:include, mod)
31
+ end
32
+
33
+ def helper_module(format)
34
+ @@helper_modules[format] ||= Module.new
35
+ end
36
+
37
+ def process(request, response)
38
+ # a 'false' handler is used to disable output handling
39
+ if response.handler_name == false
40
+ logger.info "Handling disabled for #{request.params.inspect}"
41
+ return response.body
42
+ end
43
+
44
+ #
45
+ logger.info "Handling #{response.handler_name}/#{response.treatment} for format #{response.format}"
46
+ handler = pick_handler(response.format, response.handler_name)
47
+ raise NoHandlerFound, "Requested output handler #{response.handler_name} for format #{response.format} not available!" unless handler
48
+
49
+ #
50
+ handler_instance(handler).treat(request, response)
51
+ rescue Exception => exception
52
+ log_error(exception)
53
+ raise exception
54
+ end
55
+
56
+ protected
57
+
58
+ def handler_instance(handler)
59
+ #TODO: We may cache handler instances in the future (for better performance)
60
+ handler.new
61
+ end
62
+
63
+ def pick_handler(format, name)
64
+ # We will first search in the registered handlers (plugins use this form)
65
+ if handler = output_handler(format, name)
66
+ return handler
67
+ end
68
+
69
+ # Nothing found, let's see in the application dirs
70
+ dir = Reactive.path_for(:output_handlers, format.to_s)
71
+ if name.nil?
72
+ files = Dir.entries(dir).select {|entry| File.file? entry }
73
+ name = File.basename(files.first) if files.size == 1
74
+ else
75
+ name = "#{name}_handler"
76
+ end
77
+ require "#{dir}/#{name}"
78
+ name.constantize
79
+ rescue LoadError
80
+ nil
81
+ end
82
+
83
+ # Overwrite to implement custom logging of errors. By default logs as fatal.
84
+ def log_error(exception) #:doc:
85
+ ActiveSupport::Deprecation.silence do
86
+ if exception.respond_to? :template_error
87
+ logger.fatal(exception.to_s)
88
+ else
89
+ logger.fatal(
90
+ "\n\n#{exception.class} (#{exception.message}):\n " +
91
+ clean_backtrace(exception).join("\n ") +
92
+ "\n\n"
93
+ )
94
+ end
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ @@helper_modules = {}
101
+
102
+ class ProxyModule < Module
103
+ def initialize(receiver)
104
+ @receiver = receiver
105
+ end
106
+
107
+ def include(*args)
108
+ super(*args)
109
+ @receiver.extend(*args)
110
+ end
111
+ end
112
+
113
+ attr_reader :request, :response, :helpers
114
+
115
+ def treat(request, response)
116
+ @request, @response = request, response
117
+ setup_helpers
118
+ send(response.treatment || default_treatment)
119
+ end
120
+
121
+ def controller_name
122
+ @request.params[:controller].to_s
123
+ end
124
+
125
+ def action_name
126
+ @request.params[:action].to_s
127
+ end
128
+
129
+ def request_name
130
+ "request_#{controller_name}_#{action_name}"
131
+ end
132
+
133
+ protected
134
+
135
+ def setup_helpers
136
+ return if @helpers
137
+ @helpers = ProxyModule.new(self)
138
+ @helpers.include(self.class.helper_module(request.format))
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,27 @@
1
+ module Reactive
2
+ class Request
3
+ attr_accessor :params
4
+ attr_reader :format
5
+ attr_accessor :treatment
6
+
7
+ def initialize(params = {})
8
+ parse_params(params.dup)
9
+ end
10
+
11
+ def default_format
12
+ nil
13
+ end
14
+
15
+ def default_treatment
16
+ nil
17
+ end
18
+
19
+ protected
20
+ def parse_params(params)
21
+ @format = params.delete(:format) || default_format
22
+ @treatment = params.delete(:treatment) || default_treatment
23
+ @params = params.with_indifferent_access
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ module Reactive
2
+ class Response
3
+ attr_accessor :template, :body, :variables, :redirected_to, :handler_name, :treatment, :format
4
+
5
+ def initialize
6
+ @variables = {}
7
+ end
8
+
9
+ def clear
10
+ @body = @redirected_to = @handler_name = @treatment = @format = nil
11
+ @variables = {}
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ namespace :app do
2
+
3
+ desc "Determine if an update is available"
4
+ task :update? do
5
+ require 'rubygems/source_info_cache'
6
+ if spec = Gem::SourceInfoCache.search("^#{APP_NAME}$").last
7
+ if spec.version > Gem.loaded_specs[APP_NAME].version
8
+ puts "New version available: #{spec.version} (Current version: #{Gem.loaded_specs[APP_NAME].version})"
9
+ else
10
+ puts "Sorry, no new version available"
11
+ end
12
+ else
13
+ puts "gem #{APP_NAME} not found on gem servers"
14
+ end
15
+
16
+ end
17
+
18
+ desc "Update the application gem"
19
+ task :update do
20
+ # This tasks will be run on the deployed system.
21
+ # It should get the gem server name from a config file, then update the app gem with --no-dependencies
22
+ #
23
+ # WARNING: Between rake app:update and the following rake commands, the current directory has to be changed. Because we are in the app root directory and it may have been update.
24
+ # Thus we must CD to the new app root dir. Maybe, we should have a Rakefile in the root of /path/to/myapp that will delegate to the highest version.
25
+ puts `gem install #{APP_NAME} -i "#{APP_PATH}" --ignore-dependencies`
26
+ puts "Some gem dependencies may be missing, have a look with: rake gems"
27
+ #puts "Some gem dependencies are missing, have a look with: rake gems.\nYou may install them with: rake gems:install or rake gems:embed" unless Reactive::GemDependency.missing_gems.empty?
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ desc "List the gems that this reactive application depends on"
2
+ task :gems => 'gems:base' do
3
+ puts Reactive::GemDependency.report_gems_state(false)
4
+ end
5
+
6
+ namespace :gems do
7
+ task :base do
8
+ Rake::Task[:environment].invoke
9
+ end
10
+
11
+ desc "List the gems and its dependencies that this reactive application depends on"
12
+ task :dependencies => :base do
13
+ puts Reactive::GemDependency.report_gems_state(true)
14
+ end
15
+
16
+ desc "Installs all required gems for this application."
17
+ task :install => :base do
18
+ require 'rubygems'
19
+ require 'rubygems/gem_runner'
20
+ Reactive.configuration.gems.each { |gem| puts gem.install unless gem.installed? }
21
+ end
22
+
23
+ desc "Embed the specified gem into the application gems directory."
24
+ task :embed => :base do
25
+ require 'rubygems'
26
+ require 'rubygems/gem_runner'
27
+ Reactive.configuration.gems.each do |gem|
28
+ next unless !gem.embedded? && (ENV['GEM'].blank? || ENV['GEM'] == gem.name)
29
+ puts gem.embed
30
+ end
31
+ end
32
+
33
+ namespace :embed do
34
+ desc "Embed the specified gems and its dependencies into the application gems directory"
35
+ task :dependencies => :embed do
36
+ require 'rubygems'
37
+ require 'rubygems/gem_runner'
38
+ Reactive.configuration.gems.inject([]) do |missing, gem|
39
+ gem.dependencies.inject(missing) {|missing, dependency| dependency.embedded? ? missing : missing << dependency}
40
+ end.uniq.each {|dependency| puts dependency.embed}
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ namespace :log do
2
+ desc "Truncates all *.log files in log/ to zero bytes"
3
+ task :clear do
4
+ FileList["log/*.log"].each do |log_file|
5
+ f = File.open(log_file, "w")
6
+ f.close
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ task :default => :test
2
+ task :environment do
3
+ Reactive::Initializer.run(:init)
4
+ end
5
+
@@ -0,0 +1,18 @@
1
+ $VERBOSE = nil
2
+
3
+ # Load reactive-core rakefile extensions
4
+ Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext }
5
+
6
+ # Load reactive-dev gem if available, the tasks will be loaded by the code below (plugins)
7
+ if spec = Gem.source_index.search(Gem::Dependency.new('reactive-dev', "= #{Reactive::VERSION::STRING}")).first
8
+ Gem.activate(spec.name, "= #{spec.version}")
9
+ Dir["#{spec.full_gem_path}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
10
+ end
11
+
12
+ # Load any custom rakefile extensions
13
+ Dir[Reactive.path_for("lib/tasks/**/*.rake")].sort.each { |ext| load ext }
14
+
15
+ # Load reactive-core rakefile extensions and also the ones from the plugins
16
+ Reactive.configuration.gems.map(&:loaded_spec).compact.each do |spec|
17
+ Dir["#{spec.full_gem_path}/**/tasks/**/*.rake"].sort.each { |ext| load ext }
18
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems/source_info_cache'
2
+
3
+ module Reactive
4
+ module Updater
5
+
6
+ class Base
7
+
8
+ def current_version
9
+ Gem.loaded_specs[APP_NAME].version
10
+ end
11
+
12
+ def update_available?
13
+ (spec = Gem::SourceInfoCache.search("^#{APP_NAME}$").last) && (spec.version > current_version)
14
+ end
15
+
16
+ def last_available_version
17
+ spec = Gem::SourceInfoCache.search("^#{APP_NAME}$").last
18
+ spec && spec.version
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'reactive-core/updater/base'
2
+
3
+ module Reactive
4
+ module Updater
5
+
6
+ class Cli < Base
7
+ def initialize(args = [])
8
+ args.include?('--check') ? check_update : update
9
+ end
10
+
11
+ def check_update
12
+ if update_available?
13
+ puts "There's a new version available: #{} (Current version: #{}"
14
+ else
15
+ puts "No new version available (Current version: #{})"
16
+ end
17
+ end
18
+
19
+ def update
20
+ if update_available?
21
+ puts `gem install #{APP_NAME} -i "#{APP_PATH}" --ignore-dependencies`
22
+ exec(File.join(APP_PATH, '__FILE__')) # WARNING: TODO: How about windows? should we add .cmd suffix?
23
+ end
24
+
25
+ puts Reactive::GemDependency.report_gems_state
26
+
27
+ Reactive::GemDependency.missing_gems.each do |gem|
28
+ # gem.source = gem_source
29
+ case ask "#{gem.name} #{gem.requirement.to_s}: Install, embed or skip", "Ies"
30
+ when "i"
31
+ puts gem.install
32
+ when "e"
33
+ puts gem.embed
34
+ end
35
+ end
36
+ end
37
+
38
+ def gem_source
39
+ "http://localhost:8808"
40
+ end
41
+
42
+ def ask(question, replies)
43
+ begin
44
+ print question + " (#{replies}) ?"
45
+ reply = gets.chomp.downcase
46
+ reply = replies[/[A-Z]/] || '?' if reply.empty?
47
+ end until replies.downcase.include? reply.downcase
48
+ reply
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end